diff options
15 files changed, 717 insertions, 375 deletions
diff --git a/api/current.txt b/api/current.txt index dfa95a0..093252d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8680,10 +8680,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 diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 46d8ade..d9b40b1 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -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")) { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 7419ebc..9afdbf7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -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,21 +98,57 @@ 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"; + /** + * 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"; /** - * Package name relevant to a status. + * 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_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; + 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 @@ -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) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index eb8b762..142206a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -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; diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 939cda9..d1fadd6 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -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; } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 179d5e8..02f675c 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -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; } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index a529cc3..c17f4ee 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -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( diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java index 7ad35d0..06c495e 100644 --- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java @@ -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); diff --git a/core/tests/coretests/src/android/os/storage/AsecTests.java b/core/tests/coretests/src/android/os/storage/AsecTests.java index abb8eae..4f724fe 100644 --- a/core/tests/coretests/src/android/os/storage/AsecTests.java +++ b/core/tests/coretests/src/android/os/storage/AsecTests.java @@ -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 { diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 5701678..621de18 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -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; diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index fae30e5..1f28324 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -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; - } } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 50f2ae9..ea24d7c 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -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) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 5e802de..6f60d24 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -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); + if (existingApp == null) { + throw new IllegalStateException( + "Missing existing app " + params.appPackageName); + } - throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support"); + 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 { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 38a2016..5264fc4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -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; @@ -133,29 +130,34 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private String mFinalMessage; @GuardedBy("mLock") - private File mResolvedStageDir; + private ArrayList<FileBridge> mBridges = new ArrayList<>(); + + @GuardedBy("mLock") + private IPackageInstallObserver2 mRemoteObserver; + + /** Fields derived from commit parsing */ + private String mPackageName; + private int mVersionCode; + private Signature[] mSignatures; /** - * 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). + * 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 String mResolvedBaseCodePath; + private File mResolvedBaseFile; @GuardedBy("mLock") - private ArrayList<FileBridge> mBridges = new ArrayList<>(); + private File mResolvedStageDir; @GuardedBy("mLock") - private IPackageInstallObserver2 mRemoteObserver; - - /** Fields derived from commit parsing */ - private String mPackageName; - private int mVersionCode; - private Signature[] mSignatures; + 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 @@ -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); + } + + assertApkConsistent("Existing base", existingBase); + + // Inherit base if not overridden + if (mResolvedBaseFile == null) { + mResolvedBaseFile = new File(app.getBaseCodePath()); + mResolvedInheritedFiles.add(mResolvedBaseFile); } - assertPackageConsistent("Existing base", info.packageName, info.versionCode, - info.signatures); + // 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(); + } + } + + 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 (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage"); + 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); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index aa49b27..f06992a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -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 :" |