Loading core/java/com/android/internal/content/PackageHelper.java +60 −27 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.provider.Settings; import android.util.ArraySet; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -166,6 +166,23 @@ public class PackageHelper { params.sizeBytes, testableInterface); } private static boolean checkFitOnVolume(StorageManager storageManager, String volumePath, SessionParams params) throws IOException { if (volumePath == null) { return false; } final int installFlags = translateAllocateFlags(params.installFlags); final UUID target = storageManager.getUuidForPath(new File(volumePath)); final long availBytes = storageManager.getAllocatableBytes(target, installFlags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY); if (params.sizeBytes <= availBytes) { return true; } final long cacheClearable = storageManager.getAllocatableBytes(target, installFlags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY); return params.sizeBytes <= availBytes + cacheClearable; } @VisibleForTesting public static String resolveInstallVolume(Context context, SessionParams params, TestableInterface testInterface) throws IOException { Loading @@ -178,35 +195,23 @@ public class PackageHelper { ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, params.appPackageName); // Figure out best candidate volume, and also if we fit on internal final ArraySet<String> allCandidates = new ArraySet<>(); boolean fitsOnInternal = false; VolumeInfo bestCandidate = null; long bestCandidateAvailBytes = Long.MIN_VALUE; final ArrayMap<String, String> volumePaths = new ArrayMap<>(); String internalVolumePath = null; for (VolumeInfo vol : storageManager.getVolumes()) { if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { final boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id); final UUID target = storageManager.getUuidForPath(new File(vol.path)); final long availBytes = storageManager.getAllocatableBytes(target, translateAllocateFlags(params.installFlags)); if (isInternalStorage) { fitsOnInternal = (params.sizeBytes <= availBytes); internalVolumePath = vol.path; } if (!isInternalStorage || allow3rdPartyOnInternal) { if (availBytes >= params.sizeBytes) { allCandidates.add(vol.fsUuid); } if (availBytes >= bestCandidateAvailBytes) { bestCandidate = vol; bestCandidateAvailBytes = availBytes; } volumePaths.put(vol.fsUuid, vol.path); } } } // System apps always forced to internal storage if (existingInfo != null && existingInfo.isSystemApp()) { if (fitsOnInternal) { if (checkFitOnVolume(storageManager, internalVolumePath, params)) { return StorageManager.UUID_PRIVATE_INTERNAL; } else { throw new IOException("Not enough space on existing volume " Loading @@ -228,7 +233,7 @@ public class PackageHelper { throw new IOException("Not allowed to install non-system apps on internal storage"); } if (fitsOnInternal) { if (checkFitOnVolume(storageManager, internalVolumePath, params)) { return StorageManager.UUID_PRIVATE_INTERNAL; } else { throw new IOException("Requested internal only, but not enough space"); Loading @@ -237,10 +242,14 @@ public class PackageHelper { // If app already exists somewhere, we must stay on that volume if (existingInfo != null) { if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL) && fitsOnInternal) { return StorageManager.UUID_PRIVATE_INTERNAL; } else if (allCandidates.contains(existingInfo.volumeUuid)) { String existingVolumePath = null; if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) { existingVolumePath = internalVolumePath; } else if (volumePaths.containsKey(existingInfo.volumeUuid)) { existingVolumePath = volumePaths.get(existingInfo.volumeUuid); } if (checkFitOnVolume(storageManager, existingVolumePath, params)) { return existingInfo.volumeUuid; } else { throw new IOException("Not enough space on existing volume " Loading @@ -250,13 +259,37 @@ public class PackageHelper { // We're left with new installations with either preferring external or auto, so just pick // volume with most space if (bestCandidate != null) { return bestCandidate.fsUuid; if (volumePaths.size() == 1) { if (checkFitOnVolume(storageManager, volumePaths.valueAt(0), params)) { return volumePaths.keyAt(0); } } else { String bestCandidate = null; long bestCandidateAvailBytes = Long.MIN_VALUE; for (String vol : volumePaths.keySet()) { final String volumePath = volumePaths.get(vol); final UUID target = storageManager.getUuidForPath(new File(volumePath)); // We need to take into account freeable cached space, because we're choosing the // best candidate amongst a list, not just checking if we fit at all. final long availBytes = storageManager.getAllocatableBytes(target, translateAllocateFlags(params.installFlags)); if (availBytes >= bestCandidateAvailBytes) { bestCandidate = vol; bestCandidateAvailBytes = availBytes; } } if (bestCandidateAvailBytes >= params.sizeBytes) { return bestCandidate; } } throw new IOException("No special requests, but no room on allowed volumes. " + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal); } } public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException { final StorageManager storage = context.getSystemService(StorageManager.class); Loading Loading
core/java/com/android/internal/content/PackageHelper.java +60 −27 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.provider.Settings; import android.util.ArraySet; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -166,6 +166,23 @@ public class PackageHelper { params.sizeBytes, testableInterface); } private static boolean checkFitOnVolume(StorageManager storageManager, String volumePath, SessionParams params) throws IOException { if (volumePath == null) { return false; } final int installFlags = translateAllocateFlags(params.installFlags); final UUID target = storageManager.getUuidForPath(new File(volumePath)); final long availBytes = storageManager.getAllocatableBytes(target, installFlags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY); if (params.sizeBytes <= availBytes) { return true; } final long cacheClearable = storageManager.getAllocatableBytes(target, installFlags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY); return params.sizeBytes <= availBytes + cacheClearable; } @VisibleForTesting public static String resolveInstallVolume(Context context, SessionParams params, TestableInterface testInterface) throws IOException { Loading @@ -178,35 +195,23 @@ public class PackageHelper { ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, params.appPackageName); // Figure out best candidate volume, and also if we fit on internal final ArraySet<String> allCandidates = new ArraySet<>(); boolean fitsOnInternal = false; VolumeInfo bestCandidate = null; long bestCandidateAvailBytes = Long.MIN_VALUE; final ArrayMap<String, String> volumePaths = new ArrayMap<>(); String internalVolumePath = null; for (VolumeInfo vol : storageManager.getVolumes()) { if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { final boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id); final UUID target = storageManager.getUuidForPath(new File(vol.path)); final long availBytes = storageManager.getAllocatableBytes(target, translateAllocateFlags(params.installFlags)); if (isInternalStorage) { fitsOnInternal = (params.sizeBytes <= availBytes); internalVolumePath = vol.path; } if (!isInternalStorage || allow3rdPartyOnInternal) { if (availBytes >= params.sizeBytes) { allCandidates.add(vol.fsUuid); } if (availBytes >= bestCandidateAvailBytes) { bestCandidate = vol; bestCandidateAvailBytes = availBytes; } volumePaths.put(vol.fsUuid, vol.path); } } } // System apps always forced to internal storage if (existingInfo != null && existingInfo.isSystemApp()) { if (fitsOnInternal) { if (checkFitOnVolume(storageManager, internalVolumePath, params)) { return StorageManager.UUID_PRIVATE_INTERNAL; } else { throw new IOException("Not enough space on existing volume " Loading @@ -228,7 +233,7 @@ public class PackageHelper { throw new IOException("Not allowed to install non-system apps on internal storage"); } if (fitsOnInternal) { if (checkFitOnVolume(storageManager, internalVolumePath, params)) { return StorageManager.UUID_PRIVATE_INTERNAL; } else { throw new IOException("Requested internal only, but not enough space"); Loading @@ -237,10 +242,14 @@ public class PackageHelper { // If app already exists somewhere, we must stay on that volume if (existingInfo != null) { if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL) && fitsOnInternal) { return StorageManager.UUID_PRIVATE_INTERNAL; } else if (allCandidates.contains(existingInfo.volumeUuid)) { String existingVolumePath = null; if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) { existingVolumePath = internalVolumePath; } else if (volumePaths.containsKey(existingInfo.volumeUuid)) { existingVolumePath = volumePaths.get(existingInfo.volumeUuid); } if (checkFitOnVolume(storageManager, existingVolumePath, params)) { return existingInfo.volumeUuid; } else { throw new IOException("Not enough space on existing volume " Loading @@ -250,13 +259,37 @@ public class PackageHelper { // We're left with new installations with either preferring external or auto, so just pick // volume with most space if (bestCandidate != null) { return bestCandidate.fsUuid; if (volumePaths.size() == 1) { if (checkFitOnVolume(storageManager, volumePaths.valueAt(0), params)) { return volumePaths.keyAt(0); } } else { String bestCandidate = null; long bestCandidateAvailBytes = Long.MIN_VALUE; for (String vol : volumePaths.keySet()) { final String volumePath = volumePaths.get(vol); final UUID target = storageManager.getUuidForPath(new File(volumePath)); // We need to take into account freeable cached space, because we're choosing the // best candidate amongst a list, not just checking if we fit at all. final long availBytes = storageManager.getAllocatableBytes(target, translateAllocateFlags(params.installFlags)); if (availBytes >= bestCandidateAvailBytes) { bestCandidate = vol; bestCandidateAvailBytes = availBytes; } } if (bestCandidateAvailBytes >= params.sizeBytes) { return bestCandidate; } } throw new IOException("No special requests, but no room on allowed volumes. " + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal); } } public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException { final StorageManager storage = context.getSystemService(StorageManager.class); Loading