blob: 696d4d5c7a7ce08dc1eb37695d128cfd2576dd67 [file] [log] [blame]
/*
* 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.tv.settings.device.storage;
import android.app.Activity;
import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.android.tv.settings.R;
import java.util.List;
public class FormatActivity extends Activity
implements FormatAsPrivateStepFragment.Callback,
FormatAsPublicStepFragment.Callback, SlowDriveStepFragment.Callback {
private static final String TAG = "FormatActivity";
public static final String INTENT_ACTION_FORMAT_AS_PRIVATE =
"com.android.tv.settings.device.storage.FormatActivity.formatAsPrivate";
public static final String INTENT_ACTION_FORMAT_AS_PUBLIC =
"com.android.tv.settings.device.storage.FormatActivity.formatAsPublic";
private static final String SAVE_STATE_FORMAT_PRIVATE_DISK_ID =
"StorageResetActivity.formatPrivateDiskId";
private static final String SAVE_STATE_FORMAT_DISK_DESC =
"StorageResetActivity.formatDiskDesc";
private static final String SAVE_STATE_FORMAT_PUBLIC_DISK_ID =
"StorageResetActivity.formatPrivateDiskId";
// Non-null means we're in the process of formatting this volume as private
@VisibleForTesting
String mFormatAsPrivateDiskId;
// Non-null means we're in the process of formatting this volume as public
@VisibleForTesting
String mFormatAsPublicDiskId;
private String mFormatDiskDesc;
private final BroadcastReceiver mFormatReceiver = new FormatReceiver(this);
private PackageManager mPackageManager;
private StorageManager mStorageManager;
public static Intent getFormatAsPublicIntent(Context context, String diskId) {
final Intent i = new Intent(context, FormatActivity.class);
i.setAction(INTENT_ACTION_FORMAT_AS_PUBLIC);
i.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
return i;
}
public static Intent getFormatAsPrivateIntent(Context context, String diskId) {
final Intent i = new Intent(context, FormatActivity.class);
i.setAction(INTENT_ACTION_FORMAT_AS_PRIVATE);
i.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
return i;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackageManager = getPackageManager();
mStorageManager = getSystemService(StorageManager.class);
final IntentFilter filter = new IntentFilter();
filter.addAction(SettingsStorageService.ACTION_FORMAT_AS_PRIVATE);
filter.addAction(SettingsStorageService.ACTION_FORMAT_AS_PUBLIC);
LocalBroadcastManager.getInstance(this).registerReceiver(mFormatReceiver, filter);
if (savedInstanceState != null) {
mFormatAsPrivateDiskId =
savedInstanceState.getString(SAVE_STATE_FORMAT_PRIVATE_DISK_ID);
mFormatAsPublicDiskId = savedInstanceState.getString(SAVE_STATE_FORMAT_PUBLIC_DISK_ID);
mFormatDiskDesc = savedInstanceState.getString(SAVE_STATE_FORMAT_DISK_DESC);
} else {
final String diskId = getIntent().getStringExtra(DiskInfo.EXTRA_DISK_ID);
final String action = getIntent().getAction();
final Fragment f;
if (TextUtils.equals(action, INTENT_ACTION_FORMAT_AS_PRIVATE)) {
f = FormatAsPrivateStepFragment.newInstance(diskId);
} else if (TextUtils.equals(action, INTENT_ACTION_FORMAT_AS_PUBLIC)) {
f = FormatAsPublicStepFragment.newInstance(diskId);
} else {
throw new IllegalStateException("No known action specified");
}
getFragmentManager().beginTransaction()
.add(android.R.id.content, f)
.commit();
}
}
@Override
protected void onResume() {
super.onResume();
if (!TextUtils.isEmpty(mFormatAsPrivateDiskId)) {
final VolumeInfo volumeInfo = findVolume(mFormatAsPrivateDiskId);
if (volumeInfo != null && volumeInfo.getType() == VolumeInfo.TYPE_PRIVATE) {
// Formatting must have completed while we were paused
// We've lost the benchmark data, so just assume the drive is fast enough
handleFormatAsPrivateComplete(-1, -1);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mFormatReceiver);
}
private VolumeInfo findVolume(String diskId) {
final List<VolumeInfo> vols = mStorageManager.getVolumes();
for (final VolumeInfo vol : vols) {
if (TextUtils.equals(diskId, vol.getDiskId())
&& (vol.getType() == VolumeInfo.TYPE_PRIVATE)) {
return vol;
}
}
return null;
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SAVE_STATE_FORMAT_PRIVATE_DISK_ID, mFormatAsPrivateDiskId);
outState.putString(SAVE_STATE_FORMAT_PUBLIC_DISK_ID, mFormatAsPublicDiskId);
outState.putString(SAVE_STATE_FORMAT_DISK_DESC, mFormatDiskDesc);
}
@VisibleForTesting
void handleFormatAsPrivateComplete(float privateBench, float internalBench) {
if (Math.abs(-1 - privateBench) < 0.1) {
final float frac = privateBench / internalBench;
Log.d(TAG, "New volume is " + frac + "x the speed of internal");
// TODO: better threshold
if (privateBench > 2000000000) {
getFragmentManager().beginTransaction()
.replace(android.R.id.content,
SlowDriveStepFragment.newInstance())
.commit();
return;
}
}
launchMigrateStorageAndFinish(mFormatAsPrivateDiskId);
}
@VisibleForTesting
static class FormatReceiver extends BroadcastReceiver {
private final FormatActivity mActivity;
FormatReceiver(FormatActivity activity) {
mActivity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(),
SettingsStorageService.ACTION_FORMAT_AS_PRIVATE)
&& !TextUtils.isEmpty(mActivity.mFormatAsPrivateDiskId)) {
final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID);
if (TextUtils.equals(mActivity.mFormatAsPrivateDiskId, diskId)) {
final boolean success =
intent.getBooleanExtra(SettingsStorageService.EXTRA_SUCCESS, false);
if (success) {
if (mActivity.isResumed()) {
final float privateBench = intent.getLongExtra(
SettingsStorageService.EXTRA_PRIVATE_BENCH, -1);
final float internalBench = intent.getLongExtra(
SettingsStorageService.EXTRA_INTERNAL_BENCH, -1);
mActivity.handleFormatAsPrivateComplete(privateBench, internalBench);
}
Toast.makeText(context, mActivity.getString(
R.string.storage_format_success, mActivity.mFormatDiskDesc),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context,
mActivity.getString(R.string.storage_format_failure,
mActivity.mFormatDiskDesc),
Toast.LENGTH_SHORT).show();
mActivity.finish();
}
}
} else if (TextUtils.equals(intent.getAction(),
SettingsStorageService.ACTION_FORMAT_AS_PUBLIC)
&& !TextUtils.isEmpty(mActivity.mFormatAsPublicDiskId)) {
final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID);
if (TextUtils.equals(mActivity.mFormatAsPublicDiskId, diskId)) {
final boolean success =
intent.getBooleanExtra(SettingsStorageService.EXTRA_SUCCESS, false);
if (success) {
Toast.makeText(context,
mActivity.getString(R.string.storage_format_success,
mActivity.mFormatDiskDesc), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context,
mActivity.getString(R.string.storage_format_failure,
mActivity.mFormatDiskDesc), Toast.LENGTH_SHORT).show();
}
mActivity.finish();
}
}
}
}
@Override
public void onRequestFormatAsPrivate(String diskId) {
final FormattingProgressFragment fragment = FormattingProgressFragment.newInstance();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, fragment)
.commit();
mFormatAsPrivateDiskId = diskId;
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (final VolumeInfo volume : volumes) {
if ((volume.getType() == VolumeInfo.TYPE_PRIVATE ||
volume.getType() == VolumeInfo.TYPE_PUBLIC) &&
TextUtils.equals(volume.getDiskId(), diskId)) {
mFormatDiskDesc = mStorageManager.getBestVolumeDescription(volume);
}
}
if (TextUtils.isEmpty(mFormatDiskDesc)) {
final DiskInfo info = mStorageManager.findDiskById(diskId);
if (info != null) {
mFormatDiskDesc = info.getDescription();
}
}
SettingsStorageService.formatAsPrivate(this, diskId);
}
private void launchMigrateStorageAndFinish(String diskId) {
final List<VolumeInfo> candidates =
mPackageManager.getPrimaryStorageCandidateVolumes();
VolumeInfo moveTarget = null;
for (final VolumeInfo candidate : candidates) {
if (TextUtils.equals(candidate.getDiskId(), diskId)) {
moveTarget = candidate;
break;
}
}
if (moveTarget != null) {
startActivity(MigrateStorageActivity.getLaunchIntent(this, moveTarget.getId(), true));
}
finish();
}
@Override
public void onRequestFormatAsPublic(String diskId, String volumeId) {
final FormattingProgressFragment fragment = FormattingProgressFragment.newInstance();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, fragment)
.commit();
mFormatAsPublicDiskId = diskId;
if (!TextUtils.isEmpty(volumeId)) {
final VolumeInfo info = mStorageManager.findVolumeById(volumeId);
if (info != null) {
mFormatDiskDesc = mStorageManager.getBestVolumeDescription(info);
}
}
if (TextUtils.isEmpty(mFormatDiskDesc)) {
final DiskInfo info = mStorageManager.findDiskById(diskId);
if (info != null) {
mFormatDiskDesc = info.getDescription();
}
}
SettingsStorageService.formatAsPublic(this, diskId);
}
@Override
public void onCancelFormatDialog() {
finish();
}
@Override
public void onSlowDriveWarningComplete() {
launchMigrateStorageAndFinish(mFormatAsPrivateDiskId);
}
}