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

Commit f4f093c5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Reinstate codes to enable RRO on system server"

parents 6587430d 7de2f9c7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := idmap.cpp create.cpp inspect.cpp
LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp

LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw

+55 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@ SYNOPSIS \n\
      idmap --help \n\
      idmap --fd target overlay fd \n\
      idmap --path target overlay idmap \n\
      idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
                   dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
      idmap --inspect idmap \n\
\n\
DESCRIPTION \n\
@@ -46,6 +48,11 @@ OPTIONS \n\
\n\
      --path: create idmap for target package 'target' (path to apk) and overlay package \n\
              'overlay' (path to apk); write results to 'idmap' (path). \n\
\n\
      --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
              target package 'target-package-name-to-look-for' (package name) present at\n\
              'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
              idmap file in 'dir-to-hold-idmaps' (path). \n\
\n\
      --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
                 debug-friendly format. \n\
@@ -90,6 +97,16 @@ EXAMPLES \n\
NOTES \n\
      This tool and its expected invocation from installd is modelled on dexopt.";

    bool verify_directory_readable(const char *path)
    {
        return access(path, R_OK | X_OK) == 0;
    }

    bool verify_directory_writable(const char *path)
    {
        return access(path, W_OK) == 0;
    }

    bool verify_file_readable(const char *path)
    {
        return access(path, R_OK) == 0;
@@ -150,6 +167,36 @@ NOTES \n\
        return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
    }

    int maybe_scan(const char *target_package_name, const char *target_apk_path,
            const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
    {
        if (!verify_root_or_system()) {
            fprintf(stderr, "error: permission denied: not user root or user system\n");
            return -1;
        }

        if (!verify_file_readable(target_apk_path)) {
            ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
            return -1;
        }

        if (!verify_directory_writable(idmap_dir)) {
            ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
            return -1;
        }

        const size_t N = overlay_dirs->size();
        for (size_t i = 0; i < N; i++) {
            const char *dir = overlay_dirs->itemAt(i);
            if (!verify_directory_readable(dir)) {
                ALOGD("error: no read access to %s: %s\n", dir, strerror(errno));
                return -1;
            }
        }

        return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs);
    }

    int maybe_inspect(const char *idmap_path)
    {
        // anyone (not just root or system) may do --inspect
@@ -188,6 +235,14 @@ int main(int argc, char **argv)
        return maybe_create_path(argv[2], argv[3], argv[4]);
    }

    if (argc >= 6 && !strcmp(argv[1], "--scan")) {
        android::Vector<const char *> v;
        for (int i = 5; i < argc; i++) {
            v.push(argv[i]);
        }
        return maybe_scan(argv[2], argv[3], argv[4], &v);
    }

    if (argc == 3 && !strcmp(argv[1], "--inspect")) {
        return maybe_inspect(argv[2]);
    }
+6 −0
Original line number Diff line number Diff line
@@ -25,6 +25,12 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,

int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);

// Regarding target_package_name: the idmap_scan implementation should
// be able to extract this from the manifest in target_apk_path,
// simplifying the external API.
int idmap_scan(const char *target_package_name, const char *target_apk_path,
        const char *idmap_dir, const android::Vector<const char *> *overlay_dirs);

int idmap_inspect(const char *idmap_path);

#endif // _IDMAP_H_

cmds/idmap/scan.cpp

0 → 100644
+240 −0
Original line number Diff line number Diff line
#include <dirent.h>
#include <inttypes.h>
#include <sys/file.h>
#include <sys/stat.h>

#include "idmap.h"

#include <memory>
#include <androidfw/ResourceTypes.h>
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <utils/SortedVector.h>
#include <utils/String16.h>
#include <utils/String8.h>

#define NO_OVERLAY_TAG (-1000)

using namespace android;

namespace {
    struct Overlay {
        Overlay() {}
        Overlay(const String8& a, const String8& i, int p) :
            apk_path(a), idmap_path(i), priority(p) {}

        bool operator<(Overlay const& rhs) const
        {
            return rhs.priority > priority;
        }

        String8 apk_path;
        String8 idmap_path;
        int priority;
    };

    bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
    {
        // the file is opened for appending so that it doesn't get truncated
        // before we can guarantee mutual exclusion via the flock
        FILE* fout = fopen(filename, "a");
        if (fout == NULL) {
            return false;
        }

        if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
            fclose(fout);
            return false;
        }

        if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
            TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
            fclose(fout);
            return false;
        }

        for (size_t i = 0; i < overlayVector.size(); ++i) {
            const Overlay& overlay = overlayVector[i];
            fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
        }

        TEMP_FAILURE_RETRY(fflush(fout));
        TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
        fclose(fout);

        // Make file world readable since Zygote (running as root) will read
        // it when creating the initial AssetManger object
        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
        if (chmod(filename, mode) == -1) {
            unlink(filename);
            return false;
        }

        return true;
    }

    String8 flatten_path(const char *path)
    {
        String16 tmp(path);
        tmp.replaceAll('/', '@');
        return String8(tmp);
    }

    int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
    {
        const size_t N = parser.getAttributeCount();
        String16 target;
        int priority = -1;
        for (size_t i = 0; i < N; ++i) {
            size_t len;
            String16 key(parser.getAttributeName(i, &len));
            if (key == String16("targetPackage")) {
                const char16_t *p = parser.getAttributeStringValue(i, &len);
                if (p != NULL) {
                    target = String16(p, len);
                }
            } else if (key == String16("priority")) {
                Res_value v;
                if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
                    priority = v.data;
                    if (priority < 0 || priority > 9999) {
                        return -1;
                    }
                }
            }
        }
        if (target == String16(target_package_name)) {
            return priority;
        }
        return NO_OVERLAY_TAG;
    }

    int parse_manifest(const void *data, size_t size, const char *target_package_name)
    {
        ResXMLTree parser;
        parser.setTo(data, size);
        if (parser.getError() != NO_ERROR) {
            ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
            return -1;
        }

        ResXMLParser::event_code_t type;
        do {
            type = parser.next();
            if (type == ResXMLParser::START_TAG) {
                size_t len;
                String16 tag(parser.getElementName(&len));
                if (tag == String16("overlay")) {
                    return parse_overlay_tag(parser, target_package_name);
                }
            }
        } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);

        return NO_OVERLAY_TAG;
    }

    int parse_apk(const char *path, const char *target_package_name)
    {
        std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
        if (zip.get() == NULL) {
            ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
            return -1;
        }
        ZipEntryRO entry;
        if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
            ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
            return -1;
        }
        uint32_t uncompLen = 0;
        uint16_t method;
        if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
            ALOGW("%s: failed to read entry info\n", __FUNCTION__);
            return -1;
        }
        if (method != ZipFileRO::kCompressDeflated) {
            ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
            return -1;
        }
        FileMap *dataMap = zip->createEntryFileMap(entry);
        if (dataMap == NULL) {
            ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
            return -1;
        }
        char *buf = new char[uncompLen];
        if (NULL == buf) {
            ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
            delete dataMap;
            return -1;
        }
        StreamingZipInflater inflater(dataMap, uncompLen);
        if (inflater.read(buf, uncompLen) < 0) {
            ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
            delete[] buf;
            delete dataMap;
            return -1;
        }

        int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
        delete[] buf;
        delete dataMap;
        return priority;
    }
}

int idmap_scan(const char *target_package_name, const char *target_apk_path,
        const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
{
    String8 filename = String8(idmap_dir);
    filename.appendPath("overlays.list");

    SortedVector<Overlay> overlayVector;
    const size_t N = overlay_dirs->size();
    for (size_t i = 0; i < N; ++i) {
        const char *overlay_dir = overlay_dirs->itemAt(i);
        DIR *dir = opendir(overlay_dir);
        if (dir == NULL) {
            return EXIT_FAILURE;
        }

        struct dirent *dirent;
        while ((dirent = readdir(dir)) != NULL) {
            struct stat st;
            char overlay_apk_path[PATH_MAX + 1];
            snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
            if (stat(overlay_apk_path, &st) < 0) {
                continue;
            }
            if (!S_ISREG(st.st_mode)) {
                continue;
            }

            int priority = parse_apk(overlay_apk_path, target_package_name);
            if (priority < 0) {
                continue;
            }

            String8 idmap_path(idmap_dir);
            idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
            idmap_path.append("@idmap");

            if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
                ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
                        target_apk_path, overlay_apk_path, idmap_path.string());
                continue;
            }

            Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
            overlayVector.add(overlay);
        }

        closedir(dir);
    }

    if (!writePackagesList(filename.string(), overlayVector)) {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
+93 −0
Original line number Diff line number Diff line
@@ -119,6 +119,96 @@ jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
    return block;
}

// This is called by zygote (running as user root) as part of preloadResources.
static void verifySystemIdmaps()
{
    pid_t pid;
    char system_id[10];

    snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);

    switch (pid = fork()) {
        case -1:
            ALOGE("failed to fork for idmap: %s", strerror(errno));
            break;
        case 0: // child
            {
                struct __user_cap_header_struct capheader;
                struct __user_cap_data_struct capdata;

                memset(&capheader, 0, sizeof(capheader));
                memset(&capdata, 0, sizeof(capdata));

                capheader.version = _LINUX_CAPABILITY_VERSION;
                capheader.pid = 0;

                if (capget(&capheader, &capdata) != 0) {
                    ALOGE("capget: %s\n", strerror(errno));
                    exit(1);
                }

                capdata.effective = capdata.permitted;
                if (capset(&capheader, &capdata) != 0) {
                    ALOGE("capset: %s\n", strerror(errno));
                    exit(1);
                }

                if (setgid(AID_SYSTEM) != 0) {
                    ALOGE("setgid: %s\n", strerror(errno));
                    exit(1);
                }

                if (setuid(AID_SYSTEM) != 0) {
                    ALOGE("setuid: %s\n", strerror(errno));
                    exit(1);
                }

                // Generic idmap parameters
                const char* argv[8];
                int argc = 0;
                struct stat st;

                memset(argv, NULL, sizeof(argv));
                argv[argc++] = AssetManager::IDMAP_BIN;
                argv[argc++] = "--scan";
                argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
                argv[argc++] = AssetManager::TARGET_APK_PATH;
                argv[argc++] = AssetManager::IDMAP_DIR;

                // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
                // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
                char subdir[PROP_VALUE_MAX];
                int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY,
                        subdir);
                if (len == 0) {
                    len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
                }
                if (len > 0) {
                    String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
                    if (stat(overlayPath.string(), &st) == 0) {
                        argv[argc++] = overlayPath.string();
                    }
                }
                if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
                    argv[argc++] = AssetManager::OVERLAY_DIR;
                }

                // Finally, invoke idmap (if any overlay directory exists)
                if (argc > 5) {
                    execv(AssetManager::IDMAP_BIN, (char* const*)argv);
                    ALOGE("failed to execv for idmap: %s", strerror(errno));
                    exit(1); // should never get here
                } else {
                    exit(0);
                }
            }
            break;
        default: // parent
            waitpid(pid, NULL, 0);
            break;
    }
}

// ----------------------------------------------------------------------------

// this guy is exported to other jni routines
@@ -1507,6 +1597,9 @@ static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jo

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
    if (isSystem) {
        verifySystemIdmaps();
    }
    AssetManager* am = new AssetManager();
    if (am == NULL) {
        jniThrowException(env, "java/lang/OutOfMemoryError", "");
Loading