Bug 1411654 - Part 1: Upgrade to Android-Gradle 3.0+ and build-tools;26.0.2. r=maliu
authorNick Alexander <nalexander@mozilla.com>
Thu, 26 Oct 2017 11:00:36 -0700
changeset 456956 bc47964180063422d7c81e1d71aeac9946fa25b3
parent 456955 2d2ce8d0b969a430b2029d1123c27f88c542cb0c
child 456957 d67b952ecbe41872990cc0acd1ad480476560bc9
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmaliu
bugs1411654
milestone60.0a1
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
Bug 1411654 - Part 1: Upgrade to Android-Gradle 3.0+ and build-tools;26.0.2. r=maliu New Android-Gradle plugins pin the build-tools version, and we want to be consistent between Gradle and moz.build. MozReview-Commit-ID: ApWS4rHzPuH
build.gradle
gradle/wrapper/gradle-wrapper.properties
mobile/android/app/build.gradle
mobile/android/app/src/photon/res/values/styles.xml
mobile/android/base/AndroidManifest.xml.in
mobile/android/geckoview/build.gradle
mobile/android/geckoview_example/build.gradle
mobile/android/gradle.configure
mobile/android/gradle/with_gecko_binaries.gradle
mobile/android/thirdparty/build.gradle
old-configure.in
python/mozboot/mozboot/android-packages.txt
taskcluster/scripts/misc/android-gradle-dependencies/after.sh
--- a/build.gradle
+++ b/build.gradle
@@ -11,17 +11,16 @@ def tryInt = { string ->
 allprojects {
     // Expose the per-object-directory configuration to all projects.
     ext {
         mozconfig = gradle.mozconfig
         topsrcdir = gradle.mozconfig.topsrcdir
         topobjdir = gradle.mozconfig.topobjdir
 
         compileSdkVersion = tryInt(mozconfig.substs.ANDROID_COMPILE_SDK_VERSION)
-        buildToolsVersion = tryInt(mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION)
         targetSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
         minSdkVersion = tryInt(mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION)
         manifestPlaceholders = [
             ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
             ANDROID_TARGET_SDK: mozconfig.substs.ANDROID_TARGET_SDK,
             MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
             MOZ_ANDROID_SHARED_ID: "${mozconfig.substs.ANDROID_PACKAGE_NAME}.sharedID",
         ]
@@ -47,19 +46,18 @@ buildscript {
         }
         // For in tree plugins.
         maven {
             url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
         }
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.3'
-        // Provided in tree.
-        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.3'
+        classpath 'com.android.tools.build:gradle:3.0.1'
+        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
     }
 }
 
 if ('multi' == System.env.AB_CD) {
     // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale.  This
     // causes the
     //
     // |mach build| > |mach gradle| > |make gradle-targets| > AndroidManifest.xml > strings.xml > multi/brand.dtd
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
 #Fri Sep 16 15:41:50 PDT 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
-distributionSha256Sum=ed7e9c8bb41bd10d4c9339c95b2f8b122f5bf13188bd90504a26e0f00b123b0d
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionSha256Sum=5c07b3bac2209fbc98fb1fdf6fd831f72429cdf8c503807404eae03d8c8099e5
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -6,17 +6,16 @@ apply plugin: 'com.getkeepsafe.dexcount'
 apply plugin: 'findbugs'
 
 dexcount {
     format = "tree"
 }
 
 android {
     compileSdkVersion project.ext.compileSdkVersion
-    buildToolsVersion project.ext.buildToolsVersion
 
     defaultConfig {
         targetSdkVersion project.ext.targetSdkVersion
         minSdkVersion project.ext.minSdkVersion
         manifestPlaceholders = project.ext.manifestPlaceholders
 
         applicationId mozconfig.substs.ANDROID_PACKAGE_NAME
         testApplicationId 'org.mozilla.roboexample.test'
@@ -69,16 +68,24 @@ android {
             // shrinkResources true
             minifyEnabled true
             proguardFile "${topsrcdir}/mobile/android/config/proguard/proguard.cfg"
         }
         release configureMinifyClosure
         if (mozconfig.substs.MOZILLA_OFFICIAL) {
             debug configureMinifyClosure
         }
+
+        def isDebuggable = (!mozconfig.substs.MOZILLA_OFFICIAL) || (mozconfig.substs.NIGHTLY_BUILD && mozconfig.substs.MOZ_DEBUG)
+        debug {
+            debuggable isDebuggable 
+        }
+        release {
+            debuggable isDebuggable
+        }
     }
 
     // The "audience" flavour dimension distinguishes between _local_ builds (intended for
     // development) and _official_ builds (intended for testing in automation and to ship in one of
     // the Fennec distribution channels).
     //
     // The "skin" flavor dimension distinguishes between different user interfaces.  We sometimes
     // want to develop significant new user interface pieces in-tree that don't ship (even in the
@@ -234,82 +241,65 @@ android {
             // we have tests that start test servers and the bound ports
             // collide.  We'll fix this soon to have much faster test cycles.
             maxParallelForks 1
         }
     }
 }
 
 dependencies {
-    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
 
     if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
-        compile "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+        implementation "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
     }
 
     if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
-        compile "com.google.android.gms:play-services-analytics:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-analytics:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
     }
 
     if (mozconfig.substs.MOZ_ANDROID_GCM) {
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-measurement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-measurement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
     }
 
     // Include LeakCanary in most gradle based builds. LeakCanary adds about 5k methods, so we disable
     // it for the (non-proguarded, non-predex) localOld builds to allow space for other libraries.
     // Gradle based tests include the no-op version.  Mach based builds only include the no-op version
     // of this library.
     // It doesn't seem like there is a non-trivial way to be conditional on 'localOld', so instead we explicitly
     // define a version of leakcanary for every flavor:
-    localCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
-    localOldCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
-    officialCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
-    officialCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
-    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
+    localImplementation 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
+    localOldImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
+    officialImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
+    officialImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
+    testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
 
-    // With a simple "compile", Gradle will always build these libraries in their default configuration
-    // (i.e. release), so we need to explicitly forward our own build configuration here (bug 1385695).
-    //
-    // Official builds compile a hacked up app:Official..Debug, but need the
-    // release versions of the dependencies, in order to not have debugging
-    // information.  It's not yet possible to specify just officialDebug, so we
-    // hack around it here.
-    if (mozconfig.substs.MOZILLA_OFFICIAL) {
-        debugCompile project(path: ':geckoview', configuration: "release")
-    } else {
-        debugCompile project(path: ':geckoview', configuration: "debug")
-    }
-    releaseCompile project(path: ':geckoview', configuration: "release")
+    implementation project(path: ':geckoview')
+    implementation project(path: ':thirdparty')
 
-    if (mozconfig.substs.MOZILLA_OFFICIAL) {
-        debugCompile project(path: ':thirdparty', configuration: "release")
-    } else {
-        debugCompile project(path: ':thirdparty', configuration: "debug")
-    }
-    releaseCompile project(path: ':thirdparty', configuration: "release")
-
-    testCompile 'junit:junit:4.12'
-    testCompile 'org.robolectric:robolectric:3.1.2'
-    testCompile 'org.simpleframework:simple-http:6.0.1'
-    testCompile 'org.mockito:mockito-core:1.10.19'
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.robolectric:robolectric:3.1.2'
+    testImplementation 'org.simpleframework:simple-http:6.0.1'
+    testImplementation 'org.mockito:mockito-core:1.10.19'
 
     // Including the Robotium JAR directly can cause issues with dexing.
-    androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.5.4'
+    androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.5.4'
 }
 
 // TODO: (bug 1261486): This impl is not robust -
 // we just wanted to land something.
 task checkstyle(type: Checkstyle) {
     configFile file("checkstyle.xml")
     // TODO: should use sourceSets from project instead of hard-coded str.
     source = ['../base/java/','../geckoview/src/main/java/']
@@ -376,17 +366,17 @@ android.applicationVariants.all { varian
     // :app uses :geckoview:release and handles it's own Gecko binary inclusion,
     // even though this would be most naturally done in the :geckoview project.
     if (!audienceDimension.equals('official')) {
         configureVariantWithGeckoBinaries(variant)
     }
 }
 
 android.applicationVariants.all { variant ->
-    configureVariantWithJNIWrappers(variant, "Fennec")
+    configureApplicationVariantWithJNIWrappers(variant, "Fennec")
 }
 
 if (gradle.startParameter.taskNames.any { it.endsWith('UnitTest') }) {
     // Approach cribbed from https://github.com/rwinch/jce-checker.
     int maxKeyLen = javax.crypto.Cipher.getMaxAllowedKeyLength("AES")
     if (maxKeyLen <= 128) {
         throw new GradleException(
             "Android unit tests require " +
@@ -413,17 +403,17 @@ android.applicationVariants.all { varian
         source = variant.javaCompile.source
         classpath = variant.javaCompile.classpath
 
         excludeFilter = file("findbugs-exclude.xml")
         dependsOn "assemble${variant.name.capitalize()}"
 
         reports {
             html.enabled = true // HTML reports for humans.
-            html.destination = "$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.html"
+            html.destination = file("$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.html")
             xml.enabled = false
         }
     }
 
     task("findbugsXml${variant.name.capitalize()}", type: FindBugs) {
         // TODO: figure out how to share the shared configuration.
         description "Analyze ${variant.name} code with findbugs (XML report)"
         group "Verification"
@@ -437,17 +427,17 @@ android.applicationVariants.all { varian
         source = variant.javaCompile.source
         classpath = variant.javaCompile.classpath
 
         excludeFilter = file("findbugs-exclude.xml")
         dependsOn "assemble${variant.name.capitalize()}"
 
         reports {
             xml.enabled = true // XML reports for machines.
-            xml.destination = "$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.xml"
+            xml.destination = file("$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.xml")
             html.enabled = false
         }
     }
 }
 
 // Bug 1353055 - Strip 'vars' debugging information to agree with moz.build.
 apply from: "${topsrcdir}/mobile/android/gradle/debug_level.gradle"
 android.applicationVariants.all configureVariantDebugLevel
@@ -463,18 +453,17 @@ android.applicationVariants.all { varian
     // Like 'local', 'localOld', or 'official'.
     def audienceDimension = variant.productFlavors[0].name
     if (!audienceDimension.equals('official')) {
         return
     }
 
     variant.outputs.each { output ->
         output.processManifest.doLast {
-            [output.processManifest.manifestOutputFile,
-             output.processManifest.instantRunManifestOutputFile,
+            [file("${manifestOutputDirectory}/AndroidManifest.xml"),
             ].each({ File manifestOutFile ->
                 if (manifestOutFile.exists()) {
                     def contents = manifestOutFile.getText('UTF-8')
 
                     // A non-validating, non-namespace aware XML processor.
                     def xml = new XmlSlurper(false, false).parseText(contents)
 
                     // First, reinstate our <activity-alias android:name=".App">.
--- a/mobile/android/app/src/photon/res/values/styles.xml
+++ b/mobile/android/app/src/photon/res/values/styles.xml
@@ -574,18 +574,18 @@
         <item name="android:layout_marginRight">5dip</item>
         <item name="android:layout_marginEnd">5dip</item>
         <item name="android:scaleType">fitCenter</item>
         <item name="android:layout_centerVertical">true</item>
         <item name="android:background">@drawable/action_bar_button_inverse</item>
     </style>
 
     <style name="PopupAnimation">
-        <item name="@android:windowEnterAnimation">@anim/popup_show</item>
-        <item name="@android:windowExitAnimation">@anim/popup_hide</item>
+        <item name="android:windowEnterAnimation">@anim/popup_show</item>
+        <item name="android:windowExitAnimation">@anim/popup_hide</item>
     </style>
 
     <style name="ToastBase">
         <item name="android:background">@drawable/toast_background</item>
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:layout_alignParentBottom">true</item>
         <item name="android:layout_centerHorizontal">true</item>
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -24,24 +24,29 @@
 
     <application android:label="@string/moz_app_displayname"
                  android:icon="@drawable/icon"
                  android:logo="@drawable/logo"
                  android:name="@MOZ_ANDROID_APPLICATION_CLASS@"
                  android:hardwareAccelerated="true"
                  android:supportsRtl="true"
                  android:allowBackup="false"
+#ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
+# Android-Gradle 3.0+ doesn't respect android:debuggable from AndroidManifest.xml.
+                 >
+#else
 # 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
+#endif
 
         <meta-data android:name="com.sec.android.support.multiwindow" android:value="true"/>
 
         <meta-data android:name="android.max_aspect" android:value="2.1"/>
 
 #ifdef MOZ_NATIVE_DEVICES
         <service android:name="org.mozilla.gecko.RemotePresentationService" android:exported="false"/>
 #endif
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -33,22 +33,18 @@ def computeVersionCode() {
         code += Integer.parseInt(parts[2])
     }
 
     return code;
 }
 
 android {
     compileSdkVersion project.ext.compileSdkVersion
-    buildToolsVersion project.ext.buildToolsVersion
 
     defaultConfig {
-        defaultPublishConfig 'release'
-        publishNonDefault true
-
         targetSdkVersion project.ext.targetSdkVersion
         minSdkVersion project.ext.minSdkVersion
         manifestPlaceholders = project.ext.manifestPlaceholders
 
         versionCode computeVersionCode()
         versionName "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
         consumerProguardFiles 'proguard-rules.txt'
 
@@ -149,18 +145,18 @@ android {
 
             assets {
             }
         }
     }
 }
 
 dependencies {
-    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-    compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
 }
 
 apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
 
 android.libraryVariants.all { variant ->
     // Like 'debug', 'release', or 'withGeckoBinaries'.
     def buildType = variant.buildType.name
 
@@ -213,17 +209,17 @@ android.libraryVariants.all { variant ->
         classifier 'sources'
         description = "Generate Javadoc for build variant $name"
         destinationDir = new File(destinationDir, variant.baseName)
         from files(variant.javaCompile.source)
     }
 }
 
 android.libraryVariants.all { variant ->
-    configureVariantWithJNIWrappers(variant, "Generated")
+    configureLibraryVariantWithJNIWrappers(variant, "Generated")
 }
 
 apply plugin: 'maven'
  
 uploadArchives {
     repositories.mavenDeployer {
         pom.groupId = 'org.mozilla'
         pom.artifactId = "geckoview-${mozconfig.substs.MOZ_UPDATE_CHANNEL}-${mozconfig.substs.ANDROID_CPU_ARCH}"
--- a/mobile/android/geckoview_example/build.gradle
+++ b/mobile/android/geckoview_example/build.gradle
@@ -1,15 +1,14 @@
 buildDir "${topobjdir}/gradle/build/mobile/android/geckoview_example"
 
 apply plugin: 'com.android.application'
 
 android {
     compileSdkVersion project.ext.compileSdkVersion
-    buildToolsVersion project.ext.buildToolsVersion
 
     defaultConfig {
         targetSdkVersion project.ext.targetSdkVersion
         minSdkVersion project.ext.minSdkVersion
         manifestPlaceholders = project.ext.manifestPlaceholders
 
         applicationId "org.mozilla.geckoview_example"
         versionCode 1
@@ -31,30 +30,27 @@ android {
         }
         withoutGeckoBinaries { // Logical negation of withGeckoBinaries.
             initWith debug
         }
     }
 }
 
 dependencies {
-    testCompile 'junit:junit:4.12'
+    testImplementation 'junit:junit:4.12'
 
-    compile 'com.android.support:support-annotations:23.4.0'
+    implementation 'com.android.support:support-annotations:23.4.0'
 
-    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
-    androidTestCompile 'com.android.support.test:runner:0.5'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
+    androidTestImplementation 'com.android.support.test:runner:0.5'
     // Not defining this library again results in test-app assuming 23.1.1, and the following errors:
     // "Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.4.0) and test app (23.1.1) differ."
-    androidTestCompile 'com.android.support:support-annotations:23.4.0'
+    androidTestImplementation 'com.android.support:support-annotations:23.4.0'
 
-    debugCompile project(path: ':geckoview', configuration: "debug")
-    releaseCompile project(path: ':geckoview', configuration: "release")
-    withGeckoBinariesCompile project(path: ':geckoview', configuration: "withGeckoBinaries")
-    withoutGeckoBinariesCompile project(path: ':geckoview', configuration: "withoutGeckoBinaries")
+    implementation project(path: ':geckoview')
 }
 
 apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
 
 android.applicationVariants.all { variant ->
     // Like 'debug', 'release', or 'withoutGeckoBinaries'.
     def buildType = variant.buildType.name
 
--- a/mobile/android/gradle.configure
+++ b/mobile/android/gradle.configure
@@ -87,24 +87,49 @@ def gradle_android_app_tasks(build_confi
         'app:assemble{app.variant.name}'.format(app=build_config.app),
         'app:assemble{app.variant.name}AndroidTest'.format(app=build_config.app),
     ]
 
 set_config('GRADLE_ANDROID_APP_TASKS', gradle_android_app_tasks)
 
 
 @depends(gradle_android_build_config, check_build_environment)
+@imports(_from='itertools', _import='imap')
 def gradle_android_app_apks(build_config, build_env):
     '''Paths to APK files produced by |mach android assemble-app|.'''
-    flavor = '-'.join(build_config.app.variant.productFlavors)
-    buildType = build_config.app.variant.buildType
-    f = '{}/gradle/build/mobile/android/app/outputs/apk/app-{}-{}.apk'
-    g = '{}/gradle/build/mobile/android/app/outputs/apk/app-{}-{}-androidTest.apk'
-    return namespace(app_apk=f.format(build_env.topobjdir, flavor, buildType),
-                     app_androidTest_apk=g.format(build_env.topobjdir, flavor, buildType))
+    def capitalize(s):
+        # str.capitalize lower cases trailing letters.
+        if s:
+            return s[0].upper() + s[1:]
+        else:
+            return s
+
+    def uncapitalize(s):
+        if s:
+            return s[0].lower() + s[1:]
+        else:
+            return s
+
+    # Like 'officialPhoton'.
+    productFlavor = uncapitalize(''.join(imap(capitalize, build_config.app.variant.productFlavors)))
+    # Like 'official-photon'.
+    product_flavor = '-'.join(build_config.app.variant.productFlavors)
+
+    substs = {
+        'topobjdir': build_env.topobjdir,
+        'productFlavor': productFlavor,
+        'product_flavor': product_flavor,
+        'buildType': build_config.app.variant.buildType,
+    }
+
+    f = '{topobjdir}/gradle/build/mobile/android/app/outputs/apk/{productFlavor}/{buildType}/app-{product_flavor}-{buildType}.apk'
+    g = '{topobjdir}/gradle/build/mobile/android/app/outputs/apk/androidTest/{productFlavor}/{buildType}/app-{product_flavor}-{buildType}-androidTest.apk'
+
+    return namespace(app_apk=f.format(**substs),
+                     app_androidTest_apk=g.format(**substs))
 
 set_config('GRADLE_ANDROID_APP_APK', gradle_android_app_apks.app_apk)
 set_config('GRADLE_ANDROID_APP_ANDROIDTEST_APK', gradle_android_app_apks.app_androidTest_apk)
 
 
 @depends(gradle_android_build_config)
 def gradle_android_test_tasks(build_config):
     '''Gradle tasks run by |mach android test|.'''
@@ -141,18 +166,18 @@ def gradle_android_findbugs_tasks(build_
     return [
         'app:findbugsXml{app.variant.name}'.format(app=build_config.app),
         'app:findbugsHtml{app.variant.name}'.format(app=build_config.app),
     ]
 
 set_config('GRADLE_ANDROID_FINDBUGS_TASKS', gradle_android_findbugs_tasks)
 
 
-@dependable
-def gradle_android_archive_geckoview_tasks():
+@depends(gradle_android_build_config)
+def gradle_android_archive_geckoview_tasks(build_config):
     '''Gradle tasks run by |mach android archive-geckoview|.'''
     return [
         'geckoview:assembleWithGeckoBinaries',
         'geckoview_example:assembleWithGeckoBinaries',
         'geckoview_example:assembleWithGeckoBinariesAndroidTest',
         'geckoview:uploadArchives',
     ]
 
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -1,9 +1,9 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+/* -*- Mode: Groovy; c-basic-offset: 4; tab-width: 20; 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/. */
 
 // We run fairly hard into a fundamental limitation of the Android Gradle
 // plugin.  There are many bugs filed about this, but
 // https://code.google.com/p/android/issues/detail?id=216978#c6 is a reason one.
 // The issue is that we need fine-grained control over when to include Gecko's
@@ -121,68 +121,89 @@ ext.configureVariantWithGeckoBinaries = 
     generateAssetsTask.dependsOn syncAssetsFromDistDir
 
     def sourceSet = productFlavor ? "${productFlavor}${buildType.capitalize()}" : buildType
     android.sourceSets."${sourceSet}".assets.srcDir syncOmnijarFromDistDir.destinationDir
     android.sourceSets."${sourceSet}".assets.srcDir syncAssetsFromDistDir.destinationDir
     android.sourceSets."${sourceSet}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
 }
 
-ext.configureVariantWithJNIWrappers = { variant, module ->
-    def jarTask
-    if (module == 'Generated') {
-        jarTask = tasks["package${variant.name.capitalize()}JarArtifact"]
-    } else {
-        jarTask = tasks["jar${variant.name.capitalize()}Classes"]
-    }
-
-    if (jarTask.outputs.files.size() != 1) {
-        throw new GradleException("Jar task output multiple files other than one single jar")
-    }
-
-    // At configuration time, the classpath of dependencies with internal_impl
-    // JAR files may not be correct.  This manifests in
+ext.configureLibraryVariantWithJNIWrappers = { variant, module ->
+    // Library variants have two essentially independent transform* tasks:
+    //
+    // - ...WithSyncLibJars... is used by assemble* and bundle*
+    // - ...WithPrepareIntermediateJars... is used by consuming applications.
     //
-    // 'Exception in thread "main" java.lang.NoClassDefFoundError: android/support/v4/app/ActivityCompatApi23$RequestPermissionsRequestCodeValidator'
-    //
-    // when running |mach gradle clean app:generateJNI...|.  We work around this
-    // by configuring the classpath at evaluation-time, not configuration-time.
-    //
-    // The specific dependency on the `prepareDependencies` task may not be
-    // necessary, but commits like
-    // https://github.com/evant/gradle-retrolambda/commit/15108c65ee43be499a1359d9d4f88b0851d46769
-    // suggest that it is.  It certainly doesn't hurt.
-    def prepareDependenciesTask = tasks.getByName("prepare${variant.name.capitalize()}Dependencies")
+    // It's not really possible to insert something immediately _after_
+    // ...WithPrepareIntermediateJars..., so we make the consuming moz.build
+    // system invoke this target directly, and force the
+    // ...WithPrepareIntermediateJars... dependency.  The real consumer of the
+    // JNI wrappers is the moz.build system, which always builds geckoview to
+    // consume from Fennec, so that dependency likely adds less to the build time.
+    def jarTask = tasks["transformClassesAndResourcesWithPrepareIntermediateJarsFor${variant.name.capitalize()}"]
+    def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })
 
     def wrapperTask
     if (System.env.IS_LANGUAGE_REPACK == '1') {
         // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
         // really have a build environment.
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
     } else {
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
             classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
-    
+        
             // Configure the classpath at evaluation-time, not at
             // configuration-time: see above comment.
             doFirst {
                 classpath variant.javaCompile.classpath
                 // Include android.jar.
                 classpath variant.javaCompile.options.bootClasspath
             }
     
             main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
             args module
-            args jarTask.outputs.files.iterator().next()
-    
+            args output
+            
             workingDir "${topobjdir}/mobile/android/base"
-    
+            
             dependsOn jarTask
-            dependsOn prepareDependenciesTask
         }
     }
 
     if (module == 'Generated') {
         tasks["bundle${variant.name.capitalize()}"].dependsOn wrapperTask
     } else {
         tasks["assemble${variant.name.capitalize()}"].dependsOn wrapperTask
     }
 }
+
+ext.configureApplicationVariantWithJNIWrappers = { variant, module ->
+    def jarTask = tasks["bundleAppClasses${variant.name.capitalize()}"]
+    def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })
+
+    def wrapperTask
+    if (System.env.IS_LANGUAGE_REPACK == '1') {
+        // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
+        // really have a build environment.
+        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
+    } else {
+        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
+            classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
+        
+            // Configure the classpath at evaluation-time, not at
+            // configuration-time: see above comment.
+            doFirst {
+                classpath variant.javaCompile.classpath
+                // Include android.jar.
+                classpath variant.javaCompile.options.bootClasspath
+            }
+        
+            main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
+            args module
+            args output
+        
+            workingDir "${topobjdir}/mobile/android/base"
+    
+            // This forces bundling, which isn't usually part of the assemble* process.
+            dependsOn jarTask
+        }
+    }
+}
--- a/mobile/android/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -1,20 +1,16 @@
 buildDir "${topobjdir}/gradle/build/mobile/android/thirdparty"
 
 apply plugin: 'com.android.library'
 
 android {
     compileSdkVersion project.ext.compileSdkVersion
-    buildToolsVersion project.ext.buildToolsVersion
 
     defaultConfig {
-        defaultPublishConfig 'release'
-        publishNonDefault true
-
         targetSdkVersion project.ext.targetSdkVersion
         minSdkVersion project.ext.minSdkVersion
         manifestPlaceholders = project.ext.manifestPlaceholders
     }
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
@@ -42,21 +38,21 @@ android {
                 // here is only the no-op library for mach-based builds.
                 exclude 'com/squareup/leakcanary/**'
             }
         }
     }
 }
 
 dependencies {
-    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    implementation "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     if (mozconfig.substs.MOZ_ANDROID_MMA) {
-        compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-        compile "com.android.support:support-annotations:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+        implementation "com.android.support:support-annotations:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        implementation "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
     }
 }
 
 apply plugin: 'idea'
 
 idea {
     module {
         // This is cosmetic.  See the excludes in the root project.
--- a/old-configure.in
+++ b/old-configure.in
@@ -2100,17 +2100,17 @@ AC_SUBST(MOZ_MULET)
 
 dnl ========================================================
 dnl Ensure Android SDK and build-tools versions depending on
 dnl mobile target.
 dnl ========================================================
 
 case "$MOZ_BUILD_APP" in
 mobile/android)
-    MOZ_ANDROID_SDK(23, 23, 25.0.3, 26.0.0 26.0.0-dev 25.3.2 25.3.1)
+    MOZ_ANDROID_SDK(23, 23, 26.0.2, 26.0.0 26.0.0-dev 25.3.2 25.3.1)
     ;;
 esac
 
 dnl ========================================================
 dnl =
 dnl = Toolkit Options
 dnl =
 dnl ========================================================
--- a/python/mozboot/mozboot/android-packages.txt
+++ b/python/mozboot/mozboot/android-packages.txt
@@ -1,6 +1,6 @@
 platform-tools
-build-tools;25.0.3
+build-tools;26.0.2
 platforms;android-23
 extras;android;m2repository
 extras;google;m2repository
 emulator
--- a/taskcluster/scripts/misc/android-gradle-dependencies/after.sh
+++ b/taskcluster/scripts/misc/android-gradle-dependencies/after.sh
@@ -1,16 +1,16 @@
 #!/bin/bash -vex
 
 set -x -e
 
 echo "running as" $(id)
 
 : WORKSPACE ${WORKSPACE:=/builds/worker/workspace}
-: GRADLE_VERSION ${GRADLE_VERSION:=3.4.1}
+: GRADLE_VERSION ${GRADLE_VERSION:=4.1}
 
 set -v
 
 # Package everything up.
 pushd $WORKSPACE
 mkdir -p android-gradle-dependencies /builds/worker/artifacts
 
 cp -R ${NEXUS_WORK}/storage/jcenter android-gradle-dependencies