Loading cmds/pm/src/com/android/commands/pm/Pm.java +15 −30 Original line number Diff line number Diff line Loading @@ -51,9 +51,11 @@ import android.os.Bundle; import android.os.IUserManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import libcore.io.IoUtils; Loading Loading @@ -1283,20 +1285,6 @@ public final class Pm { } } class LocalPackageMoveObserver extends IPackageMoveObserver.Stub { boolean finished; int returnCode; @Override public void packageMoved(String packageName, int returnCode) throws RemoteException { synchronized (this) { this.finished = true; this.returnCode = returnCode; notifyAll(); } } } public int runMove() { final String packageName = nextArg(); String volumeUuid = nextArg(); Loading @@ -1304,25 +1292,22 @@ public final class Pm { volumeUuid = null; } final LocalPackageMoveObserver obs = new LocalPackageMoveObserver(); try { mPm.movePackageAndData(packageName, volumeUuid, obs); final int moveId = mPm.movePackage(packageName, volumeUuid); synchronized (obs) { while (!obs.finished) { try { obs.wait(); } catch (InterruptedException e) { } int status = mPm.getMoveStatus(moveId); while (!PackageManager.isMoveStatusFinished(status)) { SystemClock.sleep(DateUtils.SECOND_IN_MILLIS); status = mPm.getMoveStatus(moveId); } if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) { if (status == PackageManager.MOVE_SUCCEEDED) { System.out.println("Success"); return 0; } else { System.err.println("Failure [" + obs.returnCode + "]"); System.err.println("Failure [" + status + "]"); return 1; } } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } Loading core/java/android/app/ApplicationPackageManager.java +174 −27 Original line number Diff line number Diff line Loading @@ -62,6 +62,9 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; Loading @@ -81,7 +84,9 @@ import com.android.internal.util.UserIcons; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; /*package*/ final class ApplicationPackageManager extends PackageManager { Loading @@ -98,6 +103,9 @@ final class ApplicationPackageManager extends PackageManager { @GuardedBy("mLock") private PackageInstaller mInstaller; @GuardedBy("mDelegates") private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>(); UserManager getUserManager() { synchronized (mLock) { if (mUserManager == null) { Loading Loading @@ -1410,57 +1418,100 @@ final class ApplicationPackageManager extends PackageManager { } @Override public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { public String getInstallerPackageName(String packageName) { try { return mPM.getInstallerPackageName(packageName); } catch (RemoteException e) { // Should never happen! } return null; } @Override public int getMoveStatus(int moveId) { try { return mPM.getMoveStatus(moveId); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void registerMoveCallback(MoveCallback callback, Handler handler) { synchronized (mDelegates) { final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback, handler.getLooper()); try { mPM.registerMoveCallback(delegate); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } mDelegates.add(delegate); } } @Override public void unregisterMoveCallback(MoveCallback callback) { synchronized (mDelegates) { for (Iterator<MoveCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) { final MoveCallbackDelegate delegate = i.next(); if (delegate.mCallback == callback) { try { mPM.movePackage(packageName, observer, flags); mPM.unregisterMoveCallback(delegate); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } i.remove(); } } } } @Override public void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer) { public int movePackage(String packageName, VolumeInfo vol) { try { mPM.movePackageAndData(packageName, volumeUuid, observer); final String volumeUuid; if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; } else if (vol.isPrimaryPhysical()) { volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; } else { volumeUuid = Preconditions.checkNotNull(vol.fsUuid); } return mPM.movePackage(packageName, volumeUuid); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); if (app.isInternal()) { return Preconditions.checkNotNull( storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)); return 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"); return storage.getPrimaryPhysicalVolume(); } else { return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid)); return storage.findVolumeByUuid(app.volumeUuid); } } @Override public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPackageCurrentVolume(app); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (isCandidateVolume(app, vol)) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) { candidates.add(vol); } } return candidates; } private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) { private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) { // Private internal is always an option if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; Loading @@ -1473,10 +1524,14 @@ final class ApplicationPackageManager extends PackageManager { 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(); // Gotta be able to write there if (!vol.isMountedWritable()) { return false; } // Moving into an ASEC on public primary is only option internal if (vol.isPrimaryPhysical()) { return app.isInternal(); } // Otherwise we can move to any private volume Loading @@ -1484,13 +1539,66 @@ final class ApplicationPackageManager extends PackageManager { } @Override public String getInstallerPackageName(String packageName) { public int movePrimaryStorage(VolumeInfo vol) { try { return mPM.getInstallerPackageName(packageName); final String volumeUuid; if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; } else if (vol.isPrimaryPhysical()) { volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; } else { volumeUuid = Preconditions.checkNotNull(vol.fsUuid); } return mPM.movePrimaryStorage(volumeUuid); } catch (RemoteException e) { // Should never happen! throw e.rethrowAsRuntimeException(); } return null; } public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final String volumeUuid = storage.getPrimaryStorageUuid(); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { return storage.getPrimaryPhysicalVolume(); } else { return storage.findVolumeByUuid(volumeUuid); } } public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { candidates.add(vol); } } return candidates; } private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) { // Private internal is always an option if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; } // Gotta be able to write there if (!vol.isMountedWritable()) { return false; } // We can move to public volumes on legacy devices if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) { return true; } // Otherwise we can move to any private volume return (vol.getType() == VolumeInfo.TYPE_PRIVATE); } @Override Loading Loading @@ -1941,6 +2049,45 @@ final class ApplicationPackageManager extends PackageManager { return null; } /** {@hide} */ private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements Handler.Callback { private static final int MSG_STARTED = 1; private static final int MSG_STATUS_CHANGED = 2; final MoveCallback mCallback; final Handler mHandler; public MoveCallbackDelegate(MoveCallback callback, Looper looper) { mCallback = callback; mHandler = new Handler(looper, this); } @Override public boolean handleMessage(Message msg) { final int moveId = msg.arg1; switch (msg.what) { case MSG_STARTED: mCallback.onStarted(moveId, (String) msg.obj); return true; case MSG_STATUS_CHANGED: mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj); return true; } return false; } @Override public void onStarted(int moveId, String title) { mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget(); } @Override public void onStatusChanged(int moveId, int status, long estMillis) { mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget(); } } private final ContextImpl mContext; private final IPackageManager mPM; Loading core/java/android/content/pm/IPackageManager.aidl +7 −3 Original line number Diff line number Diff line Loading @@ -50,7 +50,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.IntentSender; import com.android.internal.os.IResultReceiver; /** * See {@link PackageManager} for documentation on most of the APIs Loading Loading @@ -431,8 +430,13 @@ interface IPackageManager { PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage); void movePackage(String packageName, IPackageMoveObserver observer, int flags); void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); int getMoveStatus(int moveId); void registerMoveCallback(in IPackageMoveObserver callback); void unregisterMoveCallback(in IPackageMoveObserver callback); int movePackage(in String packageName, in String volumeUuid); int movePrimaryStorage(in String volumeUuid); boolean addPermissionAsync(in PermissionInfo info); Loading core/java/android/content/pm/IPackageMoveObserver.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,6 @@ package android.content.pm; * @hide */ oneway interface IPackageMoveObserver { void packageMoved(in String packageName, int returnCode); void onStarted(int moveId, String title); void onStatusChanged(int moveId, int status, long estMillis); } core/java/android/content/pm/PackageManager.java +38 −6 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.storage.VolumeInfo; Loading Loading @@ -875,7 +876,8 @@ public abstract class PackageManager { * * @hide */ public static final int MOVE_SUCCEEDED = 1; public static final int MOVE_SUCCEEDED = -100; /** * Error code that is passed to the {@link IPackageMoveObserver} by * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} Loading Loading @@ -941,6 +943,7 @@ public abstract class PackageManager { * been installed on external media. * @hide */ @Deprecated public static final int MOVE_INTERNAL = 0x00000001; /** Loading @@ -948,8 +951,12 @@ public abstract class PackageManager { * the package should be moved to external media. * @hide */ @Deprecated public static final int MOVE_EXTERNAL_MEDIA = 0x00000002; /** {@hide} */ public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID"; /** * Usable by the required verifier as the {@code verificationCode} argument * for {@link PackageManager#verifyPendingInstall} to indicate that it will Loading Loading @@ -4183,17 +4190,42 @@ public abstract class PackageManager { * @hide */ @Deprecated public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags); public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { throw new UnsupportedOperationException(); } /** {@hide} */ public abstract void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); public static boolean isMoveStatusFinished(int status) { return (status < 0 || status > 100); } /** {@hide} */ public static abstract class MoveCallback { public abstract void onStarted(int moveId, String title); public abstract void onStatusChanged(int moveId, int status, long estMillis); } /** {@hide} */ public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app); public abstract int getMoveStatus(int moveId); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app); public abstract void registerMoveCallback(MoveCallback callback, Handler handler); /** {@hide} */ public abstract void unregisterMoveCallback(MoveCallback callback); /** {@hide} */ public abstract int movePackage(String packageName, VolumeInfo vol); /** {@hide} */ public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app); /** {@hide} */ public abstract int movePrimaryStorage(VolumeInfo vol); /** {@hide} */ public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume(); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes(); /** * Returns the device identity that verifiers can use to associate their scheme to a particular Loading Loading
cmds/pm/src/com/android/commands/pm/Pm.java +15 −30 Original line number Diff line number Diff line Loading @@ -51,9 +51,11 @@ import android.os.Bundle; import android.os.IUserManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import libcore.io.IoUtils; Loading Loading @@ -1283,20 +1285,6 @@ public final class Pm { } } class LocalPackageMoveObserver extends IPackageMoveObserver.Stub { boolean finished; int returnCode; @Override public void packageMoved(String packageName, int returnCode) throws RemoteException { synchronized (this) { this.finished = true; this.returnCode = returnCode; notifyAll(); } } } public int runMove() { final String packageName = nextArg(); String volumeUuid = nextArg(); Loading @@ -1304,25 +1292,22 @@ public final class Pm { volumeUuid = null; } final LocalPackageMoveObserver obs = new LocalPackageMoveObserver(); try { mPm.movePackageAndData(packageName, volumeUuid, obs); final int moveId = mPm.movePackage(packageName, volumeUuid); synchronized (obs) { while (!obs.finished) { try { obs.wait(); } catch (InterruptedException e) { } int status = mPm.getMoveStatus(moveId); while (!PackageManager.isMoveStatusFinished(status)) { SystemClock.sleep(DateUtils.SECOND_IN_MILLIS); status = mPm.getMoveStatus(moveId); } if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) { if (status == PackageManager.MOVE_SUCCEEDED) { System.out.println("Success"); return 0; } else { System.err.println("Failure [" + obs.returnCode + "]"); System.err.println("Failure [" + status + "]"); return 1; } } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } Loading
core/java/android/app/ApplicationPackageManager.java +174 −27 Original line number Diff line number Diff line Loading @@ -62,6 +62,9 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; Loading @@ -81,7 +84,9 @@ import com.android.internal.util.UserIcons; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; /*package*/ final class ApplicationPackageManager extends PackageManager { Loading @@ -98,6 +103,9 @@ final class ApplicationPackageManager extends PackageManager { @GuardedBy("mLock") private PackageInstaller mInstaller; @GuardedBy("mDelegates") private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>(); UserManager getUserManager() { synchronized (mLock) { if (mUserManager == null) { Loading Loading @@ -1410,57 +1418,100 @@ final class ApplicationPackageManager extends PackageManager { } @Override public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { public String getInstallerPackageName(String packageName) { try { return mPM.getInstallerPackageName(packageName); } catch (RemoteException e) { // Should never happen! } return null; } @Override public int getMoveStatus(int moveId) { try { return mPM.getMoveStatus(moveId); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void registerMoveCallback(MoveCallback callback, Handler handler) { synchronized (mDelegates) { final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback, handler.getLooper()); try { mPM.registerMoveCallback(delegate); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } mDelegates.add(delegate); } } @Override public void unregisterMoveCallback(MoveCallback callback) { synchronized (mDelegates) { for (Iterator<MoveCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) { final MoveCallbackDelegate delegate = i.next(); if (delegate.mCallback == callback) { try { mPM.movePackage(packageName, observer, flags); mPM.unregisterMoveCallback(delegate); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } i.remove(); } } } } @Override public void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer) { public int movePackage(String packageName, VolumeInfo vol) { try { mPM.movePackageAndData(packageName, volumeUuid, observer); final String volumeUuid; if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; } else if (vol.isPrimaryPhysical()) { volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; } else { volumeUuid = Preconditions.checkNotNull(vol.fsUuid); } return mPM.movePackage(packageName, volumeUuid); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); if (app.isInternal()) { return Preconditions.checkNotNull( storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)); return 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"); return storage.getPrimaryPhysicalVolume(); } else { return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid)); return storage.findVolumeByUuid(app.volumeUuid); } } @Override public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPackageCurrentVolume(app); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (isCandidateVolume(app, vol)) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) { candidates.add(vol); } } return candidates; } private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) { private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) { // Private internal is always an option if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; Loading @@ -1473,10 +1524,14 @@ final class ApplicationPackageManager extends PackageManager { 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(); // Gotta be able to write there if (!vol.isMountedWritable()) { return false; } // Moving into an ASEC on public primary is only option internal if (vol.isPrimaryPhysical()) { return app.isInternal(); } // Otherwise we can move to any private volume Loading @@ -1484,13 +1539,66 @@ final class ApplicationPackageManager extends PackageManager { } @Override public String getInstallerPackageName(String packageName) { public int movePrimaryStorage(VolumeInfo vol) { try { return mPM.getInstallerPackageName(packageName); final String volumeUuid; if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; } else if (vol.isPrimaryPhysical()) { volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; } else { volumeUuid = Preconditions.checkNotNull(vol.fsUuid); } return mPM.movePrimaryStorage(volumeUuid); } catch (RemoteException e) { // Should never happen! throw e.rethrowAsRuntimeException(); } return null; } public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final String volumeUuid = storage.getPrimaryStorageUuid(); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { return storage.getPrimaryPhysicalVolume(); } else { return storage.findVolumeByUuid(volumeUuid); } } public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { candidates.add(vol); } } return candidates; } private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) { // Private internal is always an option if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; } // Gotta be able to write there if (!vol.isMountedWritable()) { return false; } // We can move to public volumes on legacy devices if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) { return true; } // Otherwise we can move to any private volume return (vol.getType() == VolumeInfo.TYPE_PRIVATE); } @Override Loading Loading @@ -1941,6 +2049,45 @@ final class ApplicationPackageManager extends PackageManager { return null; } /** {@hide} */ private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements Handler.Callback { private static final int MSG_STARTED = 1; private static final int MSG_STATUS_CHANGED = 2; final MoveCallback mCallback; final Handler mHandler; public MoveCallbackDelegate(MoveCallback callback, Looper looper) { mCallback = callback; mHandler = new Handler(looper, this); } @Override public boolean handleMessage(Message msg) { final int moveId = msg.arg1; switch (msg.what) { case MSG_STARTED: mCallback.onStarted(moveId, (String) msg.obj); return true; case MSG_STATUS_CHANGED: mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj); return true; } return false; } @Override public void onStarted(int moveId, String title) { mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget(); } @Override public void onStatusChanged(int moveId, int status, long estMillis) { mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget(); } } private final ContextImpl mContext; private final IPackageManager mPM; Loading
core/java/android/content/pm/IPackageManager.aidl +7 −3 Original line number Diff line number Diff line Loading @@ -50,7 +50,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.IntentSender; import com.android.internal.os.IResultReceiver; /** * See {@link PackageManager} for documentation on most of the APIs Loading Loading @@ -431,8 +430,13 @@ interface IPackageManager { PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage); void movePackage(String packageName, IPackageMoveObserver observer, int flags); void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); int getMoveStatus(int moveId); void registerMoveCallback(in IPackageMoveObserver callback); void unregisterMoveCallback(in IPackageMoveObserver callback); int movePackage(in String packageName, in String volumeUuid); int movePrimaryStorage(in String volumeUuid); boolean addPermissionAsync(in PermissionInfo info); Loading
core/java/android/content/pm/IPackageMoveObserver.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,6 @@ package android.content.pm; * @hide */ oneway interface IPackageMoveObserver { void packageMoved(in String packageName, int returnCode); void onStarted(int moveId, String title); void onStatusChanged(int moveId, int status, long estMillis); }
core/java/android/content/pm/PackageManager.java +38 −6 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.storage.VolumeInfo; Loading Loading @@ -875,7 +876,8 @@ public abstract class PackageManager { * * @hide */ public static final int MOVE_SUCCEEDED = 1; public static final int MOVE_SUCCEEDED = -100; /** * Error code that is passed to the {@link IPackageMoveObserver} by * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} Loading Loading @@ -941,6 +943,7 @@ public abstract class PackageManager { * been installed on external media. * @hide */ @Deprecated public static final int MOVE_INTERNAL = 0x00000001; /** Loading @@ -948,8 +951,12 @@ public abstract class PackageManager { * the package should be moved to external media. * @hide */ @Deprecated public static final int MOVE_EXTERNAL_MEDIA = 0x00000002; /** {@hide} */ public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID"; /** * Usable by the required verifier as the {@code verificationCode} argument * for {@link PackageManager#verifyPendingInstall} to indicate that it will Loading Loading @@ -4183,17 +4190,42 @@ public abstract class PackageManager { * @hide */ @Deprecated public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags); public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { throw new UnsupportedOperationException(); } /** {@hide} */ public abstract void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); public static boolean isMoveStatusFinished(int status) { return (status < 0 || status > 100); } /** {@hide} */ public static abstract class MoveCallback { public abstract void onStarted(int moveId, String title); public abstract void onStatusChanged(int moveId, int status, long estMillis); } /** {@hide} */ public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app); public abstract int getMoveStatus(int moveId); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app); public abstract void registerMoveCallback(MoveCallback callback, Handler handler); /** {@hide} */ public abstract void unregisterMoveCallback(MoveCallback callback); /** {@hide} */ public abstract int movePackage(String packageName, VolumeInfo vol); /** {@hide} */ public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app); /** {@hide} */ public abstract int movePrimaryStorage(VolumeInfo vol); /** {@hide} */ public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume(); /** {@hide} */ public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes(); /** * Returns the device identity that verifiers can use to associate their scheme to a particular Loading