Merge "Start enumerating support for VK_KHR_incremental_present."
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1e33439..ccc8378 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1074,6 +1074,8 @@
     RunCommand("VOLD DUMP", {"vdc", "dump"});
     RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
 
+    RunCommand("STORAGED TASKIOINFO", {"storaged", "-d"}, CommandOptions::WithTimeout(10).Build());
+
     RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
 
     RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index df0c6ca..ee4d541 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -53,12 +53,12 @@
 #include "otapreopt_utils.h"
 #include "utils.h"
 
+#include "MatchExtensionGen.h"
+
 #ifndef LOG_TAG
 #define LOG_TAG "installd"
 #endif
 
-#define MEASURE_EXTERNAL 0
-
 using android::base::StringPrintf;
 
 namespace android {
@@ -757,7 +757,7 @@
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete " + path);
             }
-            path = create_data_user_profiles_path(userId);
+            path = create_data_user_profile_path(userId);
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete " + path);
             }
@@ -850,54 +850,54 @@
     }
 }
 
-static bool uuidEquals(const std::unique_ptr<std::string>& a,
-        const std::unique_ptr<std::string>& b) {
-    if (!a && !b) {
-        return true;
-    } else if (!a && b) {
-        return false;
-    } else if (a && !b) {
-        return false;
-    } else {
-        return *a == *b;
-    }
-}
-
 struct stats {
     int64_t codeSize;
     int64_t dataSize;
     int64_t cacheSize;
 };
 
-static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t userId,
-        int32_t appId, struct stats* stats) {
-    struct dqblk dq;
+#if MEASURE_DEBUG
+static std::string toString(std::vector<int64_t> values) {
+    std::stringstream res;
+    res << "[";
+    for (size_t i = 0; i < values.size(); i++) {
+        res << values[i];
+        if (i < values.size() - 1) {
+            res << ",";
+        }
+    }
+    res << "]";
+    return res.str();
+}
+#endif
 
+static std::string findDeviceForUuid(const std::unique_ptr<std::string>& uuid) {
     auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
-    std::string device;
-    {
-        std::ifstream in("/proc/mounts");
-        if (!in.is_open()) {
-            PLOG(ERROR) << "Failed to read mounts";
-            return;
-        }
-        std::string source;
-        std::string target;
-        while (!in.eof()) {
-            std::getline(in, source, ' ');
-            std::getline(in, target, ' ');
-            if (target == path) {
-                device = source;
-                break;
-            }
-            // Skip to next line
-            std::getline(in, source);
-        }
+    std::ifstream in("/proc/mounts");
+    if (!in.is_open()) {
+        PLOG(ERROR) << "Failed to read mounts";
+        return "";
     }
-    if (device.empty()) {
-        PLOG(ERROR) << "Failed to resolve block device for " << path;
-        return;
+    std::string source;
+    std::string target;
+    while (!in.eof()) {
+        std::getline(in, source, ' ');
+        std::getline(in, target, ' ');
+        if (target == path) {
+            return source;
+        }
+        // Skip to next line
+        std::getline(in, source);
     }
+    PLOG(ERROR) << "Failed to resolve block device for " << path;
+    return "";
+}
+
+static void collectQuotaStats(const std::string& device, int32_t userId,
+        int32_t appId, struct stats* stats, struct stats* extStats ATTRIBUTE_UNUSED) {
+    if (device.empty()) return;
+
+    struct dqblk dq;
 
     uid_t uid = multiuser_get_uid(userId, appId);
     if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
@@ -906,6 +906,9 @@
             PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
         }
     } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
         stats->dataSize += dq.dqb_curspace;
     }
 
@@ -917,6 +920,9 @@
                 PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid;
             }
         } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace;
+#endif
             stats->cacheSize += dq.dqb_curspace;
         }
     }
@@ -929,12 +935,24 @@
                 PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid;
             }
         } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace;
+#endif
             stats->codeSize += dq.dqb_curspace;
         }
     }
+
+#if MEASURE_EXTERNAL
+    // TODO: measure using external GIDs
+#endif
 }
 
-static void collectManualStats(std::string& path, struct stats* stats) {
+static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t userId,
+        int32_t appId, struct stats* stats, struct stats* extStats) {
+    collectQuotaStats(findDeviceForUuid(uuid), userId, appId, stats, extStats);
+}
+
+static void collectManualStats(const std::string& path, struct stats* stats) {
     DIR *d;
     int dfd;
     struct dirent *de;
@@ -984,80 +1002,343 @@
     closedir(d);
 }
 
+static void collectManualStatsForUser(const std::string& path, struct stats* stats,
+        bool exclude_apps = false) {
+    DIR *d;
+    int dfd;
+    struct dirent *de;
+    struct stat s;
+
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        if (de->d_type == DT_DIR) {
+            const char *name = de->d_name;
+            if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) != 0) {
+                continue;
+            }
+            if (!strcmp(name, ".") || !strcmp(name, "..")) {
+                continue;
+            } else if (exclude_apps && (s.st_uid >= AID_APP_START && s.st_uid <= AID_APP_END)) {
+                continue;
+            } else {
+                collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats);
+            }
+        }
+    }
+    closedir(d);
+}
+
 binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
-        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
-        int64_t ceDataInode, const std::string& codePath,
-        const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return) {
+        const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+        int32_t appId, const std::vector<int64_t>& ceDataInodes,
+        const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    for (auto packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
 
-    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
-    const char* extuuid_ = externalUuid ? externalUuid->c_str() : nullptr;
-    const char* pkgname = packageName.c_str();
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId << " app " << appId;
+#endif
 
     // Here's a summary of the common storage locations across the platform,
     // and how they're each tagged:
     //
     // /data/app/com.example                           UID system
     // /data/app/com.example/oat                       UID system
-    // /data/user/0/com.example                        UID u0_a10 GID u0_a10
-    // /data/user/0/com.example/cache                  UID u0_a10 GID u0_a10_cache
-    // /data/media/0/Android/data/com.example          UID u0_a10 GID u0_a10
-    // /data/media/0/Android/data/com.example/cache    UID u0_a10 GID u0_a10_cache
-    // /data/media/0/Android/obb/com.example           UID system
+    // /data/user/0/com.example                        UID u0_a10      GID u0_a10
+    // /data/user/0/com.example/cache                  UID u0_a10      GID u0_a10_cache
+    // /data/media/0/foo.txt                           UID u0_media_rw
+    // /data/media/0/bar.jpg                           UID u0_media_rw GID u0_media_image
+    // /data/media/0/Android/data/com.example          UID u0_media_rw GID u0_a10_ext
+    // /data/media/0/Android/data/com.example/cache    UID u0_media_rw GID u0_a10_ext_cache
+    // /data/media/obb/com.example                     UID system
 
     struct stats stats;
+    struct stats extStats;
     memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
 
-    auto obbCodePath = create_data_media_package_path(extuuid_, userId, pkgname, "obb");
-    calculate_tree_size(obbCodePath, &stats.codeSize);
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
-    if (flags & FLAG_USE_QUOTA) {
-        calculate_tree_size(codePath, &stats.codeSize,
-                0, multiuser_get_shared_gid(userId, appId));
+    for (auto packageName : packageNames) {
+        auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
+        calculate_tree_size(obbCodePath, &extStats.codeSize);
+    }
 
-        collectQuotaStats(uuid, userId, appId, &stats);
-
-        // If external storage lives on a different storage device, also
-        // collect quota stats from that block device
-        if (!uuidEquals(uuid, externalUuid)) {
-            collectQuotaStats(externalUuid, userId, appId, &stats);
+    if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize, -1,
+                    multiuser_get_shared_gid(userId, appId));
         }
+
+        collectQuotaStats(uuid, userId, appId, &stats, &extStats);
+
     } else {
-        calculate_tree_size(codePath, &stats.codeSize);
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize);
+        }
 
-        auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
-        collectManualStats(cePath, &stats);
+        for (size_t i = 0; i < packageNames.size(); i++) {
+            const char* pkgname = packageNames[i].c_str();
 
-        auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
-        collectManualStats(dePath, &stats);
+            auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
+            collectManualStats(cePath, &stats);
 
-        auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
-        calculate_tree_size(userProfilePath, &stats.dataSize);
+            auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
+            collectManualStats(dePath, &stats);
 
-        auto refProfilePath = create_data_ref_profile_package_path(pkgname);
-        calculate_tree_size(refProfilePath, &stats.codeSize);
+            auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
+            calculate_tree_size(userProfilePath, &stats.dataSize);
 
-        calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
-                multiuser_get_shared_gid(userId, appId), 0);
-
-        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
-                multiuser_get_uid(userId, appId), 0);
+            auto refProfilePath = create_data_ref_profile_package_path(pkgname);
+            calculate_tree_size(refProfilePath, &stats.codeSize);
 
 #if MEASURE_EXTERNAL
-        auto extPath = create_data_media_package_path(extuuid_, userId, pkgname, "data");
-        collectManualStats(extPath, &stats);
+            auto extPath = create_data_media_package_path(uuid_, userId, pkgname, "data");
+            collectManualStats(extPath, &extStats);
 
-        auto mediaPath = create_data_media_package_path(extuuid_, userId, pkgname, "media");
-        calculate_tree_size(mediaPath, &stats.dataSize);
+            auto mediaPath = create_data_media_package_path(uuid_, userId, pkgname, "media");
+            calculate_tree_size(mediaPath, &extStats.dataSize);
 #endif
+        }
+
+        int32_t sharedGid = multiuser_get_shared_gid(userId, appId);
+        if (sharedGid != -1) {
+            calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                    sharedGid, -1);
+        }
+
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+                multiuser_get_uid(userId, appId), -1);
     }
 
     std::vector<int64_t> ret;
     ret.push_back(stats.codeSize);
     ret.push_back(stats.dataSize);
     ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+        std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId;
+#endif
+
+    struct stats stats;
+    struct stats extStats;
+    memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto obbPath = create_data_path(uuid_) + "/media/obb";
+    calculate_tree_size(obbPath, &extStats.codeSize);
+
+    if (flags & FLAG_USE_QUOTA) {
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
+
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats, true);
+
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats, true);
+
+        auto userProfilePath = create_data_user_profile_path(userId);
+        calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
+
+        auto refProfilePath = create_data_ref_profile_path();
+        calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
+
+#if MEASURE_EXTERNAL
+        // TODO: measure external storage paths
+#endif
+
+        calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                -1, -1, true);
+
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+                -1, -1, true);
+
+        auto device = findDeviceForUuid(uuid);
+        for (auto appId : appIds) {
+            if (appId >= AID_APP_START) {
+                collectQuotaStats(device, userId, appId, &stats, &extStats);
+#if MEASURE_DEBUG
+                // Sleep to make sure we don't lose logs
+                usleep(1);
+#endif
+            }
+        }
+    } else {
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
+
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats);
+
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats);
+
+        auto userProfilePath = create_data_user_profile_path(userId);
+        calculate_tree_size(userProfilePath, &stats.dataSize);
+
+        auto refProfilePath = create_data_ref_profile_path();
+        calculate_tree_size(refProfilePath, &stats.codeSize);
+
+#if MEASURE_EXTERNAL
+        // TODO: measure external storage paths
+#endif
+
+        calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
+
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize);
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring external " << userId;
+#endif
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    int64_t totalSize = 0;
+    int64_t audioSize = 0;
+    int64_t videoSize = 0;
+    int64_t imageSize = 0;
+
+    if (flags & FLAG_USE_QUOTA) {
+        struct dqblk dq;
+
+        auto device = findDeviceForUuid(uuid);
+
+        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+        if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+            }
+        } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+            totalSize = dq.dqb_curspace;
+        }
+
+        gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace;
+#endif
+            audioSize = dq.dqb_curspace;
+        }
+        gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace;
+#endif
+            videoSize = dq.dqb_curspace;
+        }
+        gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace;
+#endif
+            imageSize = dq.dqb_curspace;
+        }
+    } else {
+        FTS *fts;
+        FTSENT *p;
+        auto path = create_data_media_path(uuid_, userId);
+        char *argv[] = { (char*) path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+            return error("Failed to fts_open " + path);
+        }
+        while ((p = fts_read(fts)) != NULL) {
+            char* ext;
+            int64_t size = (p->fts_statp->st_blocks * 512);
+            switch (p->fts_info) {
+            case FTS_F:
+                // Only categorize files not belonging to apps
+                if (p->fts_statp->st_gid < AID_APP_START) {
+                    ext = strrchr(p->fts_name, '.');
+                    if (ext != nullptr) {
+                        switch (MatchExtension(++ext)) {
+                        case AID_MEDIA_AUDIO: audioSize += size; break;
+                        case AID_MEDIA_VIDEO: videoSize += size; break;
+                        case AID_MEDIA_IMAGE: imageSize += size; break;
+                        }
+                    }
+                }
+                // Fall through to always count against total
+            case FTS_D:
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                totalSize += size;
+                break;
+            }
+        }
+        fts_close(fts);
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(totalSize);
+    ret.push_back(audioSize);
+    ret.push_back(videoSize);
+    ret.push_back(imageSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
     *_aidl_return = ret;
     return ok();
 }
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index cad9e43..5397a74 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -55,10 +55,16 @@
             const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
     binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+
     binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
-            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
-            int64_t ceDataInode, const std::string& codePath,
-            const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return);
+            const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+            int32_t appId, const std::vector<int64_t>& ceDataInodes,
+            const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
+    binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+            std::vector<int64_t>* _aidl_return);
+    binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return);
 
     binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
             const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
diff --git a/cmds/installd/MatchExtensionGen.h b/cmds/installd/MatchExtensionGen.h
new file mode 100644
index 0000000..fded6b7
--- /dev/null
+++ b/cmds/installd/MatchExtensionGen.h
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2017 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
+ *
+ *      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
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+
+    switch (ext[0]) {
+    case '3':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case '2':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        }
+    case 'a': case 'A':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'c': case 'C':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'r': case 'R':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 't': case 'T':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'b': case 'B':
+        switch (ext[1]) {
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'c': case 'C':
+        switch (ext[1]) {
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'd': case 'D':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'f': case 'F':
+        switch (ext[1]) {
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'g': case 'G':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'j': case 'J':
+        switch (ext[1]) {
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'l': case 'L':
+        switch (ext[1]) {
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'm': case 'M':
+        switch (ext[1]) {
+        case '3':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case '4':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'k': case 'K':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'i': case 'I':
+                    switch (ext[4]) {
+                    case 'e': case 'E':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '3':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '4':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case 'a': case 'A':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_AUDIO;
+                        }
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'a': case 'A':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'x': case 'X':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'n': case 'N':
+        switch (ext[1]) {
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'o': case 'O':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'p': case 'P':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'c': case 'C':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'q': case 'Q':
+        switch (ext[1]) {
+        case 't': case 'T':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'r': case 'R':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 's': case 'S':
+        switch (ext[1]) {
+        case 'd': case 'D':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'z': case 'Z':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        }
+    case 't': case 'T':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'v': case 'V':
+        switch (ext[1]) {
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'w': case 'W':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case 'm': case 'M':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    }
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'x': case 'X':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 8c5d2f4..6e4d7fa 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -31,9 +31,12 @@
             int userId, int flags, long ceDataInode);
     void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, int flags, long ceDataInode);
-    long[] getAppSize(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
-            int userId, int flags, int appId, long ceDataInode, @utf8InCpp String codePath,
-            @nullable @utf8InCpp String externalUuid);
+
+    long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+            int userId, int flags, int appId, in long[] ceDataInodes,
+            in @utf8InCpp String[] codePaths);
+    long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+    long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
             @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
new file mode 100644
index 0000000..b37352b
--- /dev/null
+++ b/cmds/installd/matchgen.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 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
+#
+#      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
+# limitations under the License.
+
+import collections
+
+TYPES = {
+    "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"],
+    "AID_MEDIA_VIDEO": ["3gpp","3gp","3gpp2","3g2","avi","dl","dif","dv","fli","m4v","ts","mpeg","mpg","mpe","mp4","vob","qt","mov","mxu","webm","lsf","lsx","mkv","mng","asf","asx","wm","wmv","wmx","wvx","movie","wrf"],
+    "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"]
+}
+
+print """/*
+ * Copyright (C) 2017 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
+ *
+ *      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
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+"""
+
+trie = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: ""))))))
+
+for t in TYPES:
+    for v in TYPES[t]:
+        v = v.lower()
+        target = trie
+        for c in v:
+            target = target[c]
+        target["\0"] = t
+
+def dump(target, index):
+    prefix = "    " * (index + 1)
+    print "%sswitch (ext[%d]) {" % (prefix, index)
+    for k in sorted(target.keys()):
+        if k == "\0":
+            print "%scase '\\0': return %s;" % (prefix, target[k])
+        else:
+            upper = k.upper()
+            if k != upper:
+                print "%scase '%s': case '%s':" % (prefix, k, upper)
+            else:
+                print "%scase '%s':" % (prefix, k)
+            dump(target[k], index + 1)
+    print "%s}" % (prefix)
+
+dump(trie, 0)
+
+print """
+    return 0;
+}
+"""
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index e1a59d4..9849a0f 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -199,6 +199,10 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) {
+    return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name);
+}
+
 std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
         const char* data_type, const char* package_name) {
     return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(),
@@ -209,13 +213,17 @@
     return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
 }
 
-std::string create_data_user_profiles_path(userid_t userid) {
+std::string create_data_user_profile_path(userid_t userid) {
     return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
 }
 
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
     check_package_name(package_name);
-    return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name);
+    return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name);
+}
+
+std::string create_data_ref_profile_path() {
+    return StringPrintf("%s/ref", android_profiles_dir.path);
 }
 
 std::string create_data_ref_profile_package_path(const char* package_name) {
@@ -271,9 +279,10 @@
 }
 
 int calculate_tree_size(const std::string& path, int64_t* size,
-        gid_t include_gid, gid_t exclude_gid) {
+        int32_t include_gid, int32_t exclude_gid, bool exclude_apps) {
     FTS *fts;
     FTSENT *p;
+    int64_t matchedSize = 0;
     char *argv[] = { (char*) path.c_str(), nullptr };
     if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
         if (errno != ENOENT) {
@@ -288,17 +297,37 @@
         case FTS_F:
         case FTS_SL:
         case FTS_SLNONE:
-            if (include_gid != 0 && p->fts_statp->st_gid != include_gid) {
+            int32_t uid = p->fts_statp->st_uid;
+            int32_t gid = p->fts_statp->st_gid;
+            int32_t user_uid = multiuser_get_app_id(uid);
+            int32_t user_gid = multiuser_get_app_id(gid);
+            if (exclude_apps && ((user_uid >= AID_APP_START && user_uid <= AID_APP_END)
+                    || (user_gid >= AID_CACHE_GID_START && user_gid <= AID_CACHE_GID_END)
+                    || (user_gid >= AID_SHARED_GID_START && user_gid <= AID_SHARED_GID_END))) {
+                // Don't traverse inside or measure
+                fts_set(fts, p, FTS_SKIP);
                 break;
             }
-            if (exclude_gid != 0 && p->fts_statp->st_gid == exclude_gid) {
+            if (include_gid != -1 && gid != include_gid) {
                 break;
             }
-            *size += (p->fts_statp->st_blocks * 512);
+            if (exclude_gid != -1 && gid == exclude_gid) {
+                break;
+            }
+            matchedSize += (p->fts_statp->st_blocks * 512);
             break;
         }
     }
     fts_close(fts);
+#if MEASURE_DEBUG
+    if ((include_gid == -1) && (exclude_gid == -1)) {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize;
+    } else {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize << "; include " << include_gid
+                << " exclude " << exclude_gid;
+    }
+#endif
+    *size += matchedSize;
     return 0;
 }
 
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index ff04118..f2f0cbb 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -30,6 +30,9 @@
 
 #include <installd_constants.h>
 
+#define MEASURE_DEBUG 0
+#define MEASURE_EXTERNAL 0
+
 namespace android {
 namespace installd {
 
@@ -86,13 +89,16 @@
         userid_t user, const char* package_name);
 
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
 std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
         const char* data_type, const char* package_name);
 
 std::string create_data_misc_legacy_path(userid_t userid);
 
-std::string create_data_user_profiles_path(userid_t userid);
+std::string create_data_user_profile_path(userid_t userid);
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+
+std::string create_data_ref_profile_path();
 std::string create_data_ref_profile_package_path(const char* package_name);
 
 std::string create_data_dalvik_cache_path();
@@ -103,7 +109,7 @@
 std::vector<userid_t> get_known_users(const char* volume_uuid);
 
 int calculate_tree_size(const std::string& path, int64_t* size,
-        gid_t include_gid = 0, gid_t exclude_gid = 0);
+        int32_t include_gid = -1, int32_t exclude_gid = -1, bool exclude_apps = false);
 
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
 
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index 7ace777..ce21791 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -59,6 +59,28 @@
 ----
 The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
 
+Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on +format+ and +imageUsage+, but also on the intended usage of the swapchain. The additional usage bits are defined as
+[source,c]
+----
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+----
+
+If the driver provides the +vkGetSwapchainGrallocUsage2ANDROID+ function, the platform will use it in preference to +vkGetSwapchainGrallocUsageANDROID+ when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. +vkGetSwapchainGrallocUsage2ANDROID+ behaves in the same way as +vkGetSwapchainGrallocUsageANDROID+, and is declared as
+[source,c]
+----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    int*                grallocUsage
+);
+----
+
 +VkNativeBufferANDROID+ is a +vkCreateImage+ extension structure for creating an image backed by a gralloc buffer. This structure is provided to +vkCreateImage+ in the +VkImageCreateInfo+ structure chain. Calls to +vkCreateImage+ with this structure will happen during the first call to +vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)+. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a +VkImage+ for each one.
 
 [source,c]
@@ -78,6 +100,7 @@
 ----
 
 When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
+[source,txt]
 ----
   .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
@@ -93,6 +116,17 @@
   .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
 ----
 
+Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a +VkSwapchainImageCreateInfoANDROID+ extension structure in the +VkImageCreateInfo+ chain provided to +vkCreateImage+, containing the swapchain image usage flags:
+[source,c]
+----
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagBitsANDROID   usage;
+} VkSwapchainImageCreateInfoANDROID;
+----
+
 +vkAcquireImageANDROID+ acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:
@@ -139,6 +173,8 @@
 
 This will be called during +vkQueuePresentWSI+ on the provided queue. Effects are similar to +vkQueueSignalSemaphore+, except with a native fence instead of a semaphore. The native fence must: not signal until the +waitSemaphoreCount+ semaphores in +pWaitSemaphores+ have signaled. Unlike +vkQueueSignalSemaphore+, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set +*pNativeFenceFd+ to -1. The file descriptor returned in +*pNativeFenceFd+ is owned and will be closed by the caller. Many drivers will be able to ignore the +image+ parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to +VK_IMAGE_LAYOUT_PRESENT_SRC_KHR+.
 
+If +image+ was created with +VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID+, then the driver must tolerate +vkQueueSignalReleaseImageANDROID+ being called repeatedly without intervening calls to +vkAcquireImageANDROID+.
+
 == History ==
 
 . *2015-07-08* Initial version
@@ -158,4 +194,9 @@
 . *2016-01-08*
    * Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
 . *2016-06-17*
-   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
+   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
+. *2017-01-06*
+   * Extension version 6
+   * Added VkSwapchainImageUsageFlagBitsANDROID
+   * Added vkGetSwapchainGrallocUsage2ANDROID
+   * Added VkSwapchainImageCreateInfoANDROID
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..5ff1269 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -730,7 +730,7 @@
 /*]]>*/
 </script>
 </head>
-<body class="book">
+<body class="article">
 <div id="header">
 <h1>Vulkan on Android Implementor&#8217;s Guide</h1>
 <span id="revnumber">version 5</span>
@@ -795,7 +795,7 @@
 <div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver&#8217;s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
 <div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -806,9 +806,33 @@
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                grallocUsage
 <span style="color: #990000">);</span></tt></pre></div></div>
 <div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
+<div class="paragraph"><p>Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The additional usage bits are defined as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
+<span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
+<div class="paragraph"><p>If the driver provides the <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> function, the platform will use it in preference to <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span> when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> behaves in the same way as <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span>, and is declared as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
+    <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
+    <span style="color: #008080">VkFormat</span>            format<span style="color: #990000">,</span>
+    <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
+    <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
+    <span style="color: #009900">int</span><span style="color: #990000">*</span>                grallocUsage
+<span style="color: #990000">);</span></tt></pre></div></div>
 <div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -826,8 +850,11 @@
 <span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
 <div class="listingblock">
-<div class="content monospaced">
-<pre>  .imageType           = VK_IMAGE_TYPE_2D
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>  .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
   .extent              = the 2D dimensions requested for the gralloc buffer
   .mipLevels           = 1
@@ -838,13 +865,24 @@
   .flags               = 0
   .sharingMode         = VkSwapChainCreateInfoWSI::sharingMode
   .queueFamilyCount    = VkSwapChainCreateInfoWSI::queueFamilyCount
-  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</pre>
-</div></div>
+  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
+<div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+    <span style="color: #008080">VkStructureType</span>                        sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span>                            pNext<span style="color: #990000">;</span>
+
+    <span style="color: #008080">VkSwapchainImageUsageFlagBitsANDROID</span>   usage<span style="color: #990000">;</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -872,7 +910,7 @@
 is as if the native fence was already signalled.</p></div>
 <div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -884,6 +922,7 @@
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                pNativeFenceFd
 <span style="color: #990000">);</span></tt></pre></div></div>
 <div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
+<div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
 </div>
 </div>
 <div class="sect1">
@@ -978,6 +1017,33 @@
 </li>
 </ul></div>
 </li>
+<li>
+<p>
+<strong>2017-01-06</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extension version 6
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageUsageFlagBitsANDROID
+</p>
+</li>
+<li>
+<p>
+Added vkGetSwapchainGrallocUsage2ANDROID
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageCreateInfoANDROID
+</p>
+</li>
+</ul></div>
+</li>
 </ol></div>
 </div>
 </div>
@@ -986,7 +1052,7 @@
 <div id="footer">
 <div id="footer-text">
 Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-01-09 09:52:43 NZDT
 </div>
 </div>
 </body>