Merge "Runtime resource overlay, iteration 2"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 97baf9a..d3080e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1541,11 +1541,11 @@
     /**
      * Creates the top level resources for the given package.
      */
-    Resources getTopLevelResources(String resDir,
+    Resources getTopLevelResources(String resDir, String[] overlayDirs,
             int displayId, Configuration overrideConfiguration,
             LoadedApk pkgInfo) {
-        return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration,
-                pkgInfo.getCompatibilityInfo(), null);
+        return mResourcesManager.getTopLevelResources(resDir, overlayDirs, displayId,
+                overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
     }
 
     final Handler getHandler() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b505d4f..a280448 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -774,7 +774,7 @@
         }
         Resources r = mContext.mMainThread.getTopLevelResources(
                 app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
-                        Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
+                app.resourceDirs, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
         if (r != null) {
             return r;
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 190ddb4..8d127c6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1911,8 +1911,8 @@
         ContextImpl c = new ContextImpl();
         c.init(mPackageInfo, null, mMainThread);
         c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                getDisplayId(), overrideConfiguration, mResources.getCompatibilityInfo(),
-                mActivityToken);
+                mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration,
+                mResources.getCompatibilityInfo(), mActivityToken);
         return c;
     }
 
@@ -1929,7 +1929,7 @@
         context.mDisplay = display;
         DisplayAdjustments daj = getDisplayAdjustments(displayId);
         context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                displayId, null, daj.getCompatibilityInfo(), null);
+                mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null);
         return context;
     }
 
@@ -2041,7 +2041,8 @@
             mDisplayAdjustments.setCompatibilityInfo(compatInfo);
             mDisplayAdjustments.setActivityToken(activityToken);
             mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                    Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
+                    mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo,
+                    activityToken);
         } else {
             mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
             mDisplayAdjustments.setActivityToken(activityToken);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 4239a5d..0115d1b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -76,6 +76,7 @@
     final String mPackageName;
     private final String mAppDir;
     private final String mResDir;
+    private final String[] mOverlayDirs;
     private final String[] mSharedLibraries;
     private final String mDataDir;
     private final String mLibDir;
@@ -120,6 +121,7 @@
         final int myUid = Process.myUid();
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
+        mOverlayDirs = aInfo.resourceDirs;
         if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
             aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
                     mPackageName);
@@ -159,6 +161,7 @@
         mPackageName = name;
         mAppDir = null;
         mResDir = null;
+        mOverlayDirs = null;
         mSharedLibraries = null;
         mDataDir = null;
         mDataDirFile = null;
@@ -471,6 +474,10 @@
         return mResDir;
     }
 
+    public String[] getOverlayDirs() {
+        return mOverlayDirs;
+    }
+
     public String getDataDir() {
         return mDataDir;
     }
@@ -485,7 +492,7 @@
 
     public Resources getResources(ActivityThread mainThread) {
         if (mResources == null) {
-            mResources = mainThread.getTopLevelResources(mResDir,
+            mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
                     Display.DEFAULT_DISPLAY, null, this);
         }
         return mResources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index f55dba4..728f372 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -147,7 +147,7 @@
      * @param compatInfo the compability info. Must not be null.
      * @param token the application token for determining stack bounds.
      */
-    public Resources getTopLevelResources(String resDir, int displayId,
+    public Resources getTopLevelResources(String resDir, String[] overlayDirs, int displayId,
             Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
         final float scale = compatInfo.applicationScale;
         ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
@@ -180,6 +180,12 @@
             return null;
         }
 
+        if (overlayDirs != null) {
+            for (String idmapPath : overlayDirs) {
+                assets.addOverlayPath(idmapPath);
+            }
+        }
+
         //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
         DisplayMetrics dm = getDisplayMetricsLocked(displayId);
         Configuration config;
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index af1a6d5..785f2b4 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -227,6 +227,14 @@
     /** @hide */
     public String requiredAccountType;
 
+    /**
+     * What package, if any, this package will overlay.
+     *
+     * Package name of target package, or null.
+     * @hide
+     */
+    public String overlayTarget;
+
     public PackageInfo() {
     }
 
@@ -270,6 +278,7 @@
         dest.writeInt(requiredForAllUsers ? 1 : 0);
         dest.writeString(restrictedAccountType);
         dest.writeString(requiredAccountType);
+        dest.writeString(overlayTarget);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -311,5 +320,6 @@
         requiredForAllUsers = source.readInt() != 0;
         restrictedAccountType = source.readString();
         requiredAccountType = source.readString();
+        overlayTarget = source.readString();
     }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4607902..52564eb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -307,6 +307,7 @@
         }
         pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.requiredAccountType = p.mRequiredAccountType;
+        pi.overlayTarget = p.mOverlayTarget;
         pi.firstInstallTime = firstInstallTime;
         pi.lastUpdateTime = lastUpdateTime;
         if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -490,6 +491,11 @@
 
     public Package parsePackage(File sourceFile, String destCodePath,
             DisplayMetrics metrics, int flags) {
+        return parsePackage(sourceFile, destCodePath, metrics, flags, false);
+    }
+
+    public Package parsePackage(File sourceFile, String destCodePath,
+            DisplayMetrics metrics, int flags, boolean trustedOverlay) {
         mParseError = PackageManager.INSTALL_SUCCEEDED;
 
         mArchiveSourcePath = sourceFile.getPath();
@@ -542,7 +548,7 @@
         Exception errorException = null;
         try {
             // XXXX todo: need to figure out correct configuration.
-            pkg = parsePackage(res, parser, flags, errorText);
+            pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);
         } catch (Exception e) {
             errorException = e;
             mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
@@ -951,8 +957,8 @@
     }
 
     private Package parsePackage(
-        Resources res, XmlResourceParser parser, int flags, String[] outError)
-        throws XmlPullParserException, IOException {
+        Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,
+        String[] outError) throws XmlPullParserException, IOException {
         AttributeSet attrs = parser;
 
         mParseInstrumentationArgs = null;
@@ -1051,6 +1057,31 @@
                 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
                     return null;
                 }
+            } else if (tagName.equals("overlay")) {
+                pkg.mTrustedOverlay = trustedOverlay;
+
+                sa = res.obtainAttributes(attrs,
+                        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) {
+                    outError[0] = "<overlay> does not specify a target package";
+                    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("keys")) {
                 if (!parseKeys(pkg, res, parser, attrs, outError)) {
                     return null;
@@ -3546,6 +3577,10 @@
          */
         public ManifestDigest manifestDigest;
 
+        public String mOverlayTarget;
+        public int mOverlayPriority;
+        public boolean mTrustedOverlay;
+
         /**
          * Data used to feed the KeySetManager
          */
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 780c4be..418bdda 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -90,7 +90,7 @@
                 mNumRefs = 0;
                 incRefsLocked(this.hashCode());
             }
-            init();
+            init(false);
             if (localLOGV) Log.v(TAG, "New asset manager: " + this);
             ensureSystemAssets();
         }
@@ -113,7 +113,7 @@
                 incRefsLocked(this.hashCode());
             }
         }
-        init();
+        init(true);
         if (localLOGV) Log.v(TAG, "New asset manager: " + this);
     }
 
@@ -615,6 +615,16 @@
 
     private native final int addAssetPathNative(String path);
 
+     /**
+     * Add a set of assets to overlay an already added set of assets.
+     *
+     * This is only intended for application resources. System wide resources
+     * are handled before any Java code is executed.
+     *
+     * {@hide}
+     */
+    public native final int addOverlayPath(String idmapPath);
+
     /**
      * Add multiple sets of assets to the asset manager at once.  See
      * {@link #addAssetPath(String)} for more information.  Returns array of
@@ -752,7 +762,7 @@
     private native final int[] getArrayStringInfo(int arrayRes);
     /*package*/ native final int[] getArrayIntResource(int arrayRes);
 
-    private native final void init();
+    private native final void init(boolean isSystem);
     private native final void destroy();
 
     private final void incRefsLocked(long id) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index f96aef8..7162a1c 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -35,7 +35,16 @@
 #include <androidfw/AssetManager.h>
 #include <androidfw/ResourceTypes.h>
 
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/capability.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);
+
 
 namespace android {
 
@@ -100,6 +109,63 @@
     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);
+                }
+
+                execl(AssetManager::IDMAP_BIN, AssetManager::IDMAP_BIN, "--scan",
+                        AssetManager::OVERLAY_DIR, AssetManager::TARGET_PACKAGE_NAME,
+                        AssetManager::TARGET_APK_PATH, AssetManager::IDMAP_DIR, (char*)NULL);
+                ALOGE("failed to execl for idmap: %s", strerror(errno));
+                exit(1); // should never get here
+            }
+            break;
+        default: // parent
+            waitpid(pid, NULL, 0);
+            break;
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 // this guy is exported to other jni routines
@@ -444,6 +510,25 @@
     return (res) ? static_cast<jint>(cookie) : 0;
 }
 
+static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
+                                                     jstring idmapPath)
+{
+    ScopedUtfChars idmapPath8(env, idmapPath);
+    if (idmapPath8.c_str() == NULL) {
+        return 0;
+    }
+
+    AssetManager* am = assetManagerForJavaObject(env, clazz);
+    if (am == NULL) {
+        return 0;
+    }
+
+    int32_t cookie;
+    bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
+
+    return (res) ? (jint)cookie : 0;
+}
+
 static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
 {
     AssetManager* am = assetManagerForJavaObject(env, clazz);
@@ -1579,8 +1664,11 @@
     return array;
 }
 
-static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
+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", "");
@@ -1658,6 +1746,8 @@
         (void*) android_content_AssetManager_getAssetRemainingLength },
     { "addAssetPathNative", "(Ljava/lang/String;)I",
         (void*) android_content_AssetManager_addAssetPath },
+    { "addOverlayPath",   "(Ljava/lang/String;)I",
+        (void*) android_content_AssetManager_addOverlayPath },
     { "isUpToDate",     "()Z",
         (void*) android_content_AssetManager_isUpToDate },
 
@@ -1724,7 +1814,7 @@
         (void*) android_content_AssetManager_getArrayIntResource },
 
     // Bookkeeping.
-    { "init",           "()V",
+    { "init",           "(Z)V",
         (void*) android_content_AssetManager_init },
     { "destroy",        "()V",
         (void*) android_content_AssetManager_destroy },
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b20f5ba..4647413 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1777,6 +1777,16 @@
         <attr name="publicKey" />
     </declare-styleable>
 
+    <!-- Attributes relating to resource overlay packages. -->
+    <declare-styleable name="AndroidManifestResourceOverlay" parent="AndroidManifest">
+        <!-- Package name of base package whose resources will be overlaid. -->
+        <attr name="targetPackage" />
+
+        <!-- Load order of overlay package. -->
+        <attr name="priority" />
+
+    </declare-styleable>
+
     <!-- Declaration of an {@link android.content.Intent} object in XML.  May
          also include zero or more {@link #IntentCategory <category> and
          {@link #Extra <extra>} tags. -->
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index e0c6183..ac47900 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -70,6 +70,12 @@
 class AssetManager : public AAssetManager {
 public:
     static const char* RESOURCES_FILENAME;
+    static const char* IDMAP_BIN;
+    static const char* OVERLAY_DIR;
+    static const char* TARGET_PACKAGE_NAME;
+    static const char* TARGET_APK_PATH;
+    static const char* IDMAP_DIR;
+
     typedef enum CacheMode {
         CACHE_UNKNOWN = 0,
         CACHE_OFF,          // don't try to cache file locations
@@ -94,6 +100,7 @@
      * newly-added asset source.
      */
     bool addAssetPath(const String8& path, int32_t* cookie);
+    bool addOverlayPath(const String8& path, int32_t* cookie);
 
     /*                                                                       
      * Convenience for adding the standard system assets.  Uses the
@@ -272,19 +279,14 @@
     void setLocaleLocked(const char* locale);
     void updateResourceParamsLocked() const;
 
-    bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
-                               const String8& idmapPath);
-
-    bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
-                            const String8& idmapPath);
-
     Asset* openIdmapLocked(const struct asset_path& ap) const;
 
-    bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc);
+    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);
+        static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
 
         ZipFileRO* getZip();
 
@@ -295,6 +297,9 @@
         ResTable* setResourceTable(ResTable* res);
         
         bool isUpToDate();
+
+        void addOverlay(const asset_path& ap);
+        bool getOverlay(size_t idx, asset_path* out) const;
         
     protected:
         ~SharedZip();
@@ -310,6 +315,8 @@
         Asset* mResourceTableAsset;
         ResTable* mResourceTable;
 
+        Vector<asset_path> mOverlays;
+
         static Mutex gLock;
         static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
     };
@@ -342,6 +349,9 @@
         static String8 getPathName(const char* path);
 
         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/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 7779f7f..c022818 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1545,39 +1545,21 @@
     // Return value: on success: NO_ERROR; caller is responsible for free-ing
     // outData (using free(3)). On failure, any status_t value other than
     // NO_ERROR; the caller should not free outData.
-    status_t createIdmap(const ResTable& overlay, uint32_t targetCrc, uint32_t overlayCrc,
-                         void** outData, size_t* outSize) const;
-
     status_t createIdmap(const ResTable& overlay,
             uint32_t targetCrc, uint32_t overlayCrc,
             const char* targetPath, const char* overlayPath,
-            void** outData, uint32_t* outSize) const
-    {
-        (void)targetPath;
-        (void)overlayPath;
-        return createIdmap(overlay, targetCrc, overlayCrc, outData, outSize);
-    }
+            void** outData, uint32_t* outSize) const;
 
     enum {
-        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t),
+        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256,
     };
     // Retrieve idmap meta-data.
     //
     // This function only requires the idmap header (the first
     // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file.
     static bool getIdmapInfo(const void* idmap, size_t size,
-                             uint32_t* pTargetCrc, uint32_t* pOverlayCrc);
-
-    static bool getIdmapInfo(const void* idmap, size_t size,
             uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
-            String8* pTargetPath, String8* pOverlayPath)
-    {
-        if (*pTargetPath)
-            *pTargetPath = String8();
-        if (*pOverlayPath)
-            *pOverlayPath = String8();
-        return getIdmapInfo(idmap, size, pTargetCrc, pOverlayCrc);
-    }
+            String8* pTargetPath, String8* pOverlayPath);
 
     void print(bool inclValues) const;
     static String8 normalizeForOutput(const char* input);
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 251d47b..05a948d 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -41,10 +41,8 @@
 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
-#include <fcntl.h>
+#include <string.h> // strerror
 #include <strings.h>
-#include <sys/stat.h>
-#include <unistd.h>
 
 #ifndef TEMP_FAILURE_RETRY
 /* Used to retry syscalls that can return EINTR. */
@@ -75,7 +73,7 @@
 static const char* kAssetsRoot = "assets";
 static const char* kAppZipName = NULL; //"classes.jar";
 static const char* kSystemAssets = "framework/framework-res.apk";
-static const char* kIdmapCacheDir = "resource-cache";
+static const char* kResourceCache = "resource-cache";
 
 static const char* kExcludeExtension = ".EXCLUDE";
 
@@ -84,15 +82,19 @@
 static volatile int32_t gCount = 0;
 
 const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
+const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
+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::IDMAP_DIR = "/data/resource-cache";
 
 namespace {
-    // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
     String8 idmapPathForPackagePath(const String8& pkgPath)
     {
         const char* root = getenv("ANDROID_DATA");
         LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
         String8 path(root);
-        path.appendPath(kIdmapCacheDir);
+        path.appendPath(kResourceCache);
 
         char buf[256]; // 256 chars should be enough for anyone...
         strncpy(buf, pkgPath.string(), 255);
@@ -210,37 +212,78 @@
         *cookie = static_cast<int32_t>(mAssetPaths.size());
     }
 
-    // add overlay packages for /system/framework; apps are handled by the
-    // (Java) package manager
-    if (strncmp(path.string(), "/system/framework/", 18) == 0) {
-        // When there is an environment variable for /vendor, this
-        // should be changed to something similar to how ANDROID_ROOT
-        // and ANDROID_DATA are used in this file.
-        String8 overlayPath("/vendor/overlay/framework/");
-        overlayPath.append(path.getPathLeaf());
-        if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {
-            asset_path oap;
-            oap.path = overlayPath;
-            oap.type = ::getFileType(overlayPath.string());
-            bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay
-            if (addOverlay) {
-                oap.idmap = idmapPathForPackagePath(overlayPath);
-
-                if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
-                    addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);
-                }
-            }
-            if (addOverlay) {
-                mAssetPaths.add(oap);
-            } else {
-                ALOGW("failed to add overlay package %s\n", overlayPath.string());
-            }
-        }
+#ifdef HAVE_ANDROID_OS
+    // Load overlays, if any
+    asset_path oap;
+    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+        mAssetPaths.add(oap);
     }
+#endif
 
     return true;
 }
 
+bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
+{
+    const String8 idmapPath = idmapPathForPackagePath(packagePath);
+
+    AutoMutex _l(mLock);
+
+    for (size_t i = 0; i < mAssetPaths.size(); ++i) {
+        if (mAssetPaths[i].idmap == idmapPath) {
+           *cookie = static_cast<int32_t>(i + 1);
+            return true;
+         }
+     }
+
+    Asset* idmap = NULL;
+    if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
+        ALOGW("failed to open idmap file %s\n", idmapPath.string());
+        return false;
+    }
+
+    String8 targetPath;
+    String8 overlayPath;
+    if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
+                NULL, NULL, &targetPath, &overlayPath)) {
+        ALOGW("failed to read idmap file %s\n", idmapPath.string());
+        delete idmap;
+        return false;
+    }
+    delete idmap;
+
+    if (overlayPath != packagePath) {
+        ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
+                idmapPath.string(), packagePath.string(), overlayPath.string());
+        return false;
+    }
+    if (access(targetPath.string(), R_OK) != 0) {
+        ALOGW("failed to access file %s: %s\n", targetPath.string(), strerror(errno));
+        return false;
+    }
+    if (access(idmapPath.string(), R_OK) != 0) {
+        ALOGW("failed to access file %s: %s\n", idmapPath.string(), strerror(errno));
+        return false;
+    }
+    if (access(overlayPath.string(), R_OK) != 0) {
+        ALOGW("failed to access file %s: %s\n", overlayPath.string(), strerror(errno));
+        return false;
+    }
+
+    asset_path oap;
+    oap.path = overlayPath;
+    oap.type = ::getFileType(overlayPath.string());
+    oap.idmap = idmapPath;
+#if 0
+    ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
+            targetPath.string(), overlayPath.string(), idmapPath.string());
+#endif
+    mAssetPaths.add(oap);
+    *cookie = static_cast<int32_t>(mAssetPaths.size());
+
+    return true;
+ }
+
 bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
         uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, uint32_t* outSize)
 {
@@ -257,158 +300,13 @@
             ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
             return false;
         }
-        tables[i].add(ass, (void*)1, false);
+        tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
     }
 
     return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
             targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
 }
 
-bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
-                                      const String8& idmapPath)
-{
-    struct stat st;
-    if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) {
-        if (errno == ENOENT) {
-            return true; // non-existing idmap is always stale
-        } else {
-            ALOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno));
-            return false;
-        }
-    }
-    if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
-        ALOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size);
-        return false;
-    }
-    int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY));
-    if (fd == -1) {
-        ALOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno));
-        return false;
-    }
-    char buf[ResTable::IDMAP_HEADER_SIZE_BYTES];
-    ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES;
-    for (;;) {
-        ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft,
-                                            bytesLeft));
-        if (r < 0) {
-            TEMP_FAILURE_RETRY(close(fd));
-            return false;
-        }
-        bytesLeft -= r;
-        if (bytesLeft == 0) {
-            break;
-        }
-    }
-    TEMP_FAILURE_RETRY(close(fd));
-
-    uint32_t cachedOriginalCrc, cachedOverlayCrc;
-    if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES,
-                                &cachedOriginalCrc, &cachedOverlayCrc)) {
-        return false;
-    }
-
-    uint32_t actualOriginalCrc, actualOverlayCrc;
-    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) {
-        return false;
-    }
-    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) {
-        return false;
-    }
-    return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc;
-}
-
-bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename,
-                                        uint32_t* pCrc)
-{
-    asset_path ap;
-    ap.path = zipPath;
-    const ZipFileRO* zip = getZipFileLocked(ap);
-    if (zip == NULL) {
-        return false;
-    }
-    const ZipEntryRO entry = zip->findEntryByName(entryFilename);
-    if (entry == NULL) {
-        return false;
-    }
-
-    const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc);
-    zip->releaseEntry(entry);
-
-    return gotInfo;
-}
-
-bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
-                                         const String8& idmapPath)
-{
-    ALOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n",
-         __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string());
-    ResTable tables[2];
-    const String8* paths[2] = { &originalPath, &overlayPath };
-    uint32_t originalCrc, overlayCrc;
-    bool retval = false;
-    ssize_t offset = 0;
-    int fd = 0;
-    uint32_t* data = NULL;
-    size_t size;
-
-    for (int i = 0; i < 2; ++i) {
-        asset_path ap;
-        ap.type = kFileTypeRegular;
-        ap.path = *paths[i];
-        Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
-        if (ass == NULL) {
-            ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
-            goto error;
-        }
-        tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
-    }
-
-    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) {
-        ALOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string());
-        goto error;
-    }
-    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) {
-        ALOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string());
-        goto error;
-    }
-
-    if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc,
-                              (void**)&data, &size) != NO_ERROR) {
-        ALOGW("failed to generate idmap data for file %s\n", idmapPath.string());
-        goto error;
-    }
-
-    // This should be abstracted (eg replaced by a stand-alone
-    // application like dexopt, triggered by something equivalent to
-    // installd).
-    fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644));
-    if (fd == -1) {
-        ALOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno));
-        goto error_free;
-    }
-    for (;;) {
-        ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size));
-        if (written < 0) {
-            ALOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(),
-                 strerror(errno));
-            goto error_close;
-        }
-        size -= (size_t)written;
-        offset += written;
-        if (size == 0) {
-            break;
-        }
-    }
-
-    retval = true;
-error_close:
-    TEMP_FAILURE_RETRY(close(fd));
-error_free:
-    free(data);
-error:
-    return retval;
-}
-
 bool AssetManager::addDefaultAssets()
 {
     const char* root = getenv("ANDROID_ROOT");
@@ -685,6 +583,10 @@
                 // which we want to avoid parsing every time.
                 sharedRes = const_cast<AssetManager*>(this)->
                     mZipSet.getZipResourceTable(ap.path);
+                if (sharedRes != NULL) {
+                    // skip ahead the number of system overlay packages preloaded
+                    i += sharedRes->getTableCount() - 1;
+                }
             }
             if (sharedRes == NULL) {
                 ass = const_cast<AssetManager*>(this)->
@@ -708,6 +610,14 @@
                     ALOGV("Creating shared resources for %s", ap.path.string());
                     sharedRes = new ResTable();
                     sharedRes->add(ass, i + 1, false, idmap);
+#ifdef HAVE_ANDROID_OS
+                    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, i);
+#endif
                     sharedRes = const_cast<AssetManager*>(this)->
                         mZipSet.setZipResourceTable(ap.path, sharedRes);
                 }
@@ -791,6 +701,46 @@
     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;
+    }
+
+    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);
+
+        Asset* oass = const_cast<AssetManager*>(this)->
+            openNonAssetInPathLocked("resources.arsc",
+                    Asset::ACCESS_BUFFER,
+                    oap);
+
+        if (oass != NULL) {
+            Asset* oidmap = openIdmapLocked(oap);
+            offset++;
+            sharedRes->add(oass, offset + 1, false, oidmap);
+            const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
+            const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+        }
+    }
+    fclose(fin);
+}
+
 const ResTable& AssetManager::getResources(bool required) const
 {
     const ResTable* rt = getResTable(required);
@@ -1849,7 +1799,8 @@
     }
 }
 
-sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path)
+sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
+        bool createIfNotPresent)
 {
     AutoMutex _l(gLock);
     time_t modWhen = getFileModDate(path);
@@ -1857,6 +1808,9 @@
     if (zip != NULL && zip->mModWhen == modWhen) {
         return zip;
     }
+    if (zip == NULL && !createIfNotPresent) {
+        return NULL;
+    }
     zip = new SharedZip(path, modWhen);
     gOpen.add(path, zip);
     return zip;
@@ -1915,6 +1869,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()
 {
     //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
@@ -2038,6 +2006,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/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 609ad19..8cc98af 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -284,11 +284,37 @@
     if (!assertIdmapHeader(map, mapSize)) {
         return UNKNOWN_ERROR;
     }
+    if (mapSize <= IDMAP_HEADER_SIZE + 1) {
+        ALOGW("corrupt idmap: map size %d too short\n", mapSize);
+        return UNKNOWN_ERROR;
+    }
+    uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
+    if (typeCount == 0) {
+        ALOGW("corrupt idmap: no types\n");
+        return UNKNOWN_ERROR;
+    }
+    if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
+        ALOGW("corrupt idmap: number of types %d extends past idmap size %d\n", typeCount, mapSize);
+        return UNKNOWN_ERROR;
+    }
     const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
+    // find first defined type
     while (*p == 0) {
         ++p;
+        if (--typeCount == 0) {
+            ALOGW("corrupt idmap: types declared, none found\n");
+            return UNKNOWN_ERROR;
+        }
     }
-    *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff;
+
+    // determine package id from first entry of first type
+    const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
+    if (offset > mapSize) {
+        ALOGW("corrupt idmap: entry offset %d points outside map size %d\n", offset, mapSize);
+        return UNKNOWN_ERROR;
+    }
+    *outId = (map[offset] >> 24) & 0x000000ff;
+
     return NO_ERROR;
 }
 
@@ -5334,23 +5360,30 @@
     return NO_ERROR;
 }
 
-status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
-                               void** outData, size_t* outSize) const
+status_t ResTable::createIdmap(const ResTable& overlay,
+        uint32_t targetCrc, uint32_t overlayCrc,
+        const char* targetPath, const char* overlayPath,
+        void** outData, size_t* outSize) const
 {
     // see README for details on the format of map
     if (mPackageGroups.size() == 0) {
+        ALOGW("idmap: target package has no package groups, cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
     if (mPackageGroups[0]->packages.size() == 0) {
+        ALOGW("idmap: target package has no packages in its first package group, "
+                "cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
 
     Vector<Vector<uint32_t> > map;
+    // overlaid packages are assumed to contain only one package group
     const PackageGroup* pg = mPackageGroups[0];
     const Package* pkg = pg->packages[0];
     size_t typeCount = pkg->types.size();
     // starting size is header + first item (number of types in map)
     *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+    // overlay packages are assumed to contain only one package group
     const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
     const uint32_t pkg_id = pkg->package->id << 24;
 
@@ -5426,8 +5459,22 @@
     }
     uint32_t* data = (uint32_t*)*outData;
     *data++ = htodl(IDMAP_MAGIC);
-    *data++ = htodl(originalCrc);
+    *data++ = htodl(targetCrc);
     *data++ = htodl(overlayCrc);
+    const char* paths[] = { targetPath, overlayPath };
+    for (int j = 0; j < 2; ++j) {
+        char* p = (char*)data;
+        const char* path = paths[j];
+        const size_t I = strlen(path);
+        if (I > 255) {
+            ALOGV("path exceeds expected 255 characters: %s\n", path);
+            return UNKNOWN_ERROR;
+        }
+        for (size_t i = 0; i < 256; ++i) {
+            *p++ = i < I ? path[i] : '\0';
+        }
+        data += 256 / sizeof(uint32_t);
+    }
     const size_t mapSize = map.size();
     *data++ = htodl(mapSize);
     size_t offset = mapSize;
@@ -5442,6 +5489,10 @@
             offset += N;
         }
     }
+    if (offset == mapSize) {
+        ALOGW("idmap: no resources in overlay package present in base package\n");
+        return UNKNOWN_ERROR;
+    }
     for (size_t i = 0; i < mapSize; ++i) {
         const Vector<uint32_t>& vector = map.itemAt(i);
         const size_t N = vector.size();
@@ -5463,14 +5514,25 @@
 }
 
 bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
-                            uint32_t* pOriginalCrc, uint32_t* pOverlayCrc)
+                            uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
+                            String8* pTargetPath, String8* pOverlayPath)
 {
     const uint32_t* map = (const uint32_t*)idmap;
     if (!assertIdmapHeader(map, sizeBytes)) {
         return false;
     }
-    *pOriginalCrc = map[1];
-    *pOverlayCrc = map[2];
+    if (pTargetCrc) {
+        *pTargetCrc = map[1];
+    }
+    if (pOverlayCrc) {
+        *pOverlayCrc = map[2];
+    }
+    if (pTargetPath) {
+        pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+    }
+    if (pOverlayPath) {
+        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+    }
     return true;
 }
 
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 0d2b503..11a6498 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -211,6 +211,17 @@
         return execute(builder.toString());
     }
 
+    public int idmap(String targetApkPath, String overlayApkPath, int uid) {
+        StringBuilder builder = new StringBuilder("idmap");
+        builder.append(' ');
+        builder.append(targetApkPath);
+        builder.append(' ');
+        builder.append(overlayApkPath);
+        builder.append(' ');
+        builder.append(uid);
+        return execute(builder.toString());
+    }
+
     public int movedex(String srcPath, String dstPath) {
         StringBuilder builder = new StringBuilder("movedex");
         builder.append(' ');
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7ea1fce..74a66d6 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -217,6 +217,7 @@
     static final int SCAN_UPDATE_TIME = 1<<6;
     static final int SCAN_DEFER_DEX = 1<<7;
     static final int SCAN_BOOTING = 1<<8;
+    static final int SCAN_TRUSTED_OVERLAY = 1<<9;
 
     static final int REMOVE_CHATTY = 1<<16;
 
@@ -257,8 +258,13 @@
 
     private static final String LIB_DIR_NAME = "lib";
 
+    private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+
     static final String mTempContainerPrefix = "smdl2tmp";
 
+    private static final String IDMAP_PREFIX = "/data/resource-cache/";
+    private static final String IDMAP_SUFFIX = "@idmap";
+
     final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
             Process.THREAD_PRIORITY_BACKGROUND);
     final PackageHandler mHandler;
@@ -296,6 +302,9 @@
     // This is the object monitoring the system app dir.
     final FileObserver mVendorInstallObserver;
 
+    // This is the object monitoring the vendor overlay package dir.
+    final FileObserver mVendorOverlayInstallObserver;
+
     // This is the object monitoring mAppInstallDir.
     final FileObserver mAppInstallObserver;
 
@@ -343,6 +352,10 @@
     final HashMap<String, PackageParser.Package> mPackages =
             new HashMap<String, PackageParser.Package>();
 
+    // Tracks available target package names -> overlay package paths.
+    final HashMap<String, HashMap<String, PackageParser.Package>> mOverlays =
+        new HashMap<String, HashMap<String, PackageParser.Package>>();
+
     final Settings mSettings;
     boolean mRestoredSettings;
 
@@ -1273,6 +1286,17 @@
                 }
             }
 
+            // Collect vendor overlay packages.
+            // (Do this before scanning any apps.)
+            // For security and version matching reason, only consider
+            // overlay packages if they reside in VENDOR_OVERLAY_DIR.
+            File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
+            mVendorOverlayInstallObserver = new AppDirObserver(
+                vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false);
+            mVendorOverlayInstallObserver.startWatching();
+            scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0);
+
             // Find base frameworks (resource packages without code).
             mFrameworkInstallObserver = new AppDirObserver(
                 frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
@@ -3475,6 +3499,56 @@
         return finalList;
     }
 
+    private void createIdmapsForPackageLI(PackageParser.Package pkg) {
+        HashMap<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.mScanPath + " and " +
+                    opkg.mScanPath + ": overlay not trusted");
+            return false;
+        }
+        HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
+        if (overlaySet == null) {
+            Slog.e(TAG, "was about to create idmap for " + pkg.mScanPath + " and " +
+                    opkg.mScanPath + " but target package has no known overlays");
+            return false;
+        }
+        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+        if (mInstaller.idmap(pkg.mScanPath, opkg.mScanPath, sharedGid) != 0) {
+            Slog.e(TAG, "Failed to generate idmap for " + pkg.mScanPath + " and " + opkg.mScanPath);
+            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.applicationInfo.sourceDir;
+        }
+        return true;
+    }
+
     private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
         String[] files = dir.list();
         if (files == null) {
@@ -3572,7 +3646,7 @@
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setOnlyCoreApps(mOnlyCore);
         final PackageParser.Package pkg = pp.parsePackage(scanFile,
-                scanPath, mMetrics, parseFlags);
+                scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
 
         if (pkg == null) {
             mLastScanError = pp.getParseError();
@@ -5081,6 +5155,29 @@
             }
 
             pkgSetting.setTimeStamp(scanFileTime);
+
+            // 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 HashMap<String, PackageParser.Package>());
+                    }
+                    HashMap<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)) {
+                        mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+                        return null;
+                    }
+                }
+            } else if (mOverlays.containsKey(pkg.packageName) &&
+                    !pkg.packageName.equals("android")) {
+                // This is a regular package, with one or more known overlay packages.
+                createIdmapsForPackageLI(pkg);
+            }
         }
 
         return pkg;