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

Commit 275e3e43 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Migrate primary external storage.

Wire up through MountService to call down into vold.  Watch for
unsolicited events that report progress, including special value "82"
that signals that copy has finished.  We use this value to persist
the volumeUuid in case of unexpected reboot, since it indicates the
new volume is ready.

Wire progress updates through existing callback pipeline.

Update the volume mounting code to match against the persisted UUID
when selecting the primary external storage.

Bug: 19993667
Change-Id: Id46957610fb43517bbfbc368f29b7d430664590d
parent b423e300
Loading
Loading
Loading
Loading
+36 −5
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
@@ -237,8 +236,12 @@ public final class Pm {
            return runForceDexOpt();
        }

        if ("move".equals(op)) {
            return runMove();
        if ("move-package".equals(op)) {
            return runMovePackage();
        }

        if ("move-primary-storage".equals(op)) {
            return runMovePrimaryStorage();
        }

        try {
@@ -1285,7 +1288,7 @@ public final class Pm {
        }
    }

    public int runMove() {
    public int runMovePackage() {
        final String packageName = nextArg();
        String volumeUuid = nextArg();
        if ("internal".equals(volumeUuid)) {
@@ -1313,6 +1316,33 @@ public final class Pm {
        }
    }

    public int runMovePrimaryStorage() {
        String volumeUuid = nextArg();
        if ("internal".equals(volumeUuid)) {
            volumeUuid = null;
        }

        try {
            final int moveId = mPm.movePrimaryStorage(volumeUuid);

            int status = mPm.getMoveStatus(moveId);
            while (!PackageManager.isMoveStatusFinished(status)) {
                SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
                status = mPm.getMoveStatus(moveId);
            }

            if (status == PackageManager.MOVE_SUCCEEDED) {
                System.out.println("Success");
                return 0;
            } else {
                System.err.println("Failure [" + status + "]");
                return 1;
            }
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }

    private int runUninstall() throws RemoteException {
        int flags = 0;
        int userId = UserHandle.USER_ALL;
@@ -1860,7 +1890,8 @@ public final class Pm {
        System.err.println("       pm install-abandon SESSION_ID");
        System.err.println("       pm uninstall [-k] [--user USER_ID] PACKAGE");
        System.err.println("       pm set-installer PACKAGE INSTALLER");
        System.err.println("       pm move PACKAGE [internal|UUID]");
        System.err.println("       pm move-package PACKAGE [internal|UUID]");
        System.err.println("       pm move-primary-storage [internal|UUID]");
        System.err.println("       pm clear [--user USER_ID] PACKAGE");
        System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
        System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
+2 −0
Original line number Diff line number Diff line
@@ -1556,6 +1556,7 @@ final class ApplicationPackageManager extends PackageManager {
        }
    }

    @Override
    public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        final String volumeUuid = storage.getPrimaryStorageUuid();
@@ -1568,6 +1569,7 @@ final class ApplicationPackageManager extends PackageManager {
        }
    }

    @Override
    public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        final VolumeInfo currentVol = getPrimaryStorageCurrentVolume();
+9 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os.storage;

import android.content.pm.IPackageMoveObserver;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
@@ -1082,12 +1083,14 @@ public interface IMountService extends IInterface {
            }

            @Override
            public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
            public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
                    throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(volumeUuid);
                    _data.writeStrongBinder((callback != null ? callback.asBinder() : null));
                    mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0);
                    _reply.readException();
                } finally {
@@ -1714,7 +1717,9 @@ public interface IMountService extends IInterface {
                case TRANSACTION_setPrimaryStorageUuid: {
                    data.enforceInterface(DESCRIPTOR);
                    String volumeUuid = data.readString();
                    setPrimaryStorageUuid(volumeUuid);
                    IPackageMoveObserver listener = IPackageMoveObserver.Stub.asInterface(
                            data.readStrongBinder());
                    setPrimaryStorageUuid(volumeUuid, listener);
                    reply.writeNoException();
                    return true;
                }
@@ -2020,5 +2025,6 @@ public interface IMountService extends IInterface {
    public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;

    public String getPrimaryStorageUuid() throws RemoteException;
    public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException;
    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
            throws RemoteException;
}
+16 −4
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -642,7 +644,12 @@ public class StorageManager {
        }
    }

    /** {@hide} */
    /**
     * This is not the API you're looking for.
     *
     * @see PackageManager#getPrimaryStorageCurrentVolume()
     * @hide
     */
    public String getPrimaryStorageUuid() {
        try {
            return mMountService.getPrimaryStorageUuid();
@@ -651,10 +658,15 @@ public class StorageManager {
        }
    }

    /** {@hide} */
    public void setPrimaryStorageUuid(String volumeUuid) {
    /**
     * This is not the API you're looking for.
     *
     * @see PackageManager#movePrimaryStorage(VolumeInfo)
     * @hide
     */
    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
        try {
            mMountService.setPrimaryStorageUuid(volumeUuid);
            mMountService.setPrimaryStorageUuid(volumeUuid, callback);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
+121 −22
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.ObbInfo;
import android.mtp.MtpStorage;
@@ -178,6 +180,9 @@ class MountService extends IMountService.Stub
    /** Maximum number of ASEC containers allowed to be mounted. */
    private static final int MAX_CONTAINERS = 250;

    /** Magic value sent by MoveTask.cpp */
    private static final int MOVE_STATUS_COPY_FINISHED = 82;

    /*
     * Internal vold response code constants
     */
@@ -226,6 +231,8 @@ class MountService extends IMountService.Stub
        public static final int VOLUME_PATH_CHANGED = 655;
        public static final int VOLUME_DESTROYED = 659;

        public static final int MOVE_STATUS = 660;

        /*
         * 700 series - fstrim
         */
@@ -314,6 +321,11 @@ class MountService extends IMountService.Stub
    @GuardedBy("mLock")
    private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();

    @GuardedBy("mLock")
    private IPackageMoveObserver mMoveCallback;
    @GuardedBy("mLock")
    private String mMoveTargetUuid;

    private DiskInfo findDiskById(String id) {
        synchronized (mLock) {
            final DiskInfo disk = mDisks.get(id);
@@ -347,6 +359,17 @@ class MountService extends IMountService.Stub
        throw new IllegalArgumentException("No volume found for path " + path);
    }

    private VolumeInfo findStorageForUuid(String volumeUuid) {
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
            return findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
            return storage.getPrimaryPhysicalVolume();
        } else {
            return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
        }
    }

    private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
        VolumeMetadata meta = mMetadata.get(vol.fsUuid);
        if (meta == null) {
@@ -937,6 +960,12 @@ class MountService extends IMountService.Stub
                break;
            }

            case VoldResponseCode.MOVE_STATUS: {
                final int status = Integer.parseInt(cooked[1]);
                onMoveStatusLocked(status);
                break;
            }

            case VoldResponseCode.FstrimCompleted: {
                EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
                break;
@@ -972,24 +1001,36 @@ class MountService extends IMountService.Stub
    }

    private void onVolumeCreatedLocked(VolumeInfo vol) {
        final boolean primaryPhysical = SystemProperties.getBoolean(
                StorageManager.PROP_PRIMARY_PHYSICAL, false);
        // TODO: enable switching to another emulated primary
        if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id) && !primaryPhysical) {
        if (vol.type == VolumeInfo.TYPE_EMULATED) {
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);

            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();

            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
            }

        } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
            if (primaryPhysical) {
            // TODO: only look at first public partition
            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
                    && vol.disk.isDefaultPrimary()) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
            }

            // Adoptable public disks are visible to apps, since they meet
            // public API requirement of being in a stable location.
            final DiskInfo disk = mDisks.get(vol.getDiskId());
            if (disk != null && disk.isAdoptable()) {
            if (vol.disk.isAdoptable()) {
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
            }

@@ -1066,6 +1107,35 @@ class MountService extends IMountService.Stub
        }
    }

    private void onMoveStatusLocked(int status) {
        if (mMoveCallback == null) {
            Slog.w(TAG, "Odd, status but no move requested");
            return;
        }

        // TODO: estimate remaining time
        try {
            mMoveCallback.onStatusChanged(-1, status, -1);
        } catch (RemoteException ignored) {
        }

        // We've finished copying and we're about to clean up old data, so
        // remember that move was successful if we get rebooted
        if (status == MOVE_STATUS_COPY_FINISHED) {
            Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");

            mPrimaryStorageUuid = mMoveTargetUuid;
            writeMetadataLocked();
        }

        if (PackageManager.isMoveStatusFinished(status)) {
            Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);

            mMoveCallback = null;
            mMoveTargetUuid = null;
        }
    }

    /**
     * Refresh latest metadata into any currently active {@link VolumeInfo}.
     */
@@ -1322,13 +1392,18 @@ class MountService extends IMountService.Stub
        final VolumeInfo vol = findVolumeById(volId);

        // TODO: expand PMS to know about multiple volumes
        if (vol.isPrimary()) {
        if (vol.isPrimaryPhysical()) {
            final long ident = Binder.clearCallingIdentity();
            try {
                synchronized (mUnmountLock) {
                    mUnmountSignal = new CountDownLatch(1);
                    mPms.updateExternalMediaStatus(false, true);
                    waitForLatch(mUnmountSignal, "mUnmountSignal");
                    mUnmountSignal = null;
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        try {
@@ -1424,20 +1499,41 @@ class MountService extends IMountService.Stub
    }

    @Override
    public String getPrimaryStorageUuid() throws RemoteException {
    public String getPrimaryStorageUuid() {
        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
        waitForReady();

        synchronized (mLock) {
            return mPrimaryStorageUuid;
        }
    }

    @Override
    public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
        waitForReady();

        synchronized (mLock) {
            Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid);
            mPrimaryStorageUuid = volumeUuid;
            writeMetadataLocked();
            final VolumeInfo from = Preconditions.checkNotNull(
                    findStorageForUuid(mPrimaryStorageUuid));
            final VolumeInfo to = Preconditions.checkNotNull(
                    findStorageForUuid(volumeUuid));

            if (Objects.equals(from, to)) {
                throw new IllegalArgumentException("Primary storage already at " + from);
            }

            if (mMoveCallback != null) {
                throw new IllegalStateException("Move already in progress");
            }
            mMoveCallback = callback;
            mMoveTargetUuid = volumeUuid;

            // TODO: reevaluate all volumes we know about!
            try {
                mConnector.execute("volume", "move_storage", from.id, to.id);
            } catch (NativeDaemonConnectorException e) {
                throw e.rethrowAsParcelableException();
            }
        }
    }

@@ -2875,6 +2971,9 @@ class MountService extends IMountService.Stub
                meta.dump(pw);
            }
            pw.decreaseIndent();

            pw.println();
            pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
        }

        synchronized (mObbMounts) {
Loading