summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ApplicationPackageManager.java57
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java12
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/os/storage/DiskInfo.java15
-rw-r--r--core/java/android/os/storage/VolumeInfo.java36
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java3
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java14
7 files changed, 143 insertions, 1 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 907ae26..e71713a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -67,6 +67,8 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Display;
@@ -1425,6 +1427,61 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
+ public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ if (app.isInternal()) {
+ return Preconditions.checkNotNull(
+ storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+ } else if (app.isExternalAsec()) {
+ final List<VolumeInfo> vols = storage.getVolumes();
+ for (VolumeInfo vol : vols) {
+ if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
+ return vol;
+ }
+ }
+ throw new IllegalStateException("Failed to find primary public volume");
+ } else {
+ return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
+ }
+ }
+
+ @Override
+ public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final List<VolumeInfo> vols = storage.getVolumes();
+ final List<VolumeInfo> candidates = new ArrayList<>();
+ for (VolumeInfo vol : vols) {
+ if (isCandidateVolume(app, vol)) {
+ candidates.add(vol);
+ }
+ }
+ return candidates;
+ }
+
+ private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
+ // Private internal is always an option
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
+ return true;
+ }
+
+ // System apps and apps demanding internal storage can't be moved
+ // anywhere else
+ if (app.isSystemApp()
+ || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ return false;
+ }
+
+ // Moving into an ASEC on public primary is only an option when app is
+ // internal, or already in ASEC
+ if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
+ return app.isInternal() || app.isExternalAsec();
+ }
+
+ // Otherwise we can move to any private volume
+ return (vol.getType() == VolumeInfo.TYPE_PRIVATE);
+ }
+
+ @Override
public String getInstallerPackageName(String packageName) {
try {
return mPM.getInstallerPackageName(packageName);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5bdb7bb..caf069f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Printer;
import com.android.internal.util.ArrayUtils;
@@ -937,6 +938,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
+ /** @hide */
+ public boolean isInternal() {
+ return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+ }
+
+ /** @hide */
+ public boolean isExternalAsec() {
+ return TextUtils.isEmpty(volumeUuid)
+ && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8844ea8..a128872 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@ import android.annotation.CheckResult;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.StringRes;
@@ -4162,6 +4163,12 @@ public abstract class PackageManager {
public abstract void movePackageAndData(String packageName, String volumeUuid,
IPackageMoveObserver observer);
+ /** {@hide} */
+ public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+
+ /** {@hide} */
+ public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+
/**
* Returns the device identity that verifiers can use to associate their scheme to a particular
* device. This should not be used by anything other than a package verifier.
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index dc96640..2c60d41 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -26,6 +26,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.io.CharArrayWriter;
+import java.util.Objects;
/**
* Information about a physical disk which may contain one or more
@@ -118,6 +119,20 @@ public class DiskInfo implements Parcelable {
}
}
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof DiskInfo) {
+ return Objects.equals(id, ((DiskInfo) o).id);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
public static final Creator<DiskInfo> CREATOR = new Creator<DiskInfo>() {
@Override
public DiskInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index f06fc8c..a241728 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -38,6 +38,8 @@ import com.android.internal.util.Preconditions;
import java.io.CharArrayWriter;
import java.io.File;
+import java.util.Comparator;
+import java.util.Objects;
/**
* Information about a storage volume that may be mounted. A volume may be a
@@ -77,6 +79,22 @@ public class VolumeInfo implements Parcelable {
private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
+ private static final Comparator<VolumeInfo>
+ sDescriptionComparator = new Comparator<VolumeInfo>() {
+ @Override
+ public int compare(VolumeInfo lhs, VolumeInfo rhs) {
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) {
+ return -1;
+ } else if (lhs.getDescription() == null) {
+ return 1;
+ } else if (rhs.getDescription() == null) {
+ return -1;
+ } else {
+ return lhs.getDescription().compareTo(rhs.getDescription());
+ }
+ }
+ };
+
static {
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING);
@@ -150,6 +168,10 @@ public class VolumeInfo implements Parcelable {
return getBroadcastForEnvironment(getEnvironmentForState(state));
}
+ public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() {
+ return sDescriptionComparator;
+ }
+
public @NonNull String getId() {
return id;
}
@@ -344,6 +366,20 @@ public class VolumeInfo implements Parcelable {
}
}
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof VolumeInfo) {
+ return Objects.equals(id, ((VolumeInfo) o).id);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() {
@Override
public VolumeInfo createFromParcel(Parcel in) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a260b0e..c12545b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6280,7 +6280,8 @@ public class PackageManagerService extends IPackageManager.Stub {
!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {
+ if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId) < 0) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed linking native library dir (user=" + userId + ")");
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index e78750c..5a8c7ff 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,6 +16,7 @@
package android.test.mock;
+import android.annotation.NonNull;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Intent;
@@ -51,6 +52,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
import java.util.List;
@@ -498,6 +500,18 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
+ @Override
+ public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public String getInstallerPackageName(String packageName) {
throw new UnsupportedOperationException();