Merge f-t to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 06 Oct 2016 19:59:02 -0700
changeset 359705 4b9944879c9a60a9aba4a744a7401bc38e0f39c4
parent 359689 a5b04b518afe0eadef15f5dd3e5e7a8408d75a67 (current diff)
parent 359704 37f78aca862224d7151c0fcae1ed8373fe11c83b (diff)
child 359722 7affb66131bb766fab016f554ab1d0b97da72d41
child 359773 e907031a16123846a0817935a3f64b67286a5d73
child 359778 0e67f06bcfcc83f4317a968914232bb6c82965ac
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
4b9944879c9a / 52.0a1 / 20161007030207 / files
nightly linux64
4b9944879c9a / 52.0a1 / 20161007030207 / files
nightly mac
4b9944879c9a / 52.0a1 / 20161007030207 / files
nightly win32
4b9944879c9a / 52.0a1 / 20161007030207 / files
nightly win64
4b9944879c9a / 52.0a1 / 20161007030207 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge f-t to m-c, a=merge MozReview-Commit-ID: HiqZsOM1Bcz
mobile/android/base/moz.build
widget/nsBaseWidget.h
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
+skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   examples/bundle.js
   examples/bundle.js.map
   examples/doc-scripts.html
   examples/doc-script-switching.html
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -84,17 +84,16 @@ android {
         main {
             manifest.srcFile "${project.buildDir}/generated/source/preprocessed_manifest/AndroidManifest.xml"
 
             aidl {
                 srcDir "${topsrcdir}/mobile/android/base/aidl"
             }
 
             java {
-                srcDir "${topsrcdir}/mobile/android/geckoview/src/main/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"
                 }
@@ -225,16 +224,17 @@ dependencies {
     // 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'
     automationCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
     testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
 
+    compile project(':geckoview')
     compile project(':thirdparty')
 
     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'
 
     // Including the Robotium JAR directly can cause issues with dexing.
@@ -248,50 +248,22 @@ task checkstyle(type: Checkstyle) {
     // TODO: should use sourceSets from project instead of hard-coded str.
     source '../base/java/'
     // TODO: This ignores our pre-processed resources.
     include '**/*.java'
     // TODO: classpath should probably be something.
     classpath = files()
 }
 
-task syncOmnijarFromDistDir(type: Sync) {
-    into("${project.buildDir}/generated/omnijar")
-    from("${topobjdir}/dist/fennec/assets") {
-        include 'omni.ja'
-    }
-}
-
-task checkLibsExistInDistDir<< {
-    if (syncLibsFromDistDir.source.empty) {
-        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
-    }
-}
-
-task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
-    into("${project.buildDir}/generated/jniLibs")
-    from("${topobjdir}/dist/fennec/lib")
-}
-
-task checkAssetsExistInDistDir<< {
-    if (syncAssetsFromDistDir.source.empty) {
-        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
-    }
-}
-
-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")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
+        // All other preprocessed code is included in the geckoview project.
+        include '**/AdjustConstants.java'
+    }
 }
 
 // The localization system uses the moz.build preprocessor to interpolate a .dtd
 // file of XML entity definitions into an XML file of elements referencing those
 // entities.  (Each locale produces its own .dtd file, backstopped by the en-US
 // .dtd file in tree.)  Android Studio (and IntelliJ) don't handle these inline
 // entities smoothly.  This filter merely expands the entities in place, making
 // them appear properly throughout the IDE.  Be aware that this assumes that the
@@ -307,82 +279,48 @@ class ExpandXMLEntitiesFilter extends Fi
 task syncPreprocessedResources(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_resources")
     from("${topobjdir}/mobile/android/base/res")
     filesMatching('**/strings.xml') {
         filter(ExpandXMLEntitiesFilter)
     }
 }
 
-// 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
-
-    // See comment in :omnijar project regarding interface mismatches here.
-    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs
-
-    // Produce a single output file.
-    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
-
-    workingDir "${topobjdir}"
-
-    commandLine mozconfig.substs.GMAKE
-    args '-C'
-    args "${topobjdir}/mobile/android/base"
-    args 'gradle-omnijar'
-
-    // 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()}")
-        }
-    }
-}
-
 // It's not easy -- see the backout in Bug 1242213 -- to change the <manifest>
 // package for Fennec.  Gradle has grown a mechanism to achieve what we want for
 // Fennec, however, with applicationId.  To use the same manifest as moz.build,
 // we replace the package with org.mozilla.gecko (the eventual package) here.
 task rewriteManifestPackage(type: Copy, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_manifest")
     from("${topobjdir}/mobile/android/base/AndroidManifest.xml")
     filter { it.replaceFirst(/package=".*?"/, 'package="org.mozilla.gecko"') }
 }
 
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
 android.applicationVariants.all { variant ->
     variant.preBuild.dependsOn rewriteManifestPackage
     variant.preBuild.dependsOn syncPreprocessedCode
     variant.preBuild.dependsOn syncPreprocessedResources
 
+    // Automation builds don't include Gecko binaries, since those binaries are
+    // not produced until after build time (at package time).  Therefore,
+    // automation builds include the Gecko binaries into the APK at package
+    // time.  The "withGeckoBinaries" variant of the :geckoview project also
+    // does this.  (It does what it says on the tin!)  For notes on this
+    // approach, see mobile/android/gradle/with_gecko_binaries.gradle.
+
     // 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
+    // :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 (!productFlavor.equals('automation')) {
+        configureVariantWithGeckoBinaries(variant)
     }
-
-    syncOmnijarFromDistDir.dependsOn buildOmnijar
-    def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
-    generateAssetsTask.dependsOn syncOmnijarFromDistDir
-    generateAssetsTask.dependsOn syncLibsFromDistDir
-    generateAssetsTask.dependsOn syncAssetsFromDistDir
-
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncOmnijarFromDistDir.destinationDir
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncAssetsFromDistDir.destinationDir
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
 }
 
 apply plugin: 'spoon'
 
 spoon {
     // For now, let's be verbose.
     debug = true
     // It's not helpful to pass when we don't have a device connected.
@@ -421,37 +359,37 @@ afterEvaluate {
 
 // Bug 1299015: Complain to treeherder if checkstyle, lint, or unittest fails.  It's not obvious
 // how to listen to individual errors in most cases, so we just link to the reports for now.
 def makeTaskExecutionListener(artifactRootUrl) {
     return new TaskExecutionListener() {
         void beforeExecute(Task task) {
             // Do nothing.
         }
-    
+
         void afterExecute(Task task, TaskState state) {
             if (!state.failure) {
                 return
             }
-    
+
             // Link to the failing report.  The task path and the report path
             // depend on the android-lint task in
             // taskcluster/ci/android-stuff/kind.yml.  It's not possible to link
             // directly, so for now consumers will need to copy-paste the URL.
             switch (task.path) {
             case ':app:checkstyle':
                 def url = "${artifactRootUrl}/public/android/checkstyle/checkstyle.xml"
                 println "TEST-UNEXPECTED-FAIL | android-checkstyle | Checkstyle rule violations were found. See the report at: $url"
                 break
-    
+
             case ':app:lintAutomationDebug':
                 def url = "${artifactRootUrl}/public/android/lint/lint-results-automationDebug.html"
                 println "TEST-UNEXPECTED-FAIL | android-lint | Lint found errors in the project; aborting build. See the report at: $url"
                 break
-    
+
             case ':app:testAutomationDebugUnitTest':
                 def url = "${artifactRootUrl}/public/android/unittest/automationDebug/index.html"
                 println "TEST-UNEXPECTED-FAIL | android-test | There were failing tests. See the report at: $url"
                 break
             }
         }
     }
 }
--- a/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
@@ -30,26 +30,26 @@
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "dotgradle.tar.xz",
 "unpack": true,
 "digest": "9f082ccd71ad18991eb71fcad355c6990f50a72a09ab9b79696521485656083a72faf5a8d4714de9c4b901ee2319b6786a51964846bb7075061642a8505501c2",
 "size": 512
--- a/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -45,26 +45,26 @@
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "size": 30899096,
 "visibility": "public",
 "digest": "ac9f5f95d11580d3dbeff87e80a585fe4d324b270dabb91b1165686acab47d99fa6651074ab0be09420239a5d6af38bb2c539506962a7b44e0ed4d080bba2953",
 "algorithm": "sha512",
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -55,26 +55,26 @@
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
 "size": 97552448,
 "digest": "272438c1692a46998dc44f22bd1fe18da1be7af2e7fdcf6c52709366c80c73e30637f0c3864f45c64edf46ce6a905538c14b2313983be973f9f29a2f191ec89b",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/build.gradle
@@ -0,0 +1,86 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview"
+
+apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        targetSdkVersion 23
+        minSdkVersion 15
+    }
+
+    buildTypes {
+        withGeckoBinaries {
+            initWith release
+        }
+        withoutGeckoBinaries { // For clarity and consistency throughout the tree.
+            initWith release
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    dexOptions {
+        javaMaxHeapSize "2g"
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    sourceSets {
+        main {
+            java {
+                srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
+
+                // TODO: support WebRTC.
+                // 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"
+                // }
+
+                // TODO: don't use AppConstants.
+                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
+            }
+
+            assets {
+            }
+        }
+    }
+}
+
+dependencies {
+    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+}
+
+task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
+    into("${project.buildDir}/generated/source/preprocessed_code")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
+        // AdjustConstants is included in the main app project.
+        exclude '**/AdjustConstants.java'
+    }
+}
+
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
+android.libraryVariants.all { variant ->
+    variant.preBuild.dependsOn syncPreprocessedCode
+
+    // Like 'debug', 'release', or 'withGeckoBinaries'.
+    def buildType = variant.buildType.name
+
+    // It would be most natural for :geckoview to always include the Gecko
+    // binaries, but that's difficult; see the notes in
+    // mobile/android/gradle/with_gecko_binaries.gradle.  Instead :app uses
+    // :geckoview:release and handles it's own Gecko binary inclusion.
+    if (buildType.equals('withGeckoBinaries')) {
+        configureVariantWithGeckoBinaries(variant)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.mozilla.geckoview">
+
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <!-- READ_EXTERNAL_STORAGE was added in API 16, and is only enforced in API
+         19+.  We declare it so that the bouncer APK and the main APK have the
+         same set of permissions. -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+    <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+
+    <uses-feature android:name="android.hardware.location" android:required="false"/>
+    <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+    <uses-feature android:name="android.hardware.touchscreen"/>
+
+    <!--#ifdef MOZ_WEBRTC-->
+    <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
+    <!--<uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
+    <!--<uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
+    <!--<uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
+    <!--#endif-->
+
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera" android:required="false"/>
+    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+
+    <!-- App requires OpenGL ES 2.0 -->
+    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/build.gradle
@@ -0,0 +1,62 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview_example"
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        applicationId "org.mozilla.geckoview_example"
+        minSdkVersion 15
+        targetSdkVersion 23
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    // This is extremely frustrating, but the only way to do it automation for
+    // now.  Without this, we only get a "debugAndroidTest" configuration; we
+    // have no "withoutGeckoBinariesAndroidTest" configuration.
+    testBuildType "withoutGeckoBinaries"
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+        withGeckoBinaries { // For consistency with :geckoview project in Task Cluster invocations.
+            initWith debug
+        }
+        withoutGeckoBinaries { // Logical negation of withGeckoBinaries.
+            initWith debug
+        }
+    }
+}
+
+dependencies {
+    testCompile 'junit:junit:4.12'
+
+    compile 'com.android.support:support-annotations:23.0.1'
+
+    // Later versions (2.2.2, 0.5) requires newer support libraries, leading to
+    // "Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.0.1) and test app (23.1.1) differ."
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+    androidTestCompile 'com.android.support.test:runner:0.4.1'
+
+    compile project(':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
+
+    // It would be most natural for :geckoview to always include the Gecko
+    // binaries, but that's difficult; see the notes in
+    // mobile/android/gradle/with_gecko_binaries.gradle.  Instead we handle our
+    // own Gecko binary inclusion.
+    if (!buildType.equals('withoutGeckoBinaries')) {
+        configureVariantWithGeckoBinaries(variant)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/nalexander/.mozbuild/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/ApplicationTest.java
@@ -0,0 +1,13 @@
+package org.mozilla.geckoview_example;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/GeckoViewActivityTest.java
@@ -0,0 +1,32 @@
+/* -*- Mode: Java; 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+@RunWith(AndroidJUnit4.class)
+public class GeckoViewActivityTest {
+
+    @Rule
+    public ActivityTestRule<GeckoViewActivity> mActivityRule = new ActivityTestRule(GeckoViewActivity.class);
+
+    @Test
+    public void testA() throws InterruptedException {
+        onView(withId(R.id.gecko_view))
+                .check(matches(isDisplayed()));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="org.mozilla.geckoview_example">
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name"
+                 android:supportsRtl="true">
+
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="org.mozilla.geckoview_example.GeckoViewActivity"
+                  android:label="GeckoViewActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -0,0 +1,142 @@
+/* -*- Mode: Java; 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.GeckoView;
+import org.mozilla.gecko.PrefsHelper;
+
+public class GeckoViewActivity extends Activity {
+    private static final String LOGTAG = "GeckoViewActivity";
+
+    GeckoView mGeckoView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.geckoview_activity);
+
+        mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
+        mGeckoView.setChromeDelegate(new MyGeckoViewChrome());
+        mGeckoView.setContentDelegate(new MyGeckoViewContent());
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
+
+        GeckoThread.init(profile, /* args */ null, /* action */ null, /* debugging */ false);
+        GeckoThread.launch();
+    }
+
+    private class MyGeckoViewChrome implements GeckoView.ChromeDelegate {
+        @Override
+        public void onReady(GeckoView view) {
+            Log.i(LOGTAG, "Gecko is ready");
+            // // Inject a script that adds some code to the content window
+            // mGeckoView.importScript("resource://android/assets/script.js");
+
+            // Set up remote debugging to a port number
+            PrefsHelper.setPref("layers.dump", true);
+            PrefsHelper.setPref("devtools.debugger.remote-port", 6000);
+            PrefsHelper.setPref("devtools.debugger.unix-domain-socket", "");
+            PrefsHelper.setPref("devtools.debugger.remote-enabled", true);
+
+            // The Gecko libraries have finished loading and we can use the rendering engine.
+            // Let's add a browser (required) and load a page into it.
+            // mGeckoView.addBrowser(getResources().getString(R.string.default_url));
+        }
+
+        @Override
+        public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Alert!");
+            result.confirm();
+            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
+        }
+
+        @Override
+        public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, final GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Confirm!");
+            new AlertDialog.Builder(GeckoViewActivity.this)
+                .setTitle("javaScript dialog")
+                .setMessage(message)
+                .setPositiveButton(android.R.string.ok,
+                                   new DialogInterface.OnClickListener() {
+                                       public void onClick(DialogInterface dialog, int which) {
+                                           result.confirm();
+                                       }
+                                   })
+                .setNegativeButton(android.R.string.cancel,
+                                   new DialogInterface.OnClickListener() {
+                                       public void onClick(DialogInterface dialog, int which) {
+                                           result.cancel();
+                                       }
+                                   })
+                .create()
+                .show();
+        }
+
+        @Override
+        public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result) {
+            result.cancel();
+        }
+
+        @Override
+        public void onDebugRequest(GeckoView view, GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Remote Debug!");
+            result.confirm();
+        }
+
+        @Override
+        public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result) {
+            Log.i(LOGTAG, "Got Script Message: " + data.toString());
+            String type = data.getString("type");
+            if ("fetch".equals(type)) {
+                Bundle ret = new Bundle();
+                ret.putString("name", "Mozilla");
+                ret.putString("url", "https://mozilla.org");
+                result.success(ret);
+            }
+        }
+    }
+
+    private class MyGeckoViewContent implements GeckoView.ContentDelegate {
+        @Override
+        public void onPageStart(GeckoView view, GeckoView.Browser browser, String url) {
+
+        }
+
+        @Override
+        public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success) {
+
+        }
+
+        @Override
+        public void onPageShow(GeckoView view, GeckoView.Browser browser) {
+
+        }
+
+        @Override
+        public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title) {
+            Log.i(LOGTAG, "Received a title: " + title);
+        }
+
+        @Override
+        public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size) {
+            Log.i(LOGTAG, "Received a favicon URL: " + url);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml
@@ -0,0 +1,13 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:orientation="vertical">
+
+    <org.mozilla.gecko.GeckoView
+        android:id="@+id/gecko_view"
+        android:layout_width="fill_parent"
+        android:layout_height="match_parent"
+        android:scrollbars="none"
+        />
+
+</LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">geckoview_example</string>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/test/java/org/mozilla/geckoview_example/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package org.mozilla.geckoview_example;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() throws Exception {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -0,0 +1,105 @@
+// 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
+// binary libraries into the GeckoView AAR and the Fennec APK, and that's hard
+// to achieve.  In particular:
+//
+// * :app:automation wants :geckoview to not include Gecko binaries (automation
+// * build, before package)
+//
+// * :geckoview:withLibraries wants :geckoview to include Gecko binaries
+// * (automation build, after package)
+//
+// * non-:app:automation wants :geckoview to include Gecko binaries (local
+// * build, always after package)
+//
+// publishNonDefault (see
+// http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication)
+// is intended to address this, but doesn't handle our case.  That option always
+// builds *all* configurations, which fails when the required Gecko binaries
+// don't exist (automation build, before package).  So instead, we make both
+// :app and :geckoview both know how to include the Gecko binaries, and use a
+// non-default, non-published :geckoview:withGeckoBinaries configuration to
+// handle automation's needs.  Simple, right?
+
+// 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
+
+    // See comment in :omnijar project regarding interface mismatches here.
+    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs
+
+    // Produce a single output file.
+    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
+
+    workingDir "${topobjdir}"
+
+    commandLine mozconfig.substs.GMAKE
+    args '-C'
+    args "${topobjdir}/mobile/android/base"
+    args 'gradle-omnijar'
+
+    // 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()}")
+        }
+    }
+}
+
+task syncOmnijarFromDistDir(type: Sync) {
+    into("${project.buildDir}/generated/omnijar")
+    from("${topobjdir}/dist/fennec/assets") {
+        include 'omni.ja'
+    }
+}
+
+task checkLibsExistInDistDir<< {
+    if (syncLibsFromDistDir.source.empty) {
+        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
+    }
+}
+
+task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
+    into("${project.buildDir}/generated/jniLibs")
+    from("${topobjdir}/dist/fennec/lib")
+}
+
+task checkAssetsExistInDistDir<< {
+    if (syncAssetsFromDistDir.source.empty) {
+        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
+    }
+}
+
+task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
+    into("${project.buildDir}/generated/assets")
+    from("${topobjdir}/dist/fennec/assets") {
+        exclude 'omni.ja'
+    }
+}
+
+ext.configureVariantWithGeckoBinaries = { variant ->
+    // Like 'local' or 'localOld'; may be null.
+    def productFlavor = variant.productFlavors ? variant.productFlavors[0].name : ""
+    // Like 'debug' or 'release'.
+    def buildType = variant.buildType.name
+
+    syncOmnijarFromDistDir.dependsOn buildOmnijar
+    def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
+    generateAssetsTask.dependsOn syncOmnijarFromDistDir
+    generateAssetsTask.dependsOn syncLibsFromDistDir
+    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
+}
--- a/mobile/android/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -20,17 +20,16 @@ android {
         abortOnError false
     }
 
     sourceSets {
         main {
             manifest.srcFile 'AndroidManifest.xml'
             java {
                 srcDir '.'
-                srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
 
                 if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
                     exclude 'com/adjust/**'
                 }
 
                 // Exclude LeakCanary: It will be added again via a gradle dependency. This version
                 // here is only the no-op library for mach-based builds.
                 exclude 'com/squareup/leakcanary/**'
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,20 +24,24 @@ 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 ':geckoview'
+include ':geckoview_example'
 include ':omnijar'
 include ':thirdparty'
 
 project(':app').projectDir = new File("${json.topsrcdir}/mobile/android/app")
+project(':geckoview').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview")
+project(':geckoview_example').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview_example")
 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")
 }
 
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -110,20 +110,28 @@ android-api-15-gradle/opt:
         tier: 2
     worker-type: aws-provisioner-v1/android-api-15
     worker:
         implementation: docker-worker
         max-run-time: 7200
         env:
             # Bug 1292762 - Set GRADLE_USER_HOME to avoid sdk-manager-plugin intermittent
             GRADLE_USER_HOME: /home/worker/workspace/build/src/dotgradle
+        artifacts:
+          - name: public/android/geckoview.aar
+            path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/outputs/aar/geckoview-withGeckoBinaries.aar
+            type: file
+          - name: public/android/geckoview_example.apk
+            path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/geckoview_example-withGeckoBinaries.apk
+            type: file
+          - name: public/build
+            path: /home/worker/artifacts/
+            type: directory
     run:
         using: mozharness
         actions: [get-secrets build multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
         script: "mozharness/scripts/fx_desktop_build.py"
         custom-build-variant-cfg: api-15-gradle
         tooltool-downloads: internal
-
-
--- a/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle.py
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle.py
@@ -1,8 +1,17 @@
 config = {
     'base_name': 'Android armv7 API 15+ %(branch)s Gradle',
     'stage_platform': 'android-api-15-gradle',
     'build_type': 'api-15-gradle',
     'src_mozconfig': 'mobile/android/config/mozconfigs/android-api-15-gradle/nightly',
     'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android/releng.manifest',
     'multi_locale_config_platform': 'android',
+    # It's not obvious, but postflight_build is after packaging, so the Gecko
+    # binaries are in the object directory, ready to be packaged into the
+    # GeckoView AAR.
+    'postflight_build_mach_commands': [
+        ['gradle',
+         'geckoview:assembleWithGeckoBinaries',
+         'geckoview_example:assembleWithGeckoBinaries',
+        ],
+    ],
 }
--- a/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle_dependencies.py
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle_dependencies.py
@@ -6,11 +6,16 @@ config = {
     'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-gradle-dependencies/releng.manifest',
     'multi_locale_config_platform': 'android',
     'postflight_build_mach_commands': [
         ['gradle',
          'assembleAutomationRelease',
          'assembleAutomationDebug',
          'assembleAutomationDebugAndroidTest',
          'checkstyle',
+         # Does not include Gecko binaries -- see mobile/android/gradle/with_gecko_binaries.gradle.
+         'geckoview:assembleWithoutGeckoBinaries',
+         # So that we pick up the test dependencies for the builders.
+         'geckoview_example:assembleWithoutGeckoBinaries',
+         'geckoview_example:assembleWithoutGeckoBinariesAndroidTest',
         ],
     ],
 }
--- a/toolkit/components/telemetry/TelemetryStorage.jsm
+++ b/toolkit/components/telemetry/TelemetryStorage.jsm
@@ -45,17 +45,17 @@ XPCOMUtils.defineLazyGetter(this, "gAbor
   return OS.Path.join(gDataReportingDir, ABORTED_SESSION_FILE_NAME);
 });
 XPCOMUtils.defineLazyGetter(this, "gDeletionPingFilePath", function() {
   return OS.Path.join(gDataReportingDir, DELETION_PING_FILE_NAME);
 });
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 // Maxmimum time, in milliseconds, archive pings should be retained.
-const MAX_ARCHIVED_PINGS_RETENTION_MS = 180 * 24 * 60 * 60 * 1000;  // 180 days
+const MAX_ARCHIVED_PINGS_RETENTION_MS = 60 * 24 * 60 * 60 * 1000;  // 60 days
 
 // Maximum space the archive can take on disk (in Bytes).
 const ARCHIVE_QUOTA_BYTES = 120 * 1024 * 1024; // 120 MB
 // Maximum space the outgoing pings can take on disk, for Desktop (in Bytes).
 const PENDING_PINGS_QUOTA_BYTES_DESKTOP = 15 * 1024 * 1024; // 15 MB
 // Maximum space the outgoing pings can take on disk, for Mobile (in Bytes).
 const PENDING_PINGS_QUOTA_BYTES_MOBILE = 1024 * 1024; // 1 MB
 
--- a/toolkit/components/telemetry/tests/unit/test_PingAPI.js
+++ b/toolkit/components/telemetry/tests/unit/test_PingAPI.js
@@ -231,16 +231,17 @@ add_task(function* test_archiveCleanup()
                 "Ping" + expectedInfo.id + " should be in the archive.");
     }
   });
 
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SESSION_PING_COUNT").clear();
 
   // Create a ping which should be pruned because it is past the retention period.
   let date = fakeNow(2010, 1, 1, 1, 0, 0);
+  let firstDate = date;
   let pingId = yield TelemetryController.submitExternalPing(PING_TYPE, {}, {});
   expectedPrunedInfo.push({ id: pingId, creationDate: date });
 
   // Create a ping which should be kept because it is within the retention period.
   const oldestDirectoryDate = fakeNow(2010, 2, 1, 1, 0, 0);
   pingId = yield TelemetryController.submitExternalPing(PING_TYPE, {}, {});
   expectedNotPrunedInfo.push({ id: pingId, creationDate: oldestDirectoryDate });
 
@@ -256,18 +257,18 @@ add_task(function* test_archiveCleanup()
 
   // We expect all the pings we archived to be in this histogram.
   h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SESSION_PING_COUNT");
   Assert.equal(h.snapshot().sum, 22, "All the pings must be live-accumulated in the histogram.");
   // Reset the histogram that will be populated by the archive scan.
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS").clear();
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_OLDEST_DIRECTORY_AGE").clear();
 
-  // Move the current date 180 days ahead of the first ping.
-  let now = fakeNow(2010, 7, 1, 1, 0, 0);
+  // Move the current date 60 days ahead of the first ping.
+  let now = fakeNow(futureDate(firstDate, 60 * MILLISECONDS_PER_DAY));
   // Reset TelemetryArchive and TelemetryController to start the startup cleanup.
   yield TelemetryController.testReset();
   // Wait for the cleanup to finish.
   yield TelemetryStorage.testCleanupTaskPromise();
   // Then scan the archived dir.
   yield TelemetryArchive.promiseArchivedPingList();
 
   // Check that the archive is in the correct state.
@@ -278,29 +279,29 @@ add_task(function* test_archiveCleanup()
   Assert.equal(h.sum, 21, "The histogram must count all the pings in the archive.");
   // One directory out of four was removed as well.
   h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS").snapshot();
   Assert.equal(h.sum, 1, "Telemetry must correctly report removed archive directories.");
   // Check that the remaining directories are correctly counted.
   h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_DIRECTORIES_COUNT").snapshot();
   Assert.equal(h.sum, 3, "Telemetry must correctly report the remaining archive directories.");
   // Check that the remaining directories are correctly counted.
-  const oldestAgeInMonths = 5;
+  const oldestAgeInMonths = 1;
   h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_OLDEST_DIRECTORY_AGE").snapshot();
   Assert.equal(h.sum, oldestAgeInMonths,
                "Telemetry must correctly report age of the oldest directory in the archive.");
 
   // We need to test the archive size before we hit the quota, otherwise a special
   // value is recorded.
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SIZE_MB").clear();
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OVER_QUOTA").clear();
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTING_OVER_QUOTA_MS").clear();
 
-  // Move the current date 180 days ahead of the second ping.
-  fakeNow(2010, 8, 1, 1, 0, 0);
+  // Move the current date 60 days ahead of the second ping.
+  fakeNow(futureDate(oldestDirectoryDate, 60 * MILLISECONDS_PER_DAY));
   // Reset TelemetryController and TelemetryArchive.
   yield TelemetryController.testReset();
   // Wait for the cleanup to finish.
   yield TelemetryStorage.testCleanupTaskPromise();
   // Then scan the archived dir again.
   yield TelemetryArchive.promiseArchivedPingList();
 
   // Move the oldest ping to the unexpected pings list.
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -286,18 +286,18 @@ GMPWrapper.prototype = {
                       " - update task already running");
       return this._updateTask;
     }
 
     this._updateTask = Task.spawn(function*() {
       this._log.trace("findUpdates() - updateTask");
       try {
         let installManager = new GMPInstallManager();
-        let gmpAddons = yield installManager.checkForAddons();
-        let update = gmpAddons.find(addon => addon.id === this._plugin.id);
+        let res = yield installManager.checkForAddons();
+        let update = res.gmpAddons.find(addon => addon.id === this._plugin.id);
         if (update && update.isValid && !update.isInstalled) {
           this._log.trace("findUpdates() - found update for " +
                           this._plugin.id + ", installing");
           yield installManager.installAddon(update);
         } else {
           this._log.trace("findUpdates() - no updates for " + this._plugin.id);
         }
         this._log.info("findUpdates() - updateTask succeeded for " +
--- a/toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_gmpProvider.js
@@ -32,17 +32,20 @@ var gInstalledAddonId = "";
 var gInstallDeferred = null;
 var gPrefs = Services.prefs;
 var getKey = GMPScope.GMPPrefs.getPrefKey;
 
 function MockGMPInstallManager() {
 }
 
 MockGMPInstallManager.prototype = {
-  checkForAddons: () => Promise.resolve(gMockAddons),
+  checkForAddons: () => Promise.resolve({
+    usedFallback: true,
+    gmpAddons: gMockAddons
+  }),
 
   installAddon: addon => {
     gInstalledAddonId = addon.id;
     gInstallDeferred.resolve();
     return Promise.resolve();
   },
 };
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
@@ -37,17 +37,20 @@ for (let plugin of GMPScope.GMP_PLUGINS)
 var gInstalledAddonId = "";
 var gPrefs = Services.prefs;
 var gGetKey = GMPScope.GMPPrefs.getPrefKey;
 
 function MockGMPInstallManager() {
 }
 
 MockGMPInstallManager.prototype = {
-  checkForAddons: () => Promise.resolve([...gMockAddons.values()]),
+  checkForAddons: () => Promise.resolve({
+    usedFallback: true,
+    gmpAddons: [...gMockAddons.values()]
+  }),
 
   installAddon: addon => {
     gInstalledAddonId = addon.id;
     return Promise.resolve();
   },
 };
 
 
--- a/toolkit/themes/linux/mozapps/extensions/extensions.css
+++ b/toolkit/themes/linux/mozapps/extensions/extensions.css
@@ -30,18 +30,13 @@
   list-style-image: url("moz-icon://stock/gtk-go-up?size=16");
 }
 
 .meta-rating[showrating="average"] > .star {
   list-style-image: url("chrome://mozapps/skin/extensions/rating-not-won.png");
   padding: 0 1px;
 }
 
-.meta-rating[showrating="user"] > .star {
-  list-style-image: url("chrome://mozapps/skin/extensions/rating-unrated.png");
-  padding: 2px 3px;
-}
-
 .meta-rating > .star[on="true"],
 .meta-rating[showrating="user"] > .star[hover] {
   list-style-image: url("chrome://mozapps/skin/extensions/rating-won.png");
   padding: 0 1px;
 }
--- a/toolkit/themes/windows/mozapps/update/updates.css
+++ b/toolkit/themes/windows/mozapps/update/updates.css
@@ -101,17 +101,17 @@ wizardpage {
 }
 
 #pauseButton[paused="true"][disabled="true"] {
   -moz-image-region: rect(48px, 16px, 64px, 0px);
 }
 
 #verificationFailedIcon {
   margin-left: 5px;
-  list-style-image: url("chrome://global/skin/icons/notfound.png");
+  list-style-image: url("chrome://global/skin/icons/information-16.png");
 }
 
 /* Error Page */
 #errorReason {
   margin-top: 1px;
   margin-bottom: 2px;
   margin-inline-start: 6px !important;
   margin-inline-end: 5px;