diff options
7 files changed, 137 insertions, 78 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 0f42a5463319..5e6717cbece4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11086,6 +11086,7 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { ctor public PackageInstaller.SessionParams(int); method public int describeContents(); + method public void setAllocateAggressive(boolean); method public void setAllowDowngrade(boolean); method public void setAppIcon(android.graphics.Bitmap); method public void setAppLabel(java.lang.CharSequence); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 320c7334b3c6..9e4a86db8684 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1153,6 +1153,16 @@ public class PackageInstaller { } /** {@hide} */ + @SystemApi + public void setAllocateAggressive(boolean allocateAggressive) { + if (allocateAggressive) { + installFlags |= PackageManager.INSTALL_ALLOCATE_AGGRESSIVE; + } else { + installFlags &= ~PackageManager.INSTALL_ALLOCATE_AGGRESSIVE; + } + } + + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 10ffab2d813d..6dd1833222d7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -51,6 +51,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.util.AndroidException; import android.util.Log; @@ -610,6 +611,9 @@ public abstract class PackageManager { INSTALL_FORCE_PERMISSION_PROMPT, INSTALL_INSTANT_APP, INSTALL_DONT_KILL_APP, + INSTALL_FORCE_SDK, + INSTALL_FULL_APP, + INSTALL_ALLOCATE_AGGRESSIVE, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} @@ -717,15 +721,6 @@ public abstract class PackageManager { public static final int INSTALL_INSTANT_APP = 0x00000800; /** - * Flag parameter for {@link #installPackage} to indicate that this package is - * to be installed as a heavy weight app. This is fundamentally the opposite of - * {@link #INSTALL_INSTANT_APP}. - * - * @hide - */ - public static final int INSTALL_FULL_APP = 0x00004000; - - /** * Flag parameter for {@link #installPackage} to indicate that this package contains * a feature split to an existing application and the existing application should not * be killed during the installation process. @@ -743,6 +738,24 @@ public abstract class PackageManager { public static final int INSTALL_FORCE_SDK = 0x00002000; /** + * Flag parameter for {@link #installPackage} to indicate that this package is + * to be installed as a heavy weight app. This is fundamentally the opposite of + * {@link #INSTALL_INSTANT_APP}. + * + * @hide + */ + public static final int INSTALL_FULL_APP = 0x00004000; + + /** + * Flag parameter for {@link #installPackage} to indicate that this package + * is critical to system health or security, meaning the system should use + * {@link StorageManager#FLAG_ALLOCATE_AGGRESSIVE} internally. + * + * @hide + */ + public static final int INSTALL_ALLOCATE_AGGRESSIVE = 0x00008000; + + /** * Flag parameter for * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate * that you don't want to kill the app containing the component. Be careful when you set this diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index e088717f5fe4..9c361b37a659 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser.PackageLite; import android.os.Environment; @@ -354,10 +355,12 @@ public class PackageHelper { abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName); abstract public File getDataDirectory(); - public boolean fitsOnInternalStorage(Context context, long sizeBytes) { + public boolean fitsOnInternalStorage(Context context, SessionParams params) + throws IOException { StorageManager storage = getStorageManager(context); File target = getDataDirectory(); - return (sizeBytes <= storage.getStorageBytesUntilLow(target)); + return (params.sizeBytes <= storage.getAllocatableBytes(target, + translateAllocateFlags(params.installFlags))); } } @@ -401,6 +404,18 @@ public class PackageHelper { return sDefaultTestableInterface; } + @VisibleForTesting + @Deprecated + public static String resolveInstallVolume(Context context, String packageName, + int installLocation, long sizeBytes, TestableInterface testInterface) + throws IOException { + final SessionParams params = new SessionParams(SessionParams.MODE_INVALID); + params.appPackageName = packageName; + params.installLocation = installLocation; + params.sizeBytes = sizeBytes; + return resolveInstallVolume(context, params, testInterface); + } + /** * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual volume to install the app. Only considers @@ -410,25 +425,25 @@ public class PackageHelper { * @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null} * for internal storage. */ - public static String resolveInstallVolume(Context context, String packageName, - int installLocation, long sizeBytes) throws IOException { + public static String resolveInstallVolume(Context context, SessionParams params) + throws IOException { TestableInterface testableInterface = getDefaultTestableInterface(); - return resolveInstallVolume(context, packageName, - installLocation, sizeBytes, testableInterface); + return resolveInstallVolume(context, params.appPackageName, params.installLocation, + params.sizeBytes, testableInterface); } @VisibleForTesting - public static String resolveInstallVolume(Context context, String packageName, - int installLocation, long sizeBytes, TestableInterface testInterface) - throws IOException { + public static String resolveInstallVolume(Context context, SessionParams params, + TestableInterface testInterface) throws IOException { final boolean forceAllowOnExternal = testInterface.getForceAllowOnExternalSetting(context); final boolean allow3rdPartyOnInternal = testInterface.getAllow3rdPartyOnInternalConfig(context); // TODO: handle existing apps installed in ASEC; currently assumes // they'll end up back on internal storage - ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, packageName); + ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, + params.appPackageName); - final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, sizeBytes); + final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, params); final StorageManager storageManager = testInterface.getStorageManager(context); @@ -438,7 +453,8 @@ public class PackageHelper { return StorageManager.UUID_PRIVATE_INTERNAL; } else { throw new IOException("Not enough space on existing volume " - + existingInfo.volumeUuid + " for system app " + packageName + " upgrade"); + + existingInfo.volumeUuid + " for system app " + params.appPackageName + + " upgrade"); } } @@ -450,8 +466,9 @@ public class PackageHelper { boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id); if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable() && (!isInternalStorage || allow3rdPartyOnInternal)) { - final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path)); - if (availBytes >= sizeBytes) { + final long availBytes = storageManager.getAllocatableBytes(new File(vol.path), + translateAllocateFlags(params.installFlags)); + if (availBytes >= params.sizeBytes) { allCandidates.add(vol.fsUuid); } if (availBytes >= bestCandidateAvailBytes) { @@ -463,11 +480,11 @@ public class PackageHelper { // If app expresses strong desire for internal storage, honor it if (!forceAllowOnExternal - && installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + && params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { if (existingInfo != null && !Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) { - throw new IOException("Cannot automatically move " + packageName + " from " - + existingInfo.volumeUuid + " to internal storage"); + throw new IOException("Cannot automatically move " + params.appPackageName + + " from " + existingInfo.volumeUuid + " to internal storage"); } if (!allow3rdPartyOnInternal) { @@ -490,7 +507,7 @@ public class PackageHelper { return existingInfo.volumeUuid; } else { throw new IOException("Not enough space on existing volume " - + existingInfo.volumeUuid + " for " + packageName + " upgrade"); + + existingInfo.volumeUuid + " for " + params.appPackageName + " upgrade"); } } @@ -504,29 +521,45 @@ public class PackageHelper { } } - public static boolean fitsOnInternal(Context context, long sizeBytes) { + public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException { final StorageManager storage = context.getSystemService(StorageManager.class); final File target = Environment.getDataDirectory(); - return (sizeBytes <= storage.getStorageBytesUntilLow(target)); + return (params.sizeBytes <= storage.getAllocatableBytes(target, + translateAllocateFlags(params.installFlags))); } - public static boolean fitsOnExternal(Context context, long sizeBytes) { + public static boolean fitsOnExternal(Context context, SessionParams params) { final StorageManager storage = context.getSystemService(StorageManager.class); final StorageVolume primary = storage.getPrimaryVolume(); - return (sizeBytes > 0) && !primary.isEmulated() + return (params.sizeBytes > 0) && !primary.isEmulated() && Environment.MEDIA_MOUNTED.equals(primary.getState()) - && sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile()); + && params.sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile()); + } + + @Deprecated + public static int resolveInstallLocation(Context context, String packageName, + int installLocation, long sizeBytes, int installFlags) { + final SessionParams params = new SessionParams(SessionParams.MODE_INVALID); + params.appPackageName = packageName; + params.installLocation = installLocation; + params.sizeBytes = sizeBytes; + params.installFlags = installFlags; + try { + return resolveInstallLocation(context, params); + } catch (IOException e) { + throw new IllegalStateException(e); + } } /** * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual location to install the app. */ - public static int resolveInstallLocation(Context context, String packageName, - int installLocation, long sizeBytes, int installFlags) { + public static int resolveInstallLocation(Context context, SessionParams params) + throws IOException { ApplicationInfo existingInfo = null; try { - existingInfo = context.getPackageManager().getApplicationInfo(packageName, + existingInfo = context.getPackageManager().getApplicationInfo(params.appPackageName, PackageManager.MATCH_ANY_USER); } catch (NameNotFoundException ignored) { } @@ -534,23 +567,23 @@ public class PackageHelper { final int prefer; final boolean checkBoth; boolean ephemeral = false; - if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { + if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { prefer = RECOMMEND_INSTALL_INTERNAL; ephemeral = true; checkBoth = false; - } else if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) { + } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { prefer = RECOMMEND_INSTALL_INTERNAL; checkBoth = false; - } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { + } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { prefer = RECOMMEND_INSTALL_EXTERNAL; checkBoth = false; - } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { prefer = RECOMMEND_INSTALL_INTERNAL; checkBoth = false; - } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { prefer = RECOMMEND_INSTALL_EXTERNAL; checkBoth = true; - } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { + } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { // When app is already installed, prefer same medium if (existingInfo != null) { // TODO: distinguish if this is external ASEC @@ -570,12 +603,12 @@ public class PackageHelper { boolean fitsOnInternal = false; if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) { - fitsOnInternal = fitsOnInternal(context, sizeBytes); + fitsOnInternal = fitsOnInternal(context, params); } boolean fitsOnExternal = false; if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) { - fitsOnExternal = fitsOnExternal(context, sizeBytes); + fitsOnExternal = fitsOnExternal(context, params); } if (prefer == RECOMMEND_INSTALL_INTERNAL) { @@ -641,4 +674,12 @@ public class PackageHelper { } return str.substring(0, str.length() - before.length()) + after; } + + public static int translateAllocateFlags(int installFlags) { + if ((installFlags & PackageManager.INSTALL_ALLOCATE_AGGRESSIVE) != 0) { + return StorageManager.FLAG_ALLOCATE_AGGRESSIVE; + } else { + return 0; + } + } } diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java index c4d00c677cc8..5c497b4f527c 100644 --- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java @@ -58,9 +58,9 @@ public class PackageHelperTests extends AndroidTestCase { private static final long sAdoptedSize = 10000; private static final long sPublicSize = 1000000; - private static final StorageManager sStorageManager = createStorageManagerMock(); + private static StorageManager sStorageManager; - private static StorageManager createStorageManagerMock() { + private static StorageManager createStorageManagerMock() throws Exception { VolumeInfo internalVol = new VolumeInfo("private", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); internalVol.path = sInternalVolPath; @@ -93,6 +93,12 @@ public class PackageHelperTests extends AndroidTestCase { Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize); Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize); Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize); + Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalFile), Mockito.anyInt())) + .thenReturn(sInternalSize); + Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedFile), Mockito.anyInt())) + .thenReturn(sAdoptedSize); + Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicFile), Mockito.anyInt())) + .thenReturn(sPublicSize); return storageManager; } @@ -156,18 +162,10 @@ public class PackageHelperTests extends AndroidTestCase { } } - void failStr(String errMsg) { - Log.w(TAG, "errMsg=" + errMsg); - fail(errMsg); - } - - void failStr(Exception e) { - failStr(e.getMessage()); - } - @Override protected void setUp() throws Exception { super.setUp(); + sStorageManager = createStorageManagerMock(); if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); cleanupContainers(); } @@ -175,28 +173,25 @@ public class PackageHelperTests extends AndroidTestCase { @Override protected void tearDown() throws Exception { super.tearDown(); + sStorageManager = null; if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); cleanupContainers(); } - public void testMountAndPullSdCard() { - try { - fullId = PREFIX; - fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none", - android.os.Process.myUid(), true); - - Log.d(TAG,PackageHelper.getSdDir(fullId)); - PackageHelper.unMountSdDir(fullId); - - Runnable r1 = getMountRunnable(); - Runnable r2 = getDestroyRunnable(); - Thread thread = new Thread(r1); - Thread thread2 = new Thread(r2); - thread2.start(); - thread.start(); - } catch (Exception e) { - failStr(e); - } + public void testMountAndPullSdCard() throws Exception { + fullId = PREFIX; + fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none", + android.os.Process.myUid(), true); + + Log.d(TAG, "getSdDir=" + PackageHelper.getSdDir(fullId)); + PackageHelper.unMountSdDir(fullId); + + Runnable r1 = getMountRunnable(); + Runnable r2 = getDestroyRunnable(); + Thread thread = new Thread(r1); + Thread thread2 = new Thread(r2); + thread2.start(); + thread.start(); } public Runnable getMountRunnable() { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 37f78b40c4ff..a317ca52abb5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -637,12 +637,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub { // If caller requested explicit location, sanity check it, otherwise // resolve the best internal or adopted location. if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { - if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) { + if (!PackageHelper.fitsOnInternal(mContext, params)) { throw new IOException("No suitable internal storage available"); } } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { - if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) { + if (!PackageHelper.fitsOnExternal(mContext, params)) { throw new IOException("No suitable external storage available"); } @@ -660,8 +660,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { // requested install flags, delta size, and manifest settings. final long ident = Binder.clearCallingIdentity(); try { - params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, - params.appPackageName, params.installLocation, params.sizeBytes); + params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 2e4a3a3f7968..112907650f75 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -472,8 +472,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // If caller specified a total length, allocate it for them. Free up // cache space to grow, if needed. if (stageDir != null && lengthBytes > 0) { - mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, - lengthBytes, 0); + mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes, + PackageHelper.translateAllocateFlags(params.installFlags)); } if (offsetBytes > 0) { |