Enforce RROs for all the build-time ROs

This CL is to generate every static RRO package for its target package
automatically at build-time.

BOARD_ENFORCE_RRO build variable is added to specify whether enforcing
RRO is required or not.

BOARD_ENFORCE_RRO_EXEMPT_SOURCES build variable is added to specify
the module list of which item should be exempt from enforcing RRO.

Test: tested on bullhead and sailfish
Bug: 34097942
Change-Id: I455b2ce34e66c57a540c299b5e378b7c4e78d5b8
(cherry picked from commit 3070610b729e695668a9d93e546907903956a74d)
diff --git a/core/definitions.mk b/core/definitions.mk
index 2870869..07a8948 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -3306,3 +3306,40 @@
 #	  sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
 #	      -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
 #	  rm -f $*.d
+
+
+###########################################################
+# Append the information to generate a RRO package for the
+# source module.
+#
+#  $(1): Source module name.
+#  $(2): Whether $(3) is a manifest package name or not.
+#  $(3): Manifest package name if $(2) is true.
+#        Otherwise, android manifest file path of the
+#        source module.
+#  $(4): Whether LOCAL_EXPORT_PACKAGE_RESOURCES is set or
+#        not for the source module.
+#  $(5): Resource overlay list.
+###########################################################
+define append_enforce_rro_sources
+  $(eval ENFORCE_RRO_SOURCES += \
+      $(strip $(1))||$(strip $(2))||$(strip $(3))||$(strip $(4))||$(call normalize-path-list, $(strip $(5))))
+endef
+
+###########################################################
+# Generate all RRO packages for source modules stored in
+# ENFORCE_RRO_SOURCES
+###########################################################
+define generate_all_enforce_rro_packages
+$(foreach source,$(ENFORCE_RRO_SOURCES), \
+  $(eval _o := $(subst ||,$(space),$(source))) \
+  $(eval enforce_rro_source_module := $(word 1,$(_o))) \
+  $(eval enforce_rro_source_is_manifest_package_name := $(word 2,$(_o))) \
+  $(eval enforce_rro_source_manifest_package_info := $(word 3,$(_o))) \
+  $(eval enforce_rro_use_res_lib := $(word 4,$(_o))) \
+  $(eval enforce_rro_source_overlays := $(subst :, ,$(word 5,$(_o)))) \
+  $(eval enforce_rro_module := $(enforce_rro_source_module)__auto_generated_rro) \
+  $(eval include $(BUILD_SYSTEM)/generate_enforce_rro.mk) \
+  $(eval ALL_MODULES.$(enforce_rro_source_module).REQUIRED += $(enforce_rro_module)) \
+)
+endef
\ No newline at end of file
diff --git a/core/generate_enforce_rro.mk b/core/generate_enforce_rro.mk
new file mode 100644
index 0000000..579089c
--- /dev/null
+++ b/core/generate_enforce_rro.mk
@@ -0,0 +1,30 @@
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := $(enforce_rro_module)
+
+intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)
+rro_android_manifest_file := $(intermediates)/AndroidManifest.xml
+
+ifeq (true,$(enforce_rro_source_is_manifest_package_name))
+$(rro_android_manifest_file): PRIVATE_PACKAGE_NAME := $(enforce_rro_source_manifest_package_info)
+$(rro_android_manifest_file): build/tools/generate-enforce-rro-android-manifest.py
+	$(hide) build/tools/generate-enforce-rro-android-manifest.py -u -p $(PRIVATE_PACKAGE_NAME) -o $@
+else
+$(rro_android_manifest_file): PRIVATE_SOURCE_MANIFEST_FILE := $(enforce_rro_source_manifest_package_info)
+$(rro_android_manifest_file): $(enforce_rro_source_manifest_package_info) build/tools/generate-enforce-rro-android-manifest.py
+	$(hide) build/tools/generate-enforce-rro-android-manifest.py -p $(PRIVATE_SOURCE_MANIFEST_FILE) -o $@
+endif
+
+LOCAL_PATH:= $(intermediates)
+
+ifeq ($(enforce_rro_use_res_lib),true)
+LOCAL_RES_LIBRARIES := $(enforce_rro_source_module)
+endif
+
+LOCAL_FULL_MANIFEST_FILE := $(rro_android_manifest_file)
+LOCAL_CERTIFICATE := platform
+
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+LOCAL_RESOURCE_DIR := $(enforce_rro_source_overlays)
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/core/main.mk b/core/main.mk
index 41d903b..8200a4c 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -497,6 +497,10 @@
 ADDITIONAL_BUILD_PROPERTIES := $(strip $(ADDITIONAL_BUILD_PROPERTIES))
 .KATI_READONLY := ADDITIONAL_BUILD_PROPERTIES
 
+ifeq ($(BOARD_ENFORCE_RRO),true)
+ENFORCE_RRO_SOURCES :=
+endif
+
 ifneq ($(ONE_SHOT_MAKEFILE),)
 # We've probably been invoked by the "mm" shell function
 # with a subdirectory's makefile.
@@ -563,6 +567,13 @@
 # All module makefiles have been included at this point.
 # -------------------------------------------------------------------
 
+# -------------------------------------------------------------------
+# Enforce to generate all RRO packages for modules having resource
+# overlays.
+# -------------------------------------------------------------------
+ifeq ($(BOARD_ENFORCE_RRO),true)
+$(call generate_all_enforce_rro_packages)
+endif
 
 # -------------------------------------------------------------------
 # Fix up CUSTOM_MODULES to refer to installed files rather than
diff --git a/core/package_internal.mk b/core/package_internal.mk
index ca12437..f68a388 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -100,7 +100,32 @@
     $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \
       $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))
 
+enforce_rro_enabled :=
+ifeq ($(BOARD_ENFORCE_RRO),true)
+  ifeq (,$(filter $(LOCAL_PACKAGE_NAME), $(BOARD_ENFORCE_RRO_EXEMPT_SOURCES)))
+    ifneq ($(package_resource_overlays),)
+      enforce_rro_enabled := true
+    endif
+  endif
+
+  ifdef enforce_rro_enabled
+    ifeq (,$(LOCAL_MODULE_PATH))
+      ifeq (true,$(LOCAL_PROPRIETARY_MODULE))
+        enforce_rro_enabled :=
+      else ifeq (true,$(LOCAL_OEM_MODULE))
+        enforce_rro_enabled :=
+      else ifeq (true,$(LOCAL_ODM_MODULE))
+        enforce_rro_enabled :=
+      endif
+    else ifeq ($(filter $(TARGET_OUT)/%,$(LOCAL_MODULE_PATH)),)
+      enforce_rro_enabled :=
+    endif
+  endif
+endif
+
+ifndef enforce_rro_enabled
 LOCAL_RESOURCE_DIR := $(package_resource_overlays) $(LOCAL_RESOURCE_DIR)
+endif
 
 all_assets := $(strip \
     $(foreach dir, $(LOCAL_ASSET_DIR), \
@@ -645,3 +670,27 @@
 
 # Reset internal variables.
 all_res_assets :=
+
+ifdef enforce_rro_enabled
+  ifdef LOCAL_EXPORT_PACKAGE_RESOURCES
+    enforce_rro_use_res_lib := true
+  else
+    enforce_rro_use_res_lib := false
+  endif
+
+  ifdef LOCAL_MANIFEST_PACKAGE_NAME
+    enforce_rro_is_manifest_package_name := true
+    enforce_rro_manifest_package_info := $(LOCAL_MANIFEST_PACKAGE_NAME)
+  else
+    enforce_rro_is_manifest_package_name := false
+    enforce_rro_manifest_package_info := $(full_android_manifest)
+  endif
+
+$(call append_enforce_rro_sources, \
+    $(my_register_name), \
+    $(enforce_rro_is_manifest_package_name), \
+    $(enforce_rro_manifest_package_info), \
+    $(enforce_rro_use_res_lib), \
+    $(package_resource_overlays) \
+    )
+endif  # enforce_rro_enabled
\ No newline at end of file
diff --git a/tools/generate-enforce-rro-android-manifest.py b/tools/generate-enforce-rro-android-manifest.py
new file mode 100755
index 0000000..68331cf
--- /dev/null
+++ b/tools/generate-enforce-rro-android-manifest.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Utility to generate the Android manifest file of runtime resource overlay
+package for source module.
+"""
+from xml.dom.minidom import parseString
+import argparse
+import os
+import sys
+
+ANDROID_MANIFEST_TEMPLATE="""<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="%s.auto_generated_rro__"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="%s" android:priority="0" android:isStatic="true"/>
+</manifest>
+"""
+
+
+def get_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '-u', '--use-package-name', action='store_true',
+        help='Indicate that --package-info is a package name.')
+    parser.add_argument(
+        '-p', '--package-info', required=True,
+        help='Manifest package name or manifest file path of source module.')
+    parser.add_argument(
+        '-o', '--output', required=True,
+        help='Output manifest file path.')
+    return parser.parse_args()
+
+
+def main(argv):
+  args = get_args()
+
+  package_name = args.package_info
+  if not args.use_package_name:
+    with open(args.package_info) as f:
+      data = f.read()
+      f.close()
+      dom = parseString(data)
+      package_name = dom.documentElement.getAttribute('package')
+
+  with open(args.output, 'w+') as f:
+    f.write(ANDROID_MANIFEST_TEMPLATE % (package_name, package_name))
+    f.close()
+
+
+if __name__ == "__main__":
+  main(sys.argv)