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

Commit 6f5ba934 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change I42a0f7a5 into eclair

* changes:
  PackageManager: Support secondary ABI for native code at installation time.
parents 1778776c feba743b
Loading
Loading
Loading
Loading
+140 −55
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

@@ -2781,44 +2782,93 @@ class PackageManagerService extends IPackageManager.Stub {
        return pkg;
    }

    private int cachePackageSharedLibsLI(PackageParser.Package pkg,
            File dataPath, File scanFile) {
    // The following constants are returned by cachePackageSharedLibsForAbiLI
    // to indicate if native shared libraries were found in the package.
    // Values are:
    //    PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed
    //    PACKAGE_INSTALL_NATIVE_NO_LIBRARIES     => no native libraries in package
    //    PACKAGE_INSTALL_NATIVE_ABI_MISMATCH     => native libraries for another ABI found
    //                                        in package (and not installed)
    //
    private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0;
    private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1;
    private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2;

    // Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
    // and automatically copy them to /data/data/<appname>/lib if present.
    //
    // NOTE: this method may throw an IOException if the library cannot
    // be copied to its final destination, e.g. if there isn't enough
    // room left on the data partition, or a ZipException if the package
    // file is malformed.
    //
    private int cachePackageSharedLibsForAbiLI( PackageParser.Package  pkg,
        File dataPath, File scanFile, String cpuAbi)
    throws IOException, ZipException {
        File sharedLibraryDir = new File(dataPath.getPath() + "/lib");
        final String sharedLibraryABI = Build.CPU_ABI;
        final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/";
        final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib";
        final String sharedLibrarySuffix = ".so";
        boolean hasNativeCode = false;
        boolean installedNativeCode = false;
        try {
        final String apkLib = "lib/";
        final int apkLibLen = apkLib.length();
        final int cpuAbiLen = cpuAbi.length();
        final String libPrefix = "lib";
        final int libPrefixLen = libPrefix.length();
        final String libSuffix = ".so";
        final int libSuffixLen = libSuffix.length();
        boolean hasNativeLibraries = false;
        boolean installedNativeLibraries = false;

        // the minimum length of a valid native shared library of the form
        // lib/<something>/lib<name>.so.
        final int minEntryLen  = apkLibLen + 2 + libPrefixLen + 1 + libSuffixLen;

        ZipFile zipFile = new ZipFile(scanFile);
        Enumeration<ZipEntry> entries =
            (Enumeration<ZipEntry>) zipFile.entries();

        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            // skip directories
            if (entry.isDirectory()) {
                    if (!hasNativeCode && entry.getName().startsWith("lib")) {
                        hasNativeCode = true;
                    }
                continue;
            }
            String entryName = entry.getName();
                if (entryName.startsWith("lib/")) {
                    hasNativeCode = true;

            // check that the entry looks like lib/<something>/lib<name>.so
            // here, but don't check the ABI just yet.
            //
            // - must be sufficiently long
            // - must end with libSuffix, i.e. ".so"
            // - must start with apkLib, i.e. "lib/"
            if (entryName.length() < minEntryLen ||
                !entryName.endsWith(libSuffix) ||
                !entryName.startsWith(apkLib) ) {
                continue;
            }
                if (! (entryName.startsWith(apkSharedLibraryPrefix)
                        && entryName.endsWith(sharedLibrarySuffix))) {

            // file name must start with libPrefix, i.e. "lib"
            int lastSlash = entryName.lastIndexOf('/');

            if (lastSlash < 0 || 
                !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) {
                continue;
            }
                String libFileName = entryName.substring(
                        apkLibraryDirectory.length());
                if (libFileName.contains("/")
                        || (!FileUtils.isFilenameSafe(new File(libFileName)))) {

            hasNativeLibraries = true;

            // check the cpuAbi now, between lib/ and /lib<name>.so
            //
            if (lastSlash != apkLibLen + cpuAbiLen ||
                !entryName.regionMatches(apkLibLen, cpuAbi, 0, cpuAbiLen) )
                continue;

            // extract the library file name, ensure it doesn't contain
            // weird characters. we're guaranteed here that it doesn't contain
            // a directory separator though.
            String libFileName = entryName.substring(lastSlash+1);
            if (!FileUtils.isFilenameSafe(new File(libFileName))) {
                continue;
            }

                installedNativeCode = true;
            installedNativeLibraries = true;

            String sharedLibraryFilePath = sharedLibraryDir.getPath() +
                File.separator + libFileName;
@@ -2836,17 +2886,52 @@ class PackageManagerService extends IPackageManager.Stub {
                        sharedLibraryFile);
            }
        }
        } catch (IOException e) {
            Log.w(TAG, "Failed to cache package shared libs", e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        if (!hasNativeLibraries)
            return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;

        if (!installedNativeLibraries)
            return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;

        return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
    }

        if (hasNativeCode && !installedNativeCode) {
            Log.w(TAG, "Install failed: .apk has native code but none for arch "
                    + Build.CPU_ABI);
            return PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
    // extract shared libraries stored in the APK as lib/<cpuAbi>/lib<name>.so
    // and copy them to /data/data/<appname>/lib.
    //
    // This function will first try the main CPU ABI defined by Build.CPU_ABI
    // (which corresponds to ro.product.cpu.abi), and also try an alternate
    // one if ro.product.cpu.abi2 is defined.
    //
    private int cachePackageSharedLibsLI(PackageParser.Package  pkg,
        File dataPath, File scanFile) {
        final String cpuAbi = Build.CPU_ABI;
        try {
            int result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi);

            // some architectures are capable of supporting several CPU ABIs
            // for example, 'armeabi-v7a' also supports 'armeabi' native code
            // this is indicated by the definition of the ro.product.cpu.abi2
            // system property.
            //
            // only scan the package twice in case of ABI mismatch
            if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
                String  cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null);
                if (cpuAbi2 != null) {
                    result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi2);
                }

                if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
                    Log.w(TAG,"Native ABI mismatch from package file");
                    return PackageManager.INSTALL_FAILED_INVALID_APK;
                }
            }
        } catch (ZipException e) {
            Log.w(TAG, "Failed to extract data from package file", e);
            return PackageManager.INSTALL_FAILED_INVALID_APK;
        } catch (IOException e) {
            Log.w(TAG, "Failed to cache package shared libs", e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
        return PackageManager.INSTALL_SUCCEEDED;
    }