Merge "Mark libGLESv3 as unversioned until android-24."
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index fc6458d..ed5fc4a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -18,12 +18,17 @@
 
 #include <errno.h>
 #include <inttypes.h>
+#include <fstream>
+#include <fts.h>
 #include <regex>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/capability.h>
 #include <sys/file.h>
 #include <sys/resource.h>
+#include <sys/quota.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
@@ -37,7 +42,6 @@
 #include <cutils/log.h>               // TODO: Move everything to base/logging.
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
-#include <diskusage/dirsize.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -53,6 +57,8 @@
 #define LOG_TAG "installd"
 #endif
 
+#define MEASURE_EXTERNAL 0
+
 using android::base::StringPrintf;
 
 namespace android {
@@ -75,7 +81,7 @@
 // NOTE: keep in sync with Installer
 static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
 static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-
+static constexpr int FLAG_USE_QUOTA = 1 << 12;
 
 #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 namespace {
@@ -258,9 +264,70 @@
     return 0;
 }
 
-static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode,
-        uid_t uid) {
-    return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid);
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ */
+static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid) {
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+            // This is fine, just create it
+            if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << path;
+                return -1;
+            } else {
+                return 0;
+            }
+        } else {
+            PLOG(ERROR) << "Failed to stat " << path;
+            return -1;
+        }
+    }
+
+    if (st.st_uid != uid) {
+        // Mismatched UID is real trouble; we can't recover
+        LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
+                << " but expected " << uid;
+        return -1;
+    } else if (st.st_gid == gid && st.st_mode == target_mode) {
+        // Everything looks good!
+        return 0;
+    }
+
+    // Directory is owned correctly, but GID or mode mismatch means it's
+    // probably a platform upgrade so we need to fix them
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        PLOG(ERROR) << "Failed to fts_open " << path;
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_DP:
+            if (chmod(p->fts_accpath, target_mode) != 0) {
+                PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+            }
+            // Intentional fall through to also set GID
+        case FTS_F:
+            if (chown(p->fts_accpath, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        case FTS_SL:
+        case FTS_SLNONE:
+            if (lchown(p->fts_accpath, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        }
+    }
+    fts_close(fts);
+    return 0;
 }
 
 binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
@@ -277,14 +344,16 @@
     if (_aidl_return != nullptr) *_aidl_return = -1;
 
     uid_t uid = multiuser_get_uid(userId, appId);
-    mode_t target_mode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+    gid_t cacheGid = multiuser_get_cache_gid(userId, appId);
+    mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+
     if (flags & FLAG_STORAGE_CE) {
         auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
         bool existing = (access(path.c_str(), F_OK) == 0);
 
-        if (prepare_app_dir(path, target_mode, uid) ||
-                prepare_app_dir(path, "cache", 0771, uid) ||
-                prepare_app_dir(path, "code_cache", 0771, uid)) {
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
             return error("Failed to prepare " + path);
         }
 
@@ -313,7 +382,9 @@
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
         bool existing = (access(path.c_str(), F_OK) == 0);
 
-        if (prepare_app_dir(path, target_mode, uid)) {
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
             return error("Failed to prepare " + path);
         }
 
@@ -336,7 +407,7 @@
             const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
             // dex2oat/profman runs under the shared app gid and it needs to read/write reference
             // profiles.
-            int shared_app_gid = multiuser_get_shared_app_gid(uid);
+            int shared_app_gid = multiuser_get_shared_gid(0, appId);
             if ((shared_app_gid != -1) && fs_prepare_dir_strict(
                     ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
                 return error("Failed to prepare " + ref_profile_path);
@@ -778,8 +849,91 @@
     }
 }
 
-static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize,
-        int64_t *cachesize) {
+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;
+
+    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);
+        }
+    }
+    if (device.empty()) {
+        PLOG(ERROR) << "Failed to resolve block device for " << path;
+        return;
+    }
+
+    uid_t uid = multiuser_get_uid(userId, appId);
+    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 {
+        stats->dataSize += dq.dqb_curspace;
+    }
+
+    int cacheGid = multiuser_get_cache_gid(userId, appId);
+    if (cacheGid != -1) {
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid;
+            }
+        } else {
+            stats->cacheSize += dq.dqb_curspace;
+        }
+    }
+
+    int sharedGid = multiuser_get_shared_app_gid(uid);
+    if (sharedGid != -1) {
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid;
+            }
+        } else {
+            stats->codeSize += dq.dqb_curspace;
+        }
+    }
+}
+
+static void collectManualStats(std::string& path, struct stats* stats) {
     DIR *d;
     int dfd;
     struct dirent *de;
@@ -787,87 +941,123 @@
 
     d = opendir(path.c_str());
     if (d == nullptr) {
-        PLOG(WARNING) << "Failed to open " << path;
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
         return;
     }
     dfd = dirfd(d);
     while ((de = readdir(d))) {
         const char *name = de->d_name;
 
-        int64_t statsize = 0;
+        int64_t size = 0;
         if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-            statsize = stat_size(&s);
+            size = s.st_blocks * 512;
         }
 
         if (de->d_type == DT_DIR) {
-            int subfd;
-            int64_t dirsize = 0;
-            /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
-            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
-            if (subfd >= 0) {
-                dirsize = calculate_dir_size(subfd);
-                close(subfd);
-            }
-            // TODO: check xattrs!
-            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
-                *datasize += statsize;
-                *cachesize += dirsize;
+            if (!strcmp(name, ".")) {
+                // Don't recurse, but still count node size
+            } else if (!strcmp(name, "..")) {
+                // Don't recurse or count node size
+                continue;
             } else {
-                *datasize += dirsize + statsize;
+                // Measure all children nodes
+                size = 0;
+                calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size);
             }
-        } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
-            *codesize += statsize;
-        } else {
-            *datasize += statsize;
+
+            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
+                stats->cacheSize += size;
+            }
         }
+
+        // Legacy symlink isn't owned by app
+        if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
+            continue;
+        }
+
+        // Everything found inside is considered data
+        stats->dataSize += size;
     }
     closedir(d);
 }
 
 binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
-        const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode,
-        const std::string& codePath, std::vector<int64_t>* _aidl_return) {
+        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) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     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();
-    const char* code_path = codePath.c_str();
 
-    DIR *d;
-    int dfd;
-    int64_t codesize = 0;
-    int64_t datasize = 0;
-    int64_t cachesize = 0;
-    int64_t asecsize = 0;
+    // 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
 
-    d = opendir(code_path);
-    if (d != nullptr) {
-        dfd = dirfd(d);
-        codesize += calculate_dir_size(dfd);
-        closedir(d);
+    struct stats stats;
+    memset(&stats, 0, sizeof(stats));
+
+    auto obbCodePath = create_data_media_package_path(extuuid_, userId, pkgname, "obb");
+    calculate_tree_size(obbCodePath, &stats.codeSize);
+
+    if (flags & FLAG_USE_QUOTA) {
+        calculate_tree_size(codePath, &stats.codeSize,
+                0, multiuser_get_shared_gid(userId, appId));
+
+        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);
+        }
+    } else {
+        calculate_tree_size(codePath, &stats.codeSize);
+
+        auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+        collectManualStats(cePath, &stats);
+
+        auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
+        collectManualStats(dePath, &stats);
+
+        auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
+        calculate_tree_size(userProfilePath, &stats.dataSize);
+
+        auto refProfilePath = create_data_ref_profile_package_path(pkgname);
+        calculate_tree_size(refProfilePath, &stats.codeSize);
+
+        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);
+
+#if MEASURE_EXTERNAL
+        auto extPath = create_data_media_package_path(extuuid_, userId, pkgname, "data");
+        collectManualStats(extPath, &stats);
+
+        auto mediaPath = create_data_media_package_path(extuuid_, userId, pkgname, "media");
+        calculate_tree_size(mediaPath, &stats.dataSize);
+#endif
     }
 
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
-        add_app_data_size(path, &codesize, &datasize, &cachesize);
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
-        add_app_data_size(path, &codesize, &datasize, &cachesize);
-    }
-
-    std::vector<int64_t> res;
-    res.push_back(codesize);
-    res.push_back(datasize);
-    res.push_back(cachesize);
-    res.push_back(asecsize);
-    *_aidl_return = res;
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    *_aidl_return = ret;
     return ok();
 }
 
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index d2564e5..cad9e43 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -56,8 +56,9 @@
     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, int64_t ceDataInode,
-            const std::string& codePath, std::vector<int64_t>* _aidl_return);
+            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);
 
     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/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 46564c4..8c5d2f4 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -32,7 +32,8 @@
     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, long ceDataInode, @utf8InCpp String codePath);
+            int userId, int flags, int appId, long ceDataInode, @utf8InCpp String codePath,
+            @nullable @utf8InCpp String externalUuid);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
             @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 37e303d..e1a59d4 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -198,6 +199,12 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+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(),
+            data_type, package_name);
+}
+
 std::string create_data_misc_legacy_path(userid_t userid) {
     return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
 }
@@ -216,6 +223,14 @@
     return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
 }
 
+std::string create_data_dalvik_cache_path() {
+    return "/data/dalvik-cache";
+}
+
+std::string create_data_misc_foreign_dex_path(userid_t userid) {
+    return StringPrintf("/data/misc/profiles/cur/%d/foreign-dex", userid);
+}
+
 // Keep profile paths in sync with ActivityThread.
 constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
 
@@ -255,6 +270,38 @@
     return users;
 }
 
+int calculate_tree_size(const std::string& path, int64_t* size,
+        gid_t include_gid, gid_t exclude_gid) {
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Failed to fts_open " << path;
+        }
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_D:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE:
+            if (include_gid != 0 && p->fts_statp->st_gid != include_gid) {
+                break;
+            }
+            if (exclude_gid != 0 && p->fts_statp->st_gid == exclude_gid) {
+                break;
+            }
+            *size += (p->fts_statp->st_blocks * 512);
+            break;
+        }
+    }
+    fts_close(fts);
+    return 0;
+}
+
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 4c299fd..ff04118 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -86,6 +86,8 @@
         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_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);
 
@@ -93,10 +95,16 @@
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
 std::string create_data_ref_profile_package_path(const char* package_name);
 
+std::string create_data_dalvik_cache_path();
+std::string create_data_misc_foreign_dex_path(userid_t userid);
+
 std::string create_primary_profile(const std::string& profile_dir);
 
 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);
+
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
 
 int create_move_path(char path[PKG_PATH_MAX],