Search for runtime resource overlays in subdir.

See go/sku-colors. This changes the directory to search for framework
overlays if the right system property is defined. This allows
OEMs to specify different resources based on device SKUs.

Bug: 31692079
Change-Id: I9cb121b286b7f52aa26de1757fde1f3110cd47fd
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3473d9d..77d0343 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <sys/system_properties.h>
 
 #include <private/android_filesystem_config.h> // for AID_SYSTEM
 
@@ -41,6 +42,7 @@
 #include "ScopedUtfChars.h"
 #include "utils/Log.h"
 #include "utils/misc.h"
+#include "utils/String8.h"
 
 extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
 extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
@@ -184,11 +186,19 @@
                 argv[argc++] = AssetManager::TARGET_APK_PATH;
                 argv[argc++] = AssetManager::IDMAP_DIR;
 
-                // Directories to scan for overlays
-                // /vendor/overlay
-                if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+                // Directories to scan for overlays: if OVERLAY_SUBDIR_PROPERTY is defined,
+                // use OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ if exists, otherwise
+                // use OVERLAY_DIR if exists.
+                char subdir[PROP_VALUE_MAX];
+                int len = __system_property_get(AssetManager::OVERLAY_SUBDIR_PROPERTY, subdir);
+                if (len > 0) {
+                    String8 subdirPath = String8(AssetManager::OVERLAY_SUBDIR) + "/" + subdir;
+                    if (stat(subdirPath.string(), &st) == 0) {
+                        argv[argc++] = subdirPath.string();
+                    }
+                } else if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
                     argv[argc++] = AssetManager::OVERLAY_DIR;
-                 }
+                }
 
                 // Finally, invoke idmap (if any overlay directory exists)
                 if (argc > 5) {
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
index c67662b..5c17b23 100644
--- a/core/jni/fd_utils-inl.h
+++ b/core/jni/fd_utils-inl.h
@@ -20,6 +20,7 @@
 #include <vector>
 #include <algorithm>
 
+#include <android-base/strings.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <grp.h>
@@ -241,7 +242,8 @@
 
   // Returns true iff. a given path is whitelisted. A path is whitelisted
   // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar".
+  // under /system/framework that ends with ".jar" or if it is a system
+  // framework overlay.
   static bool IsWhitelisted(const std::string& path) {
     for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
       if (kPathWhitelist[i] == path) {
@@ -249,12 +251,37 @@
       }
     }
 
-    static const std::string kFrameworksPrefix = "/system/framework/";
-    static const std::string kJarSuffix = ".jar";
-    if (path.compare(0, kFrameworksPrefix.size(), kFrameworksPrefix) == 0 &&
-        path.compare(path.size() - kJarSuffix.size(), kJarSuffix.size(), kJarSuffix) == 0) {
+    static const char* kFrameworksPrefix = "/system/framework/";
+    static const char* kJarSuffix = ".jar";
+    if (android::base::StartsWith(path, kFrameworksPrefix)
+        && android::base::EndsWith(path, kJarSuffix)) {
       return true;
     }
+
+    // Whitelist files needed for Runtime Resource Overlay, like these:
+    // /system/vendor/overlay/framework-res.apk
+    // /system/vendor/overlay-subdir/pg/framework-res.apk
+    // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+    // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+    // See AssetManager.cpp for more details on overlay-subdir.
+    static const char* kOverlayDir = "/system/vendor/overlay/";
+    static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+    static const char* kApkSuffix = ".apk";
+
+    if ((android::base::StartsWith(path, kOverlayDir)
+            || android::base::StartsWith(path, kOverlaySubdir))
+        && android::base::EndsWith(path, kApkSuffix)
+        && path.find("/../") == std::string::npos) {
+      return true;
+    }
+
+    static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
+    static const char* kOverlayIdmapSuffix = ".apk@idmap";
+    if (android::base::StartsWith(path, kOverlayIdmapPrefix)
+        && android::base::EndsWith(path, kOverlayIdmapSuffix)) {
+      return true;
+    }
+
     return false;
   }
 
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 914ac3d..0b22802 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -72,6 +72,13 @@
     static const char* RESOURCES_FILENAME;
     static const char* IDMAP_BIN;
     static const char* OVERLAY_DIR;
+    /*
+     * If OVERLAY_SUBDIR_PROPERTY is set, search for runtime resource overlay
+     * APKs in OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ rather than in
+     * OVERLAY_DIR.
+     */
+    static const char* OVERLAY_SUBDIR;
+    static const char* OVERLAY_SUBDIR_PROPERTY;
     static const char* TARGET_PACKAGE_NAME;
     static const char* TARGET_APK_PATH;
     static const char* IDMAP_DIR;
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index f50cff4..6fb57fa 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -78,6 +78,8 @@
 const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
 const char* AssetManager::TARGET_PACKAGE_NAME = "android";
 const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
+const char* AssetManager::OVERLAY_SUBDIR = "/system/vendor/overlay-subdir";
+const char* AssetManager::OVERLAY_SUBDIR_PROPERTY = "ro.boot.vendor.overlay.subdir";
 const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
 
 namespace {