Loading api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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); core/java/android/content/pm/PackageInstaller.java +10 −0 Original line number Diff line number Diff line Loading @@ -1152,6 +1152,16 @@ public class PackageInstaller { this.installReason = installReason; } /** {@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); Loading core/java/android/content/pm/PackageManager.java +22 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading Loading @@ -716,15 +720,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 Loading @@ -742,6 +737,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 Loading core/java/com/android/internal/content/PackageHelper.java +75 −34 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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))); } } Loading Loading @@ -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 Loading @@ -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); Loading @@ -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"); } } Loading @@ -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) { Loading @@ -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) { Loading @@ -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"); } } Loading @@ -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) { } Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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; } } } core/tests/coretests/src/android/content/pm/PackageHelperTests.java +24 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); } Loading @@ -175,17 +173,17 @@ 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 { public void testMountAndPullSdCard() throws Exception { fullId = PREFIX; fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none", android.os.Process.myUid(), true); Log.d(TAG,PackageHelper.getSdDir(fullId)); Log.d(TAG, "getSdDir=" + PackageHelper.getSdDir(fullId)); PackageHelper.unMountSdDir(fullId); Runnable r1 = getMountRunnable(); Loading @@ -194,9 +192,6 @@ public class PackageHelperTests extends AndroidTestCase { Thread thread2 = new Thread(r2); thread2.start(); thread.start(); } catch (Exception e) { failStr(e); } } public Runnable getMountRunnable() { Loading Loading
api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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);
core/java/android/content/pm/PackageInstaller.java +10 −0 Original line number Diff line number Diff line Loading @@ -1152,6 +1152,16 @@ public class PackageInstaller { this.installReason = installReason; } /** {@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); Loading
core/java/android/content/pm/PackageManager.java +22 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading Loading @@ -716,15 +720,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 Loading @@ -742,6 +737,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 Loading
core/java/com/android/internal/content/PackageHelper.java +75 −34 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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))); } } Loading Loading @@ -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 Loading @@ -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); Loading @@ -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"); } } Loading @@ -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) { Loading @@ -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) { Loading @@ -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"); } } Loading @@ -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) { } Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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; } } }
core/tests/coretests/src/android/content/pm/PackageHelperTests.java +24 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); } Loading @@ -175,17 +173,17 @@ 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 { public void testMountAndPullSdCard() throws Exception { fullId = PREFIX; fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none", android.os.Process.myUid(), true); Log.d(TAG,PackageHelper.getSdDir(fullId)); Log.d(TAG, "getSdDir=" + PackageHelper.getSdDir(fullId)); PackageHelper.unMountSdDir(fullId); Runnable r1 = getMountRunnable(); Loading @@ -194,9 +192,6 @@ public class PackageHelperTests extends AndroidTestCase { Thread thread2 = new Thread(r2); thread2.start(); thread.start(); } catch (Exception e) { failStr(e); } } public Runnable getMountRunnable() { Loading