diff options
author | Kenny Root <kroot@google.com> | 2010-08-26 10:13:11 -0700 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2010-08-27 16:40:06 -0700 |
commit | 85387d7ba36e56b291cbde87acb5a5b2200fe01c (patch) | |
tree | adea29b181a25c2ffd8ecaebded62fb4d2dc1a93 /core/java | |
parent | 4f8c2f26bfd9de8c8a31369dbedd415c7d6d9699 (diff) | |
download | frameworks_base-85387d7ba36e56b291cbde87acb5a5b2200fe01c.zip frameworks_base-85387d7ba36e56b291cbde87acb5a5b2200fe01c.tar.gz frameworks_base-85387d7ba36e56b291cbde87acb5a5b2200fe01c.tar.bz2 |
Allow native shared libraries in ASEC containers
This change moves the native library handling earlier in the package
installation process so that it may be inserted into ASEC containers
before they are finalized in the DefaultContainerService.
Note that native libraries on SD card requires that vold mount ASEC
containers without the "noexec" flag on the mount point.
Change-Id: Ib34b1886bf6f94b99bb7b3781db6e9b5a58807ba
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/app/ActivityThread.java | 1 | ||||
-rw-r--r-- | core/java/android/app/ApplicationLoaders.java | 10 | ||||
-rw-r--r-- | core/java/android/app/LoadedApk.java | 8 | ||||
-rw-r--r-- | core/java/android/content/pm/ApplicationInfo.java | 20 | ||||
-rw-r--r-- | core/java/android/content/pm/InstrumentationInfo.java | 12 | ||||
-rw-r--r-- | core/java/com/android/internal/content/NativeLibraryHelper.java | 296 | ||||
-rw-r--r-- | core/java/com/android/internal/content/PackageHelper.java | 16 |
7 files changed, 338 insertions, 25 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 084f637..ca6fc8a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3181,6 +3181,7 @@ public final class ActivityThread { instrApp.sourceDir = ii.sourceDir; instrApp.publicSourceDir = ii.publicSourceDir; instrApp.dataDir = ii.dataDir; + instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, appContext.getClassLoader(), false, true); ContextImpl instrContext = new ContextImpl(); diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 2e301c9..9e3cd7e 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -19,6 +19,7 @@ package android.app; import dalvik.system.PathClassLoader; import java.util.HashMap; +import java.util.Map; class ApplicationLoaders { @@ -27,8 +28,7 @@ class ApplicationLoaders return gApplicationLoaders; } - public ClassLoader getClassLoader(String zip, String appDataDir, - ClassLoader parent) + public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent) { /* * This is the parent we use if they pass "null" in. In theory @@ -49,13 +49,13 @@ class ApplicationLoaders * new ClassLoader for the zip archive. */ if (parent == baseParent) { - ClassLoader loader = (ClassLoader)mLoaders.get(zip); + ClassLoader loader = mLoaders.get(zip); if (loader != null) { return loader; } PathClassLoader pathClassloader = - new PathClassLoader(zip, appDataDir + "/lib", parent); + new PathClassLoader(zip, libPath, parent); mLoaders.put(zip, pathClassloader); return pathClassloader; @@ -65,7 +65,7 @@ class ApplicationLoaders } } - private final HashMap mLoaders = new HashMap(); + private final Map<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>(); private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders(); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0f98152..0644f96 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -72,6 +72,7 @@ final class LoadedApk { private final String mResDir; private final String[] mSharedLibraries; private final String mDataDir; + private final String mLibDir; private final File mDataDirFile; private final ClassLoader mBaseClassLoader; private final boolean mSecurityViolation; @@ -108,6 +109,7 @@ final class LoadedApk { mSharedLibraries = aInfo.sharedLibraryFiles; mDataDir = aInfo.dataDir; mDataDirFile = mDataDir != null ? new File(mDataDir) : null; + mLibDir = aInfo.nativeLibraryDir; mBaseClassLoader = baseLoader; mSecurityViolation = securityViolation; mIncludeCode = includeCode; @@ -140,6 +142,7 @@ final class LoadedApk { mSharedLibraries = null; mDataDir = null; mDataDirFile = null; + mLibDir = null; mBaseClassLoader = null; mSecurityViolation = false; mIncludeCode = true; @@ -279,11 +282,12 @@ final class LoadedApk { * create the class loader. */ - if (ActivityThread.localLOGV) Slog.v(ActivityThread.TAG, "Class path: " + zip); + if (ActivityThread.localLOGV) + Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + mLibDir); mClassLoader = ApplicationLoaders.getDefault().getClassLoader( - zip, mDataDir, mBaseClassLoader); + zip, mLibDir, mBaseClassLoader); initializeJavaContextClassLoader(); } else { if (mBaseClassLoader == null) { diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index c812f36..ae6a311 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -291,14 +291,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_FORWARD_LOCK = 1<<29; /** - * Value for {@link #flags}: Set to true if the application is - * native-debuggable, i.e. embeds a gdbserver binary in its .apk - * - * {@hide} - */ - public static final int FLAG_NATIVE_DEBUGGABLE = 1<<28; - - /** * Value for {@link #flags}: set to <code>true</code> if the application * has reported that it is heavy-weight, and thus can not participate in * the normal application lifecycle. @@ -359,7 +351,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * data. */ public String dataDir; - + + /** + * Full path to the directory where native JNI libraries are stored. + * + * {@hide} + */ + public String nativeLibraryDir; + /** * The kernel user-ID that has been assigned to this application; * currently this is not a unique ID (multiple applications can have @@ -452,6 +451,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { flags = orig.flags; sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; + nativeLibraryDir = orig.nativeLibraryDir; resourceDirs = orig.resourceDirs; sharedLibraryFiles = orig.sharedLibraryFiles; dataDir = orig.dataDir; @@ -483,6 +483,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(flags); dest.writeString(sourceDir); dest.writeString(publicSourceDir); + dest.writeString(nativeLibraryDir); dest.writeStringArray(resourceDirs); dest.writeStringArray(sharedLibraryFiles); dest.writeString(dataDir); @@ -514,6 +515,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { flags = source.readInt(); sourceDir = source.readString(); publicSourceDir = source.readString(); + nativeLibraryDir = source.readString(); resourceDirs = source.readStringArray(); sharedLibraryFiles = source.readStringArray(); dataDir = source.readString(); diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index 3e868a7..ea47e8e 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -50,7 +50,14 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { * data. */ public String dataDir; - + + /** + * Full path to the directory where the native JNI libraries are stored. + * + * {@hide} + */ + public String nativeLibraryDir; + /** * Specifies whether or not this instrumentation will handle profiling. */ @@ -68,6 +75,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; dataDir = orig.dataDir; + nativeLibraryDir = orig.nativeLibraryDir; handleProfiling = orig.handleProfiling; functionalTest = orig.functionalTest; } @@ -88,6 +96,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { dest.writeString(sourceDir); dest.writeString(publicSourceDir); dest.writeString(dataDir); + dest.writeString(nativeLibraryDir); dest.writeInt((handleProfiling == false) ? 0 : 1); dest.writeInt((functionalTest == false) ? 0 : 1); } @@ -108,6 +117,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { sourceDir = source.readString(); publicSourceDir = source.readString(); dataDir = source.readString(); + nativeLibraryDir = source.readString(); handleProfiling = source.readInt() != 0; functionalTest = source.readInt() != 0; } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java new file mode 100644 index 0000000..8b618c7 --- /dev/null +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -0,0 +1,296 @@ +package com.android.internal.content; + +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.FileUtils; +import android.os.SystemProperties; +import android.util.Config; +import android.util.Log; +import android.util.Pair; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Native libraries helper. + * + * @hide + */ +public class NativeLibraryHelper { + private static final String TAG = "NativeHelper"; + + private static final boolean DEBUG_NATIVE = false; + + /* + * The following constants are returned by listPackageSharedLibsForAbiLI + * 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; + + // Directory in the APK that holds all the native shared libraries. + private static final String APK_LIB = "lib/"; + private static final int APK_LIB_LENGTH = APK_LIB.length(); + + // Prefix that native shared libraries must have. + private static final String LIB_PREFIX = "lib"; + private static final int LIB_PREFIX_LENGTH = LIB_PREFIX.length(); + + // Suffix that the native shared libraries must have. + private static final String LIB_SUFFIX = ".so"; + private static final int LIB_SUFFIX_LENGTH = LIB_SUFFIX.length(); + + // Name of the GDB binary. + private static final String GDBSERVER = "gdbserver"; + + // the minimum length of a valid native shared library of the form + // lib/<something>/lib<name>.so. + private static final int MIN_ENTRY_LENGTH = APK_LIB_LENGTH + 2 + LIB_PREFIX_LENGTH + 1 + + LIB_SUFFIX_LENGTH; + + /* + * Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk + * and add them to a list to be installed later. + * + * 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 static int listPackageSharedLibsForAbiLI(ZipFile zipFile, + String cpuAbi, List<Pair<ZipEntry, String>> libEntries) throws IOException, + ZipException { + final int cpuAbiLen = cpuAbi.length(); + boolean hasNativeLibraries = false; + boolean installedNativeLibraries = false; + + if (DEBUG_NATIVE) { + Slog.d(TAG, "Checking " + zipFile.getName() + " for shared libraries of CPU ABI type " + + cpuAbi); + } + + Enumeration<? extends ZipEntry> entries = 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 LIB_SUFFIX, i.e. ".so" + * - must start with APK_LIB, i.e. "lib/" + */ + if (entryName.length() < MIN_ENTRY_LENGTH || !entryName.endsWith(LIB_SUFFIX) + || !entryName.startsWith(APK_LIB)) { + continue; + } + + // file name must start with LIB_PREFIX, i.e. "lib" + int lastSlash = entryName.lastIndexOf('/'); + + if (lastSlash < 0 + || !entryName.regionMatches(lastSlash + 1, LIB_PREFIX, 0, LIB_PREFIX_LENGTH)) { + continue; + } + + hasNativeLibraries = true; + + // check the cpuAbi now, between lib/ and /lib<name>.so + if (lastSlash != APK_LIB_LENGTH + cpuAbiLen + || !entryName.regionMatches(APK_LIB_LENGTH, 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; + + if (DEBUG_NATIVE) { + Log.d(TAG, "Caching shared lib " + entry.getName()); + } + + libEntries.add(Pair.create(entry, libFileName)); + } + if (!hasNativeLibraries) + return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; + + if (!installedNativeLibraries) + return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH; + + return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; + } + + /* + * Find the gdbserver executable program in a package at + * lib/<cpuAbi>/gdbserver and add it to the list of binaries + * to be copied out later. + * + * Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success, + * or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise. + */ + private static int listPackageGdbServerLI(ZipFile zipFile, String cpuAbi, + List<Pair<ZipEntry, String>> nativeFiles) throws IOException, ZipException { + final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER; + + Enumeration<? extends ZipEntry> entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + // skip directories + if (entry.isDirectory()) { + continue; + } + String entryName = entry.getName(); + + if (!entryName.equals(apkGdbServerPath)) { + continue; + } + + if (Config.LOGD) { + Log.d(TAG, "Found gdbserver: " + entry.getName()); + } + + final String installGdbServerPath = APK_LIB + GDBSERVER; + nativeFiles.add(Pair.create(entry, installGdbServerPath)); + + return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; + } + return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; + } + + /* + * Examine shared libraries stored in the APK as + * lib/<cpuAbi>/lib<name>.so and add them to a list to be copied + * later. + * + * 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. + */ + public static int listPackageNativeBinariesLI(ZipFile zipFile, + List<Pair<ZipEntry, String>> nativeFiles) throws ZipException, IOException { + String cpuAbi = Build.CPU_ABI; + + int result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi, nativeFiles); + + /* + * 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) { + final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2", null); + if (cpuAbi2 != null) { + result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi2, nativeFiles); + } + + if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { + Slog.w(TAG, "Native ABI mismatch from package file"); + return PackageManager.INSTALL_FAILED_INVALID_APK; + } + + if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { + cpuAbi = cpuAbi2; + } + } + + /* + * Debuggable packages may have gdbserver embedded, so add it to + * the list to the list of items to be extracted (as lib/gdbserver) + * into the application's native library directory later. + */ + if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { + listPackageGdbServerLI(zipFile, cpuAbi, nativeFiles); + } + return PackageManager.INSTALL_SUCCEEDED; + } + + public static int copyNativeBinariesLI(File scanFile, File sharedLibraryDir) { + /* + * Check all the native files that need to be copied and add + * that to the container size. + */ + ZipFile zipFile; + try { + zipFile = new ZipFile(scanFile); + + List<Pair<ZipEntry, String>> nativeFiles = new LinkedList<Pair<ZipEntry, String>>(); + + NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles); + + final int N = nativeFiles.size(); + + for (int i = 0; i < N; i++) { + final Pair<ZipEntry, String> entry = nativeFiles.get(i); + + File destFile = new File(sharedLibraryDir, entry.second); + copyNativeBinaryLI(zipFile, entry.first, sharedLibraryDir, destFile); + } + } catch (ZipException e) { + Slog.w(TAG, "Failed to extract data from package file", e); + return PackageManager.INSTALL_FAILED_INVALID_APK; + } catch (IOException e) { + Slog.w(TAG, "Failed to cache package shared libs", e); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } + + return PackageManager.INSTALL_SUCCEEDED; + } + + private static void copyNativeBinaryLI(ZipFile zipFile, ZipEntry entry, + File binaryDir, File binaryFile) throws IOException { + InputStream inputStream = zipFile.getInputStream(entry); + try { + File tempFile = File.createTempFile("tmp", "tmp", binaryDir); + String tempFilePath = tempFile.getPath(); + // XXX package manager can't change owner, so the executable files for + // now need to be left as world readable and owned by the system. + if (!FileUtils.copyToFile(inputStream, tempFile) + || !tempFile.setLastModified(entry.getTime()) + || FileUtils.setPermissions(tempFilePath, FileUtils.S_IRUSR | FileUtils.S_IWUSR + | FileUtils.S_IRGRP | FileUtils.S_IXUSR | FileUtils.S_IXGRP + | FileUtils.S_IXOTH | FileUtils.S_IROTH, -1, -1) != 0 + || !tempFile.renameTo(binaryFile)) { + // Failed to properly write file. + tempFile.delete(); + throw new IOException("Couldn't create cached binary " + binaryFile + " in " + + binaryDir); + } + } finally { + inputStream.close(); + } + } +} diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 4d0a9e0..d6c43f9 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -56,22 +56,22 @@ public class PackageHelper { return null; } - public static String createSdDir(File tmpPackageFile, String cid, + public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid) { // Create mount point via MountService IMountService mountService = getMountService(); - long len = tmpPackageFile.length(); - int mbLen = (int) (len >> 20); - if ((len - (mbLen * 1024 * 1024)) > 0) { - mbLen++; + int sizeMb = (int) (sizeBytes >> 20); + if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) { + sizeMb++; } // Add buffer size - mbLen++; - if (localLOGV) Log.i(TAG, "Size of container " + mbLen + " MB " + len + " bytes"); + sizeMb++; + if (localLOGV) + Log.i(TAG, "Size of container " + sizeMb + " MB " + sizeBytes + " bytes"); try { int rc = mountService.createSecureContainer( - cid, mbLen, "fat", sdEncKey, uid); + cid, sizeMb, "fat", sdEncKey, uid); if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to create secure container " + cid); return null; |