diff options
-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; } |