Merge "Added test cases to dumpsys."
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index ed5fc4a..72efd55 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 {
@@ -756,7 +756,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);
             }
@@ -849,54 +849,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,
@@ -905,6 +905,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;
     }
 
@@ -916,6 +919,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;
         }
     }
@@ -928,12 +934,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;
@@ -983,80 +1001,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/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5025fde..868e494 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -153,57 +153,6 @@
   return count;
 }
 
-static void run_patchoat(int input_oat_fd, int input_vdex_fd, int out_oat_fd, int out_vdex_fd,
-    const char* input_oat_file_name, const char* input_vdex_file_name,
-    const char* output_oat_file_name, const char* output_vdex_file_name,
-    const char *pkgname ATTRIBUTE_UNUSED, const char *instruction_set)
-{
-    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
-    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
-    static const char* PATCHOAT_BIN = "/system/bin/patchoat";
-    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
-        ALOGE("Instruction set %s longer than max length of %d",
-              instruction_set, MAX_INSTRUCTION_SET_LEN);
-        return;
-    }
-
-    /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/
-    char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
-    char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN];
-    char input_vdex_fd_arg[strlen("--input-vdex-fd=") + MAX_INT_LEN];
-    char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
-    char output_vdex_fd_arg[strlen("--output-vdex-fd=") + MAX_INT_LEN];
-    const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art";
-    // The caller has already gotten all the locks we need.
-    const char* no_lock_arg = "--no-lock-output";
-    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
-    sprintf(output_oat_fd_arg, "--output-oat-fd=%d", out_oat_fd);
-    sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_oat_fd);
-    ALOGV("Running %s isa=%s in-oat-fd=%d (%s) in-vdex-fd=%d (%s) "
-          "out-oat-fd=%d (%s) out-vdex-fd=%d (%s)\n",
-          PATCHOAT_BIN, instruction_set,
-          input_oat_fd, input_oat_file_name,
-          input_vdex_fd, input_vdex_file_name,
-          out_oat_fd, output_oat_file_name,
-          out_vdex_fd, output_vdex_file_name);
-
-    /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
-    char* argv[9];
-    argv[0] = (char*) PATCHOAT_BIN;
-    argv[1] = (char*) patched_image_location_arg;
-    argv[2] = (char*) no_lock_arg;
-    argv[3] = instruction_set_arg;
-    argv[4] = input_oat_fd_arg;
-    argv[5] = input_vdex_fd_arg;
-    argv[6] = output_oat_fd_arg;
-    argv[7] = output_vdex_fd_arg;
-    argv[8] = NULL;
-
-    execv(PATCHOAT_BIN, (char* const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
-}
-
 static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
         const char* input_file_name, const char* output_file_name, int swap_fd,
         const char *instruction_set, const char* compiler_filter, bool vm_safe_mode,
@@ -502,7 +451,7 @@
     return kDefaultProvideSwapFile;
 }
 
-static void SetDex2OatAndPatchOatScheduling(bool set_to_bg) {
+static void SetDex2OatScheduling(bool set_to_bg) {
     if (set_to_bg) {
         if (set_sched_policy(0, SP_BACKGROUND) < 0) {
             ALOGE("set_sched_policy failed: %s\n", strerror(errno));
@@ -1062,40 +1011,12 @@
         return false;
     }
 
-    const char *input_file;
-    char in_odex_path[PKG_PATH_MAX];
-    int dexopt_action = abs(dexopt_needed);
-    bool is_odex_location = dexopt_needed < 0;
-    switch (dexopt_action) {
-        case DEX2OAT_FROM_SCRATCH:
-        case DEX2OAT_FOR_BOOT_IMAGE:
-        case DEX2OAT_FOR_FILTER:
-        case DEX2OAT_FOR_RELOCATION:
-            input_file = apk_path;
-            break;
-
-        case PATCHOAT_FOR_RELOCATION:
-            if (is_odex_location) {
-                if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
-                    return -1;
-                }
-                input_file = in_odex_path;
-            } else {
-                input_file = out_oat_path;
-            }
-            break;
-
-        default:
-            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            return 72;
-    }
-
+    const char *input_file = apk_path;
     struct stat input_stat;
     memset(&input_stat, 0, sizeof(input_stat));
     stat(input_file, &input_stat);
 
-    // Open the input file. If running dex2oat, `input_file` is the APK. If running
-    // patchoat, it is the OAT file to be relocated.
+    // Open the input file.
     base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
     if (input_fd.get() < 0) {
         ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
@@ -1117,22 +1038,12 @@
 
     // Open the existing VDEX. We do this before creating the new output VDEX, which will
     // unlink the old one.
+    char in_odex_path[PKG_PATH_MAX];
+    int dexopt_action = abs(dexopt_needed);
+    bool is_odex_location = dexopt_needed < 0;
     base::unique_fd in_vdex_fd;
     std::string in_vdex_path_str;
-    if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
-        // `input_file` is the OAT file to be relocated. The VDEX has to be there as well.
-        in_vdex_path_str = create_vdex_filename(input_file);
-        if (in_vdex_path_str.empty()) {
-            ALOGE("installd cannot compute input vdex location for '%s'\n", input_file);
-            return -1;
-        }
-        in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
-        if (in_vdex_fd.get() < 0) {
-            ALOGE("installd cannot open '%s' for input during dexopt: %s\n",
-                in_vdex_path_str.c_str(), strerror(errno));
-            return -1;
-        }
-    } else if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+    if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
         // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
         const char* path = nullptr;
         if (is_odex_location) {
@@ -1212,7 +1123,7 @@
     // Avoid generating an app image for extract only since it will not contain any classes.
     Dex2oatFileWrapper<std::function<void ()>> image_fd;
     const std::string image_path = create_image_filename(out_oat_path);
-    if (dexopt_action != PATCHOAT_FOR_RELOCATION && !image_path.empty()) {
+    if (!image_path.empty()) {
         char app_image_format[kPropertyValueMax];
         bool have_app_image_format =
                 get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
@@ -1256,42 +1167,29 @@
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
 
-        SetDex2OatAndPatchOatScheduling(boot_complete);
+        SetDex2OatScheduling(boot_complete);
         if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno));
             _exit(67);
         }
 
-        if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
-            run_patchoat(input_fd.get(),
-                         in_vdex_fd.get(),
-                         out_oat_fd.get(),
-                         out_vdex_fd,
-                         input_file,
-                         in_vdex_path_str.c_str(),
-                         out_oat_path,
-                         out_vdex_path_str.c_str(),
-                         pkgname,
-                         instruction_set);
-        } else {
-            // Pass dex2oat the relative path to the input file.
-            const char *input_file_name = get_location_from_path(input_file);
-            run_dex2oat(input_fd.get(),
-                        out_oat_fd.get(),
-                        in_vdex_fd.get(),
-                        out_vdex_fd,
-                        image_fd.get(),
-                        input_file_name,
-                        out_oat_path,
-                        swap_fd.get(),
-                        instruction_set,
-                        compiler_filter,
-                        vm_safe_mode,
-                        debuggable,
-                        boot_complete,
-                        reference_profile_fd.get(),
-                        shared_libraries);
-        }
+        // Pass dex2oat the relative path to the input file.
+        const char *input_file_name = get_location_from_path(input_file);
+        run_dex2oat(input_fd.get(),
+                    out_oat_fd.get(),
+                    in_vdex_fd.get(),
+                    out_vdex_fd,
+                    image_fd.get(),
+                    input_file_name,
+                    out_oat_path,
+                    swap_fd.get(),
+                    instruction_set,
+                    compiler_filter,
+                    vm_safe_mode,
+                    debuggable,
+                    boot_complete,
+                    reference_profile_fd.get(),
+                    shared_libraries);
         _exit(68);   /* only get here on exec failure */
     } else {
         int res = wait_child(pid);
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);