OSDN Git Service

Move packages between candidate volumes.
authorJeff Sharkey <jsharkey@android.com>
Wed, 15 Apr 2015 21:32:25 +0000 (14:32 -0700)
committerJeff Sharkey <jsharkey@android.com>
Thu, 16 Apr 2015 05:14:09 +0000 (22:14 -0700)
We now allow moving packages between all possible candidate volumes,
as determined by the framework.  Moving now jumps through wizard to
help user understand what's going on.

Bug: 19993667
Change-Id: I5416ed2865f400b1d718c68a3f0e033080fefa0e

res/layout/preference_list_fragment.xml
src/com/android/settings/applications/AppStorageSettings.java
src/com/android/settings/applications/ApplicationsState.java
src/com/android/settings/applications/ManageApplications.java
src/com/android/settings/deviceinfo/StorageSettings.java
src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java [new file with mode: 0644]
src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java [new file with mode: 0644]

index 6e8ad91..195f6eb 100644 (file)
@@ -36,7 +36,7 @@
         <ListView android:id="@android:id/list"
                   style="@style/PreferenceFragmentListSinglePane"
                   android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
+                  android:layout_height="match_parent"
                   android:paddingStart="@dimen/settings_side_margin"
                   android:paddingEnd="@dimen/settings_side_margin"
                   android:paddingTop="@dimen/dashboard_padding_top"
index 4b80fdd..647be4d 100644 (file)
@@ -23,13 +23,12 @@ import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageMoveObserver;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.text.format.Formatter;
 import android.util.Log;
 import android.view.View;
@@ -38,11 +37,16 @@ import android.widget.Button;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settings.DropDownPreference;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.ApplicationsState.AppEntry;
 import com.android.settings.applications.ApplicationsState.Callbacks;
-import com.android.settings.DropDownPreference;
+import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 
 public class AppStorageSettings extends AppInfoWithHeader
         implements OnClickListener, Callbacks, DropDownPreference.Callback {
@@ -53,7 +57,6 @@ public class AppStorageSettings extends AppInfoWithHeader
     private static final int OP_FAILED = 2;
     private static final int MSG_CLEAR_USER_DATA = 1;
     private static final int MSG_CLEAR_CACHE = 3;
-    private static final int MSG_PACKAGE_MOVE = 4;
 
     // invalid size value used initially and also when size retrieval through PackageManager
     // fails for whatever reason
@@ -64,13 +67,11 @@ public class AppStorageSettings extends AppInfoWithHeader
 
     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 2;
-    private static final int DLG_MOVE_FAILED = DLG_BASE + 3;
 
     private static final String KEY_MOVE_PREFERENCE = "app_location_setting";
     private static final String KEY_STORAGE_SETTINGS = "storage_settings";
     private static final String KEY_CACHE_SETTINGS = "cache_settings";
 
-    private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
     private TextView mTotalSize;
     private TextView mAppSize;
     private TextView mDataSize;
@@ -83,7 +84,6 @@ public class AppStorageSettings extends AppInfoWithHeader
     private Button mClearCacheButton;
 
     private DropDownPreference mMoveDropDown;
-    private boolean mMoveInProgress = false;
 
     private boolean mCanClearData = true;
     private boolean mHaveSizes = false;
@@ -97,7 +97,6 @@ public class AppStorageSettings extends AppInfoWithHeader
 
     private ClearCacheObserver mClearCacheObserver;
     private ClearUserDataObserver mClearDataObserver;
-    private PackageMoveObserver mPackageMoveObserver;
 
     // Resource strings
     private CharSequence mInvalidSizeStr;
@@ -107,7 +106,6 @@ public class AppStorageSettings extends AppInfoWithHeader
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
         addPreferencesFromResource(R.xml.app_storage_settings);
         setupViews();
     }
@@ -166,18 +164,19 @@ public class AppStorageSettings extends AppInfoWithHeader
 
     @Override
     public boolean onItemSelected(int pos, Object value) {
-        boolean selectedExternal = (Boolean) value;
-        boolean isExternal = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
-        if (selectedExternal ^ isExternal) {
-            if (mPackageMoveObserver == null) {
-                mPackageMoveObserver = new PackageMoveObserver();
-            }
-            int moveFlags = selectedExternal ? PackageManager.MOVE_EXTERNAL_MEDIA
-                    : PackageManager.MOVE_INTERNAL;
-            mMoveInProgress = true;
-            refreshButtons();
-            mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
+        final Context context = getActivity();
+
+        // If not current volume, kick off move wizard
+        final VolumeInfo targetVol = (VolumeInfo) value;
+        final VolumeInfo currentVol = context.getPackageManager().getApplicationCurrentVolume(
+                mAppEntry.info);
+        if (!Objects.equals(targetVol, currentVol)) {
+            final Intent intent = new Intent(context, StorageWizardMoveConfirm.class);
+            intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, targetVol.getId());
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
+            startActivity(intent);
         }
+
         return true;
     }
 
@@ -260,27 +259,17 @@ public class AppStorageSettings extends AppInfoWithHeader
         retrieveAppEntry();
         refreshButtons();
         refreshSizeInfo();
-        boolean isExternal = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
-        mMoveDropDown.setSelectedItem(isExternal ? 1 : 0);
+
+        final VolumeInfo currentVol = getActivity().getPackageManager()
+                .getApplicationCurrentVolume(mAppEntry.info);
+        mMoveDropDown.setSelectedValue(currentVol);
+
         return true;
     }
 
     private void refreshButtons() {
-        if (!mMoveInProgress) {
-            initMoveDropDown();
-            initDataButtons();
-        } else {
-            mMoveDropDown.setSummary(R.string.moving);
-            mMoveDropDown.setSelectable(false);
-        }
-    }
-
-    private void updateMoveEnabled(boolean enabled) {
-        mMoveDropDown.clearItems();
-        mMoveDropDown.addItem(R.string.storage_type_internal, false);
-        if (enabled) {
-            mMoveDropDown.addItem(R.string.storage_type_external, true);
-        }
+        initMoveDropDown();
+        initDataButtons();
     }
 
     private void initDataButtons() {
@@ -310,20 +299,18 @@ public class AppStorageSettings extends AppInfoWithHeader
     }
 
     private void initMoveDropDown() {
-        if (Environment.isExternalStorageEmulated()) {
-            updateMoveEnabled(false);
-            return;
-        }
-        boolean dataOnly = (mPackageInfo == null) && (mAppEntry != null);
-        boolean moveDisable = true;
-        if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-            // Always let apps move to internal storage from sdcard.
-            moveDisable = false;
-        } else {
-            mCanBeOnSdCardChecker.init();
-            moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
+        final Context context = getActivity();
+        final StorageManager storage = context.getSystemService(StorageManager.class);
+
+        final List<VolumeInfo> candidates = context.getPackageManager()
+                .getApplicationCandidateVolumes(mAppEntry.info);
+        Collections.sort(candidates, VolumeInfo.getDescriptionComparator());
+
+        mMoveDropDown.clearItems();
+        for (VolumeInfo vol : candidates) {
+            final String volDescrip = storage.getBestVolumeDescription(vol);
+            mMoveDropDown.addItem(volDescrip, vol);
         }
-        updateMoveEnabled(!moveDisable);
         mMoveDropDown.setSelectable(!mAppControlRestricted);
     }
 
@@ -351,21 +338,6 @@ public class AppStorageSettings extends AppInfoWithHeader
         }
     }
 
-    private void processMoveMsg(Message msg) {
-        int result = msg.arg1;
-        String packageName = mAppEntry.info.packageName;
-        // Refresh the button attributes.
-        mMoveInProgress = false;
-        if (result == PackageManager.MOVE_SUCCEEDED) {
-            Log.i(TAG, "Moved resources for " + packageName);
-            // Refresh size information again.
-            mState.requestSize(mPackageName, mUserId);
-        } else {
-            showDialogInner(DLG_MOVE_FAILED, result);
-        }
-        refreshUi();
-    }
-
     /*
      * Private method to handle clear message notification from observer when
      * the async operation from PackageManager is complete
@@ -382,24 +354,6 @@ public class AppStorageSettings extends AppInfoWithHeader
         }
     }
 
-    private CharSequence getMoveErrMsg(int errCode) {
-        switch (errCode) {
-            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
-                return getActivity().getString(R.string.insufficient_storage);
-            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
-                return getActivity().getString(R.string.does_not_exist);
-            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
-                return getActivity().getString(R.string.app_forward_locked);
-            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
-                return getActivity().getString(R.string.invalid_location);
-            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
-                return getActivity().getString(R.string.system_package);
-            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
-                return "";
-        }
-        return "";
-    }
-
     @Override
     protected AlertDialog createDialog(int id, int errorCode) {
         switch (id) {
@@ -427,14 +381,6 @@ public class AppStorageSettings extends AppInfoWithHeader
                             }
                         })
                         .create();
-            case DLG_MOVE_FAILED:
-                CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
-                        getMoveErrMsg(errorCode));
-                return new AlertDialog.Builder(getActivity())
-                        .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
-                        .setMessage(msg)
-                        .setNeutralButton(R.string.dlg_ok, null)
-                        .create();
         }
         return null;
     }
@@ -459,9 +405,6 @@ public class AppStorageSettings extends AppInfoWithHeader
                     // Refresh size info
                     mState.requestSize(mPackageName, mUserId);
                     break;
-                case MSG_PACKAGE_MOVE:
-                    processMoveMsg(msg);
-                    break;
             }
         }
     };
@@ -508,13 +451,4 @@ public class AppStorageSettings extends AppInfoWithHeader
            mHandler.sendMessage(msg);
         }
     }
-
-    class PackageMoveObserver extends IPackageMoveObserver.Stub {
-        public void packageMoved(String packageName, int returnCode) throws RemoteException {
-            final Message msg = mHandler.obtainMessage(MSG_PACKAGE_MOVE);
-            msg.arg1 = returnCode;
-            mHandler.sendMessage(msg);
-        }
-    }
-
 }
index 162ca77..ddfc207 100644 (file)
@@ -295,20 +295,6 @@ public class ApplicationsState {
         }
     };
 
-    public static final AppFilter FILTER_ON_SD_CARD = new AppFilter() {
-        final CanBeOnSdCardChecker mCanBeOnSdCardChecker
-                = new CanBeOnSdCardChecker();
-
-        public void init() {
-            mCanBeOnSdCardChecker.init();
-        }
-
-        @Override
-        public boolean filterApp(AppEntry entry) {
-            return mCanBeOnSdCardChecker.check(entry.info);
-        }
-    };
-
     public static final AppFilter FILTER_DISABLED = new AppFilter() {
         public void init() {
         }
index b6b2b82..362903e 100644 (file)
@@ -20,15 +20,11 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.PreferenceFrameLayout;
@@ -53,7 +49,6 @@ import android.widget.Filterable;
 import android.widget.ListView;
 import android.widget.Spinner;
 
-import com.android.internal.content.PackageHelper;
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.HelpUtils;
 import com.android.settings.InstrumentedFragment;
@@ -74,47 +69,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-final class CanBeOnSdCardChecker {
-    final IPackageManager mPm;
-    int mInstallLocation;
-
-    CanBeOnSdCardChecker() {
-        mPm = IPackageManager.Stub.asInterface(
-                ServiceManager.getService("package"));
-    }
-
-    void init() {
-        try {
-            mInstallLocation = mPm.getInstallLocation();
-        } catch (RemoteException e) {
-            Log.e("CanBeOnSdCardChecker", "Is Package Manager running?");
-            return;
-        }
-    }
-
-    boolean check(ApplicationInfo info) {
-        boolean canBe = false;
-        if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-            canBe = true;
-        } else {
-            if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
-                        info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
-                    canBe = true;
-                } else if (info.installLocation
-                        == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
-                    if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) {
-                        // For apps with no preference and the default value set
-                        // to install on sdcard.
-                        canBe = true;
-                    }
-                }
-            }
-        }
-        return canBe;
-    }
-}
-
 /**
  * Activity to pick an application that will be used to display installation information and
  * options to uninstall/delete user data for system applications. This activity
index 1e33230..74040ef 100644 (file)
@@ -41,7 +41,6 @@ import com.android.settings.search.SearchIndexableRaw;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -91,21 +90,6 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
         setHasOptionsMenu(true);
     }
 
-    private static final Comparator<VolumeInfo> sVolumeComparator = 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());
-            }
-        }
-    };
-
     private final StorageEventListener mStorageListener = new StorageEventListener() {
         @Override
         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -133,7 +117,7 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
         mExternalCategory.removeAll();
 
         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
-        Collections.sort(volumes, sVolumeComparator);
+        Collections.sort(volumes, VolumeInfo.getDescriptionComparator());
 
         for (VolumeInfo vol : volumes) {
             if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java
new file mode 100644 (file)
index 0000000..36750c0
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.storage.VolumeInfo;
+
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+
+public class StorageWizardMoveConfirm extends StorageWizardBase {
+    private String mPackageName;
+    private ApplicationInfo mApp;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.storage_wizard_generic);
+
+        try {
+            mPackageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            mApp = getPackageManager().getApplicationInfo(mPackageName, 0);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        Preconditions.checkNotNull(mVolume);
+        Preconditions.checkNotNull(mApp);
+
+        // Sanity check that target volume is candidate
+        Preconditions.checkState(
+                getPackageManager().getApplicationCandidateVolumes(mApp).contains(mVolume));
+
+        final String appName = getPackageManager().getApplicationLabel(mApp).toString();
+        final String volumeName = mStorage.getBestVolumeDescription(mVolume);
+
+        setHeaderText(R.string.storage_wizard_move_confirm_title, appName);
+        setBodyText(R.string.storage_wizard_move_confirm_body, appName, volumeName);
+
+        getNextButton().setText(R.string.move_app);
+    }
+
+    @Override
+    public void onNavigateNext() {
+        final Intent intent = new Intent(this, StorageWizardMoveProgress.class);
+        intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
+        startActivity(intent);
+        finishAffinity();
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java b/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java
new file mode 100644 (file)
index 0000000..2023335
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import static com.android.settings.deviceinfo.StorageSettings.TAG;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.storage.VolumeInfo;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+
+import java.util.concurrent.CountDownLatch;
+
+public class StorageWizardMoveProgress extends StorageWizardBase {
+    private String mPackageName;
+    private ApplicationInfo mApp;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.storage_wizard_progress);
+
+        try {
+            mPackageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            mApp = getPackageManager().getApplicationInfo(mPackageName, 0);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        Preconditions.checkNotNull(mVolume);
+        Preconditions.checkNotNull(mApp);
+
+        final String appName = getPackageManager().getApplicationLabel(mApp).toString();
+        final String volumeName = mStorage.getBestVolumeDescription(mVolume);
+
+        setHeaderText(R.string.storage_wizard_move_progress_title, appName);
+        setBodyText(R.string.storage_wizard_move_progress_body, volumeName, appName);
+
+        setCurrentProgress(20);
+
+        getNextButton().setVisibility(View.GONE);
+
+        new MoveTask().execute();
+    }
+
+    private CharSequence moveStatusToMessage(int returnCode) {
+        switch (returnCode) {
+            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
+                return getString(R.string.insufficient_storage);
+            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
+                return getString(R.string.does_not_exist);
+            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
+                return getString(R.string.app_forward_locked);
+            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
+                return getString(R.string.invalid_location);
+            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
+                return getString(R.string.system_package);
+            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
+            default:
+                return getString(R.string.insufficient_storage);
+        }
+    }
+
+    private class LocalPackageMoveObserver extends IPackageMoveObserver.Stub {
+        public int returnCode;
+        public CountDownLatch finished = new CountDownLatch(1);
+
+        @Override
+        public void packageMoved(String packageName, int returnCode) throws RemoteException {
+            this.returnCode = returnCode;
+            this.finished.countDown();
+        }
+    }
+
+    public class MoveTask extends AsyncTask<Void, Void, Integer> {
+        @Override
+        protected Integer doInBackground(Void... params) {
+            try {
+                final LocalPackageMoveObserver observer = new LocalPackageMoveObserver();
+
+                if (mApp.isExternalAsec()) {
+                    getPackageManager().movePackage(mPackageName, observer,
+                            PackageManager.MOVE_INTERNAL);
+                } else if (mVolume.getType() == VolumeInfo.TYPE_PUBLIC) {
+                    getPackageManager().movePackage(mPackageName, observer,
+                            PackageManager.MOVE_EXTERNAL_MEDIA);
+                } else {
+                    getPackageManager().movePackageAndData(mPackageName, mVolume.fsUuid, observer);
+                }
+
+                observer.finished.await();
+                return observer.returnCode;
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to move", e);
+                return PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Integer returnCode) {
+            final Context context = StorageWizardMoveProgress.this;
+            if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+                finishAffinity();
+
+            } else {
+                Log.w(TAG, "Move failed with status " + returnCode);
+                Toast.makeText(context, moveStatusToMessage(returnCode), Toast.LENGTH_LONG).show();
+                finishAffinity();
+            }
+        }
+    }
+}