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.tv.settings.device.storage; 18 19 import android.app.Activity; 20 import android.app.Fragment; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.os.Bundle; 27 import android.os.storage.DiskInfo; 28 import android.os.storage.StorageManager; 29 import android.os.storage.VolumeInfo; 30 import android.support.annotation.NonNull; 31 import android.support.annotation.Nullable; 32 import android.support.v4.content.LocalBroadcastManager; 33 import android.text.TextUtils; 34 import android.util.Log; 35 import android.widget.Toast; 36 37 import com.android.tv.settings.R; 38 39 import java.util.List; 40 41 public class FormatActivity extends Activity 42 implements FormatAsPrivateStepFragment.Callback, 43 FormatAsPublicStepFragment.Callback, SlowDriveStepFragment.Callback { 44 45 private static final String TAG = "FormatActivity"; 46 47 public static final String INTENT_ACTION_FORMAT_AS_PRIVATE = 48 "com.android.tv.settings.device.storage.FormatActivity.formatAsPrivate"; 49 public static final String INTENT_ACTION_FORMAT_AS_PUBLIC = 50 "com.android.tv.settings.device.storage.FormatActivity.formatAsPublic"; 51 52 private static final String SAVE_STATE_FORMAT_PRIVATE_DISK_ID = 53 "StorageResetActivity.formatPrivateDiskId"; 54 private static final String SAVE_STATE_FORMAT_DISK_DESC = 55 "StorageResetActivity.formatDiskDesc"; 56 private static final String SAVE_STATE_FORMAT_PUBLIC_DISK_ID = 57 "StorageResetActivity.formatPrivateDiskId"; 58 59 // Non-null means we're in the process of formatting this volume as private 60 private String mFormatAsPrivateDiskId; 61 // Non-null means we're in the process of formatting this volume as public 62 private String mFormatAsPublicDiskId; 63 64 private String mFormatDiskDesc; 65 66 private final BroadcastReceiver mFormatReceiver = new FormatReceiver(); 67 private PackageManager mPackageManager; 68 private StorageManager mStorageManager; 69 getFormatAsPublicIntent(Context context, String diskId)70 public static Intent getFormatAsPublicIntent(Context context, String diskId) { 71 final Intent i = new Intent(context, FormatActivity.class); 72 i.setAction(INTENT_ACTION_FORMAT_AS_PUBLIC); 73 i.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 74 return i; 75 } 76 getFormatAsPrivateIntent(Context context, String diskId)77 public static Intent getFormatAsPrivateIntent(Context context, String diskId) { 78 final Intent i = new Intent(context, FormatActivity.class); 79 i.setAction(INTENT_ACTION_FORMAT_AS_PRIVATE); 80 i.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 81 return i; 82 } 83 84 @Override onCreate(@ullable Bundle savedInstanceState)85 protected void onCreate(@Nullable Bundle savedInstanceState) { 86 super.onCreate(savedInstanceState); 87 88 mPackageManager = getPackageManager(); 89 mStorageManager = getSystemService(StorageManager.class); 90 91 final IntentFilter filter = new IntentFilter(); 92 filter.addAction(SettingsStorageService.ACTION_FORMAT_AS_PRIVATE); 93 filter.addAction(SettingsStorageService.ACTION_FORMAT_AS_PUBLIC); 94 LocalBroadcastManager.getInstance(this).registerReceiver(mFormatReceiver, filter); 95 96 if (savedInstanceState != null) { 97 mFormatAsPrivateDiskId = 98 savedInstanceState.getString(SAVE_STATE_FORMAT_PRIVATE_DISK_ID); 99 mFormatAsPublicDiskId = savedInstanceState.getString(SAVE_STATE_FORMAT_PUBLIC_DISK_ID); 100 mFormatDiskDesc = savedInstanceState.getString(SAVE_STATE_FORMAT_DISK_DESC); 101 } else { 102 final String diskId = getIntent().getStringExtra(DiskInfo.EXTRA_DISK_ID); 103 final String action = getIntent().getAction(); 104 final Fragment f; 105 if (TextUtils.equals(action, INTENT_ACTION_FORMAT_AS_PRIVATE)) { 106 f = FormatAsPrivateStepFragment.newInstance(diskId); 107 } else if (TextUtils.equals(action, INTENT_ACTION_FORMAT_AS_PUBLIC)) { 108 f = FormatAsPublicStepFragment.newInstance(diskId); 109 } else { 110 throw new IllegalStateException("No known action specified"); 111 } 112 getFragmentManager().beginTransaction() 113 .add(android.R.id.content, f) 114 .commit(); 115 } 116 } 117 118 @Override onResume()119 protected void onResume() { 120 super.onResume(); 121 if (!TextUtils.isEmpty(mFormatAsPrivateDiskId)) { 122 final VolumeInfo volumeInfo = findVolume(mFormatAsPrivateDiskId); 123 if (volumeInfo != null && volumeInfo.getType() == VolumeInfo.TYPE_PRIVATE) { 124 // Formatting must have completed while we were paused 125 // We've lost the benchmark data, so just assume the drive is fast enough 126 handleFormatAsPrivateComplete(-1, -1); 127 } 128 } 129 } 130 131 @Override onDestroy()132 protected void onDestroy() { 133 super.onDestroy(); 134 LocalBroadcastManager.getInstance(this).unregisterReceiver(mFormatReceiver); 135 } 136 findVolume(String diskId)137 private VolumeInfo findVolume(String diskId) { 138 final List<VolumeInfo> vols = mStorageManager.getVolumes(); 139 for (final VolumeInfo vol : vols) { 140 if (TextUtils.equals(diskId, vol.getDiskId()) 141 && (vol.getType() == VolumeInfo.TYPE_PRIVATE)) { 142 return vol; 143 } 144 } 145 return null; 146 } 147 148 @Override onSaveInstanceState(@onNull Bundle outState)149 protected void onSaveInstanceState(@NonNull Bundle outState) { 150 super.onSaveInstanceState(outState); 151 outState.putString(SAVE_STATE_FORMAT_PRIVATE_DISK_ID, mFormatAsPrivateDiskId); 152 outState.putString(SAVE_STATE_FORMAT_PUBLIC_DISK_ID, mFormatAsPublicDiskId); 153 outState.putString(SAVE_STATE_FORMAT_DISK_DESC, mFormatDiskDesc); 154 } 155 handleFormatAsPrivateComplete(float privateBench, float internalBench)156 private void handleFormatAsPrivateComplete(float privateBench, float internalBench) { 157 if (Math.abs(-1 - privateBench) < 0.1) { 158 final float frac = privateBench / internalBench; 159 Log.d(TAG, "New volume is " + frac + "x the speed of internal"); 160 161 // TODO: better threshold 162 if (privateBench > 2000000000) { 163 getFragmentManager().beginTransaction() 164 .replace(android.R.id.content, 165 SlowDriveStepFragment.newInstance()) 166 .commit(); 167 return; 168 } 169 } 170 launchMigrateStorageAndFinish(mFormatAsPrivateDiskId); 171 } 172 173 private class FormatReceiver extends BroadcastReceiver { 174 175 @Override onReceive(Context context, Intent intent)176 public void onReceive(Context context, Intent intent) { 177 if (TextUtils.equals(intent.getAction(), 178 SettingsStorageService.ACTION_FORMAT_AS_PRIVATE) 179 && !TextUtils.isEmpty(mFormatAsPrivateDiskId)) { 180 final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID); 181 if (TextUtils.equals(mFormatAsPrivateDiskId, diskId)) { 182 final boolean success = 183 intent.getBooleanExtra(SettingsStorageService.EXTRA_SUCCESS, false); 184 if (success) { 185 if (isResumed()) { 186 final float privateBench = intent.getFloatExtra( 187 SettingsStorageService.EXTRA_PRIVATE_BENCH, -1); 188 final float internalBench = intent.getFloatExtra( 189 SettingsStorageService.EXTRA_INTERNAL_BENCH, -1); 190 handleFormatAsPrivateComplete(privateBench, internalBench); 191 } 192 193 Toast.makeText(context, getString( 194 R.string.storage_format_success, mFormatDiskDesc), 195 Toast.LENGTH_SHORT).show(); 196 } else { 197 Toast.makeText(context, 198 getString(R.string.storage_format_failure, mFormatDiskDesc), 199 Toast.LENGTH_SHORT).show(); 200 finish(); 201 } 202 } 203 } else if (TextUtils.equals(intent.getAction(), 204 SettingsStorageService.ACTION_FORMAT_AS_PUBLIC) 205 && !TextUtils.isEmpty(mFormatAsPublicDiskId)) { 206 final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID); 207 if (TextUtils.equals(mFormatAsPublicDiskId, diskId)) { 208 final boolean success = 209 intent.getBooleanExtra(SettingsStorageService.EXTRA_SUCCESS, false); 210 if (success) { 211 Toast.makeText(context, 212 getString(R.string.storage_format_success, 213 mFormatDiskDesc), Toast.LENGTH_SHORT).show(); 214 } else { 215 Toast.makeText(context, 216 getString(R.string.storage_format_failure, 217 mFormatDiskDesc), Toast.LENGTH_SHORT).show(); 218 } 219 finish(); 220 } 221 } 222 } 223 } 224 225 @Override onRequestFormatAsPrivate(String diskId)226 public void onRequestFormatAsPrivate(String diskId) { 227 final FormattingProgressFragment fragment = FormattingProgressFragment.newInstance(); 228 getFragmentManager().beginTransaction() 229 .replace(android.R.id.content, fragment) 230 .commit(); 231 232 mFormatAsPrivateDiskId = diskId; 233 final List<VolumeInfo> volumes = mStorageManager.getVolumes(); 234 for (final VolumeInfo volume : volumes) { 235 if ((volume.getType() == VolumeInfo.TYPE_PRIVATE || 236 volume.getType() == VolumeInfo.TYPE_PUBLIC) && 237 TextUtils.equals(volume.getDiskId(), diskId)) { 238 mFormatDiskDesc = mStorageManager.getBestVolumeDescription(volume); 239 } 240 } 241 if (TextUtils.isEmpty(mFormatDiskDesc)) { 242 final DiskInfo info = mStorageManager.findDiskById(diskId); 243 if (info != null) { 244 mFormatDiskDesc = info.getDescription(); 245 } 246 } 247 SettingsStorageService.formatAsPrivate(this, diskId); 248 } 249 launchMigrateStorageAndFinish(String diskId)250 private void launchMigrateStorageAndFinish(String diskId) { 251 final List<VolumeInfo> candidates = 252 mPackageManager.getPrimaryStorageCandidateVolumes(); 253 VolumeInfo moveTarget = null; 254 for (final VolumeInfo candidate : candidates) { 255 if (TextUtils.equals(candidate.getDiskId(), diskId)) { 256 moveTarget = candidate; 257 break; 258 } 259 } 260 261 if (moveTarget != null) { 262 startActivity(MigrateStorageActivity.getLaunchIntent(this, moveTarget.getId(), true)); 263 } 264 265 finish(); 266 } 267 268 @Override onRequestFormatAsPublic(String diskId, String volumeId)269 public void onRequestFormatAsPublic(String diskId, String volumeId) { 270 final FormattingProgressFragment fragment = FormattingProgressFragment.newInstance(); 271 getFragmentManager().beginTransaction() 272 .replace(android.R.id.content, fragment) 273 .commit(); 274 275 mFormatAsPublicDiskId = diskId; 276 if (!TextUtils.isEmpty(volumeId)) { 277 final VolumeInfo info = mStorageManager.findVolumeById(volumeId); 278 if (info != null) { 279 mFormatDiskDesc = mStorageManager.getBestVolumeDescription(info); 280 } 281 } 282 if (TextUtils.isEmpty(mFormatDiskDesc)) { 283 final DiskInfo info = mStorageManager.findDiskById(diskId); 284 if (info != null) { 285 mFormatDiskDesc = info.getDescription(); 286 } 287 } 288 SettingsStorageService.formatAsPublic(this, diskId); 289 } 290 291 @Override onCancelFormatDialog()292 public void onCancelFormatDialog() { 293 finish(); 294 } 295 296 @Override onSlowDriveWarningComplete()297 public void onSlowDriveWarningComplete() { 298 launchMigrateStorageAndFinish(mFormatAsPrivateDiskId); 299 } 300 } 301