Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 620b32b3 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Package and storage movement callbacks.

Since package and primary storage movement can take quite awhile,
we want to have SystemUI surface progress and allow the Settings
app to be torn down while the movement proceeds in the background.

Movement requests now return a unique ID that identifies an ongoing
operation, and interested parties can observe ongoing progress and
final status.  Internally, progress and status are overloaded so
the values 0-100 are progress, and any values outside that range
are terminal status.

Add explicit constants for special-cased volume UUIDs, and change
the APIs to accept VolumeInfo to reduce confusion.  Internally the
UUID value "null" means internal storage, and "primary_physical"
means the current primary physical volume.  These values are used
for both package and primary storage movement destinations.

Persist the current primary storage location in MountService
metadata, since it can be moved over time.

Surface disk scanned events with separate volume count so we can
determine when it's partitioned successfully.  Also send broadcast
to support TvSettings launching into adoption flow.

Bug: 19993667
Change-Id: Ic8a4034033c3cb3262023dba4a642efc6795af10
parent 61044cfd
Loading
Loading
Loading
Loading
+15 −30
Original line number Original line Diff line number Diff line
@@ -51,9 +51,11 @@ import android.os.Bundle;
import android.os.IUserManager;
import android.os.IUserManager;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Log;


import libcore.io.IoUtils;
import libcore.io.IoUtils;
@@ -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() {
    public int runMove() {
        final String packageName = nextArg();
        final String packageName = nextArg();
        String volumeUuid = nextArg();
        String volumeUuid = nextArg();
@@ -1304,25 +1292,22 @@ public final class Pm {
            volumeUuid = null;
            volumeUuid = null;
        }
        }


        final LocalPackageMoveObserver obs = new LocalPackageMoveObserver();
        try {
        try {
            mPm.movePackageAndData(packageName, volumeUuid, obs);
            final int moveId = mPm.movePackage(packageName, volumeUuid);


            synchronized (obs) {
            int status = mPm.getMoveStatus(moveId);
                while (!obs.finished) {
            while (!PackageManager.isMoveStatusFinished(status)) {
                    try {
                SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
                        obs.wait();
                status = mPm.getMoveStatus(moveId);
                    } catch (InterruptedException e) {
                    }
            }
            }
                if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) {

            if (status == PackageManager.MOVE_SUCCEEDED) {
                System.out.println("Success");
                System.out.println("Success");
                return 0;
                return 0;
            } else {
            } else {
                    System.err.println("Failure [" + obs.returnCode + "]");
                System.err.println("Failure [" + status + "]");
                return 1;
                return 1;
            }
            }
            }
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
            throw e.rethrowAsRuntimeException();
        }
        }
+174 −27
Original line number Original line Diff line number Diff line
@@ -62,6 +62,9 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.SystemProperties;
@@ -81,7 +84,9 @@ import com.android.internal.util.UserIcons;


import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.List;
import java.util.Objects;


/*package*/
/*package*/
final class ApplicationPackageManager extends PackageManager {
final class ApplicationPackageManager extends PackageManager {
@@ -98,6 +103,9 @@ final class ApplicationPackageManager extends PackageManager {
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private PackageInstaller mInstaller;
    private PackageInstaller mInstaller;


    @GuardedBy("mDelegates")
    private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();

    UserManager getUserManager() {
    UserManager getUserManager() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mUserManager == null) {
            if (mUserManager == null) {
@@ -1410,57 +1418,100 @@ final class ApplicationPackageManager extends PackageManager {
    }
    }


    @Override
    @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 {
                    try {
            mPM.movePackage(packageName, observer, flags);
                        mPM.unregisterMoveCallback(delegate);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        throw e.rethrowAsRuntimeException();
                        throw e.rethrowAsRuntimeException();
                    }
                    }
                    i.remove();
                }
            }
        }
    }
    }


    @Override
    @Override
    public void movePackageAndData(String packageName, String volumeUuid,
    public int movePackage(String packageName, VolumeInfo vol) {
            IPackageMoveObserver observer) {
        try {
        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) {
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
            throw e.rethrowAsRuntimeException();
        }
        }
    }
    }


    @Override
    @Override
    public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
    public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        if (app.isInternal()) {
        if (app.isInternal()) {
            return Preconditions.checkNotNull(
            return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
                    storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
        } else if (app.isExternalAsec()) {
        } else if (app.isExternalAsec()) {
            final List<VolumeInfo> vols = storage.getVolumes();
            return storage.getPrimaryPhysicalVolume();
            for (VolumeInfo vol : vols) {
                if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
                    return vol;
                }
            }
            throw new IllegalStateException("Failed to find primary public volume");
        } else {
        } else {
            return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
            return storage.findVolumeByUuid(app.volumeUuid);
        }
        }
    }
    }


    @Override
    @Override
    public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
    public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        final VolumeInfo currentVol = getPackageCurrentVolume(app);
        final List<VolumeInfo> vols = storage.getVolumes();
        final List<VolumeInfo> vols = storage.getVolumes();
        final List<VolumeInfo> candidates = new ArrayList<>();
        final List<VolumeInfo> candidates = new ArrayList<>();
        for (VolumeInfo vol : vols) {
        for (VolumeInfo vol : vols) {
            if (isCandidateVolume(app, vol)) {
            if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) {
                candidates.add(vol);
                candidates.add(vol);
            }
            }
        }
        }
        return candidates;
        return candidates;
    }
    }


    private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
    private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
        // Private internal is always an option
        // Private internal is always an option
        if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
        if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
            return true;
            return true;
@@ -1473,10 +1524,14 @@ final class ApplicationPackageManager extends PackageManager {
            return false;
            return false;
        }
        }


        // Moving into an ASEC on public primary is only an option when app is
        // Gotta be able to write there
        // internal, or already in ASEC
        if (!vol.isMountedWritable()) {
        if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
            return false;
            return app.isInternal() || app.isExternalAsec();
        }

        // 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
        // Otherwise we can move to any private volume
@@ -1484,13 +1539,66 @@ final class ApplicationPackageManager extends PackageManager {
    }
    }


    @Override
    @Override
    public String getInstallerPackageName(String packageName) {
    public int movePrimaryStorage(VolumeInfo vol) {
        try {
        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) {
        } 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
    @Override
@@ -1941,6 +2049,45 @@ final class ApplicationPackageManager extends PackageManager {
        return null;
        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 ContextImpl mContext;
    private final IPackageManager mPM;
    private final IPackageManager mPM;


+7 −3
Original line number Original line Diff line number Diff line
@@ -50,7 +50,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
import android.content.IntentSender;
import com.android.internal.os.IResultReceiver;


/**
/**
 *  See {@link PackageManager} for documentation on most of the APIs
 *  See {@link PackageManager} for documentation on most of the APIs
@@ -431,8 +430,13 @@ interface IPackageManager {


    PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
    PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);


    void movePackage(String packageName, IPackageMoveObserver observer, int flags);
    int getMoveStatus(int moveId);
    void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);

    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);
    boolean addPermissionAsync(in PermissionInfo info);


+2 −2
Original line number Original line Diff line number Diff line
@@ -22,6 +22,6 @@ package android.content.pm;
 * @hide
 * @hide
 */
 */
oneway interface IPackageMoveObserver {
oneway interface IPackageMoveObserver {
    void packageMoved(in String packageName, int returnCode);
    void onStarted(int moveId, String title);
    void onStatusChanged(int moveId, int status, long estMillis);
}
}
+38 −6
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Environment;
import android.os.Environment;
import android.os.Handler;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeInfo;
@@ -875,7 +876,8 @@ public abstract class PackageManager {
     *
     *
     * @hide
     * @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
     * Error code that is passed to the {@link IPackageMoveObserver} by
     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
@@ -941,6 +943,7 @@ public abstract class PackageManager {
     * been installed on external media.
     * been installed on external media.
     * @hide
     * @hide
     */
     */
    @Deprecated
    public static final int MOVE_INTERNAL = 0x00000001;
    public static final int MOVE_INTERNAL = 0x00000001;


    /**
    /**
@@ -948,8 +951,12 @@ public abstract class PackageManager {
     * the package should be moved to external media.
     * the package should be moved to external media.
     * @hide
     * @hide
     */
     */
    @Deprecated
    public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
    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
     * Usable by the required verifier as the {@code verificationCode} argument
     * for {@link PackageManager#verifyPendingInstall} to indicate that it will
     * for {@link PackageManager#verifyPendingInstall} to indicate that it will
@@ -4183,17 +4190,42 @@ public abstract class PackageManager {
     * @hide
     * @hide
     */
     */
    @Deprecated
    @Deprecated
    public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
    public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
        throw new UnsupportedOperationException();
    }


    /** {@hide} */
    /** {@hide} */
    public abstract void movePackageAndData(String packageName, String volumeUuid,
    public static boolean isMoveStatusFinished(int status) {
            IPackageMoveObserver observer);
        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} */
    /** {@hide} */
    public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
    public abstract int getMoveStatus(int moveId);


    /** {@hide} */
    /** {@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
     * Returns the device identity that verifiers can use to associate their scheme to a particular
Loading