blob: 3905c88ac282507b64f14982ec6d4bb21e473963 [file] [log] [blame]
package com.android.internal.os.storage;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IMountService;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import com.android.internal.R;
/**
* Takes care of unmounting and formatting external storage.
*/
public class ExternalStorageFormatter extends Service
implements DialogInterface.OnCancelListener {
static final String TAG = "ExternalStorageFormatter";
public static final String FORMAT_ONLY = "com.android.internal.os.storage.FORMAT_ONLY";
public static final String FORMAT_AND_FACTORY_RESET = "com.android.internal.os.storage.FORMAT_AND_FACTORY_RESET";
public static final String EXTRA_ALWAYS_RESET = "always_reset";
// If non-null, the volume to format. Otherwise, will use the default external storage directory
private StorageVolume mStorageVolume;
public static final ComponentName COMPONENT_NAME
= new ComponentName("android", ExternalStorageFormatter.class.getName());
// Access using getMountService()
private IMountService mMountService = null;
private StorageManager mStorageManager = null;
private PowerManager.WakeLock mWakeLock;
private ProgressDialog mProgressDialog = null;
private boolean mFactoryReset = false;
private boolean mAlwaysReset = false;
StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onStorageStateChanged(String path, String oldState, String newState) {
Log.i(TAG, "Received storage state changed notification that " +
path + " changed state from " + oldState +
" to " + newState);
updateProgressState();
}
};
@Override
public void onCreate() {
super.onCreate();
if (mStorageManager == null) {
mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
mStorageManager.registerListener(mStorageListener);
}
mWakeLock = ((PowerManager)getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExternalStorageFormatter");
mWakeLock.acquire();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (FORMAT_AND_FACTORY_RESET.equals(intent.getAction())) {
mFactoryReset = true;
}
if (intent.getBooleanExtra(EXTRA_ALWAYS_RESET, false)) {
mAlwaysReset = true;
}
mStorageVolume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setCancelable(true);
mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
if (!mAlwaysReset) {
mProgressDialog.setOnCancelListener(this);
}
updateProgressState();
mProgressDialog.show();
}
return Service.START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
if (mStorageManager != null) {
mStorageManager.unregisterListener(mStorageListener);
}
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
mWakeLock.release();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCancel(DialogInterface dialog) {
IMountService mountService = getMountService();
String extStoragePath = mStorageVolume == null ?
Environment.getExternalStorageDirectory().toString() :
mStorageVolume.getPath();
try {
mountService.mountVolume(extStoragePath);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with mount service", e);
}
stopSelf();
}
void fail(int msg) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
if (mAlwaysReset) {
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
}
stopSelf();
}
void updateProgressState() {
String status = mStorageVolume == null ?
Environment.getExternalStorageState() :
mStorageManager.getVolumeState(mStorageVolume.getPath());
if (Environment.MEDIA_MOUNTED.equals(status)
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)) {
updateProgressDialog(R.string.progress_unmounting);
IMountService mountService = getMountService();
final String extStoragePath = mStorageVolume == null ?
Environment.getExternalStorageDirectory().toString() :
mStorageVolume.getPath();
try {
// Remove encryption mapping if this is an unmount for a factory reset.
mountService.unmountVolume(extStoragePath, true, mFactoryReset);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with mount service", e);
}
} else if (Environment.MEDIA_NOFS.equals(status)
|| Environment.MEDIA_UNMOUNTED.equals(status)
|| Environment.MEDIA_UNMOUNTABLE.equals(status)) {
updateProgressDialog(R.string.progress_erasing);
final IMountService mountService = getMountService();
final String extStoragePath = mStorageVolume == null ?
Environment.getExternalStorageDirectory().toString() :
mStorageVolume.getPath();
if (mountService != null) {
new Thread() {
@Override
public void run() {
boolean success = false;
try {
mountService.formatVolume(extStoragePath);
success = true;
} catch (Exception e) {
Toast.makeText(ExternalStorageFormatter.this,
R.string.format_error, Toast.LENGTH_LONG).show();
}
if (success) {
if (mFactoryReset) {
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
// Intent handling is asynchronous -- assume it will happen soon.
stopSelf();
return;
}
}
// If we didn't succeed, or aren't doing a full factory
// reset, then it is time to remount the storage.
if (!success && mAlwaysReset) {
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
} else {
try {
mountService.mountVolume(extStoragePath);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with mount service", e);
}
}
stopSelf();
return;
}
}.start();
} else {
Log.w(TAG, "Unable to locate IMountService");
}
} else if (Environment.MEDIA_BAD_REMOVAL.equals(status)) {
fail(R.string.media_bad_removal);
} else if (Environment.MEDIA_CHECKING.equals(status)) {
fail(R.string.media_checking);
} else if (Environment.MEDIA_REMOVED.equals(status)) {
fail(R.string.media_removed);
} else if (Environment.MEDIA_SHARED.equals(status)) {
fail(R.string.media_shared);
} else {
fail(R.string.media_unknown_state);
Log.w(TAG, "Unknown storage state: " + status);
stopSelf();
}
}
public void updateProgressDialog(int msg) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setCancelable(false);
mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mProgressDialog.show();
}
mProgressDialog.setMessage(getText(msg));
}
IMountService getMountService() {
if (mMountService == null) {
IBinder service = ServiceManager.getService("mount");
if (service != null) {
mMountService = IMountService.Stub.asInterface(service);
} else {
Log.e(TAG, "Can't get mount service");
}
}
return mMountService;
}
}