Merge "Add BOARD_SUPER_PARTITION_*"
diff --git a/Changes.md b/Changes.md
index 21a0abe..7440220 100644
--- a/Changes.md
+++ b/Changes.md
@@ -1,5 +1,80 @@
 # Build System Changes for Android.mk Writers
 
+### `export` and `unexport` deprecation  {#export_keyword}
+
+The `export` and `unexport` keywords have been deprecated, and will throw
+warnings or errors depending on where they are used.
+
+Early in the make system, during product configuration and BoardConfig.mk
+reading: these will throw a warnings, and will be an error in the future.
+Device specific configuration should not be able to affect common core build
+steps -- we're looking at triggering build steps to be invalidated if the set
+of environment variables they can access changes. If device specific
+configuration is allowed to change those, switching devices with the same
+output directory could become significantly more expensive than it already can
+be.
+
+Later, during Android.mk files, and later tasks: these will throw errors, since
+it is increasingly likely that they are being used incorrectly, attempting to
+change the environment for a single build step, and instead setting it for
+hundreds of thousands.
+
+It is not recommended to just move the environment variable setting outside of
+the build (in vendorsetup.sh, or some other configuration script or wrapper).
+We expect to limit the environment variables that the build respects in the
+future, others will be cleared. (There will be methods to get custom variables
+into the build, just not to every build step)
+
+Instead, write the export commands into the rule command lines themselves:
+
+``` make
+$(intermediates)/generated_output.img:
+	rm -rf $@
+	export MY_ENV_A="$(MY_A)"; make ...
+```
+
+If you want to set many environment variables, and/or use them many times,
+write them out to a script and source the script:
+
+``` make
+envsh := $(intermediates)/env.sh
+$(envsh):
+	rm -rf $@
+	echo 'export MY_ENV_A="$(MY_A)"' >$@
+	echo 'export MY_ENV_B="$(MY_B)"' >>$@
+
+$(intermediates)/generated_output.img: PRIVATE_ENV := $(envsh)
+$(intermediates)/generated_output.img: $(envsh) a/b/c/package.sh
+	rm -rf $@
+	source $(PRIVATE_ENV); make ...
+	source $(PRIVATE_ENV); a/b/c/package.sh ...
+```
+
+## Implicit make rules are obsolete {#implicit_rules}
+
+Implicit rules look something like the following:
+
+``` make
+$(TARGET_OUT_SHARED_LIBRARIES)/%_vendor.so: $(TARGET_OUT_SHARED_LIBRARIES)/%.so
+	...
+
+%.o : %.foo
+	...
+```
+
+These can have wide ranging effects across unrelated modules, so they're now obsolete. Instead, use static pattern rules, which are similar, but explicitly match the specified outputs:
+
+``` make
+libs := $(foreach lib,libfoo libbar,$(TARGET_OUT_SHARED_LIBRARIES)/$(lib)_vendor.so)
+$(libs): %_vendor.so: %.so
+	...
+
+files := $(wildcard $(LOCAL_PATH)/*.foo)
+gen := $(patsubst $(LOCAL_PATH)/%.foo,$(intermediates)/%.o,$(files))
+$(gen): %.o : %.foo
+	...
+```
+
 ## Removing '/' from Valid Module Names {#name_slash}
 
 The build system uses module names in path names in many places. Having an
diff --git a/core/android_manifest.mk b/core/android_manifest.mk
index 0215106..3af81ff 100644
--- a/core/android_manifest.mk
+++ b/core/android_manifest.mk
@@ -18,18 +18,14 @@
 ifndef LOCAL_DONT_MERGE_MANIFESTS
   my_full_libs_manifest_files += $(LOCAL_FULL_LIBS_MANIFEST_FILES)
 
-  ifdef LOCAL_STATIC_JAVA_AAR_LIBRARIES
-    my_full_libs_manifest_files += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
-      $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/aar/AndroidManifest.xml)
-  endif
+  my_full_libs_manifest_files += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES),\
+    $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/manifest/AndroidManifest.xml)
 endif
 
-ifdef LOCAL_STATIC_JAVA_AAR_LIBRARIES
-  # With aapt2, we'll link in the built resource from the AAR.
-  ifneq ($(LOCAL_USE_AAPT2),true)
-    LOCAL_RESOURCE_DIR += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
-      $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/aar/res)
-  endif
+# With aapt2, we'll link in the built resource from the AAR.
+ifneq ($(LOCAL_USE_AAPT2),true)
+  LOCAL_RESOURCE_DIR += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
+    $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/aar/res)
 endif
 
 full_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml
diff --git a/core/binary.mk b/core/binary.mk
index 2899d4d..60f78dd 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -1602,10 +1602,15 @@
 # libraries have already been linked into the module at that point.
 # We do, however, care about the NOTICE files for any static
 # libraries that we use. (see notice_files.mk)
-
+#
+# Don't do this in mm, since many of the targets won't exist.
+ifeq ($(ONE_SHOT_MAKEFILE),)
 installed_static_library_notice_file_targets := \
     $(foreach lib,$(my_static_libraries) $(my_whole_static_libraries), \
       NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST,TARGET)-STATIC_LIBRARIES-$(lib))
+else
+installed_static_library_notice_file_targets :=
+endif
 
 # Default is -fno-rtti.
 ifeq ($(strip $(LOCAL_RTTI_FLAG)),)
diff --git a/core/ccache.mk b/core/ccache.mk
index 893c985..d10aceb 100644
--- a/core/ccache.mk
+++ b/core/ccache.mk
@@ -32,24 +32,24 @@
 ifneq ($(filter-out false,$(USE_CCACHE)),)
   # The default check uses size and modification time, causing false misses
   # since the mtime depends when the repo was checked out
-  export CCACHE_COMPILERCHECK ?= content
+  CCACHE_COMPILERCHECK ?= content
 
   # See man page, optimizations to get more cache hits
   # implies that __DATE__ and __TIME__ are not critical for functionality.
   # Ignore include file modification time since it will depend on when
   # the repo was checked out
-  export CCACHE_SLOPPINESS := time_macros,include_file_mtime,file_macro
+  CCACHE_SLOPPINESS := time_macros,include_file_mtime,file_macro
 
   # Turn all preprocessor absolute paths into relative paths.
   # Fixes absolute paths in preprocessed source due to use of -g.
   # We don't really use system headers much so the rootdir is
   # fine; ensures these paths are relative for all Android trees
   # on a workstation.
-  export CCACHE_BASEDIR := /
+  CCACHE_BASEDIR := /
 
   # Workaround for ccache with clang.
   # See http://petereisentraut.blogspot.com/2011/09/ccache-and-clang-part-2.html
-  export CCACHE_CPP2 := true
+  CCACHE_CPP2 := true
 
   ifndef CC_WRAPPER
     CC_WRAPPER := $(CCACHE_EXEC)
diff --git a/core/config.mk b/core/config.mk
index 17c9eab..3045929 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -85,6 +85,9 @@
 $(KATI_obsolete_var PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE,Set FCM Version in device manifest instead. See $(CHANGES_URL)#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE)
 $(KATI_obsolete_var USE_CLANG_PLATFORM_BUILD,Clang is the only supported Android compiler. See $(CHANGES_URL)#USE_CLANG_PLATFORM_BUILD)
 
+# This is marked as obsolete in envsetup.mk after reading the BoardConfig.mk
+$(KATI_deprecate_export It is a global setting. See $(CHANGES_URL)#export_keyword)
+
 CHANGES_URL :=
 
 # Used to force goals to build.  Only use for conditionally defined goals.
@@ -208,8 +211,10 @@
 # ###############################################################
 # Broken build defaults
 # ###############################################################
-# Assume that all boards have duplicate rules right now.
-BUILD_BROKEN_DUP_RULES := true
+BUILD_BROKEN_ANDROIDMK_EXPORTS :=
+BUILD_BROKEN_DUP_COPY_HEADERS :=
+BUILD_BROKEN_DUP_RULES :=
+BUILD_BROKEN_PHONY_TARGETS :=
 
 # ###############################################################
 # Include sub-configuration files
@@ -356,10 +361,6 @@
 ifeq ($(CALLED_FROM_SETUP),true)
 include $(BUILD_SYSTEM)/ccache.mk
 include $(BUILD_SYSTEM)/goma.mk
-
-export CC_WRAPPER
-export CXX_WRAPPER
-export JAVAC_WRAPPER
 endif
 
 ifdef TARGET_PREFER_32_BIT
@@ -730,21 +731,13 @@
 
 COLUMN:= column
 
-ifeq ($(EXPERIMENTAL_USE_OPENJDK9),)
-ifeq ($(RUN_ERROR_PRONE),true)
-USE_OPENJDK9 :=
-else
 USE_OPENJDK9 := true
-endif
-TARGET_OPENJDK9 :=
-else ifeq ($(EXPERIMENTAL_USE_OPENJDK9),false)
-USE_OPENJDK9 :=
+
+ifeq ($(EXPERIMENTAL_USE_OPENJDK9),)
 TARGET_OPENJDK9 :=
 else ifeq ($(EXPERIMENTAL_USE_OPENJDK9),1.8)
-USE_OPENJDK9 := true
 TARGET_OPENJDK9 :=
 else ifeq ($(EXPERIMENTAL_USE_OPENJDK9),true)
-USE_OPENJDK9 := true
 TARGET_OPENJDK9 := true
 endif
 
diff --git a/core/definitions.mk b/core/definitions.mk
index 9d4c532..9f958fa 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2662,9 +2662,14 @@
 # $(1): source file
 # $(2): destination file
 define copy-init-script-file-checked
+# Host init verifier doesn't exist on darwin.
+ifneq ($(HOST_OS),darwin)
 $(2): $(1) $(HOST_INIT_VERIFIER) $(call intermediates-dir-for,ETC,passwd)/passwd
-	@echo "Copy init script: $$@"
 	$(hide) $(HOST_INIT_VERIFIER) $$< $(call intermediates-dir-for,ETC,passwd)/passwd
+else
+$(2): $(1)
+endif
+	@echo "Copy init script: $$@"
 	$$(copy-file-to-target)
 endef
 
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index cd48316..deaee56 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -232,16 +232,11 @@
 ##
 ##
 
-ifdef USE_OPENJDK9
 # For OpenJDK 9 we use --patch-module to define the core libraries code.
 # TODO(tobiast): Reorganize this when adding proper support for OpenJDK 9
 # modules. Here we treat all code in core libraries as being in java.base
 # to work around the OpenJDK 9 module system. http://b/62049770
 $(full_target): PRIVATE_BOOTCLASSPATH_ARG := --patch-module=java.base=$(PRIVATE_BOOTCLASSPATH)
-else
-# For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
-$(full_target): PRIVATE_BOOTCLASSPATH_ARG := $(addprefix -bootclasspath ,$(PRIVATE_BOOTCLASSPATH))
-endif
 $(full_target): $(full_src_files) $(LOCAL_GENERATED_SOURCES) $(full_java_libs) $(ZIPSYNC) $(LOCAL_SRCJARS) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	@echo Docs javadoc: $(PRIVATE_OUT_DIR)
 	@mkdir -p $(dir $@)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index a3e78e7..874ea91 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -273,6 +273,31 @@
 board_config_mk :=
 
 ###########################################
+# Handle BUILD_BROKEN_* settings
+vars := \
+  BUILD_BROKEN_ANDROIDMK_EXPORTS \
+  BUILD_BROKEN_DUP_COPY_HEADERS \
+  BUILD_BROKEN_DUP_RULES \
+  BUILD_BROKEN_PHONY_TARGETS
+
+$(foreach var,$(vars),$(eval $(var) := $$(strip $$($(var)))))
+
+$(foreach var,$(vars), \
+  $(if $(filter-out true false,$($(var))), \
+    $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))")))
+
+.KATI_READONLY := $(vars)
+
+CHANGES_URL := https://android.googlesource.com/platform/build/+/master/Changes.md
+
+# "" is equivalent to true currently.
+ifeq ($(BUILD_BROKEN_ANDROIDMK_EXPORTS),false)
+$(KATI_obsolete_export It is a global setting. See $(CHANGES_URL)#export_keyword)
+endif
+
+CHANGES_URL :=
+
+###########################################
 # Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR
 ifeq ($(TARGET_COPY_OUT_VENDOR),$(_vendor_path_placeholder))
 TARGET_COPY_OUT_VENDOR := system/vendor
diff --git a/core/goma.mk b/core/goma.mk
index 2fb37a7..3787dfd 100644
--- a/core/goma.mk
+++ b/core/goma.mk
@@ -14,9 +14,6 @@
 # limitations under the License.
 #
 
-# Used by the compiler wrapper, but should only be set by gomacc
-unexport GOMACC_PATH
-
 # Notice: this works only with Google's Goma build infrastructure.
 ifneq ($(filter-out false,$(USE_GOMA)),)
   # Goma requires a lot of processes and file descriptors.
diff --git a/core/host_java_library_common.mk b/core/host_java_library_common.mk
index 51e2d94..8df4b37 100644
--- a/core/host_java_library_common.mk
+++ b/core/host_java_library_common.mk
@@ -48,8 +48,3 @@
 
 LOCAL_INTERMEDIATE_SOURCE_DIR := $(intermediates.COMMON)/src
 LOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES))
-
-# If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS
-ifeq ($(RUN_ERROR_PRONE),true)
-LOCAL_JAVACFLAGS += $(LOCAL_ERROR_PRONE_FLAGS)
-endif
diff --git a/core/java.mk b/core/java.mk
index 19e6377..78b492d 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -227,11 +227,6 @@
 # Deps for generated source files must be handled separately,
 # via deps on the target that generates the sources.
 
-# If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS
-ifeq ($(RUN_ERROR_PRONE),true)
-LOCAL_JAVACFLAGS += $(LOCAL_ERROR_PRONE_FLAGS)
-endif
-
 # For user / userdebug builds, strip the local variable table and the local variable
 # type table. This has no bearing on stack traces, but will leave less information
 # available via JDWP.
diff --git a/core/java_common.mk b/core/java_common.mk
index 5dfbc5f..486f087 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -193,16 +193,25 @@
 
 annotation_processor_flags :=
 annotation_processor_deps :=
+annotation_processor_jars :=
+
+# If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS
+ifeq ($(RUN_ERROR_PRONE),true)
+annotation_processor_jars += $(ERROR_PRONE_JARS)
+LOCAL_JAVACFLAGS += $(ERROR_PRONE_FLAGS)
+LOCAL_JAVACFLAGS += '-Xplugin:ErrorProne $(ERROR_PRONE_CHECKS) $(LOCAL_ERROR_PRONE_FLAGS)'
+endif
 
 ifdef LOCAL_ANNOTATION_PROCESSORS
-  annotation_processor_jars := $(call java-lib-files,$(LOCAL_ANNOTATION_PROCESSORS),true)
-  annotation_processor_flags += -processorpath $(call normalize-path-list,$(annotation_processor_jars))
-  annotation_processor_deps += $(annotation_processor_jars)
+  annotation_processor_jars += $(call java-lib-files,$(LOCAL_ANNOTATION_PROCESSORS),true)
 
   # b/25860419: annotation processors must be explicitly specified for grok
   annotation_processor_flags += $(foreach class,$(LOCAL_ANNOTATION_PROCESSOR_CLASSES),-processor $(class))
+endif
 
-  annotation_processor_jars :=
+ifneq (,$(strip $(annotation_processor_jars)))
+annotation_processor_flags += -processorpath $(call normalize-path-list,$(annotation_processor_jars))
+annotation_processor_deps += $(annotation_processor_jars)
 endif
 
 full_static_java_libs := $(call java-lib-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
diff --git a/core/main.mk b/core/main.mk
index a08e0df..c440f55 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -416,10 +416,6 @@
 # would have been with a normal make.
 CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))
 FULL_BUILD :=
-# Stub out the notice targets, which probably aren't defined
-# when using ONE_SHOT_MAKEFILE.
-NOTICE-HOST-%: ;
-NOTICE-TARGET-%: ;
 
 # A helper goal printing out install paths
 define register_module_install_path
@@ -1270,6 +1266,16 @@
   ifeq ($(EMMA_INSTRUMENT),true)
     $(JACOCO_REPORT_CLASSES_ALL) : $(INSTALLED_SYSTEMIMAGE)
     $(call dist-for-goals, dist_files, $(JACOCO_REPORT_CLASSES_ALL))
+
+    # Put XML formatted API files in the dist dir.
+    api_xmls := $(addprefix $(TARGET_OUT_COMMON_INTERMEDIATES)/,api.xml system-api.xml test-api.xml)
+    $(api_xmls): $(TARGET_OUT_COMMON_INTERMEDIATES)/%api.xml : frameworks/base/api/%current.txt $(APICHECK)
+	$(hide) echo "Converting API file to XML: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(APICHECK_COMMAND) -convert2xml $< $@
+
+    $(call dist-for-goals, dist_files, $(api_xmls))
+    api_xmls :=
   endif
 
 # Building a full system-- the default is to build droidcore
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 1a5b389..d5b7877 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -598,6 +598,10 @@
 	$(hide) touch $(dir $@)/proguard.txt
 	$(hide) touch $(dir $@)/AndroidManifest.xml
 
+my_prebuilt_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml
+$(eval $(call copy-one-file,$(my_src_android_manifest),$(my_prebuilt_android_manifest)))
+$(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_prebuilt_android_manifest))
+
 endif
 
 $(common_classes_jar) : $(my_src_jar)
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index eeaab31..0ba2c7a 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -64,6 +64,10 @@
   my_static_library_extra_packages := $(intermediates.COMMON)/extra_packages
   $(eval $(call copy-one-file,$(LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES),$(my_static_library_extra_packages)))
   $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_extra_packages))
+
+  my_static_library_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml
+  $(eval $(call copy-one-file,$(LOCAL_FULL_MANIFEST_FILE),$(my_static_library_android_manifest)))
+  $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_android_manifest))
 endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE
 
 ifneq ($(TURBINE_ENABLED),false)
diff --git a/target/product/sdk_phone_x86.mk b/target/product/sdk_phone_x86.mk
index b34e5b6..abb46ac 100644
--- a/target/product/sdk_phone_x86.mk
+++ b/target/product/sdk_phone_x86.mk
@@ -20,6 +20,10 @@
 -include sdk/build/product_sdk.mk
 -include development/build/product_sdk.mk
 
+# keep this apk for sdk targets for now
+PRODUCT_PACKAGES += \
+    EmulatorSmokeTests
+
 # Overrides
 PRODUCT_BRAND := Android
 PRODUCT_NAME := sdk_phone_x86
diff --git a/target/product/sdk_phone_x86_64.mk b/target/product/sdk_phone_x86_64.mk
index 37c078e..828b744 100644
--- a/target/product/sdk_phone_x86_64.mk
+++ b/target/product/sdk_phone_x86_64.mk
@@ -20,6 +20,10 @@
 -include sdk/build/product_sdk.mk
 -include development/build/product_sdk.mk
 
+# keep this apk for sdk targets for now
+PRODUCT_PACKAGES += \
+    EmulatorSmokeTests
+
 # Overrides
 PRODUCT_BRAND := Android
 PRODUCT_NAME := sdk_phone_x86_64
diff --git a/target/product/vndk/Android.mk b/target/product/vndk/Android.mk
index b9ae116..5d009f9 100644
--- a/target/product/vndk/Android.mk
+++ b/target/product/vndk/Android.mk
@@ -1,4 +1,3 @@
-ifneq ($(BOARD_VNDK_VERSION),)
 LOCAL_PATH:= $(call my-dir)
 
 #####################################################################
@@ -39,7 +38,13 @@
 droidcore: check-vndk-list
 
 check-vndk-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-list-timestamp
+
+ifeq ($(TARGET_IS_64_BIT)|$(TARGET_2ND_ARCH),true|)
+# TODO(b/110429754) remove this condition when we support 64-bit-only device
+check-vndk-list: ;
+else
 check-vndk-list: $(check-vndk-list-timestamp)
+endif
 
 _vndk_check_failure_message := " error: VNDK library list has been changed.\n"
 ifeq (REL,$(PLATFORM_VERSION_CODENAME))
@@ -86,6 +91,8 @@
 endif
 	@chmod a+x $@
 
+ifneq ($(BOARD_VNDK_VERSION),)
+
 include $(CLEAR_VARS)
 LOCAL_MODULE := vndk_package
 LOCAL_REQUIRED_MODULES := \
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 756bc8a..393c33d 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -27,6 +27,12 @@
       in the apkcerts.txt file.  Option may be repeated to give
       multiple extra packages.
 
+  --skip_apks_with_path_prefix  <prefix>
+      Skip signing an APK if it has the matching prefix in its path. The prefix
+      should be matching the entry name, which has partition names in upper
+      case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be
+      repeated to give multiple prefixes.
+
   -k  (--key_mapping)  <src_key=dest_key>
       Add a mapping from the key name as specified in apkcerts.txt (the
       src_key) to the real key you wish to sign the package with
@@ -118,6 +124,7 @@
 OPTIONS = common.OPTIONS
 
 OPTIONS.extra_apks = {}
+OPTIONS.skip_apks_with_path_prefix = set()
 OPTIONS.key_map = {}
 OPTIONS.rebuild_recovery = False
 OPTIONS.replace_ota_keys = False
@@ -144,39 +151,53 @@
   return certmap
 
 
-def GetApkFileInfo(filename, compressed_extension):
+def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
   """Returns the APK info based on the given filename.
 
   Checks if the given filename (with path) looks like an APK file, by taking the
-  compressed extension into consideration.
+  compressed extension into consideration. If it appears to be an APK file,
+  further checks if the APK file should be skipped when signing, based on the
+  given path prefixes.
 
   Args:
     filename: Path to the file.
     compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
         or None if there's no compressed APKs.
+    skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
 
   Returns:
-    (is_apk, is_compressed): is_apk indicates whether the given filename is an
-    APK file. is_compressed indicates whether the APK file is compressed (only
-    meaningful when is_apk is True).
+    (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
+    given filename is an APK file. is_compressed indicates whether the APK file
+    is compressed (only meaningful when is_apk is True). should_be_skipped
+    indicates whether the filename matches any of the given prefixes to be
+    skipped.
 
   Raises:
-    AssertionError: On invalid compressed_extension input.
+    AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
   """
   assert compressed_extension is None or compressed_extension.startswith('.'), \
       "Invalid compressed_extension arg: '{}'".format(compressed_extension)
 
+  # skipped_prefixes should be one of set/list/tuple types. Other types such as
+  # str shouldn't be accepted.
+  assert (isinstance(skipped_prefixes, tuple) or
+          isinstance(skipped_prefixes, set) or
+          isinstance(skipped_prefixes, list)), \
+              "Invalid skipped_prefixes input type: {}".format(
+                  type(skipped_prefixes))
+
   compressed_apk_extension = (
       ".apk" + compressed_extension if compressed_extension else None)
   is_apk = (filename.endswith(".apk") or
             (compressed_apk_extension and
              filename.endswith(compressed_apk_extension)))
   if not is_apk:
-    return (False, False)
+    return (False, False, False)
 
   is_compressed = (compressed_apk_extension and
                    filename.endswith(compressed_apk_extension))
-  return (True, is_compressed)
+  should_be_skipped = filename.startswith(tuple(skipped_prefixes))
+  return (True, is_compressed, should_be_skipped)
 
 
 def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
@@ -193,9 +214,9 @@
   """
   unknown_apks = []
   for info in input_tf_zip.infolist():
-    (is_apk, is_compressed) = GetApkFileInfo(
-        info.filename, compressed_extension)
-    if not is_apk:
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
+    if not is_apk or should_be_skipped:
       continue
     name = os.path.basename(info.filename)
     if is_compressed:
@@ -276,9 +297,11 @@
                        apk_key_map, key_passwords, platform_api_level,
                        codename_to_api_level_map,
                        compressed_extension):
+  # maxsize measures the maximum filename length, including the ones to be
+  # skipped.
   maxsize = max(
       [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
-       if GetApkFileInfo(i.filename, compressed_extension)[0]])
+       if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
   system_root_image = misc_info.get("system_root_image") == "true"
 
   for info in input_tf_zip.infolist():
@@ -288,10 +311,18 @@
 
     data = input_tf_zip.read(filename)
     out_info = copy.copy(info)
-    (is_apk, is_compressed) = GetApkFileInfo(filename, compressed_extension)
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
+
+    if is_apk and should_be_skipped:
+      # Copy skipped APKs verbatim.
+      print(
+          "NOT signing: %s\n"
+          "        (skipped due to matching prefix)" % (filename,))
+      common.ZipWriteStr(output_tf_zip, out_info, data)
 
     # Sign APKs.
-    if is_apk:
+    elif is_apk:
       name = os.path.basename(filename)
       if is_compressed:
         name = name[:-len(compressed_extension)]
@@ -304,7 +335,9 @@
         common.ZipWriteStr(output_tf_zip, out_info, signed_data)
       else:
         # an APK we're not supposed to sign.
-        print("NOT signing: %s" % (name,))
+        print(
+            "NOT signing: %s\n"
+            "        (skipped due to special cert string)" % (name,))
         common.ZipWriteStr(output_tf_zip, out_info, data)
 
     # System properties.
@@ -794,6 +827,12 @@
       names = names.split(",")
       for n in names:
         OPTIONS.extra_apks[n] = key
+    elif o == "--skip_apks_with_path_prefix":
+      # Sanity check the prefix, which must be in all upper case.
+      prefix = a.split('/')[0]
+      if not prefix or prefix != prefix.upper():
+        raise ValueError("Invalid path prefix '%s'" % (a,))
+      OPTIONS.skip_apks_with_path_prefix.add(a)
     elif o in ("-d", "--default_key_mappings"):
       key_mapping_options.append((None, a))
     elif o in ("-k", "--key_mapping"):
@@ -853,6 +892,7 @@
       extra_opts="e:d:k:ot:",
       extra_long_opts=[
           "extra_apks=",
+          "skip_apks_with_path_prefix=",
           "default_key_mappings=",
           "key_mapping=",
           "replace_ota_keys",
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 71bd259..ac1b567 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -237,25 +237,116 @@
           AssertionError, CheckAllApksSigned, input_zip, apk_key_map, '.gz')
 
   def test_GetApkFileInfo(self):
-    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk", None)
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/apps/Chats.apk", None, [])
     self.assertTrue(is_apk)
     self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
 
-    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.dat", None)
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/apps/Chats.apk", None, [])
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
+
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/apps/Chats.dat", None, [])
     self.assertFalse(is_apk)
     self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
 
   def test_GetApkFileInfo_withCompressedApks(self):
-    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".gz")
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/apps/Chats.apk.gz", ".gz", [])
     self.assertTrue(is_apk)
     self.assertTrue(is_compressed)
+    self.assertFalse(should_be_skipped)
 
-    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".xz")
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/apps/Chats.apk.gz", ".xz", [])
     self.assertFalse(is_apk)
     self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
 
     self.assertRaises(
-        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "")
+        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "", [])
 
     self.assertRaises(
-        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk")
+        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk", [])
+
+  def test_GetApkFileInfo_withSkippedPrefixes(self):
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/preloads/apps/Chats.apk", None, set())
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
+
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "PRODUCT/preloads/apps/Chats.apk",
+        None,
+        set(["PRODUCT/preloads/"]))
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None,
+        set(["SYSTEM/preloads/", "SYSTEM_OTHER/preloads/"]))
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.apk.gz",
+        ".gz",
+        set(["PRODUCT/prebuilts/", "SYSTEM_OTHER/preloads/"]))
+    self.assertTrue(is_apk)
+    self.assertTrue(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.dat",
+        None,
+        set(["SYSTEM_OTHER/preloads/"]))
+    self.assertFalse(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertFalse(should_be_skipped)
+
+  def test_GetApkFileInfo_checkSkippedPrefixesInput(self):
+    # set
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None,
+        set(["SYSTEM_OTHER/preloads/"]))
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    # tuple
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None,
+        ("SYSTEM_OTHER/preloads/",))
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    # list
+    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
+        "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None,
+        ["SYSTEM_OTHER/preloads/"])
+    self.assertTrue(is_apk)
+    self.assertFalse(is_compressed)
+    self.assertTrue(should_be_skipped)
+
+    # str is invalid.
+    self.assertRaises(
+        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None, "SYSTEM_OTHER/preloads/")
+
+    # None is invalid.
+    self.assertRaises(
+        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
+        None, None)