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