diff options
author | David 'Digit' Turner <digit@google.com> | 2009-11-06 17:54:12 -0800 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2009-11-12 15:55:17 -0800 |
commit | feba743b47440954e6cc5b9f9f8f6b229d4ea14e (patch) | |
tree | fa7bb52d9d9b93bbed73cddd70f10297b1ba4b52 /services | |
parent | 274072089ceff6eacb9ccfb59290a18f43739c87 (diff) | |
download | frameworks_base-feba743b47440954e6cc5b9f9f8f6b229d4ea14e.zip frameworks_base-feba743b47440954e6cc5b9f9f8f6b229d4ea14e.tar.gz frameworks_base-feba743b47440954e6cc5b9f9f8f6b229d4ea14e.tar.bz2 |
PackageManager: Support secondary ABI for native code at installation time.
The goal of this change is to allow the Package Manager to look for native
shared libraries corresponding to the CPU ABI reported by ro.product.cpu.abi2
if none was found for the CPU ABI given by ro.product.cpu.abi
This is used to support both ARMv5 and ARMv7 native code on modern ARMv7-based
devices. Typically, such a device would report a primary ABI of 'armeabi-v7a'
and a secondary one of 'armeabi', to indicate that it can run the binaries
generated for both.
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/PackageManagerService.java | 195 |
1 files changed, 140 insertions, 55 deletions
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 39129d4..5ed2d35 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -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,72 +2782,156 @@ 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 { - ZipFile zipFile = new ZipFile(scanFile); - Enumeration<ZipEntry> entries = - (Enumeration<ZipEntry>) zipFile.entries(); + 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()) { + continue; + } + String entryName = entry.getName(); + + // 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; + } - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - if (entry.isDirectory()) { - if (!hasNativeCode && entry.getName().startsWith("lib")) { - hasNativeCode = true; - } - continue; + // file name must start with libPrefix, i.e. "lib" + int lastSlash = entryName.lastIndexOf('/'); + + if (lastSlash < 0 || + !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) { + continue; + } + + 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; + } + + installedNativeLibraries = true; + + String sharedLibraryFilePath = sharedLibraryDir.getPath() + + File.separator + libFileName; + File sharedLibraryFile = new File(sharedLibraryFilePath); + if (! sharedLibraryFile.exists() || + sharedLibraryFile.length() != entry.getSize() || + sharedLibraryFile.lastModified() != entry.getTime()) { + if (Config.LOGD) { + Log.d(TAG, "Caching shared lib " + entry.getName()); } - String entryName = entry.getName(); - if (entryName.startsWith("lib/")) { - hasNativeCode = true; + if (mInstaller == null) { + sharedLibraryDir.mkdir(); } - if (! (entryName.startsWith(apkSharedLibraryPrefix) - && entryName.endsWith(sharedLibrarySuffix))) { - continue; - } - String libFileName = entryName.substring( - apkLibraryDirectory.length()); - if (libFileName.contains("/") - || (!FileUtils.isFilenameSafe(new File(libFileName)))) { - continue; + cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, + sharedLibraryFile); + } + } + if (!hasNativeLibraries) + return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; + + if (!installedNativeLibraries) + return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH; + + return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; + } + + // 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); } - - installedNativeCode = true; - - String sharedLibraryFilePath = sharedLibraryDir.getPath() + - File.separator + libFileName; - File sharedLibraryFile = new File(sharedLibraryFilePath); - if (! sharedLibraryFile.exists() || - sharedLibraryFile.length() != entry.getSize() || - sharedLibraryFile.lastModified() != entry.getTime()) { - if (Config.LOGD) { - Log.d(TAG, "Caching shared lib " + entry.getName()); - } - if (mInstaller == null) { - sharedLibraryDir.mkdir(); - } - cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, - sharedLibraryFile); + + 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; } - - 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; - } - return PackageManager.INSTALL_SUCCEEDED; } |