Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5281b7f authored by Jeff Sharkey's avatar Jeff Sharkey Committed by android-build-merger
Browse files

Merge "Methods to calculate user and external disk usage." am: 4d9746d7

am: 15e80e04

Change-Id: I63799087ee8b62b68da1f39ee2495b54481bd866
parents 55f599a0 15e80e04
Loading
Loading
Loading
Loading
+362 −81
Original line number Diff line number Diff line
@@ -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 @@ binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std:
            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,36 +849,33 @@ binder::Status InstalldNativeService::rmdex(const std::string& codePath,
    }
}

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;
        return "";
    }
    std::string source;
    std::string target;
@@ -886,18 +883,21 @@ static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t
        std::getline(in, source, ' ');
        std::getline(in, target, ' ');
        if (target == path) {
                device = source;
                break;
            return source;
        }
        // Skip to next line
        std::getline(in, source);
    }
    }
    if (device.empty()) {
    PLOG(ERROR) << "Failed to resolve block device for " << path;
        return;
    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,
            reinterpret_cast<char*>(&dq)) != 0) {
@@ -905,6 +905,9 @@ static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t
            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 @@ static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t
                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 @@ static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t
                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 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(std::string& path, struct stats* stats) {
static void collectManualStats(const std::string& path, struct stats* stats) {
    DIR *d;
    int dfd;
    struct dirent *de;
@@ -983,17 +1001,55 @@ static void collectManualStats(std::string& path, struct stats* stats) {
    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);
    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:
@@ -1002,31 +1058,41 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri
    // /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/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);

    if (flags & FLAG_USE_QUOTA) {
        calculate_tree_size(codePath, &stats.codeSize,
                0, multiuser_get_shared_gid(userId, appId));
    const char* uuid_ = uuid ? uuid->c_str() : nullptr;

        collectQuotaStats(uuid, userId, appId, &stats);
    for (auto packageName : packageNames) {
        auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
        calculate_tree_size(obbCodePath, &extStats.codeSize);
    }

        // 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 {
        for (auto codePath : codePaths) {
            calculate_tree_size(codePath, &stats.codeSize);
        }

        for (size_t i = 0; i < packageNames.size(); i++) {
            const char* pkgname = packageNames[i].c_str();

        auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
            auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
            collectManualStats(cePath, &stats);

            auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
@@ -1038,25 +1104,240 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri
            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(uuid_, userId, pkgname, "data");
            collectManualStats(extPath, &extStats);

            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,
                multiuser_get_shared_gid(userId, appId), 0);
                    sharedGid, -1);
        }

        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
                multiuser_get_uid(userId, appId), 0);
                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
        auto extPath = create_data_media_package_path(extuuid_, userId, pkgname, "data");
        collectManualStats(extPath, &stats);
        // TODO: measure external storage paths
#endif

        auto mediaPath = create_data_media_package_path(extuuid_, userId, pkgname, "media");
        calculate_tree_size(mediaPath, &stats.dataSize);
        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();
}
+9 −3
Original line number Diff line number Diff line
@@ -55,10 +55,16 @@ public:
            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,
+628 −0

File added.

Preview size limit exceeded, changes collapsed.

+6 −3
Original line number Diff line number Diff line
@@ -31,9 +31,12 @@ interface IInstalld {
            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,
+80 −0
Original line number Diff line number Diff line
#!/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;
}
"""
Loading