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

Commit 8585f409 authored by Kalesh Singh's avatar Kalesh Singh
Browse files

PackageManager: Factor out extractNativeLibFromApk()



This will be reused later for running apps in compatiblity mode on
16kB devices -- if extractNativeLibs is false and the libraries are
not 16kB-page-aligned in the zipped apk.

Bug: 339709616
Test: manually install apps
Change-Id: Ifc78525114b99d1076724079e5db78c7e1050d50
Signed-off-by: default avatarKalesh Singh <kaleshsingh@google.com>
parent 03c977c2
Loading
Loading
Loading
Loading
+76 −66
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <zlib.h>

#include <memory>
#include <string>

#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -125,72 +126,10 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char
    return INSTALL_SUCCEEDED;
}

/*
 * Copy the native library if needed.
 *
 * This function assumes the library and path names passed in are considered safe.
 */
static install_status_t
copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
    static const size_t kPageSize = getpagesize();
    void** args = reinterpret_cast<void**>(arg);
    jstring* javaNativeLibPath = (jstring*) args[0];
    jboolean extractNativeLibs = *(jboolean*) args[1];
    jboolean debuggable = *(jboolean*) args[2];

    ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);

    uint32_t uncompLen;
    uint32_t when;
    uint32_t crc;

    uint16_t method;
    off64_t offset;
    uint16_t extraFieldLength;
    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
                               &extraFieldLength)) {
        ALOGE("Couldn't read zip entry info\n");
        return INSTALL_FAILED_INVALID_APK;
    }

    // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
    // easier to use wrap.sh because it only works when it is extracted, see
    // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
    bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;

    if (!extractNativeLibs && !forceExtractCurrentFile) {
        // check if library is uncompressed and page-aligned
        if (method != ZipFileRO::kCompressStored) {
            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
                fileName);
            return INSTALL_FAILED_INVALID_APK;
        }

        if (offset % kPageSize != 0) {
            ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
                  "from apk.\n", fileName, kPageSize);
            return INSTALL_FAILED_INVALID_APK;
        }

#ifdef ENABLE_PUNCH_HOLES
        // if library is uncompressed, punch hole in it in place
        if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
            ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
                  "%" PRIu64 "",
                  fileName, zipFile->getZipFileName(), offset);
        }

        // if extra field for this zip file is present with some length, possibility is that it is
        // padding added for zip alignment. Punch holes there too.
        if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
            ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
        }
#endif // ENABLE_PUNCH_HOLES

        return INSTALL_SUCCEEDED;
    }

static install_status_t extractNativeLibFromApk(ZipFileRO* zipFile, ZipEntryRO zipEntry,
                                                const char* fileName,
                                                const std::string nativeLibPath, uint32_t when,
                                                uint32_t uncompLen, uint32_t crc) {
    // Build local file path
    const size_t fileNameLen = strlen(fileName);
    char localFileName[nativeLibPath.size() + fileNameLen + 2];
@@ -312,6 +251,77 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
    return INSTALL_SUCCEEDED;
}

/*
 * Copy the native library if needed.
 *
 * This function assumes the library and path names passed in are considered safe.
 */
static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zipFile,
                                          ZipEntryRO zipEntry, const char* fileName) {
    static const size_t kPageSize = getpagesize();
    void** args = reinterpret_cast<void**>(arg);
    jstring* javaNativeLibPath = (jstring*)args[0];
    jboolean extractNativeLibs = *(jboolean*)args[1];
    jboolean debuggable = *(jboolean*)args[2];
    install_status_t ret = INSTALL_SUCCEEDED;

    ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);

    uint32_t uncompLen;
    uint32_t when;
    uint32_t crc;

    uint16_t method;
    off64_t offset;
    uint16_t extraFieldLength;
    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
                               &extraFieldLength)) {
        ALOGE("Couldn't read zip entry info\n");
        return INSTALL_FAILED_INVALID_APK;
    }

    // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
    // easier to use wrap.sh because it only works when it is extracted, see
    // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
    bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;

    if (!extractNativeLibs && !forceExtractCurrentFile) {
        // check if library is uncompressed and page-aligned
        if (method != ZipFileRO::kCompressStored) {
            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
                  fileName);
            return INSTALL_FAILED_INVALID_APK;
        }

        if (offset % kPageSize != 0) {
            ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
                  "from apk.\n",
                  fileName, kPageSize);
            return INSTALL_FAILED_INVALID_APK;
        }

#ifdef ENABLE_PUNCH_HOLES
        // if library is uncompressed, punch hole in it in place
        if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
            ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
                  "%" PRIu64 "",
                  fileName, zipFile->getZipFileName(), offset);
        }

        // if extra field for this zip file is present with some length, possibility is that it is
        // padding added for zip alignment. Punch holes there too.
        if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
            ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
        }
#endif // ENABLE_PUNCH_HOLES

        return INSTALL_SUCCEEDED;
    }

    return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(), when,
                                   uncompLen, crc);
}

/*
 * An iterator over all shared libraries in a zip file. An entry is
 * considered to be a shared library if all of the conditions below are