Merge "Installing splits into ASECs!" into lmp-dev

This commit is contained in:
Jeff Sharkey
2014-08-24 18:45:53 +00:00
committed by Android (Google) Code Review
15 changed files with 722 additions and 380 deletions

View File

@ -8675,10 +8675,12 @@ package android.content.pm {
method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
method public void uninstall(java.lang.String, android.content.IntentSender);
field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
field public static final java.lang.String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
field public static final int STATUS_FAILURE = 1; // 0x1
field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2

View File

@ -1004,6 +1004,10 @@ public final class Pm {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("-p")) {
params.mode = SessionParams.MODE_INHERIT_EXISTING;
params.appPackageName = nextOptionData();
if (params.appPackageName == null) {
throw new IllegalArgumentException("Missing inherit package name");
}
} else if (opt.equals("-S")) {
params.setSize(Long.parseLong(nextOptionData()));
} else if (opt.equals("--abi")) {

View File

@ -45,6 +45,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -86,6 +87,8 @@ public class PackageInstaller {
* <p>
* In some cases, a matching Activity may not exist, so ensure you safeguard
* against this.
* <p>
* The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
@ -95,22 +98,58 @@ public class PackageInstaller {
ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
/**
* An integer session ID.
* An integer session ID that an operation is working with.
*
* @see #ACTION_SESSION_DETAILS
* @see Intent#getIntExtra(String, int)
*/
public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
/**
* Package name relevant to a status.
* Package name that an operation is working with.
*
* @see Intent#getStringExtra(String)
*/
public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
/**
* Current status of an operation. Will be one of
* {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
* {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
* {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
* {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
* {@link #STATUS_FAILURE_STORAGE}.
* <p>
* More information about a status may be available through additional
* extras; see the individual status documentation for details.
*
* @see Intent#getIntExtra(String, int)
*/
public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
/**
* Detailed string representation of the status, including raw details that
* are useful for debugging.
*
* @see Intent#getStringExtra(String)
*/
public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
/**
* Another package name relevant to a status. This is typically the package
* responsible for causing an operation failure.
*
* @see Intent#getStringExtra(String)
*/
public static final String
EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
/**
* Storage path relevant to a status.
*
* @see Intent#getStringExtra(String)
*/
public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
/** {@hide} */
@Deprecated
public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
@ -153,8 +192,12 @@ public class PackageInstaller {
* The operation failed because it was blocked. For example, a device policy
* may be blocking the operation, a package verifier may have blocked the
* operation, or the app may be required for core system operation.
* <p>
* The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
* specific package blocking the install.
*
* @see #EXTRA_STATUS_MESSAGE
* @see #EXTRA_OTHER_PACKAGE_NAME
*/
public static final int STATUS_FAILURE_BLOCKED = 2;
@ -182,10 +225,11 @@ public class PackageInstaller {
* permission, incompatible certificates, etc. The user may be able to
* uninstall another app to fix the issue.
* <p>
* The result may also contain {@link #EXTRA_PACKAGE_NAME} with the
* The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
* specific package identified as the cause of the conflict.
*
* @see #EXTRA_STATUS_MESSAGE
* @see #EXTRA_OTHER_PACKAGE_NAME
*/
public static final int STATUS_FAILURE_CONFLICT = 5;
@ -193,8 +237,12 @@ public class PackageInstaller {
* The operation failed because of storage issues. For example, the device
* may be running low on space, or external media may be unavailable. The
* user may be able to help free space or insert different external media.
* <p>
* The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
* the storage device that caused the failure.
*
* @see #EXTRA_STATUS_MESSAGE
* @see #EXTRA_STORAGE_PATH
*/
public static final int STATUS_FAILURE_STORAGE = 6;
@ -281,6 +329,13 @@ public class PackageInstaller {
* To succeed, the caller must be the current home app.
*/
public @NonNull List<SessionInfo> getAllSessions() {
final ApplicationInfo info = mContext.getApplicationInfo();
if ("com.google.android.googlequicksearchbox".equals(info.packageName)
&& info.versionCode <= 300400070) {
Log.d(TAG, "Ignoring callback request from old prebuilt");
return Collections.EMPTY_LIST;
}
try {
return mInstaller.getAllSessions(mUserId);
} catch (RemoteException e) {

View File

@ -264,7 +264,7 @@ public class PackageParser {
public final boolean coreApp;
public final boolean multiArch;
private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;

View File

@ -321,7 +321,7 @@ public interface IMountService extends IInterface {
* Mount a secure container with the specified key and owner UID.
* Returns an int consistent with MountServiceResultCode
*/
public int mountSecureContainer(String id, String key, int ownerUid)
public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@ -331,6 +331,7 @@ public interface IMountService extends IInterface {
_data.writeString(id);
_data.writeString(key);
_data.writeInt(ownerUid);
_data.writeInt(readOnly ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@ -834,6 +835,27 @@ public interface IMountService extends IInterface {
}
return _result;
}
@Override
public int resizeSecureContainer(String id, int sizeMb, String key)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(id);
_data.writeInt(sizeMb);
_data.writeString(key);
mRemote.transact(Stub.TRANSACTION_resizeSecureContainer, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
private static final String DESCRIPTOR = "IMountService";
@ -918,6 +940,8 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_getField = IBinder.FIRST_CALL_TRANSACTION + 39;
static final int TRANSACTION_resizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 40;
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@ -1082,7 +1106,9 @@ public interface IMountService extends IInterface {
key = data.readString();
int ownerUid;
ownerUid = data.readInt();
int resultCode = mountSecureContainer(id, key, ownerUid);
boolean readOnly;
readOnly = data.readInt() != 0;
int resultCode = mountSecureContainer(id, key, ownerUid, readOnly);
reply.writeNoException();
reply.writeInt(resultCode);
return true;
@ -1308,6 +1334,19 @@ public interface IMountService extends IInterface {
reply.writeString(contents);
return true;
}
case TRANSACTION_resizeSecureContainer: {
data.enforceInterface(DESCRIPTOR);
String id;
id = data.readString();
int sizeMb;
sizeMb = data.readInt();
String key;
key = data.readString();
int resultCode = resizeSecureContainer(id, sizeMb, key);
reply.writeNoException();
reply.writeInt(resultCode);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@ -1405,7 +1444,8 @@ public interface IMountService extends IInterface {
* Mount a secure container with the specified key and owner UID. Returns an
* int consistent with MountServiceResultCode
*/
public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
throws RemoteException;
/**
* Mount external storage at given mount point. Returns an int consistent
@ -1571,4 +1611,6 @@ public interface IMountService extends IInterface {
* @return contents of field
*/
public String getField(String field) throws RemoteException;
public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException;
}

View File

@ -25,6 +25,7 @@ import static android.system.OsConstants.S_IRWXU;
import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IXOTH;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
@ -51,9 +52,11 @@ import java.util.List;
*/
public class NativeLibraryHelper {
private static final String TAG = "NativeHelper";
private static final boolean DEBUG_NATIVE = false;
public static final String LIB_DIR_NAME = "lib";
public static final String LIB64_DIR_NAME = "lib64";
// Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
// that the cpuAbiOverride must be clear.
public static final String CLEAR_ABI_OVERRIDE = "-";
@ -70,6 +73,7 @@ public class NativeLibraryHelper {
private volatile boolean mClosed;
final long[] apkHandles;
final boolean multiArch;
public static Handle create(File packageFile) throws IOException {
try {
@ -81,14 +85,15 @@ public class NativeLibraryHelper {
}
public static Handle create(Package pkg) throws IOException {
return create(pkg.getAllCodePaths());
return create(pkg.getAllCodePaths(),
(pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
return create(lite.getAllCodePaths());
return create(lite.getAllCodePaths(), lite.multiArch);
}
private static Handle create(List<String> codePaths) throws IOException {
private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
final int size = codePaths.size();
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
@ -103,11 +108,12 @@ public class NativeLibraryHelper {
}
}
return new Handle(apkHandles);
return new Handle(apkHandles, multiArch);
}
Handle(long[] apkHandles) {
Handle(long[] apkHandles, boolean multiArch) {
this.apkHandles = apkHandles;
this.multiArch = multiArch;
mGuard.open("close");
}
@ -159,8 +165,7 @@ public class NativeLibraryHelper {
* @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
* error code from that class if not
*/
public static int copyNativeBinariesIfNeededLI(Handle handle, File sharedLibraryDir,
String abi) {
public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
if (res != INSTALL_SUCCEEDED) {
@ -267,7 +272,7 @@ public class NativeLibraryHelper {
}
}
private static long sumNativeBinaries(Handle handle, String[] abiList) {
private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
int abi = findSupportedAbi(handle, abiList);
if (abi >= 0) {
return sumNativeBinaries(handle, abiList[abi]);
@ -276,7 +281,7 @@ public class NativeLibraryHelper {
}
}
public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
String[] abiList, boolean useIsaSubdir) throws IOException {
createNativeLibrarySubdir(libraryRoot);
@ -300,7 +305,7 @@ public class NativeLibraryHelper {
subDir = libraryRoot;
}
int copyRet = copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]);
int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
return copyRet;
}
@ -309,10 +314,10 @@ public class NativeLibraryHelper {
return abi;
}
public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
String abiOverride, boolean multiArch) {
public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
String abiOverride) {
try {
if (multiArch) {
if (handle.multiArch) {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
@ -322,7 +327,7 @@ public class NativeLibraryHelper {
int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@ -332,7 +337,7 @@ public class NativeLibraryHelper {
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@ -355,7 +360,7 @@ public class NativeLibraryHelper {
abiList = Build.SUPPORTED_32_BIT_ABIS;
}
int copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, abiList,
int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
@ -370,10 +375,10 @@ public class NativeLibraryHelper {
}
}
public static long sumNativeBinaries(Handle handle, String abiOverride, boolean multiArch)
public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
throws IOException {
long sum = 0;
if (multiArch) {
if (handle.multiArch) {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
@ -382,11 +387,11 @@ public class NativeLibraryHelper {
}
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
sum += sumNativeBinaries(handle, Build.SUPPORTED_32_BIT_ABIS);
sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
sum += sumNativeBinaries(handle, Build.SUPPORTED_64_BIT_ABIS);
sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
}
} else {
String cpuAbiOverride = null;
@ -403,7 +408,7 @@ public class NativeLibraryHelper {
abiList = Build.SUPPORTED_32_BIT_ABIS;
}
sum += sumNativeBinaries(handle, abiList);
sum += sumNativeBinariesForSupportedAbi(handle, abiList);
}
return sum;
}

View File

@ -16,11 +16,14 @@
package com.android.internal.content;
import static android.net.TrafficStats.MB_IN_BYTES;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
@ -77,9 +80,10 @@ public class PackageHelper {
}
}
public static String createSdDir(int sizeMb, String cid, String sdEncKey, int uid,
public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid,
boolean isExternal) {
// Create mount point via MountService
// Round up to nearest MB, plus another MB for filesystem overhead
final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
try {
IMountService mountService = getMountService();
@ -102,19 +106,39 @@ public class PackageHelper {
return null;
}
public static String mountSdDir(String cid, String key, int ownerUid) {
try {
int rc = getMountService().mountSecureContainer(cid, key, ownerUid);
if (rc != StorageResultCode.OperationSucceeded) {
Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
return null;
public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) {
// Round up to nearest MB, plus another MB for filesystem overhead
final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
try {
IMountService mountService = getMountService();
int rc = mountService.resizeSecureContainer(cid, sizeMb, sdEncKey);
if (rc == StorageResultCode.OperationSucceeded) {
return true;
}
} catch (RemoteException e) {
Log.e(TAG, "MountService running?");
}
return getMountService().getSecureContainerPath(cid);
} catch (RemoteException e) {
Log.e(TAG, "MountService running?");
Log.e(TAG, "Failed to create secure container " + cid);
return false;
}
public static String mountSdDir(String cid, String key, int ownerUid) {
return mountSdDir(cid, key, ownerUid, true);
}
public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) {
try {
int rc = getMountService().mountSecureContainer(cid, key, ownerUid, readOnly);
if (rc != StorageResultCode.OperationSucceeded) {
Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
return null;
}
return getMountService().getSecureContainerPath(cid);
} catch (RemoteException e) {
Log.e(TAG, "MountService running?");
}
return null;
}
return null;
}
public static boolean unMountSdDir(String cid) {
try {
@ -400,6 +424,37 @@ public class PackageHelper {
}
}
public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
String abiOverride) throws IOException {
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(pkg);
return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride);
} finally {
IoUtils.closeQuietly(handle);
}
}
public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
boolean isForwardLocked, String abiOverride) throws IOException {
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
if (isForwardLocked) {
sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
}
}
// Include all relevant native code
sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
return sizeBytes;
}
public static String replaceEnd(String str, String before, String after) {
if (!str.endsWith(before)) {
throw new IllegalArgumentException(

View File

@ -16,7 +16,7 @@
package android.content.pm;
import com.android.internal.content.PackageHelper;
import static android.net.TrafficStats.MB_IN_BYTES;
import android.os.IBinder;
import android.os.RemoteException;
@ -25,6 +25,8 @@ import android.os.storage.IMountService;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.internal.content.PackageHelper;
public class PackageHelperTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
@ -81,8 +83,8 @@ public class PackageHelperTests extends AndroidTestCase {
public void testMountAndPullSdCard() {
try {
fullId = PREFIX;
fullId2 = PackageHelper.createSdDir(1024, fullId, "none", android.os.Process.myUid(),
true);
fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
android.os.Process.myUid(), true);
Log.d(TAG,PackageHelper.getSdDir(fullId));
PackageHelper.unMountSdDir(fullId);

View File

@ -90,7 +90,7 @@ public class AsecTests extends AndroidTestCase {
String fullId = SECURE_CONTAINER_PREFIX + localId;
IMountService ms = getMs();
return ms.mountSecureContainer(fullId, key, android.os.Process.myUid());
return ms.mountSecureContainer(fullId, key, android.os.Process.myUid(), true);
}
private int renameContainer(String localId1, String localId2) throws Exception {

View File

@ -295,6 +295,8 @@ public:
data.writeString16(id);
data.writeString16(key);
data.writeInt32(ownerUid);
// Assume read-only
data.writeInt32(1);
if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
ALOGD("mountSecureContainer couldn't call remote");
return -1;

View File

@ -16,7 +16,7 @@
package com.android.defcontainer;
import static android.net.TrafficStats.MB_IN_BYTES;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import android.app.IntentService;
import android.content.Context;
@ -67,8 +67,6 @@ import java.io.OutputStream;
public class DefaultContainerService extends IntentService {
private static final String TAG = "DefContainer";
private static final String LIB_DIR_NAME = "lib";
// TODO: migrate native code unpacking to always be a derivative work
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
@ -168,7 +166,7 @@ public class DefaultContainerService extends IntentService {
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
sizeBytes = calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
@ -253,7 +251,7 @@ public class DefaultContainerService extends IntentService {
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
return calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
@ -315,13 +313,12 @@ public class DefaultContainerService extends IntentService {
// Calculate container size, rounding up to nearest MB and adding an
// extra MB for filesystem overhead
final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked,
abiOverride);
final int sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1;
final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle,
isForwardLocked, abiOverride);
// Create new container
final String newMountPath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(),
isExternal);
final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key,
Process.myUid(), isExternal);
if (newMountPath == null) {
throw new IOException("Failed to create container " + newCid);
}
@ -339,8 +336,8 @@ public class DefaultContainerService extends IntentService {
// Extract native code
final File libraryRoot = new File(targetDir, LIB_DIR_NAME);
final int res = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
abiOverride, pkg.multiArch);
final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
if (res != PackageManager.INSTALL_SUCCEEDED) {
throw new IOException("Failed to extract native code, res=" + res);
}
@ -415,35 +412,4 @@ public class DefaultContainerService extends IntentService {
Os.chmod(targetFile.getAbsolutePath(), 0644);
}
}
private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked,
String abiOverride) throws IOException {
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(pkg);
return calculateInstalledSizeInner(pkg, handle, isForwardLocked, abiOverride);
} finally {
IoUtils.closeQuietly(handle);
}
}
private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
boolean isForwardLocked, String abiOverride) throws IOException {
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
if (isForwardLocked) {
sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
}
}
// Include all relevant native code
sizeBytes += NativeLibraryHelper.sumNativeBinaries(handle, abiOverride, pkg.multiArch);
return sizeBytes;
}
}

View File

@ -33,7 +33,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.hardware.usb.UsbManager;
import android.app.admin.DevicePolicyManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@ -1761,6 +1760,21 @@ class MountService extends IMountService.Stub
return rc;
}
@Override
public int resizeSecureContainer(String id, int sizeMb, String key) {
validatePermission(android.Manifest.permission.ASEC_CREATE);
waitForReady();
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int finalizeSecureContainer(String id) {
validatePermission(android.Manifest.permission.ASEC_CREATE);
warnOnNotMounted();
@ -1835,7 +1849,7 @@ class MountService extends IMountService.Stub
return rc;
}
public int mountSecureContainer(String id, String key, int ownerUid) {
public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
warnOnNotMounted();
@ -1848,7 +1862,8 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid);
mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
readOnly ? "ro" : "rw");
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {

View File

@ -19,7 +19,6 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_ALL_USERS;
import static android.content.pm.PackageManager.INSTALL_FROM_ADB;
import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
import static android.net.TrafficStats.MB_IN_BYTES;
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@ -48,8 +47,11 @@ import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageParser;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
@ -208,7 +210,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
unclaimed.remove(session.internalStageDir);
unclaimed.remove(session.stageDir);
}
// Clean up orphaned staging directories
@ -234,7 +236,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
final String cid = session.externalStageCid;
final String cid = session.stageCid;
if (unclaimed.remove(cid)) {
// Claimed by active session, mount it
@ -304,10 +306,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Slog.w(TAG, "Abandoning old session first created at "
+ session.createdMillis);
valid = false;
} else if (session.internalStageDir != null
&& !session.internalStageDir.exists()) {
} else if (session.stageDir != null
&& !session.stageDir.exists()) {
Slog.w(TAG, "Abandoning internal session with missing stage "
+ session.internalStageDir);
+ session.stageDir);
valid = false;
} else {
valid = true;
@ -401,12 +403,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
session.installerPackageName);
writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
if (session.internalStageDir != null) {
if (session.stageDir != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
session.internalStageDir.getAbsolutePath());
session.stageDir.getAbsolutePath());
}
if (session.externalStageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid);
if (session.stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
}
writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
@ -479,6 +481,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
// TODO: treat INHERIT_EXISTING as install for user
// Figure out where we're going to be staging session data
final boolean stageInternal;
@ -502,22 +506,36 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Binder.restoreCallingIdentity(ident);
}
} else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
// We always stage inheriting sessions on internal storage first,
// since we don't want to grow containers until we're sure that
// everything looks legit.
stageInternal = true;
checkInternalStorage(params.sizeBytes);
// If we have a good hunch we'll end up on external storage, verify
// free space there too.
final ApplicationInfo info = mPm.getApplicationInfo(params.appPackageName, 0,
// Inheriting existing install, so stay on the same storage medium.
final ApplicationInfo existingApp = mPm.getApplicationInfo(params.appPackageName, 0,
userId);
if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
checkExternalStorage(params.sizeBytes);
throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
if (existingApp == null) {
throw new IllegalStateException(
"Missing existing app " + params.appPackageName);
}
final long existingSize;
try {
final PackageLite existingPkg = PackageParser.parsePackageLite(
new File(existingApp.getCodePath()), 0);
existingSize = PackageHelper.calculateInstalledSize(existingPkg, false,
params.abiOverride);
} catch (PackageParserException e) {
throw new IllegalStateException(
"Failed to calculate size of " + params.appPackageName);
}
if ((existingApp.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
// Internal we can link existing install into place, so we only
// need enough space for the new data.
checkInternalStorage(params.sizeBytes);
stageInternal = true;
} else {
// External we're going to copy existing install into our
// container, so we need footprint of both.
checkExternalStorage(params.sizeBytes + existingSize);
stageInternal = false;
}
} else {
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
@ -641,11 +659,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
final String cid = "smdl" + sessionId + ".tmp";
// Round up to nearest MB, plus another MB for filesystem overhead
final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
if (PackageHelper.createSdDir(sizeMb, cid, PackageManagerService.getEncryptKey(),
if (PackageHelper.createSdDir(sizeBytes, cid, PackageManagerService.getEncryptKey(),
Process.SYSTEM_UID, true) == null) {
throw new IOException("Failed to create ASEC");
}
@ -857,7 +871,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
final String existing = extras.getString(
PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, existing);
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
}
try {

View File

@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
@ -38,6 +39,7 @@ import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.Signature;
import android.os.Bundle;
@ -47,6 +49,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.system.ErrnoException;
@ -59,18 +62,21 @@ import android.util.MathUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
import libcore.io.IoUtils;
import libcore.io.Libcore;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@ -95,18 +101,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final SessionParams params;
final long createdMillis;
/** Internal location where staged data is written. */
final File internalStageDir;
/** External container where staged data is written. */
final String externalStageCid;
/**
* When a {@link SessionParams#MODE_INHERIT_EXISTING} session is installed
* into an ASEC, this is the container where the stage is combined with the
* existing install.
*/
// TODO: persist this cid once we start splicing
String combinedCid;
/** Staging location where client data is written. */
final File stageDir;
final String stageCid;
/** Note that UID is not persisted; it's always derived at runtime. */
final int installerUid;
@ -132,20 +129,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private int mFinalStatus;
private String mFinalMessage;
@GuardedBy("mLock")
private File mResolvedStageDir;
/**
* Path to the resolved base APK for this session, which may point at an APK
* inside the session (when the session defines the base), or it may point
* at the existing base APK (when adding splits to an existing app).
* <p>
* This is used when confirming permissions, since we can't fully stage the
* session inside an ASEC before confirming with user.
*/
@GuardedBy("mLock")
private String mResolvedBaseCodePath;
@GuardedBy("mLock")
private ArrayList<FileBridge> mBridges = new ArrayList<>();
@ -157,6 +140,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private int mVersionCode;
private Signature[] mSignatures;
/**
* Path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
* point at the existing base APK (when adding splits to an existing app).
* <p>
* This is used when confirming permissions, since we can't fully stage the
* session inside an ASEC before confirming with user.
*/
@GuardedBy("mLock")
private File mResolvedBaseFile;
@GuardedBy("mLock")
private File mResolvedStageDir;
@GuardedBy("mLock")
private final List<File> mResolvedStagedFiles = new ArrayList<>();
@GuardedBy("mLock")
private final List<File> mResolvedInheritedFiles = new ArrayList<>();
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@ -168,9 +170,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
commitLocked();
} catch (PackageManagerException e) {
Slog.e(TAG, "Install failed: " + e);
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, e.getMessage(), null);
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
@ -181,7 +184,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
String installerPackageName, SessionParams params, long createdMillis,
File internalStageDir, String externalStageCid, boolean sealed) {
File stageDir, String stageCid, boolean sealed) {
mCallback = callback;
mContext = context;
mPm = pm;
@ -192,12 +195,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.installerPackageName = installerPackageName;
this.params = params;
this.createdMillis = createdMillis;
this.internalStageDir = internalStageDir;
this.externalStageCid = externalStageCid;
this.stageDir = stageDir;
this.stageCid = stageCid;
if ((internalStageDir == null) == (externalStageCid == null)) {
if ((stageDir == null) == (stageCid == null)) {
throw new IllegalArgumentException(
"Exactly one of internal or external stage must be set");
"Exactly one of stageDir or stageCid stage must be set");
}
mSealed = sealed;
@ -220,7 +223,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
info.sessionId = sessionId;
info.installerPackageName = installerPackageName;
info.resolvedBaseCodePath = mResolvedBaseCodePath;
info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
mResolvedBaseFile.getAbsolutePath() : null;
info.progress = mProgress;
info.sealed = mSealed;
info.open = mOpenCount.get() > 0;
@ -253,18 +257,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* might point at an ASEC mount point, which is why we delay path resolution
* until someone actively works with the session.
*/
private File getStageDir() throws IOException {
private File resolveStageDir() throws IOException {
synchronized (mLock) {
if (mResolvedStageDir == null) {
if (internalStageDir != null) {
mResolvedStageDir = internalStageDir;
if (stageDir != null) {
mResolvedStageDir = stageDir;
} else {
final String path = PackageHelper.getSdDir(externalStageCid);
final String path = PackageHelper.getSdDir(stageCid);
if (path != null) {
mResolvedStageDir = new File(path);
} else {
throw new IOException(
"Failed to resolve container path for " + externalStageCid);
throw new IOException("Failed to resolve path to container " + stageCid);
}
}
}
@ -306,7 +309,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public String[] getNames() {
assertNotSealed("getNames");
try {
return getStageDir().list();
return resolveStageDir().list();
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
@ -339,8 +342,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
final File target = new File(getStageDir(), name);
final File target = new File(resolveStageDir(), name);
// TODO: this should delegate to DCS so the system process avoids
// holding open FDs into containers.
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
Os.chmod(target.getAbsolutePath(), 0644);
@ -350,7 +355,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (lengthBytes > 0) {
final StructStat stat = Libcore.os.fstat(targetFd);
final long deltaBytes = lengthBytes - stat.st_size;
if (deltaBytes > 0) {
// Only need to free up space when writing to internal stage
if (stageDir != null && deltaBytes > 0) {
mPm.freeStorage(deltaBytes);
}
Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
@ -385,7 +391,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
final File target = new File(getStageDir(), name);
final File target = new File(resolveStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
@ -424,22 +430,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mCallback.onSessionSealed(this);
}
final File stageDir;
try {
stageDir = getStageDir();
resolveStageDir();
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage dir", e);
"Failed to resolve stage location", e);
}
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
validateInstallLocked(stageDir);
validateInstallLocked();
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
Preconditions.checkNotNull(mResolvedBaseCodePath);
Preconditions.checkNotNull(mResolvedBaseFile);
if (!mPermissionsAccepted) {
// User needs to accept permissions; give installer an intent they
@ -454,17 +459,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
if (stageCid != null) {
// Figure out the final installed size and resize the container once
// and for all. Internally the parser handles straddling between two
// locations when inheriting.
final long finalSize = calculateInstalledSize();
resizeContainer(stageCid, finalSize);
}
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
// TODO: implement splicing into existing ASEC
spliceExistingFilesIntoStage(stageDir);
try {
if (stageCid != null) {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
copyFiles(mResolvedInheritedFiles, resolveStageDir());
} else {
linkFiles(mResolvedInheritedFiles, resolveStageDir());
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to inherit existing install", e);
}
}
// TODO: surface more granular state from dexopt
mCallback.onSessionProgressChanged(this, 0.9f);
// TODO: for ASEC based applications, grow and stream in packages
// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
// Container is ready to go, let's seal it up!
if (stageCid != null) {
finalizeAndFixContainer(stageCid);
}
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
@ -482,7 +511,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver,
mPm.installStage(mPackageName, stageDir, stageCid, localObserver,
params, installerPackageName, installerUid, new UserHandle(userId));
}
@ -490,81 +519,88 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
* <p>
* Clears and populates {@link #mResolvedBaseFile},
* {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
* <p>
* Renames package files in stage to match split names defined inside.
* <p>
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
private void validateInstallLocked(File stageDir) throws PackageManagerException {
private void validateInstallLocked() throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
mResolvedBaseCodePath = null;
final File[] files = stageDir.listFiles();
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
mResolvedInheritedFiles.clear();
final File[] files = mResolvedStageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
// Verify that all staged packages are internally consistent
final ArraySet<String> seenSplits = new ArraySet<>();
final ArraySet<String> stagedSplits = new ArraySet<>();
for (File file : files) {
// Installers can't stage directories, so it's fine to ignore
// entries like "lost+found".
if (file.isDirectory()) continue;
final ApkLite info;
final ApkLite apk;
try {
info = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to parse " + file + ": " + e);
}
if (!seenSplits.add(info.splitName)) {
if (!stagedSplits.add(apk.splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + info.splitName + " was defined multiple times");
"Split " + apk.splitName + " was defined multiple times");
}
// Use first package to define unknown values
if (mPackageName == null) {
mPackageName = info.packageName;
mVersionCode = info.versionCode;
mPackageName = apk.packageName;
mVersionCode = apk.versionCode;
}
if (mSignatures == null) {
mSignatures = info.signatures;
mSignatures = apk.signatures;
}
assertPackageConsistent(String.valueOf(file), info.packageName, info.versionCode,
info.signatures);
assertApkConsistent(String.valueOf(file), apk);
// Take this opportunity to enforce uniform naming
final String targetName;
if (info.splitName == null) {
if (apk.splitName == null) {
targetName = "base.apk";
} else {
targetName = "split_" + info.splitName + ".apk";
targetName = "split_" + apk.splitName + ".apk";
}
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + targetName);
}
final File targetFile = new File(stageDir, targetName);
final File targetFile = new File(mResolvedStageDir, targetName);
if (!file.equals(targetFile)) {
file.renameTo(targetFile);
}
// Base is coming from session
if (info.splitName == null) {
mResolvedBaseCodePath = targetFile.getAbsolutePath();
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
}
mResolvedStagedFiles.add(targetFile);
}
if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!seenSplits.contains(null)) {
if (!stagedSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
}
@ -577,67 +613,204 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Missing existing base package for " + mPackageName);
}
// Base might be inherited from existing install
if (mResolvedBaseCodePath == null) {
mResolvedBaseCodePath = app.getBaseCodePath();
}
final ApkLite info;
final PackageLite existing;
final ApkLite existingBase;
try {
info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to parse existing base " + app.getBaseCodePath() + ": " + e);
"Failed to parse existing package " + app.getCodePath() + ": " + e);
}
assertPackageConsistent("Existing base", info.packageName, info.versionCode,
info.signatures);
assertApkConsistent("Existing base", existingBase);
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(app.getBaseCodePath());
mResolvedInheritedFiles.add(mResolvedBaseFile);
}
// Inherit splits if not overridden
if (!ArrayUtils.isEmpty(existing.splitNames)) {
for (int i = 0; i < existing.splitNames.length; i++) {
final String splitName = existing.splitNames[i];
final File splitFile = new File(existing.splitCodePaths[i]);
if (!stagedSplits.contains(splitName)) {
mResolvedInheritedFiles.add(splitFile);
}
}
}
}
}
private void assertPackageConsistent(String tag, String packageName, int versionCode,
Signature[] signatures) throws PackageManagerException {
if (!mPackageName.equals(packageName)) {
private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ packageName + " inconsistent with " + mPackageName);
+ apk.packageName + " inconsistent with " + mPackageName);
}
if (mVersionCode != versionCode) {
if (mVersionCode != apk.versionCode) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
+ " version code " + versionCode + " inconsistent with "
+ " version code " + apk.versionCode + " inconsistent with "
+ mVersionCode);
}
if (!Signature.areExactMatch(mSignatures, signatures)) {
if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
tag + " signatures are inconsistent");
}
}
/**
* Application is already installed; splice existing files that haven't been
* overridden into our stage.
* Calculate the final install footprint size, combining both staged and
* existing APKs together and including unpacked native code from both.
*/
private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
private long calculateInstalledSize() throws PackageManagerException {
Preconditions.checkNotNull(mResolvedBaseFile);
int n = 0;
final File[] oldFiles = new File(app.getCodePath()).listFiles();
if (!ArrayUtils.isEmpty(oldFiles)) {
for (File oldFile : oldFiles) {
if (!PackageParser.isApkFile(oldFile)) continue;
final ApkLite baseApk;
try {
baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to parse base package " + mResolvedBaseFile + ": " + e);
}
final File newFile = new File(stageDir, oldFile.getName());
try {
Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
n++;
} catch (ErrnoException e) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to splice into stage", e);
}
final List<String> splitPaths = new ArrayList<>();
for (File file : mResolvedStagedFiles) {
if (mResolvedBaseFile.equals(file)) continue;
splitPaths.add(file.getAbsolutePath());
}
for (File file : mResolvedInheritedFiles) {
if (mResolvedBaseFile.equals(file)) continue;
splitPaths.add(file.getAbsolutePath());
}
// This is kind of hacky; we're creating a half-parsed package that is
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null,
splitPaths.toArray(new String[splitPaths.size()]));
final boolean isForwardLocked =
(params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
try {
return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to calculate install size", e);
}
}
private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
for (File fromFile : fromFiles) {
final File toFile = new File(toDir, fromFile.getName());
try {
if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
} catch (ErrnoException e) {
throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
}
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
}
private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
// Remove any partial files from previous attempt
for (File file : toDir.listFiles()) {
if (file.getName().endsWith(".tmp")) {
file.delete();
}
}
if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
for (File fromFile : fromFiles) {
final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
if (!FileUtils.copyFile(fromFile, tmpFile)) {
throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
}
final File toFile = new File(toDir, fromFile.getName());
if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
if (!tmpFile.renameTo(toFile)) {
throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
}
}
Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
}
private static void extractNativeLibraries(File packageDir, String abiOverride)
throws PackageManagerException {
if (LOGD) Slog.v(TAG, "extractNativeLibraries()");
// Always start from a clean slate
final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(packageDir);
final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
abiOverride);
if (res != PackageManager.INSTALL_SUCCEEDED) {
throw new PackageManagerException(res,
"Failed to extract native libraries, res=" + res);
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to extract native libraries", e);
} finally {
IoUtils.closeQuietly(handle);
}
}
private static void resizeContainer(String cid, long targetSize)
throws PackageManagerException {
String path = PackageHelper.getSdDir(cid);
if (path == null) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to find mounted " + cid);
}
final long currentSize = new File(path).getTotalSpace();
if (currentSize > targetSize) {
Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
+ targetSize + "; skipping resize");
return;
}
if (!PackageHelper.unMountSdDir(cid)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to unmount " + cid + " before resize");
}
if (!PackageHelper.resizeSdDir(targetSize, cid,
PackageManagerService.getEncryptKey())) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resize " + cid + " to " + targetSize + " bytes");
}
path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
Process.SYSTEM_UID, false);
if (path == null) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to mount " + cid + " after resize");
}
}
private void finalizeAndFixContainer(String cid) throws PackageManagerException {
if (!PackageHelper.finalizeSdDir(cid)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to finalize container " + cid);
}
final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
UserHandle.USER_OWNER);
final int gid = UserHandle.getSharedAppGid(uid);
if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to fix permissions on container " + cid);
}
}
void setPermissionsResult(boolean accepted) {
@ -694,12 +867,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSealed = true;
mDestroyed = true;
}
if (internalStageDir != null) {
FileUtils.deleteContents(internalStageDir);
internalStageDir.delete();
if (stageDir != null) {
FileUtils.deleteContents(stageDir);
stageDir.delete();
}
if (externalStageCid != null) {
PackageHelper.destroySdDir(externalStageCid);
if (stageCid != null) {
PackageHelper.destroySdDir(stageCid);
}
}
@ -717,8 +890,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("installerUid", installerUid);
pw.printPair("createdMillis", createdMillis);
pw.printPair("internalStageDir", internalStageDir);
pw.printPair("externalStageCid", externalStageCid);
pw.printPair("stageDir", stageDir);
pw.printPair("stageCid", stageCid);
pw.println();
params.dump(pw);

View File

@ -50,6 +50,8 @@ import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
@ -298,9 +300,6 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String LIB_DIR_NAME = "lib";
private static final String LIB64_DIR_NAME = "lib64";
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
private static String sPreferredInstructionSet;
@ -1121,7 +1120,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if ((state != null) && !state.timeoutExtended()) {
final InstallArgs args = state.getInstallArgs();
final Uri originUri = Uri.fromFile(args.originFile);
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.i(TAG, "Verification timed out for " + originUri);
mPendingVerification.remove(verificationId);
@ -1168,7 +1167,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mPendingVerification.remove(verificationId);
final InstallArgs args = state.getInstallArgs();
final Uri originUri = Uri.fromFile(args.originFile);
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
int ret;
if (state.isInstallAllowed()) {
@ -4271,7 +4270,7 @@ public class PackageManagerService extends IPackageManager.Stub {
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
getAppDexInstructionSets(ps), isMultiArch(ps));
getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
@ -4334,7 +4333,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
getAppDexInstructionSets(ps), isMultiArch(ps));
getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
@ -5527,8 +5526,9 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
} else {
abi32 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs);
abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
useIsaSpecificSubdirs);
}
}
@ -5539,8 +5539,9 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
} else {
abi64 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs);
abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
useIsaSpecificSubdirs);
}
}
@ -5578,7 +5579,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
} else {
copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
}
@ -7782,7 +7783,8 @@ public class PackageManagerService extends IPackageManager.Stub {
verificationParams.setInstallerUid(uid);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags,
final OriginInfo origin = new OriginInfo(originFile, null, false);
msg.obj = new InstallParams(origin, observer, filteredFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
@ -7794,7 +7796,8 @@ public class PackageManagerService extends IPackageManager.Stub {
params.referrerUri, installerUid, null);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags,
final OriginInfo origin = new OriginInfo(stagedDir, stagedCid, true);
msg.obj = new InstallParams(origin, observer, params.installFlags,
installerPackageName, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@ -8487,22 +8490,45 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
class InstallParams extends HandlerParams {
static class OriginInfo {
/**
* Location where install is coming from, before it has been
* copied/renamed into place. This could be a single monolithic APK
* file, or a cluster directory. This location may be untrusted.
*/
final File originFile;
final String originCid;
final File file;
final String cid;
/**
* Flag indicating that {@link #originFile} or {@link #originCid} has
* already been staged, meaning downstream users don't need to
* defensively copy the contents.
* Flag indicating that {@link #file} or {@link #cid} has already been
* staged, meaning downstream users don't need to defensively copy the
* contents.
*/
boolean originStaged;
final boolean staged;
final String resolvedPath;
final File resolvedFile;
public OriginInfo(File file, String cid, boolean staged) {
this.file = file;
this.cid = cid;
this.staged = staged;
if (cid != null) {
resolvedPath = PackageHelper.getSdDir(cid);
resolvedFile = new File(resolvedPath);
} else if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
resolvedPath = null;
resolvedFile = null;
}
}
}
class InstallParams extends HandlerParams {
final OriginInfo origin;
final IPackageInstallObserver2 observer;
int flags;
final String installerPackageName;
@ -8510,15 +8536,12 @@ public class PackageManagerService extends IPackageManager.Stub {
private InstallArgs mArgs;
private int mRet;
final String packageAbiOverride;
boolean multiArch;
InstallParams(File originFile, String originCid, boolean originStaged,
IPackageInstallObserver2 observer, int flags, String installerPackageName,
VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int flags,
String installerPackageName, VerificationParams verificationParams, UserHandle user,
String packageAbiOverride) {
super(user);
this.originFile = originFile;
this.originCid = originCid;
this.originStaged = originStaged;
this.origin = origin;
this.observer = observer;
this.flags = flags;
this.installerPackageName = installerPackageName;
@ -8529,7 +8552,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public String toString() {
return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ " file=" + originFile + " cid=" + originCid + "}";
+ " file=" + origin.file + " cid=" + origin.cid + "}";
}
public ManifestDigest getManifestDigest() {
@ -8608,11 +8631,11 @@ public class PackageManagerService extends IPackageManager.Stub {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
if (originStaged) {
if (originFile != null) {
if (origin.staged) {
if (origin.file != null) {
flags |= PackageManager.INSTALL_INTERNAL;
flags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (originCid != null) {
} else if (origin.cid != null) {
flags |= PackageManager.INSTALL_EXTERNAL;
flags &= ~PackageManager.INSTALL_INTERNAL;
} else {
@ -8622,6 +8645,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
@ -8629,21 +8653,14 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// Remote call to find out default install location
final String originPath = originFile.getAbsolutePath();
pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, flags,
packageAbiOverride);
// Keep track of whether this package is a multiArch package until
// we perform a full scan of it. We need to do this because we might
// end up extracting the package shared libraries before we perform
// a full scan.
multiArch = pkgLite.multiArch;
/*
* If we have too little free space, try to free cache
* before giving up.
*/
if (!originStaged && pkgLite.recommendedInstallLocation
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
@ -8651,11 +8668,11 @@ public class PackageManagerService extends IPackageManager.Stub {
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
originPath, isForwardLocked(), packageAbiOverride);
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
packageAbiOverride);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
flags, packageAbiOverride);
}
/*
@ -8729,10 +8746,10 @@ public class PackageManagerService extends IPackageManager.Stub {
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) {
// TODO: send verifier the install session instead of uri
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.setDataAndType(Uri.fromFile(originFile), PACKAGE_MIME_TYPE);
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final List<ResolveInfo> receivers = queryIntentReceivers(verification,
@ -8890,8 +8907,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int mRet;
MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
String packageName, String[] instructionSets, int uid, UserHandle user,
boolean isMultiArch) {
String packageName, String[] instructionSets, int uid, UserHandle user) {
super(user);
this.srcArgs = srcArgs;
this.observer = observer;
@ -8901,7 +8917,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (srcArgs != null) {
final String codePath = srcArgs.getCodePath();
targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName,
instructionSets, isMultiArch);
instructionSets);
} else {
targetArgs = null;
}
@ -9002,8 +9018,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private InstallArgs createInstallArgs(InstallParams params) {
// TODO: extend to support incoming zero-copy locations
if (installOnSd(params.flags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
@ -9016,8 +9030,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* when cleaning up old installs, or used as a move source.
*/
private InstallArgs createInstallArgsForExisting(int flags, String codePath,
String resourcePath, String nativeLibraryRoot, String[] instructionSets,
boolean isMultiArch) {
String resourcePath, String nativeLibraryRoot, String[] instructionSets) {
final boolean isInAsec;
if (installOnSd(flags)) {
/* Apps on SD card are always in ASEC containers. */
@ -9035,33 +9048,29 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isInAsec) {
return new AsecInstallArgs(codePath, instructionSets,
installOnSd(flags), installForwardLocked(flags), isMultiArch);
installOnSd(flags), installForwardLocked(flags));
} else {
return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot,
instructionSets, isMultiArch);
instructionSets);
}
}
private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName,
String[] instructionSets, boolean isMultiArch) {
String[] instructionSets) {
final File codeFile = new File(codePath);
if (installOnSd(flags) || installForwardLocked(flags)) {
String cid = getNextCodePath(codePath, pkgName, "/"
+ AsecInstallArgs.RES_FILE_NAME);
return new AsecInstallArgs(codeFile, cid, instructionSets, installOnSd(flags),
installForwardLocked(flags), isMultiArch);
installForwardLocked(flags));
} else {
return new FileInstallArgs(codeFile, instructionSets, isMultiArch);
return new FileInstallArgs(codeFile, instructionSets);
}
}
static abstract class InstallArgs {
/** @see InstallParams#originFile */
final File originFile;
/** @see InstallParams#originStaged */
final boolean originStaged;
// TODO: define inherit location
/** @see InstallParams#origin */
final OriginInfo origin;
final IPackageInstallObserver2 observer;
// Always refers to PackageManager flags only
@ -9070,19 +9079,16 @@ public class PackageManagerService extends IPackageManager.Stub {
final ManifestDigest manifestDigest;
final UserHandle user;
final String abiOverride;
final boolean multiArch;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
// if we move dex files under the common app path.
/* nullable */ String[] instructionSets;
InstallArgs(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
int flags, String installerPackageName, ManifestDigest manifestDigest,
UserHandle user, String[] instructionSets,
String abiOverride, boolean multiArch) {
this.originFile = originFile;
this.originStaged = originStaged;
InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int flags,
String installerPackageName, ManifestDigest manifestDigest, UserHandle user,
String[] instructionSets, String abiOverride) {
this.origin = origin;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
@ -9090,7 +9096,6 @@ public class PackageManagerService extends IPackageManager.Stub {
this.user = user;
this.instructionSets = instructionSets;
this.abiOverride = abiOverride;
this.multiArch = multiArch;
}
abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
@ -9161,10 +9166,9 @@ public class PackageManagerService extends IPackageManager.Stub {
/** New install */
FileInstallArgs(InstallParams params) {
super(params.originFile, params.originStaged, params.observer, params.flags,
super(params.origin, params.observer, params.flags,
params.installerPackageName, params.getManifestDigest(), params.getUser(),
null /* instruction sets */, params.packageAbiOverride,
params.multiArch);
null /* instruction sets */, params.packageAbiOverride);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
@ -9172,8 +9176,8 @@ public class PackageManagerService extends IPackageManager.Stub {
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
String[] instructionSets, boolean isMultiArch) {
super(null, false, null, 0, null, null, null, instructionSets, null, isMultiArch);
String[] instructionSets) {
super(new OriginInfo(null, null, false), null, 0, null, null, null, instructionSets, null);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
@ -9181,13 +9185,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
/** New install from existing */
FileInstallArgs(File originFile, String[] instructionSets, boolean isMultiArch) {
super(originFile, false, null, 0, null, null, null, instructionSets, null,
isMultiArch);
FileInstallArgs(File originFile, String[] instructionSets) {
super(new OriginInfo(originFile, null, false), null, 0, null, null, null, instructionSets, null);
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(),
final long sizeBytes = imcs.calculateInstalledSize(origin.file.getAbsolutePath(),
isFwdLocked(), abiOverride);
final StorageManager storage = StorageManager.from(mContext);
@ -9195,53 +9198,53 @@ public class PackageManagerService extends IPackageManager.Stub {
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
if (origin.staged) {
Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
if (originStaged) {
Slog.d(TAG, originFile + " already staged; skipping copy");
codeFile = originFile;
resourceFile = originFile;
} else {
try {
final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
try {
final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
};
ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
abiOverride, multiArch);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@ -9429,18 +9432,18 @@ public class PackageManagerService extends IPackageManager.Stub {
/** New install */
AsecInstallArgs(InstallParams params) {
super(params.originFile, params.originStaged, params.observer, params.flags,
super(params.origin, params.observer, params.flags,
params.installerPackageName, params.getManifestDigest(),
params.getUser(), null /* instruction sets */,
params.packageAbiOverride, params.multiArch);
params.packageAbiOverride);
}
/** Existing install */
AsecInstallArgs(String fullCodePath, String[] instructionSets,
boolean isExternal, boolean isForwardLocked, boolean isMultiArch) {
super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
boolean isExternal, boolean isForwardLocked) {
super(new OriginInfo(null, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSets, null, isMultiArch);
instructionSets, null);
// Hackily pretend we're still looking at a full code path
if (!fullCodePath.endsWith(RES_FILE_NAME)) {
fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
@ -9454,21 +9457,20 @@ public class PackageManagerService extends IPackageManager.Stub {
setMountPath(subStr1);
}
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked,
boolean isMultiArch) {
super(null, false, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
super(new OriginInfo(null, null, false), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSets, null, isMultiArch);
instructionSets, null);
this.cid = cid;
setMountPath(PackageHelper.getSdDir(cid));
}
/** New install from existing */
AsecInstallArgs(File originPackageFile, String cid, String[] instructionSets,
boolean isExternal, boolean isForwardLocked, boolean isMultiArch) {
super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
boolean isExternal, boolean isForwardLocked) {
super(new OriginInfo(originPackageFile, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSets, null, isMultiArch);
instructionSets, null);
this.cid = cid;
}
@ -9496,7 +9498,13 @@ public class PackageManagerService extends IPackageManager.Stub {
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
// TODO: if already staged, we only need to extract native code
if (origin.staged) {
Slog.d(TAG, origin.cid + " already staged; skipping copy");
cid = origin.cid;
setMountPath(PackageHelper.getSdDir(cid));
return PackageManager.INSTALL_SUCCEEDED;
}
if (temp) {
createCopyFile();
} else {
@ -9508,7 +9516,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final String newMountPath = imcs.copyPackageToContainer(
originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
if (newMountPath != null) {
@ -10133,8 +10141,7 @@ public class PackageManagerService extends IPackageManager.Stub {
deletedPackage.applicationInfo.getCodePath(),
deletedPackage.applicationInfo.getResourcePath(),
deletedPackage.applicationInfo.nativeLibraryRootDir,
getAppDexInstructionSets(deletedPackage.applicationInfo),
isMultiArch(deletedPackage.applicationInfo));
getAppDexInstructionSets(deletedPackage.applicationInfo));
} else {
res.removedInfo.args = null;
}
@ -10920,7 +10927,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
getAppDexInstructionSets(ps), isMultiArch(ps));
getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
return true;
@ -12725,7 +12732,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final AsecInstallArgs args = new AsecInstallArgs(cid,
getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps));
getAppDexInstructionSets(ps), isForwardLocked(ps));
// The package status is changed only if the code path
// matches between settings and the container id.
if (ps.codePathString != null
@ -13019,7 +13026,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* anyway.
*/
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user, false),
processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user),
returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
@ -13027,9 +13034,9 @@ public class PackageManagerService extends IPackageManager.Stub {
final boolean multiArch = isMultiArch(pkg.applicationInfo);
InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(),
pkg.applicationInfo.nativeLibraryRootDir, instructionSets, multiArch);
pkg.applicationInfo.nativeLibraryRootDir, instructionSets);
MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
instructionSets, pkg.applicationInfo.uid, user, multiArch);
instructionSets, pkg.applicationInfo.uid, user);
msg.obj = mp;
mHandler.sendMessage(msg);
}
@ -13107,8 +13114,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final int abi = NativeLibraryHelper.findSupportedAbi(
handle, Build.SUPPORTED_ABIS);
if (abi >= 0) {
NativeLibraryHelper.copyNativeBinariesIfNeededLI(
handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
NativeLibraryHelper.copyNativeBinaries(handle,
newNativeDir, Build.SUPPORTED_ABIS[abi]);
}
} catch (IOException ioe) {
Slog.w(TAG, "Unable to extract native libs for package :"