Merge "Allow native shared libraries in ASEC containers" into gingerbread

This commit is contained in:
Kenny Root
2010-08-27 16:42:28 -07:00
committed by Android (Google) Code Review
10 changed files with 614 additions and 399 deletions

View File

@ -3181,6 +3181,7 @@ public final class ActivityThread {
instrApp.sourceDir = ii.sourceDir; instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir; instrApp.publicSourceDir = ii.publicSourceDir;
instrApp.dataDir = ii.dataDir; instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, LoadedApk pi = getPackageInfo(instrApp,
appContext.getClassLoader(), false, true); appContext.getClassLoader(), false, true);
ContextImpl instrContext = new ContextImpl(); ContextImpl instrContext = new ContextImpl();

View File

@ -19,6 +19,7 @@ package android.app;
import dalvik.system.PathClassLoader; import dalvik.system.PathClassLoader;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
class ApplicationLoaders class ApplicationLoaders
{ {
@ -27,8 +28,7 @@ class ApplicationLoaders
return gApplicationLoaders; return gApplicationLoaders;
} }
public ClassLoader getClassLoader(String zip, String appDataDir, public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent)
ClassLoader parent)
{ {
/* /*
* This is the parent we use if they pass "null" in. In theory * 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. * new ClassLoader for the zip archive.
*/ */
if (parent == baseParent) { if (parent == baseParent) {
ClassLoader loader = (ClassLoader)mLoaders.get(zip); ClassLoader loader = mLoaders.get(zip);
if (loader != null) { if (loader != null) {
return loader; return loader;
} }
PathClassLoader pathClassloader = PathClassLoader pathClassloader =
new PathClassLoader(zip, appDataDir + "/lib", parent); new PathClassLoader(zip, libPath, parent);
mLoaders.put(zip, pathClassloader); mLoaders.put(zip, pathClassloader);
return 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 private static final ApplicationLoaders gApplicationLoaders
= new ApplicationLoaders(); = new ApplicationLoaders();

View File

@ -72,6 +72,7 @@ final class LoadedApk {
private final String mResDir; private final String mResDir;
private final String[] mSharedLibraries; private final String[] mSharedLibraries;
private final String mDataDir; private final String mDataDir;
private final String mLibDir;
private final File mDataDirFile; private final File mDataDirFile;
private final ClassLoader mBaseClassLoader; private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation; private final boolean mSecurityViolation;
@ -108,6 +109,7 @@ final class LoadedApk {
mSharedLibraries = aInfo.sharedLibraryFiles; mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir; mDataDir = aInfo.dataDir;
mDataDirFile = mDataDir != null ? new File(mDataDir) : null; mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
mLibDir = aInfo.nativeLibraryDir;
mBaseClassLoader = baseLoader; mBaseClassLoader = baseLoader;
mSecurityViolation = securityViolation; mSecurityViolation = securityViolation;
mIncludeCode = includeCode; mIncludeCode = includeCode;
@ -140,6 +142,7 @@ final class LoadedApk {
mSharedLibraries = null; mSharedLibraries = null;
mDataDir = null; mDataDir = null;
mDataDirFile = null; mDataDirFile = null;
mLibDir = null;
mBaseClassLoader = null; mBaseClassLoader = null;
mSecurityViolation = false; mSecurityViolation = false;
mIncludeCode = true; mIncludeCode = true;
@ -279,11 +282,12 @@ final class LoadedApk {
* create the class loader. * 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 = mClassLoader =
ApplicationLoaders.getDefault().getClassLoader( ApplicationLoaders.getDefault().getClassLoader(
zip, mDataDir, mBaseClassLoader); zip, mLibDir, mBaseClassLoader);
initializeJavaContextClassLoader(); initializeJavaContextClassLoader();
} else { } else {
if (mBaseClassLoader == null) { if (mBaseClassLoader == null) {

View File

@ -290,14 +290,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/ */
public static final int FLAG_FORWARD_LOCK = 1<<29; 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 * 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 * has reported that it is heavy-weight, and thus can not participate in
@ -360,6 +352,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/ */
public String dataDir; 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; * The kernel user-ID that has been assigned to this application;
* currently this is not a unique ID (multiple applications can have * 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; flags = orig.flags;
sourceDir = orig.sourceDir; sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir; publicSourceDir = orig.publicSourceDir;
nativeLibraryDir = orig.nativeLibraryDir;
resourceDirs = orig.resourceDirs; resourceDirs = orig.resourceDirs;
sharedLibraryFiles = orig.sharedLibraryFiles; sharedLibraryFiles = orig.sharedLibraryFiles;
dataDir = orig.dataDir; dataDir = orig.dataDir;
@ -483,6 +483,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(flags); dest.writeInt(flags);
dest.writeString(sourceDir); dest.writeString(sourceDir);
dest.writeString(publicSourceDir); dest.writeString(publicSourceDir);
dest.writeString(nativeLibraryDir);
dest.writeStringArray(resourceDirs); dest.writeStringArray(resourceDirs);
dest.writeStringArray(sharedLibraryFiles); dest.writeStringArray(sharedLibraryFiles);
dest.writeString(dataDir); dest.writeString(dataDir);
@ -514,6 +515,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
flags = source.readInt(); flags = source.readInt();
sourceDir = source.readString(); sourceDir = source.readString();
publicSourceDir = source.readString(); publicSourceDir = source.readString();
nativeLibraryDir = source.readString();
resourceDirs = source.readStringArray(); resourceDirs = source.readStringArray();
sharedLibraryFiles = source.readStringArray(); sharedLibraryFiles = source.readStringArray();
dataDir = source.readString(); dataDir = source.readString();

View File

@ -51,6 +51,13 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
*/ */
public String dataDir; 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. * Specifies whether or not this instrumentation will handle profiling.
*/ */
@ -68,6 +75,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
sourceDir = orig.sourceDir; sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir; publicSourceDir = orig.publicSourceDir;
dataDir = orig.dataDir; dataDir = orig.dataDir;
nativeLibraryDir = orig.nativeLibraryDir;
handleProfiling = orig.handleProfiling; handleProfiling = orig.handleProfiling;
functionalTest = orig.functionalTest; functionalTest = orig.functionalTest;
} }
@ -88,6 +96,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
dest.writeString(sourceDir); dest.writeString(sourceDir);
dest.writeString(publicSourceDir); dest.writeString(publicSourceDir);
dest.writeString(dataDir); dest.writeString(dataDir);
dest.writeString(nativeLibraryDir);
dest.writeInt((handleProfiling == false) ? 0 : 1); dest.writeInt((handleProfiling == false) ? 0 : 1);
dest.writeInt((functionalTest == false) ? 0 : 1); dest.writeInt((functionalTest == false) ? 0 : 1);
} }
@ -108,6 +117,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
sourceDir = source.readString(); sourceDir = source.readString();
publicSourceDir = source.readString(); publicSourceDir = source.readString();
dataDir = source.readString(); dataDir = source.readString();
nativeLibraryDir = source.readString();
handleProfiling = source.readInt() != 0; handleProfiling = source.readInt() != 0;
functionalTest = source.readInt() != 0; functionalTest = source.readInt() != 0;
} }

View File

@ -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();
}
}
}

View File

@ -56,22 +56,22 @@ public class PackageHelper {
return null; return null;
} }
public static String createSdDir(File tmpPackageFile, String cid, public static String createSdDir(long sizeBytes, String cid,
String sdEncKey, int uid) { String sdEncKey, int uid) {
// Create mount point via MountService // Create mount point via MountService
IMountService mountService = getMountService(); IMountService mountService = getMountService();
long len = tmpPackageFile.length(); int sizeMb = (int) (sizeBytes >> 20);
int mbLen = (int) (len >> 20); if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
if ((len - (mbLen * 1024 * 1024)) > 0) { sizeMb++;
mbLen++;
} }
// Add buffer size // Add buffer size
mbLen++; sizeMb++;
if (localLOGV) Log.i(TAG, "Size of container " + mbLen + " MB " + len + " bytes"); if (localLOGV)
Log.i(TAG, "Size of container " + sizeMb + " MB " + sizeBytes + " bytes");
try { try {
int rc = mountService.createSecureContainer( int rc = mountService.createSecureContainer(
cid, mbLen, "fat", sdEncKey, uid); cid, sizeMb, "fat", sdEncKey, uid);
if (rc != StorageResultCode.OperationSucceeded) { if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, "Failed to create secure container " + cid); Log.e(TAG, "Failed to create secure container " + cid);
return null; return null;

View File

@ -358,6 +358,7 @@ public class PackageManagerTests extends AndroidTestCase {
assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
assertEquals(srcPath, drmInstallPath); assertEquals(srcPath, drmInstallPath);
assertEquals(publicSrcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath);
assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath()));
} else { } else {
assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen); int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
@ -365,10 +366,12 @@ public class PackageManagerTests extends AndroidTestCase {
assertEquals(srcPath, appInstallPath); assertEquals(srcPath, appInstallPath);
assertEquals(publicSrcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath);
assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath()));
} else if (rLoc == INSTALL_LOC_SD){ } else if (rLoc == INSTALL_LOC_SD){
assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
} else { } else {
// TODO handle error. Install should have failed. // TODO handle error. Install should have failed.
} }

View File

@ -17,7 +17,9 @@
package com.android.defcontainer; package com.android.defcontainer;
import com.android.internal.app.IMediaContainerService; import com.android.internal.app.IMediaContainerService;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper; import com.android.internal.content.PackageHelper;
import android.content.Intent; import android.content.Intent;
import android.content.pm.IPackageManager; import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
@ -37,6 +39,7 @@ import android.os.StatFs;
import android.app.IntentService; import android.app.IntentService;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -44,6 +47,11 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import android.os.FileUtils; import android.os.FileUtils;
import android.provider.Settings; import android.provider.Settings;
@ -59,6 +67,8 @@ public class DefaultContainerService extends IntentService {
private static final String TAG = "DefContainer"; private static final String TAG = "DefContainer";
private static final boolean localLOGV = true; private static final boolean localLOGV = true;
private static final String LIB_DIR_NAME = "lib";
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/* /*
* Creates a new container and copies resource there. * Creates a new container and copies resource there.
@ -194,18 +204,51 @@ public class DefaultContainerService extends IntentService {
Log.w(TAG, "Make sure sdcard is mounted."); Log.w(TAG, "Make sure sdcard is mounted.");
return null; return null;
} }
// Create new container at newCachePath
// The .apk file
String codePath = packageURI.getPath(); String codePath = packageURI.getPath();
File codeFile = new File(codePath); File codeFile = new File(codePath);
String newCachePath = null;
// Calculate size of container needed to hold base APK.
long sizeBytes = codeFile.length();
// Check all the native files that need to be copied and add that to the container size.
ZipFile zipFile;
List<Pair<ZipEntry, String>> nativeFiles;
try {
zipFile = new ZipFile(codeFile);
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);
/*
* Note that PackageHelper.createSdDir adds a 1MB padding on
* our claimed size, so we don't have to worry about block
* alignment here.
*/
sizeBytes += entry.first.getSize();
}
} catch (ZipException e) {
Log.w(TAG, "Failed to extract data from package file", e);
return null;
} catch (IOException e) {
Log.w(TAG, "Failed to cache package shared libs", e);
return null;
}
// Create new container // Create new container
if ((newCachePath = PackageHelper.createSdDir(codeFile, String newCachePath = null;
newCid, key, Process.myUid())) == null) { if ((newCachePath = PackageHelper.createSdDir(sizeBytes, newCid, key, Process.myUid())) == null) {
Log.e(TAG, "Failed to create container " + newCid); Log.e(TAG, "Failed to create container " + newCid);
return null; return null;
} }
if (localLOGV) Log.i(TAG, "Created container for " + newCid if (localLOGV)
+ " at path : " + newCachePath); Log.i(TAG, "Created container for " + newCid + " at path : " + newCachePath);
File resFile = new File(newCachePath, resFileName); File resFile = new File(newCachePath, resFileName);
if (!FileUtils.copyFile(new File(codePath), resFile)) { if (!FileUtils.copyFile(new File(codePath), resFile)) {
Log.e(TAG, "Failed to copy " + codePath + " to " + resFile); Log.e(TAG, "Failed to copy " + codePath + " to " + resFile);
@ -213,6 +256,32 @@ public class DefaultContainerService extends IntentService {
PackageHelper.destroySdDir(newCid); PackageHelper.destroySdDir(newCid);
return null; return null;
} }
try {
File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
sharedLibraryDir.mkdir();
final int N = nativeFiles.size();
for (int i = 0; i < N; i++) {
final Pair<ZipEntry, String> entry = nativeFiles.get(i);
InputStream is = zipFile.getInputStream(entry.first);
try {
File destFile = new File(sharedLibraryDir, entry.second);
if (!FileUtils.copyToFile(is, destFile)) {
throw new IOException("Couldn't copy native binary "
+ entry.first.getName() + " to " + entry.second);
}
} finally {
is.close();
}
}
} catch (IOException e) {
Log.e(TAG, "Couldn't copy native file to container", e);
PackageHelper.destroySdDir(newCid);
return null;
}
if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile); if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
if (!PackageHelper.finalizeSdDir(newCid)) { if (!PackageHelper.finalizeSdDir(newCid)) {
Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);

File diff suppressed because it is too large Load Diff