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 static android.os.storage.VolumeInfo.TYPE_PRIVATE; 20 21 import android.content.Intent; 22 import android.content.pm.IPackageMoveObserver; 23 import android.os.AsyncTask; 24 import android.os.Bundle; 25 import android.os.IVoldTaskListener; 26 import android.os.PersistableBundle; 27 import android.os.SystemProperties; 28 import android.os.storage.StorageManager; 29 import android.os.storage.VolumeInfo; 30 import android.util.Log; 31 import android.view.View; 32 import android.widget.Toast; 33 34 import com.android.settings.R; 35 36 import java.util.Objects; 37 import java.util.concurrent.CompletableFuture; 38 import java.util.concurrent.TimeUnit; 39 import android.view.WindowManager; 40 41 public class StorageWizardFormatProgress extends StorageWizardBase { 42 private static final String TAG = "StorageWizardFormatProgress"; 43 44 private static final String PROP_DEBUG_STORAGE_SLOW = "sys.debug.storage_slow"; 45 46 private boolean mFormatPrivate; 47 48 private PartitionTask mTask; 49 50 @Override onCreate(Bundle savedInstanceState)51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 if (mDisk == null) { 54 finish(); 55 return; 56 } 57 setContentView(R.layout.storage_wizard_progress); 58 59 // hide the navigation bar for this activity only. So that user can not press back button accidentally. 60 View decorView = getWindow().getDecorView(); 61 int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 62 decorView.setSystemUiVisibility(uiOptions); 63 64 //disable touch in activity so user can not make the hidden navigation bar visible. 65 getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 66 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 67 68 setKeepScreenOn(true); 69 70 mFormatPrivate = getIntent().getBooleanExtra(EXTRA_FORMAT_PRIVATE, false); 71 72 setHeaderText(R.string.storage_wizard_format_progress_title, getDiskShortDescription()); 73 setBodyText(R.string.storage_wizard_format_progress_body, getDiskDescription()); 74 setBackButtonVisibility(View.INVISIBLE); 75 setNextButtonVisibility(View.INVISIBLE); 76 mTask = (PartitionTask) getLastCustomNonConfigurationInstance(); 77 if (mTask == null) { 78 mTask = new PartitionTask(); 79 mTask.setActivity(this); 80 mTask.execute(); 81 } else { 82 mTask.setActivity(this); 83 } 84 } 85 86 @Override onRetainCustomNonConfigurationInstance()87 public Object onRetainCustomNonConfigurationInstance() { 88 return mTask; 89 } 90 91 public static class PartitionTask extends AsyncTask<Void, Integer, Exception> { 92 public StorageWizardFormatProgress mActivity; 93 94 private volatile int mProgress = 20; 95 96 private volatile long mPrivateBench; 97 98 @Override doInBackground(Void... params)99 protected Exception doInBackground(Void... params) { 100 final StorageWizardFormatProgress activity = mActivity; 101 final StorageManager storage = mActivity.mStorage; 102 try { 103 if (activity.mFormatPrivate) { 104 storage.partitionPrivate(activity.mDisk.getId()); 105 publishProgress(40); 106 107 final VolumeInfo privateVol = activity.findFirstVolume(TYPE_PRIVATE, 25); 108 final CompletableFuture<PersistableBundle> result = new CompletableFuture<>(); 109 storage.benchmark(privateVol.getId(), new IVoldTaskListener.Stub() { 110 @Override 111 public void onStatus(int status, PersistableBundle extras) { 112 // Map benchmark 0-100% progress onto 40-80% 113 publishProgress(40 + ((status * 40) / 100)); 114 } 115 116 @Override 117 public void onFinished(int status, PersistableBundle extras) { 118 result.complete(extras); 119 } 120 }); 121 mPrivateBench = result.get(60, TimeUnit.SECONDS).getLong("run", Long.MAX_VALUE); 122 123 // If we just adopted the device that had been providing 124 // physical storage, then automatically move storage to the 125 // new emulated volume. 126 if (activity.mDisk.isDefaultPrimary() 127 && Objects.equals(storage.getPrimaryStorageUuid(), 128 StorageManager.UUID_PRIMARY_PHYSICAL)) { 129 Log.d(TAG, "Just formatted primary physical; silently moving " 130 + "storage to new emulated volume"); 131 storage.setPrimaryStorageUuid(privateVol.getFsUuid(), new SilentObserver()); 132 } 133 134 } else { 135 storage.partitionPublic(activity.mDisk.getId()); 136 } 137 return null; 138 } catch (Exception e) { 139 return e; 140 } 141 } 142 143 @Override onProgressUpdate(Integer... progress)144 protected void onProgressUpdate(Integer... progress) { 145 mProgress = progress[0]; 146 mActivity.setCurrentProgress(mProgress); 147 } 148 setActivity(StorageWizardFormatProgress activity)149 public void setActivity(StorageWizardFormatProgress activity) { 150 mActivity = activity; 151 mActivity.setCurrentProgress(mProgress); 152 } 153 154 @Override onPostExecute(Exception e)155 protected void onPostExecute(Exception e) { 156 final StorageWizardFormatProgress activity = mActivity; 157 if (activity.isDestroyed()) { 158 return; 159 } 160 161 if (e != null) { 162 Log.e(TAG, "Failed to partition", e); 163 Toast.makeText(activity, e.getMessage(), Toast.LENGTH_LONG).show(); 164 activity.finishAffinity(); 165 return; 166 } 167 168 if (activity.mFormatPrivate) { 169 // When the adoptable storage feature originally launched, we 170 // benchmarked both internal storage and the newly adopted 171 // storage and we warned if the adopted device was less than 172 // 0.25x the speed of internal. (The goal was to help set user 173 // expectations and encourage use of devices comparable to 174 // internal storage performance.) 175 176 // However, since then, internal storage has started moving from 177 // eMMC to UFS, which can significantly outperform adopted 178 // devices, causing the speed warning to always trigger. To 179 // mitigate this, we've switched to using a static threshold. 180 181 // The static threshold was derived by running the benchmark on 182 // a wide selection of SD cards from several vendors; here are 183 // some 50th percentile results from 20+ runs of each card: 184 185 // 8GB C4 40MB/s+: 3282ms 186 // 16GB C10 40MB/s+: 1881ms 187 // 32GB C10 40MB/s+: 2897ms 188 // 32GB U3 80MB/s+: 1595ms 189 // 32GB C10 80MB/s+: 1680ms 190 // 128GB U1 80MB/s+: 1532ms 191 192 // Thus a 2000ms static threshold strikes a reasonable balance 193 // to help us identify slower cards. Users can still proceed 194 // with these slower cards; we're just showing a warning. 195 196 // The above analysis was done using the "r1572:w1001:s285" 197 // benchmark, and it should be redone any time the benchmark 198 // changes. 199 200 Log.d(TAG, "New volume took " + mPrivateBench + "ms to run benchmark"); 201 if (mPrivateBench > 2000 202 || SystemProperties.getBoolean(PROP_DEBUG_STORAGE_SLOW, false)) { 203 mActivity.onFormatFinishedSlow(); 204 } else { 205 mActivity.onFormatFinished(); 206 } 207 } else { 208 mActivity.onFormatFinished(); 209 } 210 } 211 } 212 onFormatFinished()213 public void onFormatFinished() { 214 final Intent intent = new Intent(this, StorageWizardFormatSlow.class); 215 intent.putExtra(EXTRA_FORMAT_SLOW, false); 216 startActivity(intent); 217 finishAffinity(); 218 } 219 onFormatFinishedSlow()220 public void onFormatFinishedSlow() { 221 final Intent intent = new Intent(this, StorageWizardFormatSlow.class); 222 intent.putExtra(EXTRA_FORMAT_SLOW, true); 223 startActivity(intent); 224 finishAffinity(); 225 } 226 227 private static class SilentObserver extends IPackageMoveObserver.Stub { 228 @Override onCreated(int moveId, Bundle extras)229 public void onCreated(int moveId, Bundle extras) { 230 // Ignored 231 } 232 233 @Override onStatusChanged(int moveId, int status, long estMillis)234 public void onStatusChanged(int moveId, int status, long estMillis) { 235 // Ignored 236 } 237 } 238 } 239