Merge "Revert "OMS: integrate OverlayManagerService into framework""
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
index eb6da18..50ccb07 100644
--- a/cmds/idmap/Android.mk
+++ b/cmds/idmap/Android.mk
@@ -15,7 +15,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := idmap.cpp create.cpp inspect.cpp
+LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
 
 LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
 
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index d388977..3ab1915 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -13,6 +13,8 @@
       idmap --help \n\
       idmap --fd target overlay fd \n\
       idmap --path target overlay idmap \n\
+      idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
+                   dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
       idmap --inspect idmap \n\
 \n\
 DESCRIPTION \n\
@@ -47,6 +49,11 @@
       --path: create idmap for target package 'target' (path to apk) and overlay package \n\
               'overlay' (path to apk); write results to 'idmap' (path). \n\
 \n\
+      --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
+              target package 'target-package-name-to-look-for' (package name) present at\n\
+              'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
+              idmap file in 'dir-to-hold-idmaps' (path). \n\
+\n\
       --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
                  debug-friendly format. \n\
 \n\
@@ -90,6 +97,16 @@
 NOTES \n\
       This tool and its expected invocation from installd is modelled on dexopt.";
 
+    bool verify_directory_readable(const char *path)
+    {
+        return access(path, R_OK | X_OK) == 0;
+    }
+
+    bool verify_directory_writable(const char *path)
+    {
+        return access(path, W_OK) == 0;
+    }
+
     bool verify_file_readable(const char *path)
     {
         return access(path, R_OK) == 0;
@@ -150,6 +167,36 @@
         return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
     }
 
+    int maybe_scan(const char *target_package_name, const char *target_apk_path,
+            const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
+    {
+        if (!verify_root_or_system()) {
+            fprintf(stderr, "error: permission denied: not user root or user system\n");
+            return -1;
+        }
+
+        if (!verify_file_readable(target_apk_path)) {
+            ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+            return -1;
+        }
+
+        if (!verify_directory_writable(idmap_dir)) {
+            ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
+            return -1;
+        }
+
+        const size_t N = overlay_dirs->size();
+        for (size_t i = 0; i < N; i++) {
+            const char *dir = overlay_dirs->itemAt(i);
+            if (!verify_directory_readable(dir)) {
+                ALOGD("error: no read access to %s: %s\n", dir, strerror(errno));
+                return -1;
+            }
+        }
+
+        return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs);
+    }
+
     int maybe_inspect(const char *idmap_path)
     {
         // anyone (not just root or system) may do --inspect
@@ -188,6 +235,14 @@
         return maybe_create_path(argv[2], argv[3], argv[4]);
     }
 
+    if (argc >= 6 && !strcmp(argv[1], "--scan")) {
+        android::Vector<const char *> v;
+        for (int i = 5; i < argc; i++) {
+            v.push(argv[i]);
+        }
+        return maybe_scan(argv[2], argv[3], argv[4], &v);
+    }
+
     if (argc == 3 && !strcmp(argv[1], "--inspect")) {
         return maybe_inspect(argv[2]);
     }
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
index 5914de9..8d4210b 100644
--- a/cmds/idmap/idmap.h
+++ b/cmds/idmap/idmap.h
@@ -25,6 +25,12 @@
 
 int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
 
+// Regarding target_package_name: the idmap_scan implementation should
+// be able to extract this from the manifest in target_apk_path,
+// simplifying the external API.
+int idmap_scan(const char *target_package_name, const char *target_apk_path,
+        const char *idmap_dir, const android::Vector<const char *> *overlay_dirs);
+
 int idmap_inspect(const char *idmap_path);
 
 #endif // _IDMAP_H_
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
new file mode 100644
index 0000000..ab6adfb
--- /dev/null
+++ b/cmds/idmap/scan.cpp
@@ -0,0 +1,239 @@
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "idmap.h"
+
+#include <memory>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <utils/SortedVector.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#define NO_OVERLAY_TAG (-1000)
+
+using namespace android;
+
+namespace {
+    struct Overlay {
+        Overlay() {}
+        Overlay(const String8& a, const String8& i, int p) :
+            apk_path(a), idmap_path(i), priority(p) {}
+
+        bool operator<(Overlay const& rhs) const
+        {
+            return rhs.priority > priority;
+        }
+
+        String8 apk_path;
+        String8 idmap_path;
+        int priority;
+    };
+
+    bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
+    {
+        // the file is opened for appending so that it doesn't get truncated
+        // before we can guarantee mutual exclusion via the flock
+        FILE* fout = fopen(filename, "a");
+        if (fout == NULL) {
+            return false;
+        }
+
+        if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
+            fclose(fout);
+            return false;
+        }
+
+        if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
+            TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
+            fclose(fout);
+            return false;
+        }
+
+        for (size_t i = 0; i < overlayVector.size(); ++i) {
+            const Overlay& overlay = overlayVector[i];
+            fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
+        }
+
+        TEMP_FAILURE_RETRY(fflush(fout));
+        TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
+        fclose(fout);
+
+        // Make file world readable since Zygote (running as root) will read
+        // it when creating the initial AssetManger object
+        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
+        if (chmod(filename, mode) == -1) {
+            unlink(filename);
+            return false;
+        }
+
+        return true;
+    }
+
+    String8 flatten_path(const char *path)
+    {
+        String16 tmp(path);
+        tmp.replaceAll('/', '@');
+        return String8(tmp);
+    }
+
+    int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+    {
+        const size_t N = parser.getAttributeCount();
+        String16 target;
+        int priority = -1;
+        for (size_t i = 0; i < N; ++i) {
+            size_t len;
+            String16 key(parser.getAttributeName(i, &len));
+            if (key == String16("targetPackage")) {
+                const char16_t *p = parser.getAttributeStringValue(i, &len);
+                if (p != NULL) {
+                    target = String16(p, len);
+                }
+            } else if (key == String16("priority")) {
+                Res_value v;
+                if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+                    priority = v.data;
+                    if (priority < 0 || priority > 9999) {
+                        return -1;
+                    }
+                }
+            }
+        }
+        if (target == String16(target_package_name)) {
+            return priority;
+        }
+        return NO_OVERLAY_TAG;
+    }
+
+    int parse_manifest(const void *data, size_t size, const char *target_package_name)
+    {
+        ResXMLTree parser;
+        parser.setTo(data, size);
+        if (parser.getError() != NO_ERROR) {
+            ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
+            return -1;
+        }
+
+        ResXMLParser::event_code_t type;
+        do {
+            type = parser.next();
+            if (type == ResXMLParser::START_TAG) {
+                size_t len;
+                String16 tag(parser.getElementName(&len));
+                if (tag == String16("overlay")) {
+                    return parse_overlay_tag(parser, target_package_name);
+                }
+            }
+        } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+
+        return NO_OVERLAY_TAG;
+    }
+
+    int parse_apk(const char *path, const char *target_package_name)
+    {
+        std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
+        if (zip.get() == NULL) {
+            ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
+            return -1;
+        }
+        ZipEntryRO entry;
+        if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
+            ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
+            return -1;
+        }
+        uint32_t uncompLen = 0;
+        uint16_t method;
+        if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
+            ALOGW("%s: failed to read entry info\n", __FUNCTION__);
+            return -1;
+        }
+        if (method != ZipFileRO::kCompressDeflated) {
+            ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
+            return -1;
+        }
+        FileMap *dataMap = zip->createEntryFileMap(entry);
+        if (dataMap == NULL) {
+            ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
+            return -1;
+        }
+        char *buf = new char[uncompLen];
+        if (NULL == buf) {
+            ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
+            delete dataMap;
+            return -1;
+        }
+        StreamingZipInflater inflater(dataMap, uncompLen);
+        if (inflater.read(buf, uncompLen) < 0) {
+            ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
+            delete[] buf;
+            delete dataMap;
+            return -1;
+        }
+
+        int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
+        delete[] buf;
+        delete dataMap;
+        return priority;
+    }
+}
+
+int idmap_scan(const char *target_package_name, const char *target_apk_path,
+        const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
+{
+    String8 filename = String8(idmap_dir);
+    filename.appendPath("overlays.list");
+
+    SortedVector<Overlay> overlayVector;
+    const size_t N = overlay_dirs->size();
+    for (size_t i = 0; i < N; ++i) {
+        const char *overlay_dir = overlay_dirs->itemAt(i);
+        DIR *dir = opendir(overlay_dir);
+        if (dir == NULL) {
+            return EXIT_FAILURE;
+        }
+
+        struct dirent *dirent;
+        while ((dirent = readdir(dir)) != NULL) {
+            struct stat st;
+            char overlay_apk_path[PATH_MAX + 1];
+            snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
+            if (stat(overlay_apk_path, &st) < 0) {
+                continue;
+            }
+            if (!S_ISREG(st.st_mode)) {
+                continue;
+            }
+
+            int priority = parse_apk(overlay_apk_path, target_package_name);
+            if (priority < 0) {
+                continue;
+            }
+
+            String8 idmap_path(idmap_dir);
+            idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
+            idmap_path.append("@idmap");
+
+            if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
+                ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
+                        target_apk_path, overlay_apk_path, idmap_path.string());
+                continue;
+            }
+
+            Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
+            overlayVector.add(overlay);
+        }
+
+        closedir(dir);
+    }
+
+    if (!writePackagesList(filename.string(), overlayVector)) {
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f801e45..56f1e0c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2065,6 +2065,9 @@
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+                pkg.mOverlayPriority = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
+                        -1);
                 sa.recycle();
 
                 if (pkg.mOverlayTarget == null) {
@@ -2072,6 +2075,12 @@
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return null;
                 }
+                if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
+                    outError[0] = "<overlay> priority must be between 0 and 9999";
+                    mParseError =
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return null;
+                }
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals(TAG_KEY_SETS)) {
@@ -5509,6 +5518,7 @@
         public String mRequiredAccountType;
 
         public String mOverlayTarget;
+        public int mOverlayPriority;
         public boolean mTrustedOverlay;
 
         /**
@@ -5985,6 +5995,7 @@
             mRestrictedAccountType = dest.readString();
             mRequiredAccountType = dest.readString();
             mOverlayTarget = dest.readString();
+            mOverlayPriority = dest.readInt();
             mTrustedOverlay = (dest.readInt() == 1);
             mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
             mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
@@ -6100,6 +6111,7 @@
             dest.writeString(mRestrictedAccountType);
             dest.writeString(mRequiredAccountType);
             dest.writeString(mOverlayTarget);
+            dest.writeInt(mOverlayPriority);
             dest.writeInt(mTrustedOverlay ? 1 : 0);
             dest.writeArraySet(mSigningKeys);
             dest.writeArraySet(mUpgradeKeySets);
@@ -6548,7 +6560,6 @@
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
         ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
-        ai.resourceDirs = state.resourceDirs;
     }
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index ee56a18..24f1164 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,8 +31,6 @@
 
 import com.android.internal.util.ArrayUtils;
 
-import java.util.Arrays;
-
 /**
  * Per-user state information about a package.
  * @hide
@@ -56,8 +54,6 @@
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
-    public String[] resourceDirs;
-
     public PackageUserState() {
         installed = true;
         hidden = false;
@@ -85,8 +81,6 @@
         installReason = o.installReason;
         disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
         enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
-        resourceDirs =
-            o.resourceDirs == null ? null : Arrays.copyOf(o.resourceDirs, o.resourceDirs.length);
     }
 
     /**
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 314595f..723dce6 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -119,6 +119,96 @@
     return block;
 }
 
+// This is called by zygote (running as user root) as part of preloadResources.
+static void verifySystemIdmaps()
+{
+    pid_t pid;
+    char system_id[10];
+
+    snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+
+    switch (pid = fork()) {
+        case -1:
+            ALOGE("failed to fork for idmap: %s", strerror(errno));
+            break;
+        case 0: // child
+            {
+                struct __user_cap_header_struct capheader;
+                struct __user_cap_data_struct capdata;
+
+                memset(&capheader, 0, sizeof(capheader));
+                memset(&capdata, 0, sizeof(capdata));
+
+                capheader.version = _LINUX_CAPABILITY_VERSION;
+                capheader.pid = 0;
+
+                if (capget(&capheader, &capdata) != 0) {
+                    ALOGE("capget: %s\n", strerror(errno));
+                    exit(1);
+                }
+
+                capdata.effective = capdata.permitted;
+                if (capset(&capheader, &capdata) != 0) {
+                    ALOGE("capset: %s\n", strerror(errno));
+                    exit(1);
+                }
+
+                if (setgid(AID_SYSTEM) != 0) {
+                    ALOGE("setgid: %s\n", strerror(errno));
+                    exit(1);
+                }
+
+                if (setuid(AID_SYSTEM) != 0) {
+                    ALOGE("setuid: %s\n", strerror(errno));
+                    exit(1);
+                }
+
+                // Generic idmap parameters
+                const char* argv[8];
+                int argc = 0;
+                struct stat st;
+
+                memset(argv, NULL, sizeof(argv));
+                argv[argc++] = AssetManager::IDMAP_BIN;
+                argv[argc++] = "--scan";
+                argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
+                argv[argc++] = AssetManager::TARGET_APK_PATH;
+                argv[argc++] = AssetManager::IDMAP_DIR;
+
+                // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+                // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+                char subdir[PROP_VALUE_MAX];
+                int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY,
+                        subdir);
+                if (len == 0) {
+                    len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
+                }
+                if (len > 0) {
+                    String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+                    if (stat(overlayPath.string(), &st) == 0) {
+                        argv[argc++] = overlayPath.string();
+                    }
+                }
+                if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+                    argv[argc++] = AssetManager::OVERLAY_DIR;
+                }
+
+                // Finally, invoke idmap (if any overlay directory exists)
+                if (argc > 5) {
+                    execv(AssetManager::IDMAP_BIN, (char* const*)argv);
+                    ALOGE("failed to execv for idmap: %s", strerror(errno));
+                    exit(1); // should never get here
+                } else {
+                    exit(0);
+                }
+            }
+            break;
+        default: // parent
+            waitpid(pid, NULL, 0);
+            break;
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 // this guy is exported to other jni routines
@@ -1507,6 +1597,9 @@
 
 static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
 {
+    if (isSystem) {
+        verifySystemIdmaps();
+    }
     AssetManager* am = new AssetManager();
     if (am == NULL) {
         jniThrowException(env, "java/lang/OutOfMemoryError", "");
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 84111ae..acacd76 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -202,6 +202,15 @@
         *cookie = static_cast<int32_t>(mAssetPaths.size());
     }
 
+#ifdef __ANDROID__
+    // Load overlays, if any
+    asset_path oap;
+    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+        oap.isSystemAsset = isSystemAsset;
+        mAssetPaths.add(oap);
+    }
+#endif
+
     if (mResources != NULL) {
         appendPathToResTable(ap, appAsLib);
     }
@@ -484,6 +493,11 @@
 }
 
 bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
+    // skip those ap's that correspond to system overlays
+    if (ap.isSystemOverlay) {
+        return true;
+    }
+
     Asset* ass = NULL;
     ResTable* sharedRes = NULL;
     bool shared = true;
@@ -525,6 +539,14 @@
                 ALOGV("Creating shared resources for %s", ap.path.string());
                 sharedRes = new ResTable();
                 sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
+#ifdef __ANDROID__
+                const char* data = getenv("ANDROID_DATA");
+                LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+                String8 overlaysListPath(data);
+                overlaysListPath.appendPath(kResourceCache);
+                overlaysListPath.appendPath("overlays.list");
+                addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+#endif
                 sharedRes = const_cast<AssetManager*>(this)->
                     mZipSet.setZipResourceTable(ap.path, sharedRes);
             }
@@ -633,6 +655,58 @@
     return ass;
 }
 
+void AssetManager::addSystemOverlays(const char* pathOverlaysList,
+        const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
+{
+    FILE* fin = fopen(pathOverlaysList, "r");
+    if (fin == NULL) {
+        return;
+    }
+
+#ifndef _WIN32
+    if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) {
+        fclose(fin);
+        return;
+    }
+#endif
+    char buf[1024];
+    while (fgets(buf, sizeof(buf), fin)) {
+        // format of each line:
+        //   <path to apk><space><path to idmap><newline>
+        char* space = strchr(buf, ' ');
+        char* newline = strchr(buf, '\n');
+        asset_path oap;
+
+        if (space == NULL || newline == NULL || newline < space) {
+            continue;
+        }
+
+        oap.path = String8(buf, space - buf);
+        oap.type = kFileTypeRegular;
+        oap.idmap = String8(space + 1, newline - space - 1);
+        oap.isSystemOverlay = true;
+
+        Asset* oass = const_cast<AssetManager*>(this)->
+            openNonAssetInPathLocked("resources.arsc",
+                    Asset::ACCESS_BUFFER,
+                    oap);
+
+        if (oass != NULL) {
+            Asset* oidmap = openIdmapLocked(oap);
+            offset++;
+            sharedRes->add(oass, oidmap, offset + 1, false);
+            const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
+            const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+            delete oidmap;
+        }
+    }
+
+#ifndef _WIN32
+    TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN));
+#endif
+    fclose(fin);
+}
+
 const ResTable& AssetManager::getResources(bool required) const
 {
     const ResTable* rt = getResTable(required);
@@ -1372,6 +1446,20 @@
     return mModWhen == modWhen;
 }
 
+void AssetManager::SharedZip::addOverlay(const asset_path& ap)
+{
+    mOverlays.add(ap);
+}
+
+bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
+{
+    if (idx >= mOverlays.size()) {
+        return false;
+    }
+    *out = mOverlays[idx];
+    return true;
+}
+
 AssetManager::SharedZip::~SharedZip()
 {
     if (kIsDebug) {
@@ -1490,6 +1578,22 @@
     return true;
 }
 
+void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
+{
+    int idx = getIndex(path);
+    sp<SharedZip> zip = mZipFile[idx];
+    zip->addOverlay(overlay);
+}
+
+bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
+{
+    sp<SharedZip> zip = SharedZip::get(path, false);
+    if (zip == NULL) {
+        return false;
+    }
+    return zip->getOverlay(idx, out);
+}
+
 /*
  * Compute the zip file's index.
  *
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index f1e8b93..becd307 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -202,10 +202,12 @@
 private:
     struct asset_path
     {
-        asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemAsset(false) {}
+        asset_path() : path(""), type(kFileTypeRegular), idmap(""),
+                       isSystemOverlay(false), isSystemAsset(false) {}
         String8 path;
         FileType type;
         String8 idmap;
+        bool isSystemOverlay;
         bool isSystemAsset;
     };
 
@@ -235,6 +237,9 @@
 
     Asset* openIdmapLocked(const struct asset_path& ap) const;
 
+    void addSystemOverlays(const char* pathOverlaysList, const String8& targetPackagePath,
+            ResTable* sharedRes, size_t offset) const;
+
     class SharedZip : public RefBase {
     public:
         static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
@@ -249,6 +254,9 @@
 
         bool isUpToDate();
 
+        void addOverlay(const asset_path& ap);
+        bool getOverlay(size_t idx, asset_path* out) const;
+
     protected:
         ~SharedZip();
 
@@ -263,6 +271,8 @@
         Asset* mResourceTableAsset;
         ResTable* mResourceTable;
 
+        Vector<asset_path> mOverlays;
+
         static Mutex gLock;
         static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
     };
@@ -296,6 +306,9 @@
 
         bool isUpToDate();
 
+        void addOverlay(const String8& path, const asset_path& overlay);
+        bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
+
     private:
         void closeZip(int idx);
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5b4dd48..34164b16 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -118,8 +118,6 @@
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
     <!-- Permission needed to hold a wakelock in dumpstate.cpp (drop_root_user()) -->
     <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <!-- Permission needed to enable/disable overlays -->
-    <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
 
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index cb13a3d..3f97d4f 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Trace;
 import android.util.Slog;
@@ -106,25 +105,22 @@
                         + ": service constructor threw an exception", ex);
             }
 
-            startService(service);
+            // Register it.
+            mServices.add(service);
+
+            // Start it.
+            try {
+                service.onStart();
+            } catch (RuntimeException ex) {
+                throw new RuntimeException("Failed to start service " + name
+                        + ": onStart threw an exception", ex);
+            }
             return service;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
 
-    public void startService(@NonNull final SystemService service) {
-        // Register it.
-        mServices.add(service);
-        // Start it.
-        try {
-            service.onStart();
-        } catch (RuntimeException ex) {
-            throw new RuntimeException("Failed to start service " + service.getClass().getName()
-                    + ": onStart threw an exception", ex);
-        }
-    }
-
     /**
      * Starts the specified boot phase for all system services that have been started up to
      * this point.
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 88e8ce6..cc709ce 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -664,38 +664,7 @@
     }
 
     private void updateAssets(final int userId, List<String> targetPackageNames) {
-        final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-        final boolean updateFrameworkRes = targetPackageNames.contains("android");
-        if (updateFrameworkRes) {
-            targetPackageNames = pm.getTargetPackageNames(userId);
-        }
-
-        final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
-        synchronized (mLock) {
-            final int N = targetPackageNames.size();
-            for (int i = 0; i < N; i++) {
-                final String targetPackageName = targetPackageNames.get(i);
-                pendingChanges.put(targetPackageName,
-                        mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
-            }
-        }
-
-        final int N = targetPackageNames.size();
-        for (int i = 0; i < N; i++) {
-            final String targetPackageName = targetPackageNames.get(i);
-            if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName))) {
-                Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
-            }
-        }
-
-        final IActivityManager am = ActivityManagerNative.getDefault();
-        try {
-            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
-        } catch (RemoteException e) {
-            // Intentionally left empty.
-        }
+        // TODO: implement when we integrate OMS properly
     }
 
     private void schedulePersistSettings() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b6884b8..975ae06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -661,12 +661,9 @@
     final ArrayMap<String, Set<String>> mKnownCodebase =
             new ArrayMap<String, Set<String>>();
 
-    // List of APK paths to load for each user and package. This data is never
-    // persisted by the package manager. Instead, the overlay manager will
-    // ensure the data is up-to-date in runtime.
-    @GuardedBy("mPackages")
-    final SparseArray<ArrayMap<String, ArrayList<String>>> mEnabledOverlayPaths =
-        new SparseArray<ArrayMap<String, ArrayList<String>>>();
+    // Tracks available target package names -> overlay package paths.
+    final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays =
+        new ArrayMap<String, ArrayMap<String, PackageParser.Package>>();
 
     /**
      * Tracks new system packages [received in an OTA] that we expect to
@@ -3737,7 +3734,6 @@
             ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
                     ps.readUserState(userId), userId);
             if (ai != null) {
-                rebaseEnabledOverlays(ai, userId);
                 ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
             }
             return ai;
@@ -3772,7 +3768,6 @@
                 ApplicationInfo ai = PackageParser.generateApplicationInfo(
                         p, flags, ps.readUserState(userId), userId);
                 if (ai != null) {
-                    rebaseEnabledOverlays(ai, userId);
                     ai.packageName = resolveExternalPackageNameLPr(p);
                 }
                 return ai;
@@ -3789,26 +3784,6 @@
         return null;
     }
 
-    private void rebaseEnabledOverlays(@NonNull ApplicationInfo ai, int userId) {
-        List<String> paths = new ArrayList<>();
-        ArrayMap<String, ArrayList<String>> userSpecificOverlays =
-            mEnabledOverlayPaths.get(userId);
-        if (userSpecificOverlays != null) {
-            if (!"android".equals(ai.packageName)) {
-                ArrayList<String> frameworkOverlays = userSpecificOverlays.get("android");
-                if (frameworkOverlays != null) {
-                    paths.addAll(frameworkOverlays);
-                }
-            }
-
-            ArrayList<String> appOverlays = userSpecificOverlays.get(ai.packageName);
-            if (appOverlays != null) {
-                paths.addAll(appOverlays);
-            }
-        }
-        ai.resourceDirs = paths.size() > 0 ? paths.toArray(new String[paths.size()]) : null;
-    }
-
     private String normalizePackageNameLPr(String packageName) {
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         return normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -7172,7 +7147,6 @@
                         ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
-                            rebaseEnabledOverlays(ai, userId);
                             ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
                         }
                     } else {
@@ -7196,7 +7170,6 @@
                         ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
-                            rebaseEnabledOverlays(ai, userId);
                             ai.packageName = resolveExternalPackageNameLPr(p);
                             list.add(ai);
                         }
@@ -7340,7 +7313,6 @@
                         ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
-                            rebaseEnabledOverlays(ai, userId);
                             finalList.add(ai);
                         }
                     }
@@ -7476,6 +7448,60 @@
         return finalList;
     }
 
+    private void createIdmapsForPackageLI(PackageParser.Package pkg) {
+        ArrayMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName);
+        if (overlays == null) {
+            Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages");
+            return;
+        }
+        for (PackageParser.Package opkg : overlays.values()) {
+            // Not much to do if idmap fails: we already logged the error
+            // and we certainly don't want to abort installation of pkg simply
+            // because an overlay didn't fit properly. For these reasons,
+            // ignore the return value of createIdmapForPackagePairLI.
+            createIdmapForPackagePairLI(pkg, opkg);
+        }
+    }
+
+    private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
+            PackageParser.Package opkg) {
+        if (!opkg.mTrustedOverlay) {
+            Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
+                    opkg.baseCodePath + ": overlay not trusted");
+            return false;
+        }
+        ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
+        if (overlaySet == null) {
+            Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
+                    opkg.baseCodePath + " but target package has no known overlays");
+            return false;
+        }
+        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+        // TODO: generate idmap for split APKs
+        try {
+            mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid);
+        } catch (InstallerException e) {
+            Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
+                    + opkg.baseCodePath);
+            return false;
+        }
+        PackageParser.Package[] overlayArray =
+            overlaySet.values().toArray(new PackageParser.Package[0]);
+        Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
+            public int compare(PackageParser.Package p1, PackageParser.Package p2) {
+                return p1.mOverlayPriority - p2.mOverlayPriority;
+            }
+        };
+        Arrays.sort(overlayArray, cmp);
+
+        pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
+        int i = 0;
+        for (PackageParser.Package p : overlayArray) {
+            pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
+        }
+        return true;
+    }
+
     private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
         try {
@@ -9964,6 +9990,7 @@
         // writer
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
 
+        boolean createIdmapFailed = false;
         synchronized (mPackages) {
             // We don't expect installation to fail beyond this point
 
@@ -10304,9 +10331,36 @@
                     mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
                 }
             }
+
+            // Create idmap files for pairs of (packages, overlay packages).
+            // Note: "android", ie framework-res.apk, is handled by native layers.
+            if (pkg.mOverlayTarget != null) {
+                // This is an overlay package.
+                if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
+                    if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
+                        mOverlays.put(pkg.mOverlayTarget,
+                                new ArrayMap<String, PackageParser.Package>());
+                    }
+                    ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
+                    map.put(pkg.packageName, pkg);
+                    PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
+                    if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
+                        createIdmapFailed = true;
+                    }
+                }
+            } else if (mOverlays.containsKey(pkg.packageName) &&
+                    !pkg.packageName.equals("android")) {
+                // This is a regular package, with one or more known overlay packages.
+                createIdmapsForPackageLI(pkg);
+            }
         }
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+        if (createIdmapFailed) {
+            throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                    "scanPackageLI failed to createIdmap");
+        }
     }
 
     private static void maybeRenameForeignDexMarkers(PackageParser.Package existing,
@@ -20105,7 +20159,6 @@
         public static final int DUMP_FROZEN = 1 << 19;
         public static final int DUMP_DEXOPT = 1 << 20;
         public static final int DUMP_COMPILER_STATS = 1 << 21;
-        public static final int DUMP_ENABLED_OVERLAYS = 1 << 22;
 
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -20225,7 +20278,6 @@
                 pw.println("    check-permission <permission> <package> [<user>]: does pkg hold perm?");
                 pw.println("    dexopt: dump dexopt state");
                 pw.println("    compiler-stats: dump compiler statistics");
-                pw.println("    enabled-overlays: dump list of enabled overlay packages");
                 pw.println("    <package.name>: info about given package");
                 return;
             } else if ("--checkin".equals(opt)) {
@@ -20354,8 +20406,6 @@
                 dumpState.setDump(DumpState.DUMP_DEXOPT);
             } else if ("compiler-stats".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
-            } else if ("enabled-overlays".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_ENABLED_OVERLAYS);
             } else if ("write".equals(cmd)) {
                 synchronized (mPackages) {
                     mSettings.writeLPr();
@@ -20726,11 +20776,6 @@
                 dumpCompilerStatsLPr(pw, packageName);
             }
 
-            if (!checkin && dumpState.isDumping(DumpState.DUMP_ENABLED_OVERLAYS)) {
-                if (dumpState.onTitlePrinted()) pw.println();
-                dumpEnabledOverlaysLPr(pw);
-            }
-
             if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
                 if (dumpState.onTitlePrinted()) pw.println();
                 mSettings.dumpReadMessagesLPr(pw, dumpState);
@@ -20827,23 +20872,6 @@
         }
     }
 
-    private void dumpEnabledOverlaysLPr(PrintWriter pw) {
-        pw.println("Enabled overlay paths:");
-        final int N = mEnabledOverlayPaths.size();
-        for (int i = 0; i < N; i++) {
-            final int userId = mEnabledOverlayPaths.keyAt(i);
-            pw.println(String.format("    User %d:", userId));
-            final ArrayMap<String, ArrayList<String>> userSpecificOverlays =
-                mEnabledOverlayPaths.valueAt(i);
-            final int M = userSpecificOverlays.size();
-            for (int j = 0; j < M; j++) {
-                final String targetPackageName = userSpecificOverlays.keyAt(j);
-                final ArrayList<String> overlayPackagePaths = userSpecificOverlays.valueAt(j);
-                pw.println(String.format("        %s: %s", targetPackageName, overlayPackagePaths));
-            }
-        }
-    }
-
     private String dumpDomainString(String packageName) {
         List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
                 .getList();
@@ -22947,43 +22975,10 @@
 
 
         @Override
-        public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
-                @Nullable List<String> overlayPackageNames) {
-            synchronized (mPackages) {
-                if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
-                    Slog.e(TAG, "failed to find package " + targetPackageName);
-                    return false;
-                }
-
-                ArrayList<String> paths = null;
-                if (overlayPackageNames != null) {
-                    final int N = overlayPackageNames.size();
-                    paths = new ArrayList<String>(N);
-                    for (int i = 0; i < N; i++) {
-                        final String packageName = overlayPackageNames.get(i);
-                        final PackageParser.Package pkg = mPackages.get(packageName);
-                        if (pkg == null) {
-                            Slog.e(TAG, "failed to find package " + packageName);
-                            return false;
-                        }
-                        paths.add(pkg.baseCodePath);
-                    }
-                }
-
-                ArrayMap<String, ArrayList<String>> userSpecificOverlays =
-                    mEnabledOverlayPaths.get(userId);
-                if (userSpecificOverlays == null) {
-                    userSpecificOverlays = new ArrayMap<String, ArrayList<String>>();
-                    mEnabledOverlayPaths.put(userId, userSpecificOverlays);
-                }
-
-                if (paths != null && paths.size() > 0) {
-                    userSpecificOverlays.put(targetPackageName, paths);
-                } else {
-                    userSpecificOverlays.remove(targetPackageName);
-                }
-                return true;
-            }
+        public boolean setEnabledOverlayPackages(int userId, String targetPackageName,
+                List<String> overlayPackageNames) {
+            // TODO: implement when we integrate OMS properly
+            return false;
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e586482..a5f1945 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -82,7 +82,6 @@
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.notification.NotificationManagerService;
-import com.android.server.om.OverlayManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.pm.Installer;
@@ -593,11 +592,6 @@
         mActivityManagerService.setSystemProcess();
         traceEnd();
 
-        // Manages Overlay packages
-        traceBeginAndSlog("StartOverlayManagerService");
-        mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
-        traceEnd();
-
         // The sensor service needs access to package manager service, app ops
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 28596f7..384f49f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -197,6 +197,7 @@
         assertEquals(a.installLocation, b.installLocation);
         assertEquals(a.coreApp, b.coreApp);
         assertEquals(a.mRequiredForAllUsers, b.mRequiredForAllUsers);
+        assertEquals(a.mOverlayPriority, b.mOverlayPriority);
         assertEquals(a.mTrustedOverlay, b.mTrustedOverlay);
         assertEquals(a.use32bitAbi, b.use32bitAbi);
         assertEquals(a.packageName, b.packageName);
@@ -432,6 +433,7 @@
         pkg.installLocation = 100;
         pkg.coreApp = true;
         pkg.mRequiredForAllUsers = true;
+        pkg.mOverlayPriority = 100;
         pkg.mTrustedOverlay = true;
         pkg.use32bitAbi = true;
         pkg.packageName = "foo";