Merge "Fix a memory corruption issue when vector resize"
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index f69a72d..a33dcf3 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -238,9 +238,6 @@
         dump_file("LAST KMSG", "/proc/last_kmsg");
     }
 
-    dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
-    dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
-
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
     run_command("LAST LOGCAT", 10, "logcat", "-L", "-v", "threadtime",
                                              "-b", "all", "-d", "*:v", NULL);
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 8224e94..6dec7f6 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -1,6 +1,6 @@
 LOCAL_PATH := $(call my-dir)
 
-common_src_files := commands.c utils.c
+common_src_files := commands.cpp utils.cpp
 common_cflags := -Wall -Werror
 
 #
@@ -12,7 +12,12 @@
 LOCAL_MODULE_TAGS := eng tests
 LOCAL_SRC_FILES := $(common_src_files)
 LOCAL_CFLAGS := $(common_cflags)
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblogwrap \
+
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
 include $(BUILD_STATIC_LIBRARY)
 
 #
@@ -23,8 +28,15 @@
 LOCAL_MODULE := installd
 LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := $(common_cflags)
-LOCAL_SRC_FILES := installd.c $(common_src_files)
-LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux
+LOCAL_SRC_FILES := installd.cpp $(common_src_files)
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    liblog \
+    liblogwrap \
+    libselinux \
+
 LOCAL_STATIC_LIBRARIES := libdiskusage
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.cpp
similarity index 86%
rename from cmds/installd/commands.c
rename to cmds/installd/commands.cpp
index 48bccc3..de6fd96 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.cpp
@@ -14,14 +14,22 @@
 ** limitations under the License.
 */
 
+#include "installd.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <cutils/sched_policy.h>
+#include <diskusage/dirsize.h>
+#include <logwrap/logwrap.h>
+#include <system/thread_defs.h>
+#include <selinux/android.h>
+
 #include <inttypes.h>
 #include <sys/capability.h>
 #include <sys/file.h>
-#include "installd.h"
-#include <cutils/sched_policy.h>
-#include <diskusage/dirsize.h>
-#include <selinux/android.h>
-#include <system/thread_defs.h>
+#include <unistd.h>
+
+using android::base::StringPrintf;
 
 /* Directory records that are used in execution of commands. */
 dir_rec_t android_data_dir;
@@ -30,34 +38,20 @@
 dir_rec_t android_app_private_dir;
 dir_rec_t android_app_lib_dir;
 dir_rec_t android_media_dir;
+dir_rec_t android_mnt_expand_dir;
 dir_rec_array_t android_system_dirs;
 
-int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
-{
-    char pkgdir[PKG_PATH_MAX];
-    char libsymlink[PKG_PATH_MAX];
-    char applibdir[PKG_PATH_MAX];
-    struct stat libStat;
+static const char* kCpPath = "/system/bin/cp";
 
+int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
+{
     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
         ALOGE("invalid uid/gid: %d %d\n", uid, gid);
         return -1;
     }
 
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
-        ALOGE("cannot create package path\n");
-        return -1;
-    }
-
-    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) {
-        ALOGE("cannot create package lib symlink origin path\n");
-        return -1;
-    }
-
-    if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
-        ALOGE("cannot create package lib symlink dest path\n");
-        return -1;
-    }
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, 0));
+    const char* pkgdir = _pkgdir.c_str();
 
     if (mkdir(pkgdir, 0751) < 0) {
         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
@@ -69,42 +63,14 @@
         return -1;
     }
 
-    if (lstat(libsymlink, &libStat) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
-            return -1;
-        }
-    } else {
-        if (S_ISDIR(libStat.st_mode)) {
-            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
-                ALOGE("couldn't delete lib directory during install for: %s", libsymlink);
-                return -1;
-            }
-        } else if (S_ISLNK(libStat.st_mode)) {
-            if (unlink(libsymlink) < 0) {
-                ALOGE("couldn't unlink lib directory during install for: %s", libsymlink);
-                return -1;
-            }
-        }
-    }
-
     if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) {
         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libsymlink);
         unlink(pkgdir);
         return -errno;
     }
 
-    if (symlink(applibdir, libsymlink) < 0) {
-        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir,
-                strerror(errno));
-        unlink(pkgdir);
-        return -1;
-    }
-
     if (chown(pkgdir, uid, gid) < 0) {
         ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libsymlink);
         unlink(pkgdir);
         return -1;
     }
@@ -112,12 +78,10 @@
     return 0;
 }
 
-int uninstall(const char *pkgname, userid_t userid)
+int uninstall(const char *uuid, const char *pkgname, userid_t userid)
 {
-    char pkgdir[PKG_PATH_MAX];
-
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))
-        return -1;
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, userid));
+    const char* pkgdir = _pkgdir.c_str();
 
     remove_profile_file(pkgname);
 
@@ -142,9 +106,8 @@
     return 0;
 }
 
-int fix_uid(const char *pkgname, uid_t uid, gid_t gid)
+int fix_uid(const char *uuid, const char *pkgname, uid_t uid, gid_t gid)
 {
-    char pkgdir[PKG_PATH_MAX];
     struct stat s;
 
     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
@@ -152,10 +115,8 @@
         return -1;
     }
 
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
-        ALOGE("cannot create package path\n");
-        return -1;
-    }
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, 0));
+    const char* pkgdir = _pkgdir.c_str();
 
     if (stat(pkgdir, &s) < 0) return -1;
 
@@ -178,35 +139,18 @@
     return 0;
 }
 
-int delete_user_data(const char *pkgname, userid_t userid)
+int delete_user_data(const char *uuid, const char *pkgname, userid_t userid)
 {
-    char pkgdir[PKG_PATH_MAX];
-
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))
-        return -1;
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, userid));
+    const char* pkgdir = _pkgdir.c_str();
 
     return delete_dir_contents(pkgdir, 0, NULL);
 }
 
-int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo)
+int make_user_data(const char *uuid, const char *pkgname, uid_t uid, userid_t userid, const char* seinfo)
 {
-    char pkgdir[PKG_PATH_MAX];
-    char applibdir[PKG_PATH_MAX];
-    char libsymlink[PKG_PATH_MAX];
-    struct stat libStat;
-
-    // Create the data dir for the package
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) {
-        return -1;
-    }
-    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userid)) {
-        ALOGE("cannot create package lib symlink origin path\n");
-        return -1;
-    }
-    if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
-        ALOGE("cannot create package lib symlink dest path\n");
-        return -1;
-    }
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, userid));
+    const char* pkgdir = _pkgdir.c_str();
 
     if (mkdir(pkgdir, 0751) < 0) {
         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
@@ -218,47 +162,14 @@
         return -errno;
     }
 
-    if (lstat(libsymlink, &libStat) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("couldn't stat lib dir for non-primary: %s\n", strerror(errno));
-            unlink(pkgdir);
-            return -1;
-        }
-    } else {
-        if (S_ISDIR(libStat.st_mode)) {
-            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
-                ALOGE("couldn't delete lib directory during install for non-primary: %s",
-                        libsymlink);
-                unlink(pkgdir);
-                return -1;
-            }
-        } else if (S_ISLNK(libStat.st_mode)) {
-            if (unlink(libsymlink) < 0) {
-                ALOGE("couldn't unlink lib directory during install for non-primary: %s",
-                        libsymlink);
-                unlink(pkgdir);
-                return -1;
-            }
-        }
-    }
-
     if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) {
         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libsymlink);
         unlink(pkgdir);
         return -errno;
     }
 
-    if (symlink(applibdir, libsymlink) < 0) {
-        ALOGE("couldn't symlink directory for non-primary '%s' -> '%s': %s\n", libsymlink,
-                applibdir, strerror(errno));
-        unlink(pkgdir);
-        return -1;
-    }
-
     if (chown(pkgdir, uid, uid) < 0) {
         ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libsymlink);
         unlink(pkgdir);
         return -errno;
     }
@@ -266,6 +177,80 @@
     return 0;
 }
 
+int move_user_data(const char *from_uuid, const char *to_uuid,
+        const char *package_name, appid_t appid, const char* seinfo) {
+    std::vector<userid_t> users = get_known_users(from_uuid);
+
+    // Copy package private data for all known users
+    for (auto user : users) {
+        std::string from(create_package_data_path(from_uuid, package_name, user));
+        std::string to(create_package_data_path(to_uuid, package_name, user));
+        std::string to_user(create_data_user_path(to_uuid, user));
+
+        // Data source may not exist for all users; that's okay
+        if (access(from.c_str(), F_OK) != 0) {
+            LOG(INFO) << "Missing source " << from;
+            continue;
+        }
+
+        std::string user_path(create_data_user_path(to_uuid, user));
+        if (fs_prepare_dir(user_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM) != 0) {
+            LOG(ERROR) << "Failed to prepare user target " << user_path;
+            goto fail;
+        }
+
+        uid_t uid = multiuser_get_uid(user, appid);
+        if (make_user_data(to_uuid, package_name, uid, user, seinfo) != 0) {
+            LOG(ERROR) << "Failed to create package target " << to;
+            goto fail;
+        }
+
+        char *argv[] = {
+            (char*) kCpPath,
+            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+            (char*) "-p", /* preserve timestamps, ownership, and permissions */
+            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+            (char*) "-P", /* Do not follow symlinks [default] */
+            (char*) "-d", /* don't dereference symlinks */
+            (char*) from.c_str(),
+            (char*) to_user.c_str()
+        };
+
+        LOG(DEBUG) << "Copying " << from << " to " << to;
+        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+
+        if (rc != 0) {
+            LOG(ERROR) << "Failed copying " << from << " to " << to
+                    << ": status " << rc;
+            goto fail;
+        }
+
+        if (restorecon_data(to_uuid, package_name, seinfo, uid) != 0) {
+            LOG(ERROR) << "Failed to restorecon " << to;
+            goto fail;
+        }
+    }
+
+    // Copy successful, so delete old data
+    for (auto user : users) {
+        std::string from(create_package_data_path(from_uuid, package_name, user));
+        if (delete_dir_contents(from.c_str(), 1, NULL) != 0) {
+            LOG(WARNING) << "Failed to delete " << from;
+        }
+    }
+    return 0;
+
+fail:
+    // Nuke everything we might have already copied
+    for (auto user : users) {
+        std::string to(create_package_data_path(to_uuid, package_name, user));
+        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+            LOG(WARNING) << "Failed to rollback " << to;
+        }
+    }
+    return -1;
+}
+
 int make_user_config(userid_t userid)
 {
     if (ensure_config_user_dirs(userid) == -1) {
@@ -275,49 +260,49 @@
     return 0;
 }
 
-int delete_user(userid_t userid)
+int delete_user(const char *uuid, userid_t userid)
 {
     int status = 0;
 
-    char data_path[PKG_PATH_MAX];
-    if ((create_user_path(data_path, userid) != 0)
-            || (delete_dir_contents(data_path, 1, NULL) != 0)) {
+    std::string data_path(create_data_user_path(uuid, userid));
+    if (delete_dir_contents(data_path.c_str(), 1, NULL) != 0) {
         status = -1;
     }
 
-    char media_path[PATH_MAX];
-    if ((create_user_media_path(media_path, userid) != 0)
-            || (delete_dir_contents(media_path, 1, NULL) != 0)) {
+    std::string media_path(create_data_media_path(uuid, userid));
+    if (delete_dir_contents(media_path.c_str(), 1, NULL) != 0) {
         status = -1;
     }
 
-    char config_path[PATH_MAX];
-    if ((create_user_config_path(config_path, userid) != 0)
-            || (delete_dir_contents(config_path, 1, NULL) != 0)) {
-        status = -1;
+    // Config paths only exist on internal storage
+    if (uuid == nullptr) {
+        char config_path[PATH_MAX];
+        if ((create_user_config_path(config_path, userid) != 0)
+                || (delete_dir_contents(config_path, 1, NULL) != 0)) {
+            status = -1;
+        }
     }
 
     return status;
 }
 
-int delete_cache(const char *pkgname, userid_t userid)
+int delete_cache(const char *uuid, const char *pkgname, userid_t userid)
 {
-    char cachedir[PKG_PATH_MAX];
-
-    if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, userid))
-        return -1;
+    std::string _cachedir(
+            create_package_data_path(uuid, pkgname, userid) + CACHE_DIR_POSTFIX);
+    const char* cachedir = _cachedir.c_str();
 
     /* delete contents, not the directory, no exceptions */
     return delete_dir_contents(cachedir, 0, NULL);
 }
 
-int delete_code_cache(const char *pkgname, userid_t userid)
+int delete_code_cache(const char *uuid, const char *pkgname, userid_t userid)
 {
-    char codecachedir[PKG_PATH_MAX];
-    struct stat s;
+    std::string _codecachedir(
+            create_package_data_path(uuid, pkgname, userid) + CACHE_DIR_POSTFIX);
+    const char* codecachedir = _codecachedir.c_str();
 
-    if (create_pkg_path(codecachedir, pkgname, CODE_CACHE_DIR_POSTFIX, userid))
-        return -1;
+    struct stat s;
 
     /* it's okay if code cache is missing */
     if (lstat(codecachedir, &s) == -1 && errno == ENOENT) {
@@ -335,7 +320,7 @@
  * also require that apps constantly modify file metadata even
  * when just reading from the cache, which is pretty awful.
  */
-int free_cache(int64_t free_size)
+int free_cache(const char *uuid, int64_t free_size)
 {
     cache_t* cache;
     int64_t avail;
@@ -344,7 +329,9 @@
     char tmpdir[PATH_MAX];
     char *dirpos;
 
-    avail = data_disk_free();
+    std::string data_path(create_data_path(uuid));
+
+    avail = data_disk_free(data_path);
     if (avail < 0) return -1;
 
     ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
@@ -352,15 +339,16 @@
 
     cache = start_cache_collection();
 
-    // Collect cache files for primary user.
-    if (create_user_path(tmpdir, 0) == 0) {
-        //ALOGI("adding cache files from %s\n", tmpdir);
-        add_cache_files(cache, tmpdir, "cache");
+    // Special case for owner on internal storage
+    if (uuid == nullptr) {
+        std::string _tmpdir(create_data_user_path(nullptr, 0));
+        add_cache_files(cache, _tmpdir.c_str(), "cache");
     }
 
     // Search for other users and add any cache files from them.
-    snprintf(tmpdir, sizeof(tmpdir), "%s%s", android_data_dir.path,
-            SECONDARY_USER_PREFIX);
+    std::string _tmpdir(create_data_path(uuid) + "/" + SECONDARY_USER_PREFIX);
+    strcpy(tmpdir, _tmpdir.c_str());
+
     dirpos = tmpdir + strlen(tmpdir);
     d = opendir(tmpdir);
     if (d != NULL) {
@@ -412,10 +400,10 @@
         closedir(d);
     }
 
-    clear_cache_files(cache, free_size);
+    clear_cache_files(data_path, cache, free_size);
     finish_cache_collection(cache);
 
-    return data_disk_free() >= free_size ? 0 : -1;
+    return data_disk_free(data_path) >= free_size ? 0 : -1;
 }
 
 int move_dex(const char *src, const char *dst, const char *instruction_set)
@@ -466,7 +454,7 @@
     }
 }
 
-int get_size(const char *pkgname, userid_t userid, const char *apkpath,
+int get_size(const char *uuid, const char *pkgname, userid_t userid, const char *apkpath,
              const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath,
              const char *instruction_set, int64_t *_codesize, int64_t *_datasize,
              int64_t *_cachesize, int64_t* _asecsize)
@@ -523,11 +511,10 @@
         }
     }
 
-    if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, userid)) {
-        goto done;
-    }
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, userid));
+    const char* pkgdir = _pkgdir.c_str();
 
-    d = opendir(path);
+    d = opendir(pkgdir);
     if (d == NULL) {
         goto done;
     }
@@ -987,8 +974,8 @@
 }
 
 int dexopt(const char *apk_path, uid_t uid, bool is_public,
-           const char *pkgname, const char *instruction_set,
-           bool vm_safe_mode, bool is_patchoat, bool debuggable, const char* oat_dir)
+           const char *pkgname, const char *instruction_set, int dexopt_needed,
+           bool vm_safe_mode, bool debuggable, const char* oat_dir)
 {
     struct utimbuf ut;
     struct stat input_stat;
@@ -1020,13 +1007,25 @@
         }
     }
 
-    if (is_patchoat) {
-        if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
-          return -1;
-        }
-        input_file = in_odex_path;
-    } else {
-        input_file = apk_path;
+    switch (dexopt_needed) {
+        case DEXOPT_DEX2OAT_NEEDED:
+            input_file = apk_path;
+            break;
+
+        case DEXOPT_PATCHOAT_NEEDED:
+            if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+                return -1;
+            }
+            input_file = in_odex_path;
+            break;
+
+        case DEXOPT_SELF_PATCHOAT_NEEDED:
+            input_file = out_path;
+            break;
+
+        default:
+            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
+            exit(72);
     }
 
     memset(&input_stat, 0, sizeof(input_stat));
@@ -1061,7 +1060,7 @@
     }
 
     // Create a swap file if necessary.
-    if (!is_patchoat && ShouldUseSwapFileForDexopt()) {
+    if (ShouldUseSwapFileForDexopt()) {
         // Make sure there really is enough space.
         size_t out_len = strlen(out_path);
         if (out_len + strlen(".swap") + 1 <= PKG_PATH_MAX) {
@@ -1120,9 +1119,10 @@
             exit(67);
         }
 
-        if (is_patchoat) {
+        if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
+            || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
             run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
-        } else {
+        } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
             const char *input_file_name = strrchr(input_file, '/');
             if (input_file_name == NULL) {
                 input_file_name = input_file;
@@ -1131,6 +1131,9 @@
             }
             run_dex2oat(input_fd, out_fd, input_file_name, out_path, swap_fd, pkgname,
                         instruction_set, vm_safe_mode, debuggable);
+        } else {
+            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
+            exit(73);
         }
         exit(68);   /* only get here on exec failure */
     } else {
@@ -1440,21 +1443,16 @@
     return 0;
 }
 
-int linklib(const char* pkgname, const char* asecLibDir, int userId)
+int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
 {
-    char pkgdir[PKG_PATH_MAX];
-    char libsymlink[PKG_PATH_MAX];
     struct stat s, libStat;
     int rc = 0;
 
-    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) {
-        ALOGE("cannot create package path\n");
-        return -1;
-    }
-    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) {
-        ALOGE("cannot create package lib symlink origin path\n");
-        return -1;
-    }
+    std::string _pkgdir(create_package_data_path(uuid, pkgname, userId));
+    std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
+
+    const char* pkgdir = _pkgdir.c_str();
+    const char* libsymlink = _libsymlink.c_str();
 
     if (stat(pkgdir, &s) < 0) return -1;
 
@@ -1623,14 +1621,12 @@
     return -1;
 }
 
-int restorecon_data(const char* pkgName, const char* seinfo, uid_t uid)
+int restorecon_data(const char* uuid, const char* pkgName,
+                    const char* seinfo, uid_t uid)
 {
     struct dirent *entry;
     DIR *d;
     struct stat s;
-    char *userdir;
-    char *primarydir;
-    char *pkgdir;
     int ret = 0;
 
     // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
@@ -1641,26 +1637,20 @@
         return -1;
     }
 
-    if (asprintf(&primarydir, "%s%s%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgName) < 0) {
-        return -1;
-    }
+    // Special case for owner on internal storage
+    if (uuid == nullptr) {
+        std::string path(create_package_data_path(nullptr, pkgName, 0));
 
-    // Relabel for primary user.
-    if (selinux_android_restorecon_pkgdir(primarydir, seinfo, uid, flags) < 0) {
-        ALOGE("restorecon failed for %s: %s\n", primarydir, strerror(errno));
-        ret |= -1;
-    }
-
-    if (asprintf(&userdir, "%s%s", android_data_dir.path, SECONDARY_USER_PREFIX) < 0) {
-        free(primarydir);
-        return -1;
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, flags) < 0) {
+            PLOG(ERROR) << "restorecon failed for " << path;
+            ret |= -1;
+        }
     }
 
     // Relabel package directory for all secondary users.
-    d = opendir(userdir);
+    std::string userdir(create_data_path(uuid) + "/" + SECONDARY_USER_PREFIX);
+    d = opendir(userdir.c_str());
     if (d == NULL) {
-        free(primarydir);
-        free(userdir);
         return -1;
     }
 
@@ -1681,25 +1671,18 @@
             continue;
         }
 
-        if (asprintf(&pkgdir, "%s%s/%s", userdir, user, pkgName) < 0) {
+        std::string pkgdir(StringPrintf("%s%s/%s", userdir.c_str(), user, pkgName));
+        if (stat(pkgdir.c_str(), &s) < 0) {
             continue;
         }
 
-        if (stat(pkgdir, &s) < 0) {
-            free(pkgdir);
-            continue;
-        }
-
-        if (selinux_android_restorecon_pkgdir(pkgdir, seinfo, s.st_uid, flags) < 0) {
-            ALOGE("restorecon failed for %s: %s\n", pkgdir, strerror(errno));
+        if (selinux_android_restorecon_pkgdir(pkgdir.c_str(), seinfo, s.st_uid, flags) < 0) {
+            PLOG(ERROR) << "restorecon failed for " << pkgdir;
             ret |= -1;
         }
-        free(pkgdir);
     }
 
     closedir(d);
-    free(primarydir);
-    free(userdir);
     return ret;
 }
 
@@ -1711,12 +1694,7 @@
         ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir);
         return -1;
     }
-    if ((mkdir(oat_dir, S_IRWXU|S_IRWXG|S_IXOTH) < 0) && (errno != EEXIST))  {
-        ALOGE("cannot create dir '%s': %s\n", oat_dir, strerror(errno));
-        return -1;
-    }
-    if (chmod(oat_dir, S_IRWXU|S_IRWXG|S_IXOTH) < 0) {
-        ALOGE("cannot chmod dir '%s': %s\n", oat_dir, strerror(errno));
+    if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
         return -1;
     }
     if (selinux_android_restorecon(oat_dir, 0)) {
@@ -1724,12 +1702,7 @@
         return -1;
     }
     snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
-    if ((mkdir(oat_instr_dir, S_IRWXU|S_IRWXG|S_IXOTH) < 0)  && (errno != EEXIST)) {
-        ALOGE("cannot create dir '%s': %s\n", oat_instr_dir, strerror(errno));
-        return -1;
-    }
-    if (chmod(oat_instr_dir, S_IRWXU|S_IRWXG|S_IXOTH) < 0) {
-        ALOGE("cannot chmod dir '%s': %s\n", oat_dir, strerror(errno));
+    if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
         return -1;
     }
     return 0;
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.cpp
similarity index 82%
rename from cmds/installd/installd.c
rename to cmds/installd/installd.cpp
index 5b71175..3a86181 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.cpp
@@ -1,31 +1,40 @@
 /*
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
+#include "installd.h"
+
+#include <base/logging.h>
+
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <selinux/android.h>
 #include <selinux/avc.h>
 
-#include "installd.h"
-
-
 #define BUFFER_MAX    1024  /* input buffer for commands */
 #define TOKEN_MAX     16    /* max number of arguments in buffer */
 #define REPLY_MAX     256   /* largest reply allowed */
 
+static char* parse_null(char* arg) {
+    if (strcmp(arg, "!") == 0) {
+        return nullptr;
+    } else {
+        return arg;
+    }
+}
+
 static int do_ping(char **arg __unused, char reply[REPLY_MAX] __unused)
 {
     return 0;
@@ -33,15 +42,15 @@
 
 static int do_install(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */
+    return install(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4]); /* uuid, pkgname, uid, gid, seinfo */
 }
 
 static int do_dexopt(char **arg, char reply[REPLY_MAX] __unused)
 {
-    /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate
-       debuggable, outputPath */
-    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], atoi(arg[5]), 0, 
-                  atoi(arg[6]), arg[7]);
+    /* apk_path, uid, is_public, pkgname, instruction_set,
+     * dexopt_needed, vm_safe_mode, debuggable, oat_dir */
+    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], atoi(arg[5]),
+                  atoi(arg[6]), atoi(arg[7]), arg[8]);
 }
 
 static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] __unused)
@@ -61,7 +70,7 @@
 
 static int do_remove(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */
+    return uninstall(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */
 }
 
 static int do_rename(char **arg, char reply[REPLY_MAX] __unused)
@@ -71,22 +80,22 @@
 
 static int do_fixuid(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return fix_uid(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */
+    return fix_uid(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3])); /* uuid, pkgname, uid, gid */
 }
 
 static int do_free_cache(char **arg, char reply[REPLY_MAX] __unused) /* TODO int:free_size */
 {
-    return free_cache((int64_t)atoll(arg[0])); /* free_size */
+    return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1])); /* uuid, free_size */
 }
 
 static int do_rm_cache(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return delete_cache(arg[0], atoi(arg[1])); /* pkgname, userid */
+    return delete_cache(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */
 }
 
 static int do_rm_code_cache(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return delete_code_cache(arg[0], atoi(arg[1])); /* pkgname, userid */
+    return delete_code_cache(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */
 }
 
 static int do_get_size(char **arg, char reply[REPLY_MAX])
@@ -97,9 +106,9 @@
     int64_t asecsize = 0;
     int res = 0;
 
-        /* pkgdir, userid, apkpath */
-    res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4], arg[5],
-            arg[6], &codesize, &datasize, &cachesize, &asecsize);
+        /* uuid, pkgdir, userid, apkpath */
+    res = get_size(parse_null(arg[0]), arg[1], atoi(arg[2]), arg[3], arg[4], arg[5], arg[6],
+            arg[7], &codesize, &datasize, &cachesize, &asecsize);
 
     /*
      * Each int64_t can take up 22 characters printed out. Make sure it
@@ -112,13 +121,19 @@
 
 static int do_rm_user_data(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */
+    return delete_user_data(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */
+}
+
+static int do_mv_user_data(char **arg, char reply[REPLY_MAX] __unused)
+{
+    // from_uuid, to_uuid, pkgname, appid, seinfo
+    return move_user_data(parse_null(arg[0]), parse_null(arg[1]), arg[2], atoi(arg[3]), arg[4]);
 }
 
 static int do_mk_user_data(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]);
-                             /* pkgname, uid, userid, seinfo */
+    return make_user_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4]);
+                             /* uuid, pkgname, uid, userid, seinfo */
 }
 
 static int do_mk_user_config(char **arg, char reply[REPLY_MAX] __unused)
@@ -128,7 +143,7 @@
 
 static int do_rm_user(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return delete_user(atoi(arg[0])); /* userid */
+    return delete_user(parse_null(arg[0]), atoi(arg[1])); /* uuid, userid */
 }
 
 static int do_movefiles(char **arg __unused, char reply[REPLY_MAX] __unused)
@@ -138,7 +153,7 @@
 
 static int do_linklib(char **arg, char reply[REPLY_MAX] __unused)
 {
-    return linklib(arg[0], arg[1], atoi(arg[2]));
+    return linklib(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
 }
 
 static int do_idmap(char **arg, char reply[REPLY_MAX] __unused)
@@ -148,14 +163,8 @@
 
 static int do_restorecon_data(char **arg, char reply[REPLY_MAX] __attribute__((unused)))
 {
-    return restorecon_data(arg[0], arg[1], atoi(arg[2]));
-                             /* pkgName, seinfo, uid*/
-}
-
-static int do_patchoat(char **arg, char reply[REPLY_MAX] __unused) {
-    /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate,
-       debuggable, outputPath */
-    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0, 1, 0, "!");
+    return restorecon_data(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
+                             /* uuid, pkgName, seinfo, uid*/
 }
 
 static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] __unused)
@@ -178,34 +187,34 @@
 
 struct cmdinfo cmds[] = {
     { "ping",                 0, do_ping },
-    { "install",              4, do_install },
-    { "dexopt",               8, do_dexopt },
+    { "install",              5, do_install },
+    { "dexopt",               9, do_dexopt },
     { "markbootcomplete",     1, do_mark_boot_complete },
     { "movedex",              3, do_move_dex },
     { "rmdex",                2, do_rm_dex },
-    { "remove",               2, do_remove },
+    { "remove",               3, do_remove },
     { "rename",               2, do_rename },
-    { "fixuid",               3, do_fixuid },
-    { "freecache",            1, do_free_cache },
-    { "rmcache",              2, do_rm_cache },
-    { "rmcodecache",          2, do_rm_code_cache },
-    { "getsize",              7, do_get_size },
-    { "rmuserdata",           2, do_rm_user_data },
+    { "fixuid",               4, do_fixuid },
+    { "freecache",            2, do_free_cache },
+    { "rmcache",              3, do_rm_cache },
+    { "rmcodecache",          3, do_rm_code_cache },
+    { "getsize",              8, do_get_size },
+    { "rmuserdata",           3, do_rm_user_data },
+    { "mvuserdata",           5, do_mv_user_data },
     { "movefiles",            0, do_movefiles },
-    { "linklib",              3, do_linklib },
-    { "mkuserdata",           4, do_mk_user_data },
+    { "linklib",              4, do_linklib },
+    { "mkuserdata",           5, do_mk_user_data },
     { "mkuserconfig",         1, do_mk_user_config },
-    { "rmuser",               1, do_rm_user },
+    { "rmuser",               2, do_rm_user },
     { "idmap",                3, do_idmap },
-    { "restorecondata",       3, do_restorecon_data },
-    { "patchoat",             5, do_patchoat },
+    { "restorecondata",       4, do_restorecon_data },
     { "createoatdir",         2, do_create_oat_dir },
     { "rmpackagedir",         1, do_rm_package_dir},
 };
 
 static int readx(int s, void *_buf, int count)
 {
-    char *buf = _buf;
+    char *buf = (char *) _buf;
     int n = 0, r;
     if (count < 0) return -1;
     while (n < count) {
@@ -226,7 +235,7 @@
 
 static int writex(int s, const void *_buf, int count)
 {
-    const char *buf = _buf;
+    const char *buf = (const char *) _buf;
     int n = 0, r;
     if (count < 0) return -1;
     while (n < count) {
@@ -352,10 +361,15 @@
         return -1;
     }
 
+    // Get the android external app directory.
+    if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/") < 0) {
+        return -1;
+    }
+
     // Take note of the system and vendor directories.
     android_system_dirs.count = 4;
 
-    android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t));
+    android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
     if (android_system_dirs.dirs == NULL) {
         ALOGE("Couldn't allocate array for dirs; aborting\n");
         return -1;
@@ -373,10 +387,10 @@
     android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR);
     android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
 
-    android_system_dirs.dirs[2].path = "/vendor/app/";
+    android_system_dirs.dirs[2].path = strdup("/vendor/app/");
     android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path);
 
-    android_system_dirs.dirs[3].path = "/oem/app/";
+    android_system_dirs.dirs[3].path = strdup("/oem/app/");
     android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path);
 
     return 0;
@@ -520,7 +534,7 @@
         version = 2;
     }
 
-    if (ensure_media_user_dirs(0) == -1) {
+    if (ensure_media_user_dirs(nullptr, 0) == -1) {
         ALOGE("Failed to setup media for user 0");
         goto fail;
     }
@@ -615,46 +629,6 @@
     return res;
 }
 
-static void drop_privileges() {
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
-        exit(1);
-    }
-
-    if (setgid(AID_INSTALL) < 0) {
-        ALOGE("setgid() can't drop privileges; exiting.\n");
-        exit(1);
-    }
-
-    if (setuid(AID_INSTALL) < 0) {
-        ALOGE("setuid() can't drop privileges; exiting.\n");
-        exit(1);
-    }
-
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
-    capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted        |= CAP_TO_MASK(CAP_CHOWN);
-    capdata[CAP_TO_INDEX(CAP_SETUID)].permitted       |= CAP_TO_MASK(CAP_SETUID);
-    capdata[CAP_TO_INDEX(CAP_SETGID)].permitted       |= CAP_TO_MASK(CAP_SETGID);
-    capdata[CAP_TO_INDEX(CAP_FOWNER)].permitted       |= CAP_TO_MASK(CAP_FOWNER);
-
-    capdata[0].effective = capdata[0].permitted;
-    capdata[1].effective = capdata[1].permitted;
-    capdata[0].inheritable = 0;
-    capdata[1].inheritable = 0;
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        ALOGE("capset failed: %s\n", strerror(errno));
-        exit(1);
-    }
-}
-
 static int log_callback(int type, const char *fmt, ...) {
     va_list ap;
     int priority;
@@ -676,13 +650,16 @@
     return 0;
 }
 
-int main(const int argc __unused, const char *argv[] __unused) {
+int main(const int argc __unused, char *argv[]) {
     char buf[BUFFER_MAX];
     struct sockaddr addr;
     socklen_t alen;
     int lsocket, s;
     int selinux_enabled = (is_selinux_enabled() > 0);
 
+    setenv("ANDROID_LOG_TAGS", "*:v", 1);
+    android::base::InitLogging(argv);
+
     ALOGI("installd firing up\n");
 
     union selinux_callback cb;
@@ -704,8 +681,6 @@
         exit(1);
     }
 
-    drop_privileges();
-
     lsocket = android_get_control_socket(SOCKET_PATH);
     if (lsocket < 0) {
         ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 9c30a35..f31bf4f 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -31,6 +31,8 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <string>
+#include <vector>
 
 #include <cutils/fs.h>
 #include <cutils/sockets.h>
@@ -83,6 +85,13 @@
 #define PKG_NAME_MAX  128   /* largest allowed package name */
 #define PKG_PATH_MAX  256   /* max size of any path we use */
 
+/* dexopt needed flags matching those in dalvik.system.DexFile */
+#define DEXOPT_DEX2OAT_NEEDED        1
+#define DEXOPT_PATCHOAT_NEEDED       2
+#define DEXOPT_SELF_PATCHOAT_NEEDED  3
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
 /* data structures */
 
 typedef struct {
@@ -101,6 +110,7 @@
 extern dir_rec_t android_data_dir;
 extern dir_rec_t android_asec_dir;
 extern dir_rec_t android_media_dir;
+extern dir_rec_t android_mnt_expand_dir;
 extern dir_rec_array_t android_system_dirs;
 
 typedef struct cache_dir_struct {
@@ -132,20 +142,22 @@
 
 /* util.c */
 
-int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
-                                const dir_rec_t* dir,
-                                const char* pkgname,
-                                const char* postfix);
+// TODO: rename to create_data_user_package_path
+std::string create_package_data_path(const char* volume_uuid,
+        const char* package_name, userid_t user);
 
 int create_pkg_path(char path[PKG_PATH_MAX],
                     const char *pkgname,
                     const char *postfix,
                     userid_t userid);
 
-int create_user_path(char path[PKG_PATH_MAX],
-                    userid_t userid);
+std::string create_data_path(const char* volume_uuid);
 
-int create_user_media_path(char path[PKG_PATH_MAX], userid_t userid);
+std::string create_data_user_path(const char* volume_uuid, userid_t userid);
+
+std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+
+std::vector<userid_t> get_known_users(const char* volume_uuid);
 
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
 
@@ -169,13 +181,13 @@
 
 int lookup_media_dir(char basepath[PATH_MAX], const char *dir);
 
-int64_t data_disk_free();
+int64_t data_disk_free(const std::string& data_path);
 
 cache_t* start_cache_collection();
 
 void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir);
 
-void clear_cache_files(cache_t* cache, int64_t free_size);
+void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
 
 void finish_cache_collection(cache_t* cache);
 
@@ -191,43 +203,48 @@
 
 int append_and_increment(char** dst, const char* src, size_t* dst_size);
 
-char *build_string2(char *s1, char *s2);
-char *build_string3(char *s1, char *s2, char *s3);
+char *build_string2(const char *s1, const char *s2);
+char *build_string3(const char *s1, const char *s2, const char *s3);
 
 int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
-int ensure_media_user_dirs(userid_t userid);
+int ensure_media_user_dirs(const char* uuid, userid_t userid);
 int ensure_config_user_dirs(userid_t userid);
 int create_profile_file(const char *pkgname, gid_t gid);
 void remove_profile_file(const char *pkgname);
 
 /* commands.c */
 
-int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo);
-int uninstall(const char *pkgname, userid_t userid);
+int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo);
+int uninstall(const char *uuid, const char *pkgname, userid_t userid);
 int renamepkg(const char *oldpkgname, const char *newpkgname);
-int fix_uid(const char *pkgname, uid_t uid, gid_t gid);
-int delete_user_data(const char *pkgname, userid_t userid);
-int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo);
+int fix_uid(const char *uuid, const char *pkgname, uid_t uid, gid_t gid);
+int delete_user_data(const char *uuid, const char *pkgname, userid_t userid);
+int make_user_data(const char *uuid, const char *pkgname, uid_t uid,
+        userid_t userid, const char* seinfo);
+int move_user_data(const char* from_uuid, const char *to_uuid,
+        const char *package_name, appid_t appid, const char* seinfo);
 int make_user_config(userid_t userid);
-int delete_user(userid_t userid);
-int delete_cache(const char *pkgname, userid_t userid);
-int delete_code_cache(const char *pkgname, userid_t userid);
+int delete_user(const char *uuid, userid_t userid);
+int delete_cache(const char *uuid, const char *pkgname, userid_t userid);
+int delete_code_cache(const char *uuid, const char *pkgname, userid_t userid);
 int move_dex(const char *src, const char *dst, const char *instruction_set);
 int rm_dex(const char *path, const char *instruction_set);
 int protect(char *pkgname, gid_t gid);
-int get_size(const char *pkgname, userid_t userid, const char *apkpath, const char *libdirpath,
+int get_size(const char *uuid, const char *pkgname, userid_t userid, const char *apkpath, const char *libdirpath,
              const char *fwdlock_apkpath, const char *asecpath, const char *instruction_set,
              int64_t *codesize, int64_t *datasize, int64_t *cachesize, int64_t *asecsize);
-int free_cache(int64_t free_size);
+int free_cache(const char *uuid, int64_t free_size);
 int dexopt(const char *apk_path, uid_t uid, bool is_public, const char *pkgName,
-           const char *instruction_set, bool vm_safe_mode, bool should_relocate, bool debuggable, 
-           const char* outputPath);
+           const char *instruction_set, int dexopt_needed, bool vm_safe_mode,
+           bool debuggable, const char* oat_dir);
 int mark_boot_complete(const char *instruction_set);
 int movefiles();
-int linklib(const char* target, const char* source, int userId);
+int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
 int idmap(const char *target_path, const char *overlay_path, uid_t uid);
-int restorecon_data();
+int restorecon_data(const char *uuid, const char* pkgName, const char* seinfo, uid_t uid);
 int create_oat_dir(const char* oat_dir, const char *instruction_set);
 int rm_package_dir(const char* apk_path);
 int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
-                            const char *instruction_set);
\ No newline at end of file
+                            const char *instruction_set);
+int move_package_dir(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
+                            const char *instruction_set);
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
index 7300b29..38a9f69 100644
--- a/cmds/installd/tests/Android.mk
+++ b/cmds/installd/tests/Android.mk
@@ -8,6 +8,7 @@
     installd_utils_test.cpp
 
 shared_libraries := \
+    libbase \
     libutils \
     libcutils \
 
@@ -25,5 +26,6 @@
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_C_INCLUDES := $(c_includes)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_CLANG := true) \
     $(eval include $(BUILD_NATIVE_TEST)) \
 )
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 94e4792..4ce559d 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -17,19 +17,18 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define LOG_TAG "utils_test"
-#include <utils/Log.h>
-
 #include <gtest/gtest.h>
 
-extern "C" {
 #include "installd.h"
-}
+
+#undef LOG_TAG
+#define LOG_TAG "utils_test"
 
 #define TEST_DATA_DIR "/data/"
 #define TEST_APP_DIR "/data/app/"
 #define TEST_APP_PRIVATE_DIR "/data/app-private/"
 #define TEST_ASEC_DIR "/mnt/asec/"
+#define TEST_EXPAND_DIR "/mnt/expand/"
 
 #define TEST_SYSTEM_DIR1 "/system/app/"
 #define TEST_SYSTEM_DIR2 "/vendor/app/"
@@ -49,25 +48,28 @@
 class UtilsTest : public testing::Test {
 protected:
     virtual void SetUp() {
-        android_app_dir.path = TEST_APP_DIR;
+        android_app_dir.path = (char*) TEST_APP_DIR;
         android_app_dir.len = strlen(TEST_APP_DIR);
 
-        android_app_private_dir.path = TEST_APP_PRIVATE_DIR;
+        android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR;
         android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
 
-        android_data_dir.path = TEST_DATA_DIR;
+        android_data_dir.path = (char*) TEST_DATA_DIR;
         android_data_dir.len = strlen(TEST_DATA_DIR);
 
-        android_asec_dir.path = TEST_ASEC_DIR;
+        android_asec_dir.path = (char*) TEST_ASEC_DIR;
         android_asec_dir.len = strlen(TEST_ASEC_DIR);
 
+        android_mnt_expand_dir.path = (char*) TEST_EXPAND_DIR;
+        android_mnt_expand_dir.len = strlen(TEST_EXPAND_DIR);
+
         android_system_dirs.count = 2;
 
         android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
-        android_system_dirs.dirs[0].path = TEST_SYSTEM_DIR1;
+        android_system_dirs.dirs[0].path = (char*) TEST_SYSTEM_DIR1;
         android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1);
 
-        android_system_dirs.dirs[1].path = TEST_SYSTEM_DIR2;
+        android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
         android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
     }
 
@@ -319,6 +321,7 @@
 
     const char *prefix = TEST_DATA_DIR PRIMARY_USER_PREFIX;
     size_t offset = strlen(prefix);
+
     EXPECT_STREQ(pkgname, path + offset)
              << "Package path should be a really long string of a's";
 }
@@ -369,40 +372,6 @@
             << "Package path should be in /data/user/";
 }
 
-TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) {
-    char path[PKG_PATH_MAX];
-
-    dir_rec_t dir;
-    dir.path = "/data/app-private/";
-    dir.len = strlen(dir.path);
-
-    EXPECT_EQ(0, create_pkg_path_in_dir(path, &dir, "com.example.package", ".apk"))
-            << "Should successfully create package path.";
-
-    EXPECT_STREQ("/data/app-private/com.example.package.apk", path)
-            << "Package path should be in /data/app-private/";
-}
-
-TEST_F(UtilsTest, CreatePersonaPath_Primary) {
-    char path[PKG_PATH_MAX];
-
-    EXPECT_EQ(0, create_user_path(path, 0))
-            << "Should successfully build primary user path.";
-
-    EXPECT_STREQ("/data/data/", path)
-            << "Primary user should have correct path";
-}
-
-TEST_F(UtilsTest, CreatePersonaPath_Secondary) {
-    char path[PKG_PATH_MAX];
-
-    EXPECT_EQ(0, create_user_path(path, 1))
-            << "Should successfully build primary user path.";
-
-    EXPECT_STREQ("/data/user/1/", path)
-            << "Primary user should have correct path";
-}
-
 TEST_F(UtilsTest, CreateMovePath_Primary) {
     char path[PKG_PATH_MAX];
 
@@ -432,7 +401,7 @@
     dir_rec_t dst;
     dir_rec_t src;
 
-    src.path = "/data/";
+    src.path = (char*) "/data/";
     src.len = strlen(src.path);
 
     EXPECT_EQ(0, copy_and_append(&dst, &src, "app/"))
@@ -480,4 +449,40 @@
             << "String should fail because it's too large to fit";
 }
 
+TEST_F(UtilsTest, CreateDataPath) {
+    EXPECT_EQ("/data", create_data_path(nullptr));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b",
+            create_data_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b"));
+}
+
+TEST_F(UtilsTest, CreateDataUserPath) {
+    EXPECT_EQ("/data/data", create_data_user_path(nullptr, 0));
+    EXPECT_EQ("/data/user/10", create_data_user_path(nullptr, 10));
+
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0",
+            create_data_user_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10",
+            create_data_user_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10));
+}
+
+TEST_F(UtilsTest, CreateDataMediaPath) {
+    EXPECT_EQ("/data/media/0", create_data_media_path(nullptr, 0));
+    EXPECT_EQ("/data/media/10", create_data_media_path(nullptr, 10));
+
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/media/0",
+            create_data_media_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/media/10",
+            create_data_media_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10));
+}
+
+TEST_F(UtilsTest, CreatePackageDataPath) {
+    EXPECT_EQ("/data/data/com.example", create_package_data_path(nullptr, "com.example", 0));
+    EXPECT_EQ("/data/user/10/com.example", create_package_data_path(nullptr, "com.example", 10));
+
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0/com.example",
+            create_package_data_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", "com.example", 0));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10/com.example",
+            create_package_data_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", "com.example", 10));
+}
+
 }
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.cpp
similarity index 87%
rename from cmds/installd/utils.c
rename to cmds/installd/utils.cpp
index 8f366a0..ba411cd 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.cpp
@@ -16,137 +16,119 @@
 
 #include "installd.h"
 
+#include <base/stringprintf.h>
+#include <base/logging.h>
+
 #define CACHE_NOISY(x) //x
 
-int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
-                                const dir_rec_t* dir,
-                                const char* pkgname,
-                                const char* postfix)
-{
-     const size_t postfix_len = strlen(postfix);
+using android::base::StringPrintf;
 
-     const size_t pkgname_len = strlen(pkgname);
-     if (pkgname_len > PKG_NAME_MAX) {
-         return -1;
-     }
-
-     if (is_valid_package_name(pkgname) < 0) {
-         return -1;
-     }
-
-     if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) {
-         return -1;
-     }
-
-     char *dst = path;
-     size_t dst_size = PKG_PATH_MAX;
-
-     if (append_and_increment(&dst, dir->path, &dst_size) < 0
-             || append_and_increment(&dst, pkgname, &dst_size) < 0
-             || append_and_increment(&dst, postfix, &dst_size) < 0) {
-         ALOGE("Error building APK path");
-         return -1;
-     }
-
-     return 0;
+/**
+ * Check that given string is valid filename, and that it attempts no
+ * parent or child directory traversal.
+ */
+static bool is_valid_filename(const std::string& name) {
+    if (name.empty() || (name == ".") || (name == "..")
+            || (name.find('/') != std::string::npos)) {
+        return false;
+    } else {
+        return true;
+    }
 }
 
 /**
- * Create the package path name for a given package name with a postfix for
- * a certain userid. Returns 0 on success, and -1 on failure.
+ * Create the path name where package data should be stored for the given
+ * volume UUID, package name, and user ID. An empty UUID is assumed to be
+ * internal storage.
  */
-int create_pkg_path(char path[PKG_PATH_MAX],
-                    const char *pkgname,
-                    const char *postfix,
-                    userid_t userid)
-{
-    size_t userid_len;
-    char* userid_prefix;
-    if (userid == 0) {
-        userid_prefix = PRIMARY_USER_PREFIX;
-        userid_len = 0;
-    } else {
-        userid_prefix = SECONDARY_USER_PREFIX;
-        userid_len = snprintf(NULL, 0, "%d", userid);
-    }
+std::string create_package_data_path(const char* volume_uuid,
+        const char* package_name, userid_t user) {
+    CHECK(is_valid_filename(package_name));
+    CHECK(is_valid_package_name(package_name) == 0);
 
-    const size_t prefix_len = android_data_dir.len + strlen(userid_prefix)
-            + userid_len + 1 /*slash*/;
-    char prefix[prefix_len + 1];
+    return StringPrintf("%s/%s", create_data_user_path(volume_uuid, user).c_str(), package_name);
+}
 
-    char *dst = prefix;
-    size_t dst_size = sizeof(prefix);
-
-    if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
-            || append_and_increment(&dst, userid_prefix, &dst_size) < 0) {
-        ALOGE("Error building prefix for APK path");
+int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname,
+        const char *postfix, userid_t userid) {
+    if (is_valid_package_name(pkgname) != 0) {
+        path[0] = '\0';
         return -1;
     }
 
-    if (userid != 0) {
-        int ret = snprintf(dst, dst_size, "%d/", userid);
-        if (ret < 0 || (size_t) ret != userid_len + 1) {
-            ALOGW("Error appending UID to APK path");
-            return -1;
-        }
+    std::string _tmp(create_package_data_path(nullptr, pkgname, userid) + postfix);
+    const char* tmp = _tmp.c_str();
+    if (strlen(tmp) >= PKG_PATH_MAX) {
+        path[0] = '\0';
+        return -1;
+    } else {
+        strcpy(path, tmp);
+        return 0;
     }
+}
 
-    dir_rec_t dir;
-    dir.path = prefix;
-    dir.len = prefix_len;
-
-    return create_pkg_path_in_dir(path, &dir, pkgname, postfix);
+std::string create_data_path(const char* volume_uuid) {
+    if (volume_uuid == nullptr) {
+        return "/data";
+    } else {
+        CHECK(is_valid_filename(volume_uuid));
+        return StringPrintf("/mnt/expand/%s", volume_uuid);
+    }
 }
 
 /**
  * Create the path name for user data for a certain userid.
- * Returns 0 on success, and -1 on failure.
  */
-int create_user_path(char path[PKG_PATH_MAX],
-                    userid_t userid)
-{
-    size_t userid_len;
-    char* userid_prefix;
-    if (userid == 0) {
-        userid_prefix = PRIMARY_USER_PREFIX;
-        userid_len = 0;
+std::string create_data_user_path(const char* volume_uuid, userid_t userid) {
+    std::string data(create_data_path(volume_uuid));
+    if (volume_uuid == nullptr) {
+        if (userid == 0) {
+            return StringPrintf("%s/data", data.c_str());
+        } else {
+            return StringPrintf("%s/user/%u", data.c_str(), userid);
+        }
     } else {
-        userid_prefix = SECONDARY_USER_PREFIX;
-        userid_len = snprintf(NULL, 0, "%d/", userid);
+        return StringPrintf("%s/user/%u", data.c_str(), userid);
     }
-
-    char *dst = path;
-    size_t dst_size = PKG_PATH_MAX;
-
-    if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
-            || append_and_increment(&dst, userid_prefix, &dst_size) < 0) {
-        ALOGE("Error building prefix for user path");
-        return -1;
-    }
-
-    if (userid != 0) {
-        if (dst_size < userid_len + 1) {
-            ALOGE("Error building user path");
-            return -1;
-        }
-        int ret = snprintf(dst, dst_size, "%d/", userid);
-        if (ret < 0 || (size_t) ret != userid_len) {
-            ALOGE("Error appending userid to path");
-            return -1;
-        }
-    }
-    return 0;
 }
 
 /**
  * Create the path name for media for a certain userid.
- * Returns 0 on success, and -1 on failure.
  */
-int create_user_media_path(char path[PATH_MAX], userid_t userid) {
-    if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) {
-        return -1;
+std::string create_data_media_path(const char* volume_uuid, userid_t userid) {
+    return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
+}
+
+std::vector<userid_t> get_known_users(const char* volume_uuid) {
+    std::vector<userid_t> users;
+
+    // We always have an owner
+    users.push_back(0);
+
+    std::string path(create_data_path(volume_uuid) + "/" + SECONDARY_USER_PREFIX);
+    DIR* dir = opendir(path.c_str());
+    if (dir == NULL) {
+        // Unable to discover other users, but at least return owner
+        PLOG(ERROR) << "Failed to opendir " << path;
+        return users;
     }
-    return 0;
+
+    struct dirent* ent;
+    while ((ent = readdir(dir))) {
+        if (ent->d_type != DT_DIR) {
+            continue;
+        }
+
+        char* end;
+        userid_t user = strtol(ent->d_name, &end, 10);
+        if (*end == '\0' && user != 0) {
+            LOG(DEBUG) << "Found valid user " << user;
+            users.push_back(user);
+        }
+    }
+    closedir(dir);
+
+    return users;
 }
 
 /**
@@ -182,6 +164,10 @@
     const char *x = pkgname;
     int alpha = -1;
 
+    if (strlen(pkgname) > PKG_NAME_MAX) {
+        return -1;
+    }
+
     while (*x) {
         if (isalnum(*x) || (*x == '_')) {
                 /* alphanumeric or underscore are fine */
@@ -472,13 +458,13 @@
     return -1;
 }
 
-int64_t data_disk_free()
+int64_t data_disk_free(const std::string& data_path)
 {
     struct statfs sfs;
-    if (statfs(android_data_dir.path, &sfs) == 0) {
+    if (statfs(data_path.c_str(), &sfs) == 0) {
         return sfs.f_bavail * sfs.f_bsize;
     } else {
-        ALOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno));
+        PLOG(ERROR) << "Couldn't statfs " << data_path;
         return -1;
     }
 }
@@ -516,7 +502,7 @@
     int8_t* res = cache->curMemBlockAvail;
     int8_t* nextPos = res + len;
     if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) {
-        int8_t* newBlock = malloc(CACHE_BLOCK_SIZE);
+        int8_t* newBlock = (int8_t*) malloc(CACHE_BLOCK_SIZE);
         if (newBlock == NULL) {
             return NULL;
         }
@@ -836,7 +822,7 @@
     return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0);
 }
 
-void clear_cache_files(cache_t* cache, int64_t free_size)
+void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size)
 {
     size_t i;
     int skip = 0;
@@ -861,7 +847,7 @@
     for (i=0; i<cache->numFiles; i++) {
         skip++;
         if (skip > 10) {
-            if (data_disk_free() > free_size) {
+            if (data_disk_free(data_path) > free_size) {
                 return;
             }
             skip = 0;
@@ -910,14 +896,14 @@
  * The path is allowed to have at most one subdirectory and no indirections
  * to top level directories (i.e. have "..").
  */
-static int validate_path(const dir_rec_t* dir, const char* path) {
+static int validate_path(const dir_rec_t* dir, const char* path, int maxSubdirs) {
     size_t dir_len = dir->len;
     const char* subdir = strchr(path + dir_len, '/');
 
     // Only allow the path to have at most one subdirectory.
     if (subdir != NULL) {
         ++subdir;
-        if (strchr(subdir, '/') != NULL) {
+        if ((--maxSubdirs == 0) && strchr(subdir, '/') != NULL) {
             ALOGE("invalid apk path '%s' (subdir?)\n", path);
             return -1;
         }
@@ -942,7 +928,7 @@
     for (i = 0; i < android_system_dirs.count; i++) {
         const size_t dir_len = android_system_dirs.dirs[i].len;
         if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) {
-            return validate_path(android_system_dirs.dirs + i, path);
+            return validate_path(android_system_dirs.dirs + i, path, 1);
         }
     }
 
@@ -1000,7 +986,7 @@
             // Add space for slash and terminating null.
             size_t dst_size = path_len + 2;
 
-            rec->path = malloc(dst_size);
+            rec->path = (char*) malloc(dst_size);
             if (rec->path == NULL) {
                 return -1;
             }
@@ -1042,6 +1028,7 @@
 int validate_apk_path(const char *path)
 {
     const dir_rec_t* dir = NULL;
+    int maxSubdirs = 1;
 
     if (!strncmp(path, android_app_dir.path, android_app_dir.len)) {
         dir = &android_app_dir;
@@ -1049,11 +1036,14 @@
         dir = &android_app_private_dir;
     } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) {
         dir = &android_asec_dir;
+    } else if (!strncmp(path, android_mnt_expand_dir.path, android_mnt_expand_dir.len)) {
+        dir = &android_mnt_expand_dir;
+        maxSubdirs = 2;
     } else {
         return -1;
     }
 
-    return validate_path(dir, path);
+    return validate_path(dir, path, maxSubdirs);
 }
 
 int append_and_increment(char** dst, const char* src, size_t* dst_size) {
@@ -1066,13 +1056,13 @@
     return 0;
 }
 
-char *build_string2(char *s1, char *s2) {
+char *build_string2(const char *s1, const char *s2) {
     if (s1 == NULL || s2 == NULL) return NULL;
 
     int len_s1 = strlen(s1);
     int len_s2 = strlen(s2);
     int len = len_s1 + len_s2 + 1;
-    char *result = malloc(len);
+    char *result = (char *) malloc(len);
     if (result == NULL) return NULL;
 
     strcpy(result, s1);
@@ -1081,14 +1071,14 @@
     return result;
 }
 
-char *build_string3(char *s1, char *s2, char *s3) {
+char *build_string3(const char *s1, const char *s2, const char *s3) {
     if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL;
 
     int len_s1 = strlen(s1);
     int len_s2 = strlen(s2);
     int len_s3 = strlen(s3);
     int len = len_s1 + len_s2 + len_s3 + 1;
-    char *result = malloc(len);
+    char *result = (char *) malloc(len);
     if (result == NULL) return NULL;
 
     strcpy(result, s1);
@@ -1099,12 +1089,9 @@
 }
 
 /* Ensure that /data/media directories are prepared for given user. */
-int ensure_media_user_dirs(userid_t userid) {
-    char media_user_path[PATH_MAX];
-
-    // Ensure /data/media/<userid> exists
-    create_user_media_path(media_user_path, userid);
-    if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+int ensure_media_user_dirs(const char* uuid, userid_t userid) {
+    std::string media_user_path(create_data_media_path(uuid, userid));
+    if (fs_prepare_dir(media_user_path.c_str(), 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
         return -1;
     }
 
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index ef0feab..d56fa89 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -76,6 +76,9 @@
     // when a new frame becomes available.
     void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
 
+    // See IGraphicBufferConsumer::detachBuffer
+    status_t detachBuffer(int slot);
+
 private:
     ConsumerBase(const ConsumerBase&);
     void operator=(const ConsumerBase&);
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index b874e3a..e576018 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -163,6 +163,21 @@
     mFrameAvailableListener = listener;
 }
 
+status_t ConsumerBase::detachBuffer(int slot) {
+    CB_LOGV("detachBuffer");
+    Mutex::Autolock lock(mMutex);
+
+    status_t result = mConsumer->detachBuffer(slot);
+    if (result != NO_ERROR) {
+        CB_LOGE("Failed to detach buffer: %d", result);
+        return result;
+    }
+
+    freeBufferLocked(slot);
+
+    return result;
+}
+
 void ConsumerBase::dump(String8& result) const {
     dump(result, "");
 }