1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.deviceinfo;
18 
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.app.DialogFragment;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.pm.IPackageMoveObserver;
26 import android.os.AsyncTask;
27 import android.os.Bundle;
28 import android.os.storage.DiskInfo;
29 import android.os.storage.StorageManager;
30 import android.os.storage.VolumeInfo;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.Toast;
35 
36 import com.android.settings.R;
37 
38 import java.util.Objects;
39 
40 import static com.android.settings.deviceinfo.StorageSettings.TAG;
41 
42 public class StorageWizardFormatProgress extends StorageWizardBase {
43     private static final String TAG_SLOW_WARNING = "slow_warning";
44 
45     private boolean mFormatPrivate;
46 
47     private PartitionTask mTask;
48 
49     @Override
onCreate(Bundle savedInstanceState)50     protected void onCreate(Bundle savedInstanceState) {
51         super.onCreate(savedInstanceState);
52         if (mDisk == null) {
53             finish();
54             return;
55         }
56         setContentView(R.layout.storage_wizard_progress);
57         setKeepScreenOn(true);
58 
59         mFormatPrivate = getIntent().getBooleanExtra(
60                 StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, false);
61         setIllustrationType(
62                 mFormatPrivate ? ILLUSTRATION_INTERNAL : ILLUSTRATION_PORTABLE);
63 
64         setHeaderText(R.string.storage_wizard_format_progress_title, mDisk.getDescription());
65         setBodyText(R.string.storage_wizard_format_progress_body, mDisk.getDescription());
66 
67         getNextButton().setVisibility(View.GONE);
68 
69         mTask = (PartitionTask) getLastNonConfigurationInstance();
70         if (mTask == null) {
71             mTask = new PartitionTask();
72             mTask.setActivity(this);
73             mTask.execute();
74         } else {
75             mTask.setActivity(this);
76         }
77     }
78 
79     @Override
onRetainNonConfigurationInstance()80     public Object onRetainNonConfigurationInstance() {
81         return mTask;
82     }
83 
84     public static class PartitionTask extends AsyncTask<Void, Integer, Exception> {
85         public StorageWizardFormatProgress mActivity;
86 
87         private volatile int mProgress = 20;
88 
89         private volatile long mInternalBench;
90         private volatile long mPrivateBench;
91 
92         @Override
doInBackground(Void... params)93         protected Exception doInBackground(Void... params) {
94             final StorageWizardFormatProgress activity = mActivity;
95             final StorageManager storage = mActivity.mStorage;
96             try {
97                 if (activity.mFormatPrivate) {
98                     storage.partitionPrivate(activity.mDisk.getId());
99                     publishProgress(40);
100 
101                     mInternalBench = storage.benchmark(null);
102                     publishProgress(60);
103 
104                     final VolumeInfo privateVol = activity.findFirstVolume(VolumeInfo.TYPE_PRIVATE);
105                     mPrivateBench = storage.benchmark(privateVol.getId());
106 
107                     // If we just adopted the device that had been providing
108                     // physical storage, then automatically move storage to the
109                     // new emulated volume.
110                     if (activity.mDisk.isDefaultPrimary()
111                             && Objects.equals(storage.getPrimaryStorageUuid(),
112                                     StorageManager.UUID_PRIMARY_PHYSICAL)) {
113                         Log.d(TAG, "Just formatted primary physical; silently moving "
114                                 + "storage to new emulated volume");
115                         storage.setPrimaryStorageUuid(privateVol.getFsUuid(), new SilentObserver());
116                     }
117 
118                 } else {
119                     storage.partitionPublic(activity.mDisk.getId());
120                 }
121                 return null;
122             } catch (Exception e) {
123                 return e;
124             }
125         }
126 
127         @Override
onProgressUpdate(Integer... progress)128         protected void onProgressUpdate(Integer... progress) {
129             mProgress = progress[0];
130             mActivity.setCurrentProgress(mProgress);
131         }
132 
setActivity(StorageWizardFormatProgress activity)133         public void setActivity(StorageWizardFormatProgress activity) {
134             mActivity = activity;
135             mActivity.setCurrentProgress(mProgress);
136         }
137 
138         @Override
onPostExecute(Exception e)139         protected void onPostExecute(Exception e) {
140             final StorageWizardFormatProgress activity = mActivity;
141             if (activity.isDestroyed()) {
142                 return;
143             }
144 
145             if (e != null) {
146                 Log.e(TAG, "Failed to partition", e);
147                 Toast.makeText(activity, e.getMessage(), Toast.LENGTH_LONG).show();
148                 activity.finishAffinity();
149                 return;
150             }
151 
152             if (activity.mFormatPrivate) {
153                 final float pct = (float) mInternalBench / (float) mPrivateBench;
154                 Log.d(TAG, "New volume is " + pct + "x the speed of internal");
155 
156                 // To help set user expectations around device performance, we
157                 // warn if the adopted media is 0.25x the speed of internal
158                 // storage or slower.
159                 if (Float.isNaN(pct) || pct < 0.25) {
160                     final SlowWarningFragment dialog = new SlowWarningFragment();
161                     dialog.showAllowingStateLoss(activity.getFragmentManager(), TAG_SLOW_WARNING);
162                 } else {
163                     activity.onFormatFinished();
164                 }
165             } else {
166                 activity.onFormatFinished();
167             }
168         }
169     }
170 
171     public static class SlowWarningFragment extends DialogFragment {
172         @Override
onCreateDialog(Bundle savedInstanceState)173         public Dialog onCreateDialog(Bundle savedInstanceState) {
174             final Context context = getActivity();
175 
176             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
177 
178             final StorageWizardFormatProgress target =
179                     (StorageWizardFormatProgress) getActivity();
180             final String descrip = target.getDiskDescription();
181             final String genericDescip = target.getGenericDiskDescription();
182             builder.setMessage(TextUtils.expandTemplate(getText(R.string.storage_wizard_slow_body),
183                     descrip, genericDescip));
184 
185             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
186                 @Override
187                 public void onClick(DialogInterface dialog, int which) {
188                     final StorageWizardFormatProgress target =
189                             (StorageWizardFormatProgress) getActivity();
190                     target.onFormatFinished();
191                 }
192             });
193 
194             return builder.create();
195         }
196     }
197 
getDiskDescription()198     private String getDiskDescription() {
199         return mDisk.getDescription();
200     }
201 
getGenericDiskDescription()202     private String getGenericDiskDescription() {
203         // TODO: move this directly to DiskInfo
204         if (mDisk.isSd()) {
205             return getString(com.android.internal.R.string.storage_sd_card);
206         } else if (mDisk.isUsb()) {
207             return getString(com.android.internal.R.string.storage_usb_drive);
208         } else {
209             return null;
210         }
211     }
212 
onFormatFinished()213     private void onFormatFinished() {
214         final String forgetUuid = getIntent().getStringExtra(
215                 StorageWizardFormatConfirm.EXTRA_FORGET_UUID);
216         if (!TextUtils.isEmpty(forgetUuid)) {
217             mStorage.forgetVolume(forgetUuid);
218         }
219 
220         final boolean offerMigrate;
221         if (mFormatPrivate) {
222             // Offer to migrate only if storage is currently internal
223             final VolumeInfo privateVol = getPackageManager()
224                     .getPrimaryStorageCurrentVolume();
225             offerMigrate = (privateVol != null
226                     && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.getId()));
227         } else {
228             offerMigrate = false;
229         }
230 
231         if (offerMigrate) {
232             final Intent intent = new Intent(this, StorageWizardMigrate.class);
233             intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
234             startActivity(intent);
235         } else {
236             final Intent intent = new Intent(this, StorageWizardReady.class);
237             intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
238             startActivity(intent);
239         }
240         finishAffinity();
241     }
242 
243     private static class SilentObserver extends IPackageMoveObserver.Stub {
244         @Override
onCreated(int moveId, Bundle extras)245         public void onCreated(int moveId, Bundle extras) {
246             // Ignored
247         }
248 
249         @Override
onStatusChanged(int moveId, int status, long estMillis)250         public void onStatusChanged(int moveId, int status, long estMillis) {
251             // Ignored
252         }
253     }
254 }
255