Backed out 10 changesets (bug 1234629, bug 1233882) for bustage on Android rc tests
authorNigel Babu <nigelbabu@gmail.com>
Fri, 05 Feb 2016 15:42:28 +0530
changeset 321417 1836c1f9ef198fda8e0cb76e314769191ee4a5d0
parent 321416 20a5e16ef24ed977ecb3084427cab480231882f2
child 321418 3b7757168119020d95e11d6484f7efb8e949adad
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1234629, 1233882
milestone47.0a1
backs out9aa32d9a25df32ef740835d14836ff20db0a0cc4
d73d36c9619bed640e6a756746b81ece5ef492cb
a7f63b3721cd3ba105990bbb37a87044383d26d9
c79f4a8c3e86aa0c1c693f637b9d5ac053a6fd2f
400c030d3c01d8818fe07d4547e0c145e19f099e
54a3d0851bc60d26fb4744c605787031070052c8
f80912ecfa87584394d22e28ed09e68c182d1b35
50bcadca213183aaa64e39632892b8f00957dcfc
aaa420ed66d754ecc17b19f5a12297d24371f1ca
7a62e97f07c2c90d9f34c2c7de40f3cb193f6312
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 10 changesets (bug 1234629, bug 1233882) for bustage on Android rc tests Backed out changeset 9aa32d9a25df (bug 1234629) Backed out changeset d73d36c9619b (bug 1234629) Backed out changeset a7f63b3721cd (bug 1233882) Backed out changeset c79f4a8c3e86 (bug 1234629) Backed out changeset 400c030d3c01 (bug 1234629) Backed out changeset 54a3d0851bc6 (bug 1234629) Backed out changeset f80912ecfa87 (bug 1234629) Backed out changeset 50bcadca2131 (bug 1234629) Backed out changeset aaa420ed66d7 (bug 1234629) Backed out changeset 7a62e97f07c2 (bug 1234629)
config/makefiles/java-build.mk
configure.in
mobile/android/app/base/build.gradle
mobile/android/app/base/lint.xml
mobile/android/app/build.gradle
mobile/android/app/lint.xml
mobile/android/app/src/main/resources/robolectric.properties
mobile/android/app/src/test/java/org/mozilla/gecko/TestGeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/distribution/Distribution.java
mobile/android/base/moz.build
mobile/android/bouncer/AndroidManifest.xml.in
mobile/android/bouncer/Makefile.in
mobile/android/bouncer/assets/example_asset.txt
mobile/android/bouncer/build.gradle
mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java
mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java
mobile/android/bouncer/moz.build
mobile/android/bouncer/res/drawable-v21/logo.xml
mobile/android/bouncer/res/drawable/logo.xml
mobile/android/confvars.sh
mobile/android/docs/bouncer.rst
mobile/android/docs/defaultdomains.rst
mobile/android/docs/gradle.rst
mobile/android/docs/index.rst
mobile/android/moz.build
mobile/android/tests/background/junit4/resources/robolectric.properties
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java
settings.gradle
toolkit/mozapps/installer/upload-files.mk
--- a/config/makefiles/java-build.mk
+++ b/config/makefiles/java-build.mk
@@ -74,17 +74,16 @@ android_res_files := $(wildcard $(addsuf
 )
 
 # aapt flag -m: 'make package directories under location specified by -J'.
 # The --extra-package list is colon separated.
 .aapt.deps: $(android_manifest) $(android_res_files) $(wildcard $(ANDROID_ASSETS_DIRS))
 	@$(TOUCH) $@
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar $(_ANDROID_RES_FLAG) $(_ANDROID_ASSETS_FLAG) \
 		--custom-package $(ANDROID_APK_PACKAGE) \
-		--rename-manifest-package $(ANDROID_APK_PACKAGE) \
 		--non-constant-id \
 		--auto-add-overlay \
 		$(if $(ANDROID_EXTRA_PACKAGES),--extra-packages $(subst $(NULL) ,:,$(strip $(ANDROID_EXTRA_PACKAGES)))) \
 		$(if $(ANDROID_EXTRA_RES_DIRS),$(addprefix -S ,$(ANDROID_EXTRA_RES_DIRS))) \
 		-m \
 		-J ${@D}/generated \
 		-F $(ANDROID_APK_NAME).ap_
 
--- a/configure.in
+++ b/configure.in
@@ -8535,17 +8535,16 @@ AC_SUBST(MOZ_D3DCOMPILER_XP_CAB)
 AC_SUBST(MOZ_ANDROID_HISTORY)
 AC_SUBST(MOZ_WEBSMS_BACKEND)
 AC_SUBST(MOZ_ANDROID_BEAM)
 AC_SUBST(MOZ_LOCALE_SWITCHER)
 AC_SUBST(MOZ_DISABLE_GECKOVIEW)
 AC_SUBST(MOZ_ANDROID_GCM)
 AC_SUBST(MOZ_ANDROID_GECKOLIBS_AAR)
 AC_SUBST(MOZ_ANDROID_SEARCH_ACTIVITY)
-AC_SUBST(MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER)
 AC_SUBST(MOZ_ANDROID_MLS_STUMBLER)
 AC_SUBST(MOZ_ANDROID_DOWNLOADS_INTEGRATION)
 AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
 AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE)
 AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
 AC_SUBST(MOZ_INSTALL_TRACKING)
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/base/build.gradle
@@ -0,0 +1,142 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/base"
+
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.1"
+
+    defaultConfig {
+        targetSdkVersion 23
+        minSdkVersion 15
+        // Used by Robolectric based tests; see TestRunner.
+        buildConfigField 'String', 'BUILD_DIR', "\"${project.buildDir}\""
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile "${topsrcdir}/mobile/android/base/AndroidManifest.xml"
+
+            java {
+                srcDir "${topsrcdir}/mobile/android/base/java"
+                srcDir "${topsrcdir}/mobile/android/search/java"
+                srcDir "${topsrcdir}/mobile/android/javaaddons/java"
+                srcDir "${topsrcdir}/mobile/android/services/src/main/java"
+
+                if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
+                    srcDir "${topsrcdir}/mobile/android/stumbler/java"
+                }
+
+                if (!mozconfig.substs.MOZ_CRASHREPORTER) {
+                    exclude 'org/mozilla/gecko/CrashReporter.java'
+                }
+
+                if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
+                    exclude 'org/mozilla/gecko/ChromeCast.java'
+                    exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
+                    exclude 'org/mozilla/gecko/MediaPlayerManager.java'
+                }
+
+                if (mozconfig.substs.MOZ_WEBRTC) {
+                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
+                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
+                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_render/android/java/src"
+                }
+
+                if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
+                    exclude 'org/mozilla/gecko/adjust/StubAdjustHelper.java'
+                } else {
+                    exclude 'org/mozilla/gecko/adjust/AdjustHelper.java'
+                }
+
+                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
+            }
+
+            res {
+                srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res"
+                srcDir "${project.buildDir}/generated/source/preprocessed_resources" // See syncPreprocessedResources.
+                srcDir "${topsrcdir}/mobile/android/base/resources"
+                srcDir "${topsrcdir}/mobile/android/services/src/main/res"
+                if (mozconfig.substs.MOZ_CRASHREPORTER) {
+                    srcDir "${topsrcdir}/mobile/android/base/crashreporter/res"
+                }
+            }
+
+            assets {
+                srcDir "${topsrcdir}/mobile/android/app/assets"
+            }
+        }
+
+        test {
+            java {
+                srcDir "${topsrcdir}/mobile/android/tests/background/junit4/src"
+            }
+
+            resources {
+                srcDir "${topsrcdir}/mobile/android/tests/background/junit4/resources"
+            }
+        }
+    }
+}
+
+task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
+    into("${project.buildDir}/generated/source/preprocessed_code")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed")
+}
+
+task syncPreprocessedResources(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
+    into("${project.buildDir}/generated/source/preprocessed_resources")
+    from("${topobjdir}/mobile/android/base/res")
+}
+
+android.libraryVariants.all { variant ->
+    variant.preBuild.dependsOn syncPreprocessedCode
+    variant.preBuild.dependsOn syncPreprocessedResources
+}
+
+dependencies {
+    compile 'com.android.support:support-v4:23.0.1'
+    compile 'com.android.support:appcompat-v7:23.0.1'
+    compile 'com.android.support:recyclerview-v7:23.0.1'
+    compile 'com.android.support:design:23.0.1'
+
+    if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
+        compile 'com.android.support:mediarouter-v7:23.0.1'
+        compile 'com.google.android.gms:play-services-basement:8.1.0'
+        compile 'com.google.android.gms:play-services-base:8.1.0'
+        compile 'com.google.android.gms:play-services-cast:8.1.0'
+    }
+
+    if (mozconfig.substs.MOZ_ANDROID_GCM) {
+        compile 'com.google.android.gms:play-services-basement:8.1.0'
+        compile 'com.google.android.gms:play-services-base:8.1.0'
+        compile 'com.google.android.gms:play-services-gcm:8.1.0'
+    }
+
+    // Gradle based builds include LeakCanary. Mach based builds only include the no-op version of
+    // this library.
+    compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
+
+    compile project(':thirdparty')
+
+    testCompile 'junit:junit:4.12'
+    testCompile 'org.robolectric:robolectric:3.0'
+    testCompile 'org.simpleframework:simple-http:6.0.1'
+    testCompile 'org.mockito:mockito-core:1.10.19'
+}
+
+apply plugin: 'idea'
+
+idea {
+    module {
+    }
+}
rename from mobile/android/app/lint.xml
rename to mobile/android/app/base/lint.xml
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -12,18 +12,16 @@ android {
         applicationId mozconfig.substs.ANDROID_PACKAGE_NAME
         testApplicationId 'org.mozilla.roboexample.test'
         testInstrumentationRunner 'org.mozilla.gecko.FennecInstrumentationTestRunner'
         manifestPlaceholders = [
             ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
             MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
             MOZ_ANDROID_SHARED_ID: "${mozconfig.substs.ANDROID_PACKAGE_NAME}.sharedID",
         ]
-        // Used by Robolectric based tests; see TestRunner.
-        buildConfigField 'String', 'BUILD_DIR', "\"${project.buildDir}\""
     }
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
  
     dexOptions {
@@ -62,78 +60,23 @@ android {
         // Automation builds.
         automation {
         }
     }
 
     sourceSets {
         main {
             manifest.srcFile "${topobjdir}/mobile/android/base/AndroidManifest.xml"
-
-            java {
-                srcDir "${topsrcdir}/mobile/android/base/java"
-                srcDir "${topsrcdir}/mobile/android/search/java"
-                srcDir "${topsrcdir}/mobile/android/javaaddons/java"
-                srcDir "${topsrcdir}/mobile/android/services/src/main/java"
-
-                if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
-                    srcDir "${topsrcdir}/mobile/android/stumbler/java"
-                }
-
-                if (!mozconfig.substs.MOZ_CRASHREPORTER) {
-                    exclude 'org/mozilla/gecko/CrashReporter.java'
-                }
-
-                if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
-                    exclude 'org/mozilla/gecko/ChromeCast.java'
-                    exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
-                    exclude 'org/mozilla/gecko/MediaPlayerManager.java'
-                }
-
-                if (mozconfig.substs.MOZ_WEBRTC) {
-                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
-                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
-                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_render/android/java/src"
-                }
-
-                if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
-                    exclude 'org/mozilla/gecko/adjust/StubAdjustHelper.java'
-                } else {
-                    exclude 'org/mozilla/gecko/adjust/AdjustHelper.java'
-                }
-
-                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
-            }
-
-            res {
-                srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res"
-                srcDir "${project.buildDir}/generated/source/preprocessed_resources" // See syncPreprocessedResources.
-                srcDir "${topsrcdir}/mobile/android/base/resources"
-                srcDir "${topsrcdir}/mobile/android/services/src/main/res"
-                if (mozconfig.substs.MOZ_CRASHREPORTER) {
-                    srcDir "${topsrcdir}/mobile/android/base/crashreporter/res"
-                }
-            }
-
             assets {
-                srcDir "${topsrcdir}/mobile/android/app/assets"
-                if (mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY && !mozconfig.substs.MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER) {
-                    // If we are packaging the bouncer, it will have the distribution, so don't put
-                    // it in the main APK as well.
+                if (mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY) {
                     srcDir "${mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY}/assets"
                 }
             }
         }
 
-        test {
-            java {
-                srcDir "${topsrcdir}/mobile/android/tests/background/junit4/src"
-            }
-        }
-
         androidTest {
             java {
                 srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/src"
                 srcDir "${topsrcdir}/mobile/android/tests/background/junit3/src"
                 srcDir "${topsrcdir}/mobile/android/tests/browser/junit3/src"
                 srcDir "${topsrcdir}/mobile/android/tests/javaddons/src"
             }
             res {
@@ -142,46 +85,17 @@ android {
             assets {
                 srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/assets"
             }
         }
     }
 }
 
 dependencies {
-    compile 'com.android.support:support-v4:23.0.1'
-    compile 'com.android.support:appcompat-v7:23.0.1'
-    compile 'com.android.support:recyclerview-v7:23.0.1'
-    compile 'com.android.support:design:23.0.1'
-
-    if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
-        compile 'com.android.support:mediarouter-v7:23.0.1'
-        compile 'com.google.android.gms:play-services-basement:8.1.0'
-        compile 'com.google.android.gms:play-services-base:8.1.0'
-        compile 'com.google.android.gms:play-services-cast:8.1.0'
-    }
-
-    if (mozconfig.substs.MOZ_ANDROID_GCM) {
-        compile 'com.google.android.gms:play-services-basement:8.1.0'
-        compile 'com.google.android.gms:play-services-base:8.1.0'
-        compile 'com.google.android.gms:play-services-gcm:8.1.0'
-    }
-
-    // Gradle based builds include LeakCanary.  Gradle based tests include the no-op version.  Mach
-    // based builds only include the no-op version of this library.
-    compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
-    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
-
-    compile project(':thirdparty')
-
-    testCompile 'junit:junit:4.12'
-    testCompile 'org.robolectric:robolectric:3.0'
-    testCompile 'org.simpleframework:simple-http:6.0.1'
-    testCompile 'org.mockito:mockito-core:1.10.19'
-
+    compile project(':base')
     // Including the Robotium JAR directly can cause issues with dexing.
     androidTestCompile 'com.jayway.android.robotium:robotium-solo:4.3.1'
 }
 
 task syncOmnijarFromDistDir(type: Sync) {
     into("${project.buildDir}/generated/omnijar")
     from("${topobjdir}/dist/fennec/assets") {
         include 'omni.ja'
@@ -207,26 +121,16 @@ task checkAssetsExistInDistDir<< {
 
 task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
     into("${project.buildDir}/generated/assets")
     from("${topobjdir}/dist/fennec/assets") {
         exclude 'omni.ja'
     }
 }
 
-task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
-    into("${project.buildDir}/generated/source/preprocessed_code")
-    from("${topobjdir}/mobile/android/base/generated/preprocessed")
-}
-
-task syncPreprocessedResources(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
-    into("${project.buildDir}/generated/source/preprocessed_resources")
-    from("${topobjdir}/mobile/android/base/res")
-}
-
 // The omnijar inputs are listed as resource directory inputs to a dummy JAR.
 // That arrangement labels them nicely in IntelliJ.  See the comment in the
 // :omnijar project for more context.
 evaluationDependsOn(':omnijar')
 
 task buildOmnijar(type:Exec) {
     dependsOn rootProject.generateCodeAndResources
 
@@ -250,19 +154,16 @@ task buildOmnijar(type:Exec) {
     doLast {
         if (execResult.exitValue != 0) {
             throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
         }
     }
 }
 
 android.applicationVariants.all { variant ->
-    variant.preBuild.dependsOn syncPreprocessedCode
-    variant.preBuild.dependsOn syncPreprocessedResources
-
     // Like 'local' or 'localOld'.
     def productFlavor = variant.productFlavors[0].name
     // Like 'debug' or 'release'.
     def buildType = variant.buildType.name
 
     // We insert omni.ja and the .so libraries into all local builds.
     if (!productFlavor.startsWith('local')) {
         return
deleted file mode 100644
--- a/mobile/android/app/src/test/java/org/mozilla/gecko/TestGeckoApplication.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko;
-
-import android.app.Application;
-
-import org.robolectric.TestLifecycleApplication;
-
-import java.lang.reflect.Method;
-
-/**
- * GeckoApplication isn't test-lifecycle friendly: onCreate is called multiple times, which
- * re-registers Gecko event listeners, which fails.  This class is magically named so that
- * Robolectric uses it instead of the application defined in the Android manifest.  See
- * http://robolectric.blogspot.ca/2013/04/the-test-lifecycle-in-20.html.
- */
-public class TestGeckoApplication extends Application implements TestLifecycleApplication {
-  @Override public void beforeTest(Method method) {
-  }
-
-  @Override public void prepareTest(Object test) {
-  }
-
-  @Override public void afterTest(Method method) {
-  }
-}
--- a/mobile/android/base/java/org/mozilla/gecko/distribution/Distribution.java
+++ b/mobile/android/base/java/org/mozilla/gecko/distribution/Distribution.java
@@ -458,23 +458,21 @@ public class Distribution {
         // We've done the work once; don't do it again.
         if (this.state == STATE_SET) {
             // Note that we don't compute the distribution directory.
             // Call `ensureDistributionDir` if you need it.
             runReadyQueue();
             return true;
         }
 
-        // We try to find the install intent, then the APK, then the system directory, and finally
-        // an already copied distribution.  Already copied might originate from the bouncer APK.
+        // We try the install intent, then the APK, then the system directory.
         final boolean distributionSet =
                 checkIntentDistribution(referrer) ||
                 copyAndCheckAPKDistribution() ||
-                checkSystemDistribution() ||
-                checkDataDistribution();
+                checkSystemDistribution();
 
         // If this is our first run -- and thus we weren't already in STATE_NONE or STATE_SET above --
         // and we didn't find a distribution already, then we should hold on to callbacks in case we
         // get a late distribution.
         this.shouldDelayLateCallbacks = !distributionSet;
         this.state = distributionSet ? STATE_SET : STATE_NONE;
         settings.edit().putInt(keyName, this.state).apply();
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -881,23 +881,19 @@ ANDROID_GENERATED_RESFILES += [
     'res/values/strings.xml',
 ]
 
 ANDROID_ASSETS_DIRS += [
     '/mobile/android/app/assets',
 ]
 
 if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY']:
-    # If you change this, also change its equivalent in mobile/android/bouncer.
-    if not CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']:
-        # If we are packaging the bouncer, it will have the distribution, so don't put
-        # it in the main APK as well.
-        ANDROID_ASSETS_DIRS += [
-            '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets',
-        ]
+    ANDROID_ASSETS_DIRS += [
+        '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets',
+    ]
 
 # We do not expose MOZ_INSTALL_TRACKING_ADJUST_SDK_APP_TOKEN here because that
 # would leak the value to build logs.  Instead we expose the token quietly where
 # appropriate in Makefile.in.
 for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_LINKER_EXTRACT', 'MOZ_DEBUG',
             'MOZ_ANDROID_SEARCH_ACTIVITY', 'MOZ_NATIVE_DEVICES', 'MOZ_ANDROID_MLS_STUMBLER',
             'MOZ_ANDROID_DOWNLOADS_INTEGRATION', 'MOZ_INSTALL_TRACKING',
             'MOZ_ANDROID_GCM'):
deleted file mode 100644
--- a/mobile/android/bouncer/AndroidManifest.xml.in
+++ /dev/null
@@ -1,96 +0,0 @@
-#filter substitution
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="org.mozilla.gecko"
-      android:installLocation="auto"
-      android:versionCode="@ANDROID_VERSION_CODE@"
-      android:versionName="@MOZ_APP_VERSION@"
-#ifdef MOZ_ANDROID_SHARED_ID
-      android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
-#endif
-      >
-    <uses-sdk android:minSdkVersion="@MOZ_ANDROID_MIN_SDK_VERSION@"
-#ifdef MOZ_ANDROID_MAX_SDK_VERSION
-              android:maxSdkVersion="@MOZ_ANDROID_MAX_SDK_VERSION@"
-#endif
-              android:targetSdkVersion="23"/>
-
-<!-- The bouncer APK and the main APK should define the same set of
-     <permission>, <uses-permission>, and <uses-feature> elements.  This reduces
-     the likelihood of permission-related surprises when installing the main APK
-     on top of a pre-installed bouncer APK.  Add such shared elements in the
-     fileincluded here, so that they can be referenced by both APKs. -->
-#include ../base/FennecManifest_permissions.xml.in
-
-    <application android:label="@MOZ_APP_DISPLAYNAME@"
-                 android:icon="@drawable/icon"
-                 android:logo="@drawable/logo"
-                 android:hardwareAccelerated="true"
-                 android:allowBackup="false"
-# The preprocessor does not yet support arbitrary parentheses, so this cannot
-# be parenthesized thus to clarify that the logical AND operator has precedence:
-#   !defined(MOZILLA_OFFICIAL) || (defined(NIGHTLY_BUILD) && defined(MOZ_DEBUG))
-#if !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) && defined(MOZ_DEBUG)
-                 android:debuggable="true">
-#else
-                 android:debuggable="false">
-#endif
-
-        <activity
-            android:name="@MOZ_ANDROID_BROWSER_INTENT_CLASS@"
-            android:label="@MOZ_APP_DISPLAYNAME@"
-            android:theme="@android:style/Theme.Translucent">
-
-            <!-- Aping org.mozilla.gecko.BrowserApp. -->
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-                <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
-                <category android:name="android.intent.category.APP_BROWSER" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-
-            <meta-data android:name="com.sec.minimode.icon.portrait.normal"
-                       android:resource="@drawable/icon"/>
-
-            <meta-data android:name="com.sec.minimode.icon.landscape.normal"
-                       android:resource="@drawable/icon" />
-
-            <intent-filter>
-                <action android:name="android.intent.action.WEB_SEARCH" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-            </intent-filter>
-
-            <!-- Aping org.mozilla.gecko.tabqueue.TabQueueDispatcher. -->
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:scheme="about" />
-                <data android:scheme="javascript" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="file" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:mimeType="text/html"/>
-                <data android:mimeType="text/plain"/>
-                <data android:mimeType="application/xhtml+xml"/>
-            </intent-filter>
-        </activity>
-
-        <service
-            android:name="org.mozilla.bouncer.BouncerService"
-            android:exported="false" />
-
-    </application>
-</manifest>
deleted file mode 100644
--- a/mobile/android/bouncer/Makefile.in
+++ /dev/null
@@ -1,30 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/config.mk
-
-JAVAFILES := \
-	java/org/mozilla/bouncer/BouncerService.java \
-	java/org/mozilla/gecko/BrowserApp.java \
-  $(NULL)
-
-ANDROID_EXTRA_JARS := \
-  $(NULL)
-
-PP_TARGETS += manifest
-manifest := $(srcdir)/AndroidManifest.xml.in
-manifest_TARGET := export
-# Special 'cuz they are set in mobile/android/defs.mk.
-manifest_FLAGS += \
-  -DMOZ_ANDROID_SHARED_ID="$(MOZ_ANDROID_SHARED_ID)" \
-  -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE="$(MOZ_ANDROID_SHARED_ACCOUNT_TYPE)" \
-  -DMOZ_ANDROID_SHARED_FXACCOUNT_TYPE="$(MOZ_ANDROID_SHARED_FXACCOUNT_TYPE)" \
-  $(NULL)
-
-# Targets built very early during a Gradle build.
-gradle-targets: $(abspath AndroidManifest.xml)
-
-.PHONY: gradle-targets
-
-libs:: $(ANDROID_APK_NAME).apk
deleted file mode 100644
--- a/mobile/android/bouncer/assets/example_asset.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-This is an example asset.
deleted file mode 100644
--- a/mobile/android/bouncer/build.gradle
+++ /dev/null
@@ -1,76 +0,0 @@
-buildDir "${topobjdir}/gradle/build/mobile/android/bouncer"
-
-apply plugin: 'com.android.application'
-
-android {
-    compileSdkVersion 23
-    buildToolsVersion "23.0.1"
-
-    defaultConfig {
-        targetSdkVersion 23
-        minSdkVersion 15 
-        applicationId mozconfig.substs.ANDROID_PACKAGE_NAME
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
- 
-    dexOptions {
-        javaMaxHeapSize "2g"
-    }
-
-    lintOptions {
-        abortOnError false
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled false
-        }
-    }
-
-    sourceSets {
-        main {
-            manifest.srcFile "${topobjdir}/mobile/android/bouncer/AndroidManifest.xml"
-            assets {
-                if (mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY) {
-                    srcDir "${mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY}/assets"
-                }
-            }
-            java {
-                srcDir 'java'
-            }
-            res {
-                srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res" // For the icon.
-                srcDir 'res'
-            }
-        }
-    }
-}
-
-task generateCodeAndResources(type:Exec) {
-    workingDir "${topobjdir}"
-
-    commandLine mozconfig.substs.GMAKE
-    args '-C'
-    args "${topobjdir}/mobile/android/bouncer"
-    args 'gradle-targets'
-
-    // Only show the output if something went wrong.
-    ignoreExitValue = true
-    standardOutput = new ByteArrayOutputStream()
-    errorOutput = standardOutput
-    doLast {
-        if (execResult.exitValue != 0) {
-            throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
-        }
-    }
-}
-
-afterEvaluate {
-    android.applicationVariants.all {
-        preBuild.dependsOn generateCodeAndResources
-    }
-}
deleted file mode 100644
--- a/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.bouncer;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-public class BouncerService extends IntentService {
-
-    private static final String LOGTAG = "GeckoBouncerService";
-
-    public BouncerService() {
-        super("BouncerService");
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        final byte[] buffer = new byte[8192];
-
-        Log.d(LOGTAG, "Preparing to copy distribution files");
-
-        final List<String> files;
-        try {
-            files = getFiles("distribution");
-        } catch (IOException e) {
-            Log.e(LOGTAG, "Error getting distribution files from assets/distribution/**", e);
-            return;
-        }
-
-        InputStream in = null;
-        for (String path : files) {
-            try {
-                Log.d(LOGTAG, "Copying distribution file: " + path);
-
-                in = getAssets().open(path);
-
-                final File outFile = getDataFile(path);
-                writeStream(in, outFile, buffer);
-            } catch (IOException e) {
-                Log.e(LOGTAG, "Error opening distribution input stream from assets", e);
-            } finally {
-                if (in != null) {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        Log.e(LOGTAG, "Error closing distribution input stream", e);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Recursively traverse a directory to list paths to all files.
-     *
-     * @param path Directory to traverse.
-     * @return List of all files in given directory.
-     * @throws IOException
-     */
-    private List<String> getFiles(String path) throws IOException {
-        List<String> paths = new ArrayList<>();
-        getFiles(path, paths);
-        return paths;
-    }
-
-    /**
-     * Recursively traverse a directory to list paths to all files.
-     *
-     * @param path Directory to traverse.
-     * @param acc Accumulator of paths seen.
-     * @throws IOException
-     */
-    private void getFiles(String path, List<String> acc) throws IOException {
-        final String[] list = getAssets().list(path);
-        if (list.length > 0) {
-            // We're a directory -- recurse.
-            for (final String file : list) {
-                getFiles(path + "/" + file, acc);
-            }
-        } else {
-            // We're a file -- accumulate.
-            acc.add(path);
-        }
-    }
-
-    private String getDataDir() {
-        return getApplicationInfo().dataDir;
-    }
-
-    private File getDataFile(final String path) {
-        File outFile = new File(getDataDir(), path);
-        File dir = outFile.getParentFile();
-
-        if (dir != null && !dir.exists()) {
-            Log.d(LOGTAG, "Creating " + dir.getAbsolutePath());
-            if (!dir.mkdirs()) {
-                Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath());
-                return null;
-            }
-        }
-
-        return outFile;
-    }
-
-    private void writeStream(InputStream fileStream, File outFile, byte[] buffer)
-            throws IOException {
-        final OutputStream outStream = new FileOutputStream(outFile);
-        try {
-            int count;
-            while ((count = fileStream.read(buffer)) > 0) {
-                outStream.write(buffer, 0, count);
-            }
-        } finally {
-            outStream.close();
-        }
-    }
-}
deleted file mode 100644
--- a/mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import org.mozilla.bouncer.BouncerService;
-
-/**
- * Bouncer activity version of BrowserApp.
- *
- * This class has the same name as org.mozilla.gecko.BrowserApp to preserve
- * shortcuts created by the bouncer app.
- */
-public class BrowserApp extends Activity {
-    private static final String LOGTAG = "GeckoBouncerActivity";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // This races distribution installation against the Play Store killing our process to
-        // install the update.  We'll live with it.  To do better, consider using an Intent to
-        // notify when the service has completed.
-        startService(new Intent(this, BouncerService.class));
-
-        final String appPackageName = Uri.encode(getPackageName());
-        final Uri uri = Uri.parse("market://details?id=" + appPackageName);
-        Log.i(LOGTAG, "Lanching activity with URL: " + uri.toString());
-
-        // It might be more correct to catch failure in case the Play Store isn't installed.  The
-        // fallback action is to open the Play Store website... but doing so may offer Firefox as
-        // browser (since even the bouncer offers to view URLs), which will be very confusing.
-        // Therefore, we don't try to be fancy here, and we just fail (silently).
-        startActivity(new Intent(Intent.ACTION_VIEW, uri));
-
-        finish();
-    }
-}
deleted file mode 100644
--- a/mobile/android/bouncer/moz.build
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEFINES['ANDROID_VERSION_CODE'] = '1'
-
-for var in ('ANDROID_PACKAGE_NAME',
-            'MOZ_ANDROID_BROWSER_INTENT_CLASS',
-            'MOZ_APP_DISPLAYNAME',
-            'MOZ_APP_VERSION'):
-    DEFINES[var] = CONFIG[var]
-
-ANDROID_APK_NAME = 'bouncer'
-ANDROID_APK_PACKAGE = CONFIG['ANDROID_PACKAGE_NAME']
-
-# Putting branding earlier allows branders to override default resources.
-ANDROID_RES_DIRS += [
-    '/' + CONFIG['MOZ_BRANDING_DIRECTORY'] + '/res', # For the icon.
-    'res',
-]
-
-ANDROID_ASSETS_DIRS += [
-    'assets',
-]
-
-if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY']:
-    # If you change this, also change its equivalent in mobile/android/base.
-    ANDROID_ASSETS_DIRS += [
-        '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets',
-    ]
deleted file mode 100644
--- a/mobile/android/bouncer/res/drawable-v21/logo.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!-- The action bar scales the application icon to be too large (bug 1132751)
-     so add some padding to prevent it from scaling so much. -->
-<inset
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/icon"
-    android:insetTop="6dp"
-    android:insetBottom="6dp"
-    android:insetLeft="6dp"
-    android:insetRight="6dp"
-    />
deleted file mode 100644
--- a/mobile/android/bouncer/res/drawable/logo.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!-- Overidden. -->
-<bitmap
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/icon"/>
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -88,19 +88,16 @@ MOZ_WEBGL_CONFORMANT=1
 MOZ_ANDROID_SEARCH_ACTIVITY=1
 
 # Enable the Mozilla Location Service stumbler.
 MOZ_ANDROID_MLS_STUMBLER=1
 
 # Enable adding to the system downloads list.
 MOZ_ANDROID_DOWNLOADS_INTEGRATION=1
 
-# Build and package the install bouncer APK by default.
-MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER=1
-
 # Use the low-memory GC tuning.
 export JS_GC_SMALL_CHUNK_SIZE=1
 
 # Enable GCM registration on Nightly builds only.
 if test "$NIGHTLY_BUILD"; then
   MOZ_ANDROID_GCM=1
 fi
 
deleted file mode 100644
--- a/mobile/android/docs/bouncer.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-.. -*- Mode: rst; fill-column: 100; -*-
-
-=========================================
- The Firefox for Android install bouncer
-=========================================
-
-`Bug 1234629 <https://bugzilla.mozilla.org/show_bug.cgi?id=1234629>`_ and `Bug 1163082
-<https://bugzilla.mozilla.org/show_bug.cgi?id=1163082>`_ combine to allow building a very small
-Fennec-like "bouncer" APK that redirects (bounces) a potential Fennec user to the marketplace of
-their choice -- usually the Google Play Store -- to install the real Firefox for Android application
-APK.
-
-The real APK should install seamlessly over top of the bouncer APK.  Care is taken to keep the
-bouncer and application APK <permission> manifest definitions identical, and to have the bouncer APK
-<activity> manifest definitions look similar to the application APK <activity> manifest definitions.
-
-In addition, the bouncer APK can carry a Fennec distribution, which it copies onto the device before
-redirecting to the marketplace.  The application APK recognizes the installed distribution and
-customizes itself accordingly on first run.
-
-The motivation is to allow partners to pre-install the very small bouncer APK on shipping devices
-and to have a smooth path to upgrade to the full application APK, with a partner-specific
-distribution in place.
-
-Technical details
-=================
-
-To build the bouncer APK, define ``MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER``.  To pack a distribution
-into the bouncer APK (and *not* into the application APK), add a line like::
-
-  ac_add_options --with-android-distribution-directory=/path/to/fennec-distribution-sample
-
-to your ``mozconfig`` file.  See the `general distribution documentation on the wiki
-<https://wiki.mozilla.org/Mobile/Distribution_Files>`_ for more information.
-
-The ``distribution`` directory should end up in the ``assets/distribution`` directory of the bouncer
-APK.  It will be copied into ``/data/data/$ANDROID_PACKAGE_NAME/distribution`` when the bouncer
-executes.
--- a/mobile/android/docs/defaultdomains.rst
+++ b/mobile/android/docs/defaultdomains.rst
@@ -67,17 +67,16 @@ list of domains:
 * Remove any URL shorteners and redirecters.
 * Remove any content/CDN domains. Some sites use separate domains to store images and other static
   content.
 
 Guidelines for Adult Content
 ============================
 
 Generally the Adult category includes sites whose dominant theme is either:
-
 * To appeal to the prurient interest in sex without any serious literary, artistic, political, or
   scientific value
 * The depiction or description of nudity, including sexual or excretory activities or organs in a
   lascivious way
 * The depiction or description of sexually explicit conduct in a lascivious way (e.g. for
   entertainment purposes)
 
 For a more complete definition and guidelines of adult content, use the full DMOZ guidelines at
new file mode 100644
--- /dev/null
+++ b/mobile/android/docs/gradle.rst
@@ -0,0 +1,97 @@
+.. -*- Mode: rst; fill-column: 80; -*-
+
+======================
+ Building with Gradle
+======================
+
+Instructions
+============
+
+.. code-block:: shell
+
+  ./mach build && ./mach package
+  ./mach gradle build
+
+The debug APK will be at
+``$OBJDIR/mobile/android/gradle/app/build/outputs/apk/app-debug.apk``.
+
+The ``$OBJDIR/mobile/android/gradle`` directory can be imported into IntelliJ as
+follows:
+
+- File > Import Project...
+- [select ``$OBJDIR/mobile/android/gradle``]
+- Import project from external model > Gradle
+- [select Use customizable Gradle wrapper]
+
+When prompted, do not add any files to git.  You may need to re-open the
+project, or restart IntelliJ, to pick up a compiler language-level change.
+
+Technical overview
+==================
+
+Caveats
+-------
+
+* The Gradle build will "succeed" but crash on start up if the object directory
+  has not been properly packaged.
+* Changes to preprocessed source code and resources (namely, ``strings.xml.in``
+  and the accompanying DTD files) may not be recognized.
+* There's minimal support for editing JavaScript.
+* There's no support for editing C/C++.
+
+How the Gradle project is laid out
+----------------------------------
+
+To the greatest extent possible, the Gradle configuration lives in the object
+directory.
+
+At the time of writing, there are three main sub-modules: *app*, *base*, and
+*thirdparty*, and several smaller sub-modules.
+
+*app* is the Fennec wrapper; it generates the **org.mozilla.fennec.R** resource
+package.  *base* is the Gecko code; it generates the **org.mozilla.gecko.R**
+resource package.  Together, *app* and *base* address the "two package
+namespaces" that has plagued Fennec from day one.
+
+Due to limitations in the Android Gradle plugin, all test code is shoved into
+the *app* module.  (The issue is that, at the time of writing, there is no
+support for test-only APKs.)  For no particular reason, the compiled C/C++
+libraries are included in the *app* module; they could be included in the *base*
+module.  I expect *base* to rebuilt slightly more frequently than *app*, so I'm
+hoping this choice will allow for faster incremental builds.
+
+*thirdparty* is the external code we use in Fennec; it's built as an Android
+library but uses no resources.  It's separate simply to allow the build system
+to cache the compiled and pre-dexed artifacts, hopefully allowing for faster
+incremental builds.
+
+Recursive make backend details
+------------------------------
+
+The ``mobile/android/gradle`` directory writes the following into
+``$OBJDIR/mobile/android/gradle``:
+
+1) the Gradle wrapper;
+2) ``gradle.properties``;
+3) symlinks to certain source and resource directories.
+
+The Gradle wrapper is written to make it easy to build with Gradle from the
+object directory.  The wrapper is `intended to be checked into version
+control`_.
+
+``gradle.properties`` is the single source of per-object directory Gradle
+configuration, and provides the Gradle configuration access to
+configure/moz.build variables.
+
+The symlinks are not necessary for the Gradle build itself, but they prevent
+nested directory errors and incorrect Java package scoping when the Gradle
+project is imported into IntelliJ.  Because IntelliJ treats the Gradle project
+as authoritative, it's not sufficient to fix these manually in IntelliJ after
+the initial import -- IntelliJ reverts to the Gradle configuration after every
+build.  Since there aren't many symlinks, I've done them in the Makefile rather
+than at a higher level of abstraction (like a moz.build definition, or a custom
+build backend).  In future, I expect to be able to remove all such symlinks by
+making our in-tree directory structures agree with what Gradle and IntelliJ
+expect.
+
+.. _intended to be checked into version control: http://www.gradle.org/docs/current/userguide/gradle_wrapper.html
--- a/mobile/android/docs/index.rst
+++ b/mobile/android/docs/index.rst
@@ -1,25 +1,24 @@
 .. Firefox for Android documentation master file, created by
    sphinx-quickstart on Fri Dec  4 22:51:57 2015.
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Firefox for Android
-===================
+Welcome to Firefox for Android's documentation!
+===============================================
 
 Contents:
 
 .. toctree::
    :maxdepth: 2
 
    localeswitching
    uitelemetry
    adjust
    defaultdomains
-   bouncer
 
 Indices and tables
 ==================
 
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
--- a/mobile/android/moz.build
+++ b/mobile/android/moz.build
@@ -21,18 +21,15 @@ DIRS += [
     'components',
     'modules',
     'themes/core',
     'app',
     'fonts',
     'geckoview_library',
 ]
 
-if CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']:
-    DIRS += ['bouncer'] # No ordering implied with respect to base.
-
 DIRS += ['../../xulrunner/tools/redit']
 
 TEST_DIRS += [
     'tests',
 ]
 
 SPHINX_TREES['fennec'] = 'docs'
rename from mobile/android/app/src/main/resources/robolectric.properties
rename to mobile/android/tests/background/junit4/resources/robolectric.properties
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java
@@ -132,18 +132,16 @@ public class testDistribution extends Co
         String mockPackagePath = getMockPackagePath();
 
         // Wait for any startup-related background distribution shenanigans to
         // finish. This reduces the chance of us racing with startup pref writes.
         waitForBackgroundHappiness();
 
         // Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests
         clearDistributionPref();
-        clearDistributionFromDataData();
-
         setTestLocale("en-US");
         try {
             initDistribution(mockPackagePath);
         } catch(NoSuchElementException e) {
             // TODO: determine why this exception is intermittently thrown
             Log.w(LOGTAG, "NoSuchElementException on first initDistribution -- will retry");
             mSolo.sleep(4000);
             initDistribution(mockPackagePath);
@@ -151,29 +149,26 @@ public class testDistribution extends Co
         checkPreferences();
         checkAndroidPreferences();
         checkLocalizedPreferences("en-US");
         checkSearchPlugin();
         checkAddon();
 
         // Pre-clear distribution pref, and run es-MX localized preferences Test
         clearDistributionPref();
-        clearDistributionFromDataData();
         setTestLocale("es-MX");
         initDistribution(mockPackagePath);
         checkLocalizedPreferences("es-MX");
 
         // Test the (stubbed) download interaction.
         setTestLocale("en-US");
         clearDistributionPref();
-        clearDistributionFromDataData();
         doTestValidReferrerIntent();
 
         clearDistributionPref();
-        clearDistributionFromDataData();
         doTestInvalidReferrerIntent();
     }
 
     private void setOSLocale(Locale locale) {
         Locale.setDefault(locale);
         BrowserLocaleManager.storeAndNotifyOSLocale(GeckoSharedPrefs.forProfile(mActivity), locale);
     }
 
@@ -503,32 +498,16 @@ public class testDistribution extends Co
     private void clearDistributionPref() {
         mAsserter.dumpLog("Clearing distribution pref.");
         SharedPreferences settings = mActivity.getSharedPreferences("GeckoApp", Activity.MODE_PRIVATE);
         String keyName = mActivity.getPackageName() + ".distribution_state";
         settings.edit().remove(keyName).commit();
         TestableDistribution.clearReferrerDescriptorForTesting();
     }
 
-    /**
-     * Clears any distribution found in /data/data.
-     */
-    private void clearDistributionFromDataData() throws Exception {
-        File dataDir = new File(mActivity.getApplicationInfo().dataDir);
-
-        // Recursively delete distribution files that Distribution.init copied to data directory.
-        File distDir = new File(dataDir, "distribution");
-        if (distDir.exists()) {
-            mAsserter.dumpLog("Clearing distribution from " + distDir.getAbsolutePath());
-            delete(distDir);
-        } else {
-            mAsserter.dumpLog("No distribution to clear from " + distDir.getAbsolutePath());
-        }
-    }
-
     @Override
     public void setUp() throws Exception {
         // TODO: Set up the content provider after setting the distribution.
         super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db");
     }
 
     private void delete(File file) throws Exception {
       if (file.isDirectory()) {
@@ -543,14 +522,17 @@ public class testDistribution extends Co
     @Override
     public void tearDown() throws Exception {
         File dataDir = new File(mActivity.getApplicationInfo().dataDir);
 
         // Delete mock package from data directory.
         File mockPackage = new File(dataDir, MOCK_PACKAGE);
         mAsserter.ok(mockPackage.delete(), "clean up mock package", "deleted " + mockPackage.getPath());
 
-        clearDistributionFromDataData();
+        // Recursively delete distribution files that Distribution.init copied to data directory.
+        File distDir = new File(dataDir, "distribution");
+        delete(distDir);
+
         clearDistributionPref();
 
         super.tearDown();
     }
 }
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,28 +24,25 @@ if (json.substs.MOZ_BUILD_APP != 'mobile
 // Set the Android SDK location.  This is the *least specific* mechanism, which
 // is unfortunate: we'd prefer to use the *most specific* mechanism.  That is,
 // local.properties (first 'sdk.dir', then 'android.dir') and then the
 // environment variable ANDROID_HOME will override this.  That's unfortunate,
 // but it's hard to automatically arrange better.
 System.setProperty('android.home', json.substs.ANDROID_SDK_ROOT)
 
 include ':app'
+include ':base'
 include ':omnijar'
 include ':thirdparty'
 
 project(':app').projectDir = new File("${json.topsrcdir}/mobile/android/app")
+project(':base').projectDir = new File("${json.topsrcdir}/mobile/android/app/base")
 project(':omnijar').projectDir = new File("${json.topsrcdir}/mobile/android/app/omnijar")
 project(':thirdparty').projectDir = new File("${json.topsrcdir}/mobile/android/thirdparty")
 
-if (json.substs.MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER) {
-    include ':bouncer'
-    project(':bouncer').projectDir = new File("${json.topsrcdir}/mobile/android/bouncer")
-}
-
 // The Gradle instance is shared between settings.gradle and all the
 // other build.gradle files (see
 // http://forums.gradle.org/gradle/topics/define_extension_properties_from_settings_xml).
 // We use this ext property to pass the per-object-directory mozconfig
 // between scripts.  This lets us execute set-up code before we gradle
 // tries to configure the project even once, and as a side benefit
 // saves invoking |mach environment| multiple times.
 gradle.ext.mozconfig = json
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -332,37 +332,16 @@ ROBOCOP_PATH = $(topobjdir)/mobile/andro
 INNER_ROBOCOP_PACKAGE= \
   cp $(GECKO_APP_AP_PATH)/fennec_ids.txt $(ABS_DIST) && \
   $(call RELEASE_SIGN_ANDROID_APK,$(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk,$(ABS_DIST)/robocop.apk)
 endif
 else
 INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Android Robocop for you'
 endif
 
-ifdef MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER
-UPLOAD_EXTRA_FILES += bouncer.apk
-
-bouncer_package=$(ABS_DIST)/bouncer.apk
-
-# Package and release sign the install bouncer APK.  This assumes that the main
-# APK (that is, $(PACKAGE)) has already been produced, and verifies that the
-# bouncer APK and the main APK define the same set of permissions.  The
-# intention is to avoid permission-related surprises when bouncing to the
-# installation process in the Play Store.  N.b.: sort -u is Posix and saves
-# invoking uniq separately.  diff -u is *not* Posix, so we only add -c.
-INNER_INSTALL_BOUNCER_PACKAGE=\
-  $(call RELEASE_SIGN_ANDROID_APK,$(topobjdir)/mobile/android/bouncer/bouncer-unsigned-unaligned.apk,$(bouncer_package)) && \
-  ($(AAPT) dump permissions $(PACKAGE) | sort -u > $(PACKAGE).permissions && \
-   $(AAPT) dump permissions $(bouncer_package) | sort -u > $(bouncer_package).permissions && \
-   diff -c $(PACKAGE).permissions $(bouncer_package).permissions || \
-   (echo "*** Error: The permissions of the bouncer package differ from the permissions of the main package.  Ensure the bouncer and main package Android manifests agree, rebuild mobile/android, and re-package." && exit 1))
-else
-INNER_INSTALL_BOUNCER_PACKAGE=echo 'Install bouncer is disabled - No trampolines for you'
-endif # MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER
-
 # Create geckoview_library/geckoview_{assets,library}.zip for third-party GeckoView consumers.
 ifdef NIGHTLY_BUILD
 ifndef MOZ_DISABLE_GECKOVIEW
 INNER_MAKE_GECKOVIEW_LIBRARY= \
   $(MAKE) -C ../mobile/android/geckoview_library package
 else
 INNER_MAKE_GECKOVIEW_LIBRARY=echo 'GeckoView library packaging is disabled'
 endif
@@ -494,17 +473,16 @@ INNER_MAKE_PACKAGE	= \
   $(INNER_SZIP_LIBRARIES) && \
   make -C $(GECKO_APP_AP_PATH) gecko-nodeps.ap_ && \
   cp $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ $(ABS_DIST)/gecko.ap_ && \
   ( (test ! -f $(GECKO_APP_AP_PATH)/R.txt && echo "*** Warning: The R.txt that is being packaged might not agree with the R.txt that was built. This is normal during l10n repacks.") || \
     diff $(GECKO_APP_AP_PATH)/R.txt $(GECKO_APP_AP_PATH)/gecko-nodeps/R.txt >/dev/null || \
     (echo "*** Error: The R.txt that was built and the R.txt that is being packaged are not the same. Rebuild mobile/android/base and re-package." && exit 1)) && \
   $(INNER_MAKE_APK) && \
   $(INNER_ROBOCOP_PACKAGE) && \
-  $(INNER_INSTALL_BOUNCER_PACKAGE) && \
   $(INNER_MAKE_GECKOLIBS_AAR) && \
   $(INNER_MAKE_GECKOVIEW_LIBRARY)
 endif
 
 ifeq ($(MOZ_BUILD_APP),mobile/android/b2gdroid)
 INNER_MAKE_PACKAGE	= \
   $(INNER_SZIP_LIBRARIES) && \
   cp $(topobjdir)/mobile/android/b2gdroid/app/classes.dex $(ABS_DIST)/classes.dex && \