Bug 1108782 - Part 2: Explode AAR files at configure time. r=glandium
authorNick Alexander <nalexander@mozilla.com>
Tue, 22 Sep 2015 10:04:26 -0700
changeset 263975 d30da7e9633b39fbf27ae9cb5b9b17b6893f16f7
parent 263974 8396485db77e11211d1444d2a38a86c28f5bd195
child 263976 511e9b64bd6c35ffdd216b590512c673663a92dc
push id65484
push usercbook@mozilla.com
push dateWed, 23 Sep 2015 10:47:13 +0000
treeherdermozilla-inbound@88067e5193f0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1108782
milestone44.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 1108782 - Part 2: Explode AAR files at configure time. r=glandium This gets us a limited version of AAR support: we can consume static AAR libraries, where here static does not refer to linking, but to static assets that are fixed at build-backend time and not modified (or produced) during the build. This lets us pin our dependencies (and move to Google's versioned Maven repository packages, away from Google's unversioned ad-hoc packages). By restricting to static AAR libraries, we avoid having to handle truly complicated dependency trees, as changing parts of generated AAR files require delicate rebuilding of the APKs (and internal libraries) that depend on the AAR files. It is possible that we will generate AARs in the tree at some time. Right now, we don't do that, even for GeckoView: the AARs produced are assembled as artifacts at package time and are intended for external consumption. We might want this for GeckoView and Fennec at some time; we should consider using Gradle everywhere at that point. The patch itself does the simplest possible thing (which has precedent from Gradle and other build systems): it simply "explodes" the AAR into the object directory and uses existing mechanisms to refer to the exploded pieces. AARs have both required and optional components. Each component is defined with an expected and required flag. If a component is expected and not present, or not expected and is present, an error is raised. If the component is expected and present, autoconf's ifelse() macro is used to define the relevant AAR_* component variables. If the component is not expected and not present, no action is taken. A consuming build backend therefore can guard all AAR_* component variables with just the top-level AAR variable. Many AAR files have empty assets/ directories. This patch doesn't explode empty assets/ directories, protecting against trivial changes to AAR files that don't impact the build. There's a lot not to like in this approach, including: * We need to manually reference internal AAR libs; * I haven't separated the pinned version numbers out of configure.in. However, it's closer to what we want than what we have!
build/autoconf/android.m4
build/mobile/robocop/Makefile.in
mobile/android/base/Makefile.in
mobile/android/base/moz.build
mobile/android/stumbler/moz.build
mobile/android/tests/background/junit3/moz.build
mobile/android/tests/browser/junit3/moz.build
python/mozbuild/mozbuild/action/explode_aar.py
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -229,51 +229,81 @@ if test "$OS_TARGET" = "Android" -a -z "
     fi
     CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS"
 fi
 AC_SUBST([MOZ_ANDROID_LIBSTDCXX])
 AC_SUBST([STLPORT_LIBS])
 
 ])
 
+
+AC_DEFUN([concat],[$1$2$3$4])
+
+dnl Find a component of an AAR.
+dnl Arg 1: variable name to expose, like ANDROID_SUPPORT_V4_LIB.
+dnl Arg 2: path to component.
+dnl Arg 3: if non-empty, expect and require component.
+AC_DEFUN([MOZ_ANDROID_AAR_COMPONENT], [
+  ifelse([$3], ,
+  [
+    if test -e "$$1" ; then
+      AC_MSG_ERROR([Found unexpected exploded $1!])
+    fi
+  ],
+  [
+    AC_MSG_CHECKING([for $1])
+    $1="$2"
+    if ! test -e "$$1" ; then
+      AC_MSG_ERROR([Could not find required exploded $1!])
+    fi
+    AC_MSG_RESULT([$$1])
+    AC_SUBST($1)
+  ])
+])
+
+dnl Find an AAR and expose variables representing its exploded components.
+dnl AC_SUBSTs ANDROID_NAME_{AAR,AAR_RES,AAR_LIB,AAR_INTERNAL_LIB}.
+dnl Arg 1: name, like play-services-base
+dnl Arg 2: version, like 7.8.0
+dnl Arg 3: extras subdirectory, either android or google
+dnl Arg 4: package subdirectory, like com/google/android/gms
+dnl Arg 5: if non-empty, expect and require internal_impl JAR.
+dnl Arg 6: if non-empty, expect and require assets/ directory.
+AC_DEFUN([MOZ_ANDROID_AAR],[
+  define([local_aar_var_base], translit($1, [-a-z], [_A-Z]))
+  define([local_aar_var], concat(ANDROID_, local_aar_var_base, _AAR))
+  local_aar_var="$ANDROID_SDK_ROOT/extras/$3/m2repository/$4/$1/$2/$1-$2.aar"
+  AC_MSG_CHECKING([for $1 AAR])
+  if ! test -e "$local_aar_var" ; then
+    AC_MSG_ERROR([You must download the $1 AAR.  Run the Android SDK tool and install the Android and Google Support Repositories under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (Looked for $local_aar_var)])
+  fi
+  AC_SUBST(local_aar_var)
+  AC_MSG_RESULT([$local_aar_var])
+
+  if ! $PYTHON -m mozbuild.action.explode_aar --destdir=$MOZ_BUILD_ROOT/dist/exploded-aar $local_aar_var ; then
+    AC_MSG_ERROR([Could not explode $local_aar_var!])
+  fi
+
+  define([root], $MOZ_BUILD_ROOT/dist/exploded-aar/$1-$2/)
+  MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _LIB), concat(root, $1-$2-classes.jar), REQUIRED)
+  MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _RES), concat(root, res), REQUIRED)
+  MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _INTERNAL_LIB), concat(root, libs/$1-$2-internal_impl-$2.jar), $5)
+  MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _ASSETS), concat(root, assets), $6)
+])
+
 AC_DEFUN([MOZ_ANDROID_GOOGLE_PLAY_SERVICES],
 [
 
 if test -n "$MOZ_NATIVE_DEVICES" ; then
     AC_SUBST(MOZ_NATIVE_DEVICES)
 
-    AC_MSG_CHECKING([for google play services])
-    GOOGLE_PLAY_SERVICES_LIB="${ANDROID_SDK_ROOT}/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar"
-    GOOGLE_PLAY_SERVICES_RES="${ANDROID_SDK_ROOT}/extras/google/google_play_services/libproject/google-play-services_lib/res"
-    AC_SUBST(GOOGLE_PLAY_SERVICES_LIB)
-    AC_SUBST(GOOGLE_PLAY_SERVICES_RES)
-    if ! test -e $GOOGLE_PLAY_SERVICES_LIB ; then
-        AC_MSG_ERROR([You must download Google Play Services to build with native video casting support enabled.  Run the Android SDK tool and install Google Play Services under Extras.  See http://developer.android.com/google/play-services/setup.html for more info. (looked for $GOOGLE_PLAY_SERVICES_LIB) ])
-    fi
-    AC_MSG_RESULT([$GOOGLE_PLAY_SERVICES_LIB])
-
-    ANDROID_APPCOMPAT_LIB="$ANDROID_COMPAT_DIR_BASE/v7/appcompat/libs/android-support-v7-appcompat.jar"
-    ANDROID_APPCOMPAT_RES="$ANDROID_COMPAT_DIR_BASE/v7/appcompat/res"
-    AC_MSG_CHECKING([for v7 appcompat library])
-    if ! test -e $ANDROID_APPCOMPAT_LIB ; then
-        AC_MSG_ERROR([You must download the v7 app compat Android support library when targeting Android with native video casting support enabled.  Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_APPCOMPAT_LIB)])
-    fi
-    AC_MSG_RESULT([$ANDROID_APPCOMPAT_LIB])
-    AC_SUBST(ANDROID_APPCOMPAT_LIB)
-    AC_SUBST(ANDROID_APPCOMPAT_RES)
-
-    ANDROID_MEDIAROUTER_LIB="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/libs/android-support-v7-mediarouter.jar"
-    ANDROID_MEDIAROUTER_RES="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/res"
-    AC_MSG_CHECKING([for v7 mediarouter library])
-    if ! test -e $ANDROID_MEDIAROUTER_LIB ; then
-        AC_MSG_ERROR([You must download the v7 media router Android support library when targeting Android with native video casting support enabled.  Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_MEDIAROUTER_LIB)])
-    fi
-    AC_MSG_RESULT([$ANDROID_MEDIAROUTER_LIB])
-    AC_SUBST(ANDROID_MEDIAROUTER_LIB)
-    AC_SUBST(ANDROID_MEDIAROUTER_RES)
+    MOZ_ANDROID_AAR(play-services-base, 7.8.0, google, com/google/android/gms)
+    MOZ_ANDROID_AAR(play-services-cast, 7.8.0, google, com/google/android/gms)
+    MOZ_ANDROID_AAR(appcompat-v7, 22.2.1, android, com/android/support)
+    MOZ_ANDROID_AAR(mediarouter-v7, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
 fi
 
 ])
 
 AC_DEFUN([MOZ_ANDROID_SDK],
 [
 
 MOZ_ARG_WITH_STRING(android-sdk,
@@ -350,50 +380,37 @@ case "$target" in
         AC_MSG_RESULT([$android_build_tools])
     else
         AC_MSG_ERROR([not found. Please check your SDK for the subdirectory of build-tools. With the current configuration, it should be in $android_sdk_root/build_tools])
     fi
 
     ANDROID_SDK="${android_sdk}"
     ANDROID_SDK_ROOT="${android_sdk_root}"
 
-    AC_MSG_CHECKING([for compat library dirs])
-    if test -e "${android_sdk_root}/extras/android/compatibility/v4/android-support-v4.jar" ; then
-        ANDROID_COMPAT_DIR_BASE="${android_sdk_root}/extras/android/compatibility";
-    else
-        ANDROID_COMPAT_DIR_BASE="${android_sdk_root}/extras/android/support";
-    fi
-    AC_MSG_RESULT([$ANDROID_COMPAT_DIR_BASE])
-
     ANDROID_TOOLS="${android_tools}"
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
     ANDROID_BUILD_TOOLS="${android_build_tools}"
     AC_SUBST(ANDROID_SDK_ROOT)
     AC_SUBST(ANDROID_SDK)
     AC_SUBST(ANDROID_TOOLS)
     AC_SUBST(ANDROID_PLATFORM_TOOLS)
     AC_SUBST(ANDROID_BUILD_TOOLS)
 
-    ANDROID_COMPAT_LIB=$ANDROID_COMPAT_DIR_BASE/v4/android-support-v4.jar
-    AC_MSG_CHECKING([for v4 compat library])
-    AC_SUBST(ANDROID_COMPAT_LIB)
-    if ! test -e $ANDROID_COMPAT_LIB ; then
-        AC_MSG_ERROR([You must download the Android v4 support library when targeting Android.  Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)])
-    fi
-    AC_MSG_RESULT([$ANDROID_COMPAT_LIB])
+    MOZ_ANDROID_AAR(support-v4, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
+    MOZ_ANDROID_AAR(recyclerview-v7, 22.2.1, android, com/android/support)
 
-    ANDROID_RECYCLERVIEW_LIB="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/libs/android-support-v7-recyclerview.jar"
-    ANDROID_RECYCLERVIEW_RES="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/res"
-    AC_MSG_CHECKING([for v7 recyclerview library])
-    if ! test -e $ANDROID_RECYCLERVIEW_LIB ; then
-        AC_MSG_ERROR([You must download the v7 recyclerview Android support library.  Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_RECYCLERVIEW_LIB)])
+    ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/22.2.1/support-annotations-22.2.1.jar"
+    AC_MSG_CHECKING([for support-annotations JAR])
+    if ! test -e $ANDROID_SUPPORT_ANNOTATIONS_JAR ; then
+        AC_MSG_ERROR([You must download the support-annotations lib.  Run the Android SDK tool and install the Android Support Repository under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_SUPPORT_ANNOTATIONS_JAR)])
     fi
-    AC_MSG_RESULT([$ANDROID_RECYCLERVIEW_LIB])
-    AC_SUBST(ANDROID_RECYCLERVIEW_LIB)
-    AC_SUBST(ANDROID_RECYCLERVIEW_RES)
+    AC_MSG_RESULT([$ANDROID_SUPPORT_ANNOTATIONS_JAR])
+    AC_SUBST(ANDROID_SUPPORT_ANNOTATIONS_JAR)
+    ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB=$ANDROID_SUPPORT_ANNOTATIONS_JAR
+    AC_SUBST(ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB)
 
     dnl Google has a history of moving the Android tools around.  We don't
     dnl care where they are, so let's try to find them anywhere we can.
     ALL_ANDROID_TOOLS_PATHS="$ANDROID_TOOLS$all_android_build_tools:$ANDROID_PLATFORM_TOOLS"
     MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$ALL_ANDROID_TOOLS_PATHS])
     MOZ_PATH_PROG(DX, dx, :, [$ALL_ANDROID_TOOLS_PATHS])
     MOZ_PATH_PROG(AAPT, aapt, :, [$ALL_ANDROID_TOOLS_PATHS])
     MOZ_PATH_PROG(AIDL, aidl, :, [$ALL_ANDROID_TOOLS_PATHS])
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -61,11 +61,12 @@ tools:: $(ANDROID_APK_NAME).apk
 # The test APK needs to know the contents of the target APK while not
 # being linked against them.  This is a best effort to avoid getting
 # out of sync with base's build config.
 jars_dir := $(DEPTH)/mobile/android/base
 stumbler_jars_dir := $(DEPTH)/mobile/android/stumbler
 ANDROID_CLASSPATH_JARS += \
   $(wildcard $(jars_dir)/*.jar) \
   $(wildcard $(stumbler_jars_dir)/*.jar) \
-  $(ANDROID_COMPAT_LIB) \
-  $(ANDROID_RECYCLERVIEW_LIB) \
+  $(ANDROID_SUPPORT_V4_AAR_LIB) \
+  $(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
+  $(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
   $(NULL)
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -44,47 +44,56 @@ GARBAGE_DIRS += classes db jars res sync
 
 # The bootclasspath is functionally identical to the classpath, but allows the
 # classes given to redefine classes in core packages, such as java.lang.
 # android.jar is here as it provides Android's definition of the Java Standard
 # Library. The compatability lib here tweaks a few of the core classes to paint
 # over changes in behaviour between versions.
 JAVA_BOOTCLASSPATH := \
     $(ANDROID_SDK)/android.jar \
-    $(ANDROID_COMPAT_LIB) \
     $(NULL)
 
 JAVA_BOOTCLASSPATH := $(subst $(NULL) ,:,$(strip $(JAVA_BOOTCLASSPATH)))
 
-JAVA_CLASSPATH += $(ANDROID_RECYCLERVIEW_LIB)
+JAVA_CLASSPATH += \
+    $(ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB) \
+    $(ANDROID_SUPPORT_V4_AAR_LIB) \
+    $(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
+    $(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
+    $(NULL)
 
 # If native devices are enabled, add Google Play Services and some of the v7
 # compat libraries.
 ifdef MOZ_NATIVE_DEVICES
     JAVA_CLASSPATH += \
-        $(GOOGLE_PLAY_SERVICES_LIB) \
-        $(ANDROID_MEDIAROUTER_LIB) \
-        $(ANDROID_APPCOMPAT_LIB) \
+        $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \
+        $(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \
+        $(ANDROID_MEDIAROUTER_V7_AAR_LIB) \
+        $(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \
+        $(ANDROID_APPCOMPAT_V7_AAR_LIB) \
         $(NULL)
 endif
 
 JAVA_CLASSPATH := $(subst $(NULL) ,:,$(strip $(JAVA_CLASSPATH)))
 
 # Library jars that we're bundling: these are subject to Proguard before inclusion
 # into classes.dex.
 java_bundled_libs := \
-    $(ANDROID_COMPAT_LIB) \
-    $(ANDROID_RECYCLERVIEW_LIB) \
+    $(ANDROID_SUPPORT_V4_AAR_LIB) \
+    $(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
+    $(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
     $(NULL)
 
 ifdef MOZ_NATIVE_DEVICES
     java_bundled_libs += \
-        $(GOOGLE_PLAY_SERVICES_LIB) \
-        $(ANDROID_MEDIAROUTER_LIB) \
-        $(ANDROID_APPCOMPAT_LIB) \
+        $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \
+        $(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \
+        $(ANDROID_MEDIAROUTER_V7_AAR_LIB) \
+        $(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \
+        $(ANDROID_APPCOMPAT_V7_AAR_LIB) \
         $(NULL)
 endif
 
 java_bundled_libs := $(subst $(NULL) ,:,$(strip $(java_bundled_libs)))
 
 # All the jars we're compiling from source. (not to be confused with
 # java_bundled_libs, which holds the jars which we're including as binaries).
 ALL_JARS = \
@@ -353,20 +362,22 @@ endif
 # produced by aapt; this saves aapt invocations.  The trailing
 # semi-colon defines an empty recipe; defining no recipe at all causes
 # Make to treat the target differently, in a way that defeats our
 # dependencies.
 
 generated/org/mozilla/gecko/R.java: .aapt.deps ;
 
 # If native devices are enabled, add Google Play Services, build their resources
+generated/android/support/v4/R.java: .aapt.deps ;
 generated/android/support/v7/appcompat/R.java: .aapt.deps ;
 generated/android/support/v7/mediarouter/R.java: .aapt.deps ;
 generated/android/support/v7/recyclerview/R.java: .aapt.deps ;
 generated/com/google/android/gms/R.java: .aapt.deps ;
+generated/com/google/android/gms/cast/R.java: .aapt.deps ;
 
 gecko.ap_: .aapt.deps ;
 R.txt: .aapt.deps ;
 
 # [Comment 2/3] This tom-foolery provides a target that forces a
 # rebuild of gecko.ap_.  This is used during packaging to ensure that
 # resources are fresh.  The alternative would be complicated; see
 # [Comment 1/3].
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -39,19 +39,23 @@ else:
     ]
 
 resjar = add_java_jar('gecko-R')
 resjar.sources = []
 resjar.generated_sources += [
     'org/mozilla/gecko/R.java',
 ]
 
-if CONFIG['ANDROID_RECYCLERVIEW_RES']:
+if CONFIG['ANDROID_SUPPORT_V4_AAR']:
+    ANDROID_EXTRA_PACKAGES += ['android.support.v4']
+    ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_SUPPORT_V4_AAR_RES']]
+    resjar.generated_sources += ['android/support/v4/R.java']
+if CONFIG['ANDROID_RECYCLERVIEW_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.recyclerview']
-    ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_RES']]
+    ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_RES']]
     resjar.generated_sources += ['android/support/v7/recyclerview/R.java']
 
 resjar.javac_flags += ['-Xlint:all']
 
 mgjar = add_java_jar('gecko-mozglue')
 mgjar.sources += [
     'mozglue/ByteBufferInputStream.java',
     'mozglue/ContextUtils.java',
@@ -102,17 +106,19 @@ gujar.sources += [
     'util/StringUtils.java',
     'util/ThreadUtils.java',
     'util/UIAsyncTask.java',
     'util/WeakReferenceHandler.java',
     'util/WebActivityMapper.java',
     'util/WindowUtils.java',
 ]
 gujar.extra_jars = [
-    CONFIG['ANDROID_COMPAT_LIB'],
+    CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
     'constants.jar',
     'gecko-mozglue.jar',
 ]
 gujar.javac_flags += ['-Xlint:all,-deprecation']
 
 stjar = add_java_jar('sync-thirdparty')
 stjar.sources += [ thirdparty_source_dir + f for f in sync_thirdparty_java_files ]
 stjar.javac_flags = ['-Xlint:none']
@@ -581,17 +587,19 @@ gbjar.sources += [
 gbjar.sources += [ thirdparty_source_dir + f for f in [
     'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
     'com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
     'com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
 ] ]
 android_package_dir = CONFIG['ANDROID_PACKAGE_NAME'].replace('.', '/')
 gbjar.generated_sources = [] # Keep it this way.
 gbjar.extra_jars += [
-    CONFIG['ANDROID_COMPAT_LIB'],
+    CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
     'constants.jar'
 ]
 if CONFIG['MOZ_CRASHREPORTER']:
     gbjar.sources += [ 'CrashReporter.java' ]
     ANDROID_RES_DIRS += [ 'crashreporter/res' ]
 
 if CONFIG['MOZ_ANDROID_SHARE_OVERLAY']:
     gbjar.sources += [
@@ -635,46 +643,53 @@ gbjar.extra_jars += [
     'gecko-R.jar',
     'gecko-mozglue.jar',
     'gecko-thirdparty.jar',
     'gecko-util.jar',
     'sync-thirdparty.jar',
 ]
 
 moz_native_devices_jars = [
-    CONFIG['ANDROID_APPCOMPAT_LIB'],
-    CONFIG['ANDROID_MEDIAROUTER_LIB'],
-    CONFIG['GOOGLE_PLAY_SERVICES_LIB'],
+    CONFIG['ANDROID_APPCOMPAT_V7_AAR_LIB'],
+    CONFIG['ANDROID_MEDIAROUTER_V7_AAR_LIB'],
+    CONFIG['ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB'],
+    CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_LIB'],
+    CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_LIB'],
 ]
 moz_native_devices_sources = [
     'ChromeCast.java',
     'GeckoMediaPlayer.java',
     'MediaPlayerManager.java',
     'PresentationMediaPlayerManager.java',
 ]
 if CONFIG['MOZ_NATIVE_DEVICES']:
     gbjar.extra_jars += moz_native_devices_jars
     gbjar.sources += moz_native_devices_sources
 
-    if CONFIG['ANDROID_APPCOMPAT_RES']:
+    if CONFIG['ANDROID_APPCOMPAT_V7_AAR']:
         ANDROID_EXTRA_PACKAGES += ['android.support.v7.appcompat']
-        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_RES']]
+        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_V7_AAR_RES']]
         resjar.generated_sources += ['android/support/v7/appcompat/R.java']
 
-    if CONFIG['ANDROID_MEDIAROUTER_RES']:
+    if CONFIG['ANDROID_MEDIAROUTER_V7_AAR']:
         ANDROID_EXTRA_PACKAGES += ['android.support.v7.mediarouter']
-        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_MEDIAROUTER_RES']]
+        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_MEDIAROUTER_V7_AAR_RES']]
         resjar.generated_sources += ['android/support/v7/mediarouter/R.java']
 
-    if CONFIG['GOOGLE_PLAY_SERVICES_RES']:
+    if CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR']:
         ANDROID_EXTRA_PACKAGES += ['com.google.android.gms']
-        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['GOOGLE_PLAY_SERVICES_RES']]
+        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_RES']]
         resjar.generated_sources += ['com/google/android/gms/R.java']
 
-gbjar.extra_jars += [CONFIG['ANDROID_RECYCLERVIEW_LIB']]
+    if CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR']:
+        ANDROID_EXTRA_PACKAGES += ['com.google.android.gms.cast']
+        ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_RES']]
+        resjar.generated_sources += ['com/google/android/gms/cast/R.java']
+
+gbjar.extra_jars += [CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB']]
 
 gbjar.javac_flags += ['-Xlint:all,-deprecation,-fallthrough', '-J-Xmx512m', '-J-Xms128m']
 
 # gecko-thirdparty is a good place to put small independent libraries
 gtjar = add_java_jar('gecko-thirdparty')
 gtjar.sources += [ thirdparty_source_dir + f for f in [
     'com/keepsafe/switchboard/AsyncConfigLoader.java',
     'com/keepsafe/switchboard/DeviceUuidFactory.java',
@@ -736,17 +751,21 @@ gtjar.sources += [ thirdparty_source_dir
     'com/squareup/picasso/Stats.java',
     'com/squareup/picasso/StatsSnapshot.java',
     'com/squareup/picasso/Target.java',
     'com/squareup/picasso/TargetAction.java',
     'com/squareup/picasso/Transformation.java',
     'com/squareup/picasso/UrlConnectionDownloader.java',
     'com/squareup/picasso/Utils.java'
 ] ]
-#gtjar.javac_flags += ['-Xlint:all']
+gtjar.extra_jars = [
+    CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
+]
 
 if not CONFIG['MOZILLA_OFFICIAL']:
     gtjar.sources += [ thirdparty_source_dir + f for f in [
         'org/lucasr/dspec/DesignSpec.java',
         'org/lucasr/dspec/RawResource.java'
     ] ]
 
 if CONFIG['MOZ_INSTALL_TRACKING']:
@@ -843,17 +862,19 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']
     # that depends on the Geckoview jars.
     search_source_dir = SRCDIR + '/../search'
     include('../search/search_activity_sources.mozbuild')
 
     search_activity = add_java_jar('search-activity')
     search_activity.sources += [search_source_dir + '/' + f for f in search_activity_sources]
     search_activity.javac_flags += ['-Xlint:all']
     search_activity.extra_jars = [
-        CONFIG['ANDROID_COMPAT_LIB'],
+        CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
+        CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+        CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
         'constants.jar',
         'gecko-R.jar',
         'gecko-browser.jar',
         'gecko-mozglue.jar',
         'gecko-thirdparty.jar',
         'gecko-util.jar'
     ]
 
--- a/mobile/android/stumbler/moz.build
+++ b/mobile/android/stumbler/moz.build
@@ -3,10 +3,10 @@
 # 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('stumbler_sources.mozbuild')
 
 stumbler_jar = add_java_jar('stumbler')
 stumbler_jar.sources += stumbler_sources
-stumbler_jar.extra_jars += [CONFIG['ANDROID_COMPAT_LIB']]
+stumbler_jar.extra_jars += [CONFIG['ANDROID_SUPPORT_V4_AAR_LIB']]
 stumbler_jar.javac_flags += ['-Xlint:all']
--- a/mobile/android/tests/background/junit3/moz.build
+++ b/mobile/android/tests/background/junit3/moz.build
@@ -9,18 +9,18 @@ DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG
 ANDROID_APK_NAME = 'background-junit3-debug'
 ANDROID_APK_PACKAGE = 'org.mozilla.gecko.background.tests'
 
 include('background_junit3_sources.mozbuild')
 
 jar = add_java_jar('background-junit3')
 jar.sources += background_junit3_sources
 jar.extra_jars += [
-    CONFIG['ANDROID_COMPAT_LIB'],
-    CONFIG['ANDROID_RECYCLERVIEW_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB'],
     TOPOBJDIR + '/mobile/android/base/constants.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-R.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-browser.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-mozglue.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-thirdparty.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-util.jar',
     TOPOBJDIR + '/mobile/android/base/sync-thirdparty.jar',
 ]
--- a/mobile/android/tests/browser/junit3/moz.build
+++ b/mobile/android/tests/browser/junit3/moz.build
@@ -24,18 +24,18 @@ jar.sources += [
     'src/org/mozilla/tests/browser/junit3/TestRemoteTabs.java',
     'src/org/mozilla/tests/browser/junit3/TestSuggestedSites.java',
     'src/org/mozilla/tests/browser/junit3/TestTopSitesCursorWrapper.java',
 ]
 jar.generated_sources = [] # None yet -- try to keep it this way.
 jar.javac_flags += ['-Xlint:all']
 
 jar.extra_jars += [
-    CONFIG['ANDROID_COMPAT_LIB'],
-    CONFIG['ANDROID_RECYCLERVIEW_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB'],
     TOPOBJDIR + '/mobile/android/base/constants.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-R.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-browser.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-mozglue.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-thirdparty.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-util.jar',
     TOPOBJDIR + '/mobile/android/base/sync-thirdparty.jar',
 ]
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/action/explode_aar.py
@@ -0,0 +1,72 @@
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import argparse
+import errno
+import os
+import shutil
+import sys
+import zipfile
+
+from mozpack.files import FileFinder
+import mozpack.path as mozpath
+from mozbuild.util import ensureParentDir
+
+def explode(aar, destdir):
+    # Take just the support-v4-22.2.1 part.
+    name, _ = os.path.splitext(os.path.basename(aar))
+
+    destdir = mozpath.join(destdir, name)
+    if os.path.exists(destdir):
+        # We always want to start fresh.
+        shutil.rmtree(destdir)
+    ensureParentDir(destdir)
+    with zipfile.ZipFile(aar) as zf:
+        zf.extractall(destdir)
+
+    # classes.jar is always present.  However, multiple JAR files with the same
+    # name confuses our staged Proguard process in
+    # mobile/android/base/Makefile.in, so we make the names unique here.
+    classes_jar = mozpath.join(destdir, name + '-classes.jar')
+    os.rename(mozpath.join(destdir, 'classes.jar'), classes_jar)
+
+    # Embedded JAR libraries are optional.
+    finder = FileFinder(mozpath.join(destdir, 'libs'), find_executables=False)
+    for p, _ in finder.find('*.jar'):
+        jar = mozpath.join(finder.base, name + '-' + p)
+        os.rename(mozpath.join(finder.base, p), jar)
+
+    # Frequently assets/ is present but empty.  Protect against meaningless
+    # changes to the AAR files by deleting empty assets/ directories.
+    assets = mozpath.join(destdir, 'assets')
+    try:
+        os.rmdir(assets)
+    except OSError, e:
+        if e.errno in (errno.ENOTEMPTY, errno.ENOENT):
+            pass
+        else:
+            raise
+
+    return True
+
+
+def main(argv):
+    parser = argparse.ArgumentParser(
+        description='Explode Android AAR file.')
+
+    parser.add_argument('--destdir', required=True, help='Destination directory.')
+    parser.add_argument('aars', nargs='+', help='Path to AAR file(s).')
+
+    args = parser.parse_args(argv)
+
+    for aar in args.aars:
+        if not explode(aar, args.destdir):
+            return 1
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))