Implement system data migration support.

This adds three new features:

- <original-package android:name="com.foo" /> manifest tag.
  This allows an .apk to specify another package it originally came from,
  propagating all state and data from the old to new package.

- <adopt-permissions android:name="com.foo" /> manifest tag.
  In some more complicated cases, a new .apk may be a combination
  of multiple older .apks that each declared their own permissions.
  This allows you to propagate the permissions from these other
  .apks into the new one.

- A new system/etc/updatecmds directory.
  You can place files here which describe data files to move from
  one package to another.  (See below for details.)

Also in this change: we now clean up the data directories of
.apks that disappear from the system image, and some improvements
to logging and reporting error messages.

A typical file in the updatecmds directory looks like this:

-------
com.google.android.gsf:com.google.android.providers.talk
    databases/talk.db
com.google.android.gsf:com.google.android.googleapps
    databases/gls.db
-------

This says that for com.google.android.sfs, there are two packages to
move files from:

From com.google.android.providers.talk, the file databases/talk.db.
From com.google.android.googleapps, the file databases/gls.db

As part of moving the file, its owner will be changed from the old
package to whoever is the owner of the new package's data directory.

If those two files had existed, after booting you would now have the
files:

/data/data/com.google.android.gsf/databases/talk.db
/data/data/com.google.android.gsf/databases/gls.db

Note that all three of these facilities assume that the older .apk
is completely removed from the newer system.  The WILL NOT work
correctly if the older .apk still remains.
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 79bda74..1e8555b 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -78,6 +78,30 @@
     return delete_dir_contents(pkgdir, 1, 0);
 }
 
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag)
+{
+    char oldpkgdir[PKG_PATH_MAX];
+    char newpkgdir[PKG_PATH_MAX];
+
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(oldpkgdir, PKG_SEC_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(newpkgdir, PKG_SEC_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+            return -1;
+    }
+
+    if (rename(oldpkgdir, newpkgdir) < 0) {
+        LOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno));
+        return -errno;
+    }
+    return 0;
+}
+
 int delete_user_data(const char *pkgname, int encrypted_fs_flag)
 {
     char pkgdir[PKG_PATH_MAX];
@@ -636,3 +660,203 @@
     }
     return -1;
 }
+
+int create_move_path(char path[PKG_PATH_MAX],
+    const char* prefix,
+    const char* pkgname,
+    const char* leaf)
+{
+    if ((strlen(prefix) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) {
+        return -1;
+    }
+    
+    sprintf(path, "%s%s/%s", prefix, pkgname, leaf);
+    return 0;
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid)
+{
+    while (path[basepos] != 0) {
+        if (path[basepos] == '/') {
+            path[basepos] = 0;
+            LOGI("Making directory: %s\n", path);
+            if (mkdir(path, mode) == 0) {
+                chown(path, uid, gid);
+            }
+            path[basepos] = '/';
+            basepos++;
+        }
+        basepos++;
+    }
+}
+
+int movefiles()
+{
+    DIR *d;
+    int dfd, subfd;
+    struct dirent *de;
+    struct stat s;
+    char buf[PKG_PATH_MAX+1];
+    int bufp, bufe, bufi, readlen;
+
+    char srcpkg[PKG_NAME_MAX];
+    char dstpkg[PKG_NAME_MAX];
+    char srcpath[PKG_PATH_MAX];
+    char dstpath[PKG_PATH_MAX];
+    int dstuid, dstgid;
+    int hasspace;
+
+    d = opendir(UPDATE_COMMANDS_DIR_PREFIX);
+    if (d == NULL) {
+        goto done;
+    }
+    dfd = dirfd(d);
+
+        /* Iterate through all files in the directory, executing the
+         * file movements requested there-in.
+         */
+    while ((de = readdir(d))) {
+        const char *name = de->d_name;
+
+        if (de->d_type == DT_DIR) {
+            continue;
+        } else {
+            subfd = openat(dfd, name, O_RDONLY);
+            if (subfd < 0) {
+                LOGW("Unable to open update commands at %s%s\n",
+                        UPDATE_COMMANDS_DIR_PREFIX, name);
+                continue;
+            }
+            
+            bufp = 0;
+            bufe = 0;
+            buf[PKG_PATH_MAX] = 0;
+            srcpkg[0] = dstpkg[0] = 0;
+            while (1) {
+                bufi = bufp;
+                while (bufi < bufe && buf[bufi] != '\n') {
+                    bufi++;
+                }
+                if (bufi < bufe) {
+                    buf[bufi] = 0;
+                    LOGV("Processing line: %s\n", buf+bufp);
+                    hasspace = 0;
+                    while (bufp < bufi && isspace(buf[bufp])) {
+                        hasspace = 1;
+                        bufp++;
+                    }
+                    if (buf[bufp] == '#' || bufp == bufi) {
+                        // skip comments and empty lines.
+                    } else if (hasspace) {
+                        if (dstpkg[0] == 0) {
+                            LOGW("Path before package line in %s%s: %s\n",
+                                    UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+                        } else if (srcpkg[0] == 0) {
+                            // Skip -- source package no longer exists.
+                        } else {
+                            LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
+                            if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) &&
+                                    !create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) {
+                                LOGI("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid);
+                                mkinnerdirs(dstpath, strlen(dstpath)-(bufi-bufp),
+                                    S_IRWXU|S_IRWXG|S_IXOTH, dstuid, dstgid);
+                                if (rename(srcpath, dstpath) >= 0) {
+                                    if (chown(dstpath, dstuid, dstgid) < 0) {
+                                        LOGE("cannot chown %s: %s\n", dstpath, strerror(errno));
+                                        unlink(dstpath);
+                                    }
+                                } else {
+                                    LOGW("Unable to rename %s to %s: %s\n",
+                                        srcpath, dstpath, strerror(errno));
+                                }
+                            }
+                        }
+                    } else {
+                        char* div = strchr(buf+bufp, ':');
+                        if (div == NULL) {
+                            LOGW("Bad package spec in %s%s; no ':' sep: %s\n",
+                                    UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+                        } else {
+                            *div = 0;
+                            div++;
+                            if (strlen(buf+bufp) < PKG_NAME_MAX) {
+                                strcpy(dstpkg, buf+bufp);
+                            } else {
+                                srcpkg[0] = dstpkg[0] = 0;
+                                LOGW("Package name too long in %s%s: %s\n",
+                                        UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+                            }
+                            if (strlen(div) < PKG_NAME_MAX) {
+                                strcpy(srcpkg, div);
+                            } else {
+                                srcpkg[0] = dstpkg[0] = 0;
+                                LOGW("Package name too long in %s%s: %s\n",
+                                        UPDATE_COMMANDS_DIR_PREFIX, name, div);
+                            }
+                            if (srcpkg[0] != 0) {
+                                if (!create_pkg_path(srcpath, PKG_DIR_PREFIX, srcpkg,
+                                        PKG_DIR_POSTFIX)) {
+                                    if (lstat(srcpath, &s) < 0) {
+                                        // Package no longer exists -- skip.
+                                        srcpkg[0] = 0;
+                                    }
+                                } else {
+                                    srcpkg[0] = 0;
+                                    LOGW("Can't create path %s in %s%s\n",
+                                            div, UPDATE_COMMANDS_DIR_PREFIX, name);
+                                }
+                                if (srcpkg[0] != 0) {
+                                    if (!create_pkg_path(dstpath, PKG_DIR_PREFIX, dstpkg,
+                                            PKG_DIR_POSTFIX)) {
+                                        if (lstat(dstpath, &s) == 0) {
+                                            dstuid = s.st_uid;
+                                            dstgid = s.st_gid;
+                                        } else {
+                                            srcpkg[0] = 0;
+                                            LOGW("Can't stat path %s in %s%s: %s\n",
+                                                    dstpath, UPDATE_COMMANDS_DIR_PREFIX,
+                                                    name, strerror(errno));
+                                        }
+                                    } else {
+                                        srcpkg[0] = 0;
+                                        LOGW("Can't create path %s in %s%s\n",
+                                                div, UPDATE_COMMANDS_DIR_PREFIX, name);
+                                    }
+                                }
+                                LOGV("Transfering from %s to %s: uid=%d\n",
+                                    srcpkg, dstpkg, dstuid);
+                            }
+                        }
+                    }
+                    bufp = bufi+1;
+                } else {
+                    if (bufp == 0) {
+                        if (bufp < bufe) {
+                            LOGW("Line too long in %s%s, skipping: %s\n",
+                                    UPDATE_COMMANDS_DIR_PREFIX, name, buf);
+                        }
+                    } else if (bufp < bufe) {
+                        memcpy(buf, buf+bufp, bufe-bufp);
+                        bufe -= bufp;
+                        bufp = 0;
+                    }
+                    readlen = read(subfd, buf+bufe, PKG_PATH_MAX-bufe);
+                    if (readlen < 0) {
+                        LOGW("Failure reading update commands in %s%s: %s\n",
+                                UPDATE_COMMANDS_DIR_PREFIX, name, strerror(errno));
+                        break;
+                    } else if (readlen == 0) {
+                        break;
+                    }
+                    bufe += readlen;
+                    buf[bufe] = 0;
+                    LOGV("Read buf: %s\n", buf);
+                }
+            }
+            close(subfd);
+        }
+    }
+    closedir(d);
+done:
+    return 0;
+}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 5bc6c86..882c493 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -53,6 +53,11 @@
     return uninstall(arg[0], atoi(arg[1])); /* pkgname */
 }
 
+static int do_rename(char **arg, char reply[REPLY_MAX])
+{
+    return renamepkg(arg[0], arg[1], atoi(arg[2])); /* oldpkgname, newpkgname */
+}
+
 static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */
 {
     return free_cache(atoi(arg[0])); /* free_size */
@@ -87,6 +92,11 @@
     return delete_user_data(arg[0], atoi(arg[1])); /* pkgname */
 }
 
+static int do_movefiles(char **arg, char reply[REPLY_MAX])
+{
+    return movefiles();
+}
+
 struct cmdinfo {
     const char *name;
     unsigned numargs;
@@ -100,11 +110,13 @@
     { "movedex",              2, do_move_dex },
     { "rmdex",                1, do_rm_dex },
     { "remove",               2, do_remove },
+    { "rename",               3, do_rename },
     { "freecache",            1, do_free_cache },
     { "rmcache",              2, do_rm_cache },
     { "protect",              2, do_protect },
     { "getsize",              4, do_get_size },
     { "rmuserdata",           2, do_rm_user_data },
+    { "movefiles",            0, do_movefiles },
 };
 
 static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 35a173e..92ae310 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -73,6 +73,7 @@
 #define DALVIK_CACHE_PREFIX   "/data/dalvik-cache/"
 #define DALVIK_CACHE_POSTFIX  "/classes.dex"
 
+#define UPDATE_COMMANDS_DIR_PREFIX  "/system/etc/updatecmds/"
 
 #define PKG_NAME_MAX  128   /* largest allowed package name */
 #define PKG_PATH_MAX  256   /* max size of any path we use */
@@ -97,6 +98,7 @@
 
 int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid);
 int uninstall(const char *pkgname, int encrypted_fs_flag);
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag);
 int delete_user_data(const char *pkgname, int encrypted_fs_flag);
 int delete_cache(const char *pkgname, int encrypted_fs_flag);
 int move_dex(const char *src, const char *dst);
@@ -106,3 +108,4 @@
              int *codesize, int *datasize, int *cachesize, int encrypted_fs_flag);
 int free_cache(int free_size);
 int dexopt(const char *apk_path, uid_t uid, int is_public);
+int movefiles();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bbde0a6..bac55cc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -953,6 +953,39 @@
                     return null;
                 }
                 
+            } else if (tagName.equals("original-package")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+                String name = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
+
+                sa.recycle();
+
+                if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+                    pkg.mOriginalPackage = name;
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+                
+            } else if (tagName.equals("adopt-permissions")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+                String name = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
+
+                sa.recycle();
+
+                if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+                    if (pkg.mAdoptPermissions == null) {
+                        pkg.mAdoptPermissions = new ArrayList<String>();
+                    }
+                    pkg.mAdoptPermissions.add(name);
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+                
             } else if (tagName.equals("eat-comment")) {
                 // Just skip this tag
                 XmlUtils.skipCurrentTag(parser);
@@ -2528,6 +2561,9 @@
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
 
+        public String mOriginalPackage = null;
+        public ArrayList<String> mAdoptPermissions = null;
+        
         // We store the application meta-data independently to avoid multiple unwanted references
         public Bundle mAppMetaData = null;
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7728c50..54781e3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -923,6 +923,19 @@
         <attr name="name" />
     </declare-styleable>
 
+    <!-- Private tag to declare the original package name that this package is
+         based on.  Only used for packages installed in the system image.  If
+         given, and different than the actual package name, and the given
+         original package was previously installed on the device but the new
+         one was not, then the data for the old one will be renamed to be
+         for the new package.
+
+         <p>This appears as a child tag of the root
+         {@link #AndroidManifest manifest} tag. -->
+    <declare-styleable name="AndroidManifestOriginalPackage" parent="AndroidManifest">
+        <attr name="name" />
+    </declare-styleable>
+
     <!-- The <code>provider</code> tag declares a
          {@link android.content.ContentProvider} class that is available
          as part of the package's application components, supplying structured
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 88b1f02..11297d5 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -222,6 +222,21 @@
         return execute(builder.toString());
     }
 
+    public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
+        StringBuilder builder = new StringBuilder("rename");
+        builder.append(' ');
+        builder.append(oldname);
+        builder.append(' ');
+        builder.append(newname);
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
+        return execute(builder.toString());
+    }
+
     public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("rmcache");
         builder.append(' ');
@@ -308,4 +323,8 @@
             return -1;
         }
     }    
+
+    public int moveFiles() {
+        return execute("movefiles");
+    }
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 238403c..387f139 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -96,11 +96,13 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -117,6 +119,7 @@
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_SETTINGS = false;
     private static final boolean DEBUG_PREFERRED = false;
+    private static final boolean DEBUG_UPGRADE = false;
 
     private static final boolean MULTIPLE_APPLICATION_UIDS = true;
     private static final int RADIO_UID = Process.PHONE_UID;
@@ -221,7 +224,6 @@
 
     final Settings mSettings;
     boolean mRestoredSettings;
-    boolean mReportedUidError;
 
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
     int[] mGlobalGids;
@@ -271,6 +273,10 @@
     final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups =
             new HashMap<String, PackageParser.PermissionGroup>();
 
+    // Packages whose data we have transfered into another package, thus
+    // should no longer exist.
+    final HashSet<String> mTransferedPackages = new HashSet<String>();
+    
     // Broadcast actions that are only available to the system.
     final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
 
@@ -661,16 +667,43 @@
                 }
             }
 
+            // Find base frameworks (resource packages without code).
             mFrameworkInstallObserver = new AppDirObserver(
                 mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
             mFrameworkInstallObserver.startWatching();
             scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
                     scanMode | SCAN_NO_DEX);
+            
+            // Collect all system packages.
             mSystemAppDir = new File(Environment.getRootDirectory(), "app");
             mSystemInstallObserver = new AppDirObserver(
                 mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
             mSystemInstallObserver.startWatching();
             scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
+            
+            if (mInstaller != null) {
+                if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
+                mInstaller.moveFiles();
+            }
+            
+            // Prune any system packages that no longer exist.
+            Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+            while (psit.hasNext()) {
+                PackageSetting ps = psit.next();
+                if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
+                        && !mPackages.containsKey(ps.name)) {
+                    psit.remove();
+                    String msg = "System package " + ps.name
+                            + " no longer exists; wiping its data";
+                    reportSettingsProblem(Log.WARN, msg);
+                    if (mInstaller != null) {
+                        // XXX how to set useEncryptedFSDir for packages that
+                        // are not encrypted?
+                        mInstaller.remove(ps.name, true);
+                    }
+                }
+            }
+            
             mAppInstallDir = new File(dataDir, "app");
             if (mInstaller == null) {
                 // Make sure these dirs exist, when we are running in
@@ -2081,14 +2114,21 @@
         }
     }
 
+    private static File getSettingsProblemFile() {
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        File fname = new File(systemDir, "uiderrors.txt");
+        return fname;
+    }
+    
     private static void reportSettingsProblem(int priority, String msg) {
         try {
-            File dataDir = Environment.getDataDirectory();
-            File systemDir = new File(dataDir, "system");
-            File fname = new File(systemDir, "uiderrors.txt");
+            File fname = getSettingsProblemFile();
             FileOutputStream out = new FileOutputStream(fname, true);
             PrintWriter pw = new PrintWriter(out);
-            pw.println(msg);
+            SimpleDateFormat formatter = new SimpleDateFormat();
+            String dateString = formatter.format(new Date(System.currentTimeMillis()));
+            pw.println(dateString + ": " + msg);
             pw.close();
             FileUtils.setPermissions(
                     fname.toString(),
@@ -2295,6 +2335,21 @@
                 ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
     }
     
+    private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
+        if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+            Log.w(TAG, "Unable to update from " + oldPkg.name
+                    + " to " + newPkg.packageName
+                    + ": old package not in system partition");
+            return false;
+        } else if (mPackages.get(oldPkg.name) != null) {
+            Log.w(TAG, "Unable to update from " + oldPkg.name
+                    + " to " + newPkg.packageName
+                    + ": old package still exists");
+            return false;
+        }
+        return true;
+    }
+    
     private PackageParser.Package scanPackageLI(
         PackageParser.Package pkg, int parseFlags, int scanMode) {
         File scanFile = new File(pkg.mScanPath);
@@ -2439,22 +2494,67 @@
                 }
             }
 
+            // Check if we are renaming from an original package name.
+            PackageSetting origPackage = null;
+            if (pkg.mOriginalPackage != null) {
+                // We will only retrieve the setting for it if it already
+                // exists; otherwise we need to make a new one later.
+                origPackage = mSettings.peekPackageLP(pkg.mOriginalPackage);
+                if (origPackage != null) {
+                    if (!verifyPackageUpdate(origPackage, pkg)) {
+                        origPackage = null;
+                    } else if (origPackage.sharedUser != null) {
+                        if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
+                            Log.w(TAG, "Unable to migrate data from " + origPackage.name
+                                    + " to " + pkg.packageName + ": old uid "
+                                    + origPackage.sharedUser.name
+                                    + " differs from " + pkg.mSharedUserId);
+                            origPackage = null;
+                        }
+                    } else {
+                        if (DEBUG_UPGRADE) Log.v(TAG, "Migrating data from "
+                                + origPackage.name + " to " + pkg.packageName);
+                    }
+                }
+            }
+            
+            if (mTransferedPackages.contains(pkg.packageName)) {
+                Log.w(TAG, "Package " + pkg.packageName
+                        + " was transferred to another, but its .apk remains");
+            }
+            
             // Just create the setting, don't add it yet. For already existing packages
             // the PkgSetting exists already and doesn't have to be created.
-            pkgSetting = mSettings.getPackageLP(pkg, suid, destCodeFile,
+            pkgSetting = mSettings.getPackageLP(pkg, origPackage, suid, destCodeFile,
                             destResourceFile, pkg.applicationInfo.flags, true, false);
             if (pkgSetting == null) {
                 Log.w(TAG, "Creating application package " + pkgName + " failed");
                 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                 return null;
             }
-            if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) {
+            if (mSettings.mDisabledSysPackages.get(pkg.packageName) != null) {
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
             pkg.applicationInfo.uid = pkgSetting.userId;
             pkg.mExtras = pkgSetting;
 
+            if (pkg.mAdoptPermissions != null) {
+                // This package wants to adopt ownership of permissions from
+                // another package.
+                for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) {
+                    String origName = pkg.mAdoptPermissions.get(i);
+                    PackageSetting orig = mSettings.peekPackageLP(origName);
+                    if (orig != null) {
+                        if (verifyPackageUpdate(orig, pkg)) {
+                            if (DEBUG_UPGRADE) Log.v(TAG, "Adopting permissions from "
+                                    + origName + " to " + pkg.packageName);
+                            mSettings.transferPermissions(origName, pkg.packageName);
+                        }
+                    }
+                }
+            }
+            
             if (!verifySignaturesLP(pkgSetting, pkg, parseFlags,
                     (scanMode&SCAN_UPDATE_SIGNATURE) != 0)) {
                 if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) {
@@ -2543,6 +2643,9 @@
             } else {
                 dataPath = new File(mAppDataDir, pkgName);
             }
+            
+            boolean uidError = false;
+            
             if (dataPath.exists()) {
                 mOutPermissions[1] = 0;
                 FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
@@ -2556,7 +2659,7 @@
                         // current data so the application will still work.
                         if (mInstaller != null) {
                             int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
-                            if(ret >= 0) {
+                            if (ret >= 0) {
                                 // Old data gone!
                                 String msg = "System package " + pkg.packageName
                                         + " has changed from uid: "
@@ -2591,12 +2694,12 @@
                                 + mOutPermissions[1] + " on disk, "
                                 + pkg.applicationInfo.uid + " in settings";
                         synchronized (mPackages) {
-                            if (!mReportedUidError) {
-                                mReportedUidError = true;
-                                msg = msg + "; read messages:\n"
-                                        + mSettings.getReadMessagesLP();
+                            mSettings.mReadMessages.append(msg);
+                            mSettings.mReadMessages.append('\n');
+                            uidError = true;
+                            if (!pkgSetting.uidError) {
+                                reportSettingsProblem(Log.ERROR, msg);
                             }
-                            reportSettingsProblem(Log.ERROR, msg);
                         }
                     }
                 }
@@ -2604,6 +2707,29 @@
             } else {
                 if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV)
                     Log.v(TAG, "Want this data dir: " + dataPath);
+                if (pkgSetting.origPackage != null) {
+                    synchronized (mPackages) {
+                        // This package is being update from another; rename the
+                        // old one's data dir.
+                        String msg = "Transfering data from old package "
+                                + pkgSetting.origPackage.name + " to new package "
+                                + pkgSetting.name;
+                        reportSettingsProblem(Log.WARN, msg);
+                        if (mInstaller != null) {
+                            int ret = mInstaller.rename(pkgSetting.origPackage.name,
+                                    pkgName, useEncryptedFSDir);
+                            if(ret < 0) {
+                                msg = "Error transfering data from old package "
+                                    + pkgSetting.origPackage.name + " to new package "
+                                    + pkgSetting.name;
+                                reportSettingsProblem(Log.WARN, msg);
+                            }
+                        }
+                        // And now uninstall the old package.
+                        mInstaller.remove(pkgSetting.origPackage.name, useEncryptedFSDir);
+                        mSettings.removePackageLP(pkgSetting.origPackage.name);
+                    }
+                }
                 //invoke installer to do the actual installation
                 if (mInstaller != null) {
                     int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
@@ -2629,8 +2755,16 @@
                     pkg.applicationInfo.dataDir = null;
                 }
             }
+            
+            pkgSetting.uidError = uidError;
         }
 
+        // No longer need to retain this.
+        if (pkgSetting.origPackage != null) {
+            mTransferedPackages.add(pkgSetting.origPackage.name);
+            pkgSetting.origPackage = null;
+        }
+        
         // Perform shared library installation and dex validation and
         // optimization, if this is not a system app.
         if (mInstaller != null) {
@@ -5951,16 +6085,31 @@
                     }
                 }
             }
+            
             pw.println(" ");
             pw.println("Settings parse messages:");
             pw.println(mSettings.mReadMessages.toString());
+            
+            pw.println(" ");
+            pw.println("Package warning messages:");
+            File fname = getSettingsProblemFile();
+            FileInputStream in;
+            try {
+                in = new FileInputStream(fname);
+                int avail = in.available();
+                byte[] data = new byte[avail];
+                in.read(data);
+                pw.println(new String(data));
+            } catch (FileNotFoundException e) {
+            } catch (IOException e) {
+            }
         }
 
         synchronized (mProviders) {
             pw.println(" ");
             pw.println("Registered ContentProviders:");
             for (PackageParser.Provider p : mProviders.values()) {
-                pw.println("  ["); pw.println(p.info.authority); pw.println("]: ");
+                pw.print("  ["); pw.print(p.info.authority); pw.print("]: ");
                         pw.println(p.toString());
             }
         }
@@ -5972,7 +6121,7 @@
         final static int TYPE_DYNAMIC = 2;
 
         final String name;
-        final String sourcePackage;
+        String sourcePackage;
         final int type;
         PackageParser.Permission perm;
         PermissionInfo pendingInfo;
@@ -6437,6 +6586,8 @@
         private String timeStampString = "0";
         int versionCode;
 
+        boolean uidError;
+        
         PackageSignatures signatures = new PackageSignatures();
 
         boolean permissionsFixed;
@@ -6448,6 +6599,8 @@
         int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
         int installStatus = PKG_INSTALL_COMPLETE;
 
+        PackageSettingBase origPackage;
+        
         /* package name of the app that installed this package */
         String installerPackageName;
 
@@ -6455,13 +6608,17 @@
                 int pVersionCode, int pkgFlags) {
             super(pkgFlags);
             this.name = name;
+            init(codePath, resourcePath, pVersionCode);
+        }
+
+        void init(File codePath, File resourcePath, int pVersionCode) {
             this.codePath = codePath;
             this.codePathString = codePath.toString();
             this.resourcePath = resourcePath;
             this.resourcePathString = resourcePath.toString();
             this.versionCode = pVersionCode;
         }
-
+        
         public void setInstallerPackageName(String packageName) {
             installerPackageName = packageName;
         }
@@ -6667,11 +6824,11 @@
             mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
         }
 
-        PackageSetting getPackageLP(PackageParser.Package pkg,
+        PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
                 SharedUserSetting sharedUser, File codePath, File resourcePath,
                 int pkgFlags, boolean create, boolean add) {
             final String name = pkg.packageName;
-            PackageSetting p = getPackageLP(name, sharedUser, codePath,
+            PackageSetting p = getPackageLP(name, origPackage, sharedUser, codePath,
                     resourcePath, pkg.mVersionCode, pkgFlags, create, add);
             return p;
         }
@@ -6813,7 +6970,31 @@
             return null;
         }
 
-        private PackageSetting getPackageLP(String name,
+        // Transfer ownership of permissions from one package to another.
+        private void transferPermissions(String origPkg, String newPkg) {
+            // Transfer ownership of permissions to the new package.
+            for (int i=0; i<2; i++) {
+                HashMap<String, BasePermission> permissions =
+                        i == 0 ? mPermissionTrees : mPermissions;
+                for (BasePermission bp : permissions.values()) {
+                    if (origPkg.equals(bp.sourcePackage)) {
+                        if (DEBUG_UPGRADE) Log.v(TAG,
+                                "Moving permission " + bp.name
+                                + " from pkg " + bp.sourcePackage
+                                + " to " + newPkg);
+                        bp.sourcePackage = newPkg;
+                        bp.perm = null;
+                        if (bp.pendingInfo != null) {
+                            bp.sourcePackage = newPkg;
+                        }
+                        bp.uid = 0;
+                        bp.gids = null;
+                    }
+                }
+            }
+        }
+        
+        private PackageSetting getPackageLP(String name, PackageSetting origPackage,
                 SharedUserSetting sharedUser, File codePath, File resourcePath,
                 int vc, int pkgFlags, boolean create, boolean add) {
             PackageSetting p = mPackages.get(name);
@@ -6857,36 +7038,49 @@
                     return null;
                 }
                 p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags);
-                p.setTimeStamp(codePath.lastModified());
-                p.sharedUser = sharedUser;
-                if (sharedUser != null) {
-                    p.userId = sharedUser.userId;
-                } else if (MULTIPLE_APPLICATION_UIDS) {
-                    // Clone the setting here for disabled system packages
-                    PackageSetting dis = mDisabledSysPackages.get(name);
-                    if (dis != null) {
-                        // For disabled packages a new setting is created
-                        // from the existing user id. This still has to be
-                        // added to list of user id's
-                        // Copy signatures from previous setting
-                        if (dis.signatures.mSignatures != null) {
-                            p.signatures.mSignatures = dis.signatures.mSignatures.clone();
-                        }
-                        p.userId = dis.userId;
-                        // Clone permissions
-                        p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
-                        p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
-                        // Clone component info
-                        p.disabledComponents = new HashSet<String>(dis.disabledComponents);
-                        p.enabledComponents = new HashSet<String>(dis.enabledComponents);
-                        // Add new setting to list of user ids
-                        addUserIdLP(p.userId, p, name);
-                    } else {
-                        // Assign new user id
-                        p.userId = newUserIdLP(p);
-                    }
+                if (origPackage != null) {
+                    // We are consuming the data from an existing package.
+                    if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
+                            + " is adopting original package " + origPackage.name);
+                    p.copyFrom(origPackage);
+                    p.sharedUser = origPackage.sharedUser;
+                    p.userId = origPackage.userId;
+                    p.origPackage = origPackage;
+                    transferPermissions(origPackage.name, name);
+                    // Update new package state.
+                    p.setTimeStamp(codePath.lastModified());
                 } else {
-                    p.userId = FIRST_APPLICATION_UID;
+                    p.setTimeStamp(codePath.lastModified());
+                    p.sharedUser = sharedUser;
+                    if (sharedUser != null) {
+                        p.userId = sharedUser.userId;
+                    } else if (MULTIPLE_APPLICATION_UIDS) {
+                        // Clone the setting here for disabled system packages
+                        PackageSetting dis = mDisabledSysPackages.get(name);
+                        if (dis != null) {
+                            // For disabled packages a new setting is created
+                            // from the existing user id. This still has to be
+                            // added to list of user id's
+                            // Copy signatures from previous setting
+                            if (dis.signatures.mSignatures != null) {
+                                p.signatures.mSignatures = dis.signatures.mSignatures.clone();
+                            }
+                            p.userId = dis.userId;
+                            // Clone permissions
+                            p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
+                            p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
+                            // Clone component info
+                            p.disabledComponents = new HashSet<String>(dis.disabledComponents);
+                            p.enabledComponents = new HashSet<String>(dis.enabledComponents);
+                            // Add new setting to list of user ids
+                            addUserIdLP(p.userId, p, name);
+                        } else {
+                            // Assign new user id
+                            p.userId = newUserIdLP(p);
+                        }
+                    } else {
+                        p.userId = FIRST_APPLICATION_UID;
+                    }
                 }
                 if (p.userId < 0) {
                     reportSettingsProblem(Log.WARN,
@@ -7248,6 +7442,9 @@
                 serializer.attribute(null, "sharedUserId",
                         Integer.toString(pkg.userId));
             }
+            if (pkg.uidError) {
+                serializer.attribute(null, "uidError", "true");
+            }
             if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
                 serializer.attribute(null, "enabled",
                         pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED
@@ -7441,7 +7638,7 @@
                 final PendingPackage pp = mPendingPackages.get(i);
                 Object idObj = getUserIdLP(pp.sharedId);
                 if (idObj != null && idObj instanceof SharedUserSetting) {
-                    PackageSetting p = getPackageLP(pp.name,
+                    PackageSetting p = getPackageLP(pp.name, null,
                             (SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
                             pp.versionCode, pp.pkgFlags, true, true);
                     if (p == null) {
@@ -7610,6 +7807,7 @@
             String resourcePathStr = null;
             String systemStr = null;
             String installerPackageName = null;
+            String uidError = null;
             int pkgFlags = 0;
             String timeStampStr;
             long timeStamp = 0;
@@ -7619,6 +7817,7 @@
             try {
                 name = parser.getAttributeValue(null, "name");
                 idStr = parser.getAttributeValue(null, "userId");
+                uidError = parser.getAttributeValue(null, "uidError");
                 sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
                 codePathStr = parser.getAttributeValue(null, "codePath");
                 resourcePathStr = parser.getAttributeValue(null, "resourcePath");
@@ -7712,6 +7911,7 @@
                         + parser.getPositionDescription());
             }
             if (packageSetting != null) {
+                packageSetting.uidError = "true".equals(uidError);
                 packageSetting.installerPackageName = installerPackageName;
                 final String enabledStr = parser.getAttributeValue(null, "enabled");
                 if (enabledStr != null) {