Home | History | Annotate | Download | only in storage
      1 package com.android.internal.os.storage;
      2 
      3 import android.app.ProgressDialog;
      4 import android.app.Service;
      5 import android.content.ComponentName;
      6 import android.content.Context;
      7 import android.content.DialogInterface;
      8 import android.content.Intent;
      9 import android.os.Environment;
     10 import android.os.IBinder;
     11 import android.os.PowerManager;
     12 import android.os.RemoteException;
     13 import android.os.ServiceManager;
     14 import android.os.storage.IMountService;
     15 import android.os.storage.StorageEventListener;
     16 import android.os.storage.StorageManager;
     17 import android.os.storage.StorageVolume;
     18 import android.util.Log;
     19 import android.view.WindowManager;
     20 import android.widget.Toast;
     21 
     22 import com.android.internal.R;
     23 
     24 /**
     25  * Takes care of unmounting and formatting external storage.
     26  */
     27 public class ExternalStorageFormatter extends Service
     28         implements DialogInterface.OnCancelListener {
     29     static final String TAG = "ExternalStorageFormatter";
     30 
     31     public static final String FORMAT_ONLY = "com.android.internal.os.storage.FORMAT_ONLY";
     32     public static final String FORMAT_AND_FACTORY_RESET = "com.android.internal.os.storage.FORMAT_AND_FACTORY_RESET";
     33 
     34     public static final String EXTRA_ALWAYS_RESET = "always_reset";
     35 
     36     // If non-null, the volume to format. Otherwise, will use the default external storage directory
     37     private StorageVolume mStorageVolume;
     38 
     39     public static final ComponentName COMPONENT_NAME
     40             = new ComponentName("android", ExternalStorageFormatter.class.getName());
     41 
     42     // Access using getMountService()
     43     private IMountService mMountService = null;
     44 
     45     private StorageManager mStorageManager = null;
     46 
     47     private PowerManager.WakeLock mWakeLock;
     48 
     49     private ProgressDialog mProgressDialog = null;
     50 
     51     private boolean mFactoryReset = false;
     52     private boolean mAlwaysReset = false;
     53 
     54     StorageEventListener mStorageListener = new StorageEventListener() {
     55         @Override
     56         public void onStorageStateChanged(String path, String oldState, String newState) {
     57             Log.i(TAG, "Received storage state changed notification that " +
     58                     path + " changed state from " + oldState +
     59                     " to " + newState);
     60             updateProgressState();
     61         }
     62     };
     63 
     64     @Override
     65     public void onCreate() {
     66         super.onCreate();
     67 
     68         if (mStorageManager == null) {
     69             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
     70             mStorageManager.registerListener(mStorageListener);
     71         }
     72 
     73         mWakeLock = ((PowerManager)getSystemService(Context.POWER_SERVICE))
     74                 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExternalStorageFormatter");
     75         mWakeLock.acquire();
     76     }
     77 
     78     @Override
     79     public int onStartCommand(Intent intent, int flags, int startId) {
     80         if (FORMAT_AND_FACTORY_RESET.equals(intent.getAction())) {
     81             mFactoryReset = true;
     82         }
     83         if (intent.getBooleanExtra(EXTRA_ALWAYS_RESET, false)) {
     84             mAlwaysReset = true;
     85         }
     86 
     87         mStorageVolume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
     88 
     89         if (mProgressDialog == null) {
     90             mProgressDialog = new ProgressDialog(this);
     91             mProgressDialog.setIndeterminate(true);
     92             mProgressDialog.setCancelable(true);
     93             mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
     94             if (!mAlwaysReset) {
     95                 mProgressDialog.setOnCancelListener(this);
     96             }
     97             updateProgressState();
     98             mProgressDialog.show();
     99         }
    100 
    101         return Service.START_REDELIVER_INTENT;
    102     }
    103 
    104     @Override
    105     public void onDestroy() {
    106         if (mStorageManager != null) {
    107             mStorageManager.unregisterListener(mStorageListener);
    108         }
    109         if (mProgressDialog != null) {
    110             mProgressDialog.dismiss();
    111         }
    112         mWakeLock.release();
    113         super.onDestroy();
    114     }
    115 
    116     @Override
    117     public IBinder onBind(Intent intent) {
    118         return null;
    119     }
    120 
    121     @Override
    122     public void onCancel(DialogInterface dialog) {
    123         IMountService mountService = getMountService();
    124         String extStoragePath = mStorageVolume == null ?
    125                 Environment.getExternalStorageDirectory().toString() :
    126                 mStorageVolume.getPath();
    127         try {
    128             mountService.mountVolume(extStoragePath);
    129         } catch (RemoteException e) {
    130             Log.w(TAG, "Failed talking with mount service", e);
    131         }
    132         stopSelf();
    133     }
    134 
    135     void fail(int msg) {
    136         Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
    137         if (mAlwaysReset) {
    138             sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
    139         }
    140         stopSelf();
    141     }
    142 
    143     void updateProgressState() {
    144         String status = mStorageVolume == null ?
    145                 Environment.getExternalStorageState() :
    146                 mStorageManager.getVolumeState(mStorageVolume.getPath());
    147         if (Environment.MEDIA_MOUNTED.equals(status)
    148                 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)) {
    149             updateProgressDialog(R.string.progress_unmounting);
    150             IMountService mountService = getMountService();
    151             final String extStoragePath = mStorageVolume == null ?
    152                     Environment.getExternalStorageDirectory().toString() :
    153                     mStorageVolume.getPath();
    154             try {
    155                 // Remove encryption mapping if this is an unmount for a factory reset.
    156                 mountService.unmountVolume(extStoragePath, true, mFactoryReset);
    157             } catch (RemoteException e) {
    158                 Log.w(TAG, "Failed talking with mount service", e);
    159             }
    160         } else if (Environment.MEDIA_NOFS.equals(status)
    161                 || Environment.MEDIA_UNMOUNTED.equals(status)
    162                 || Environment.MEDIA_UNMOUNTABLE.equals(status)) {
    163             updateProgressDialog(R.string.progress_erasing);
    164             final IMountService mountService = getMountService();
    165             final String extStoragePath = mStorageVolume == null ?
    166                     Environment.getExternalStorageDirectory().toString() :
    167                     mStorageVolume.getPath();
    168             if (mountService != null) {
    169                 new Thread() {
    170                     @Override
    171                     public void run() {
    172                         boolean success = false;
    173                         try {
    174                             mountService.formatVolume(extStoragePath);
    175                             success = true;
    176                         } catch (Exception e) {
    177                             Toast.makeText(ExternalStorageFormatter.this,
    178                                     R.string.format_error, Toast.LENGTH_LONG).show();
    179                         }
    180                         if (success) {
    181                             if (mFactoryReset) {
    182                                 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
    183                                 // Intent handling is asynchronous -- assume it will happen soon.
    184                                 stopSelf();
    185                                 return;
    186                             }
    187                         }
    188                         // If we didn't succeed, or aren't doing a full factory
    189                         // reset, then it is time to remount the storage.
    190                         if (!success && mAlwaysReset) {
    191                             sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
    192                         } else {
    193                             try {
    194                                 mountService.mountVolume(extStoragePath);
    195                             } catch (RemoteException e) {
    196                                 Log.w(TAG, "Failed talking with mount service", e);
    197                             }
    198                         }
    199                         stopSelf();
    200                         return;
    201                     }
    202                 }.start();
    203             } else {
    204                 Log.w(TAG, "Unable to locate IMountService");
    205             }
    206         } else if (Environment.MEDIA_BAD_REMOVAL.equals(status)) {
    207             fail(R.string.media_bad_removal);
    208         } else if (Environment.MEDIA_CHECKING.equals(status)) {
    209             fail(R.string.media_checking);
    210         } else if (Environment.MEDIA_REMOVED.equals(status)) {
    211             fail(R.string.media_removed);
    212         } else if (Environment.MEDIA_SHARED.equals(status)) {
    213             fail(R.string.media_shared);
    214         } else {
    215             fail(R.string.media_unknown_state);
    216             Log.w(TAG, "Unknown storage state: " + status);
    217             stopSelf();
    218         }
    219     }
    220 
    221     public void updateProgressDialog(int msg) {
    222         if (mProgressDialog == null) {
    223             mProgressDialog = new ProgressDialog(this);
    224             mProgressDialog.setIndeterminate(true);
    225             mProgressDialog.setCancelable(false);
    226             mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    227             mProgressDialog.show();
    228         }
    229 
    230         mProgressDialog.setMessage(getText(msg));
    231     }
    232 
    233     IMountService getMountService() {
    234         if (mMountService == null) {
    235             IBinder service = ServiceManager.getService("mount");
    236             if (service != null) {
    237                 mMountService = IMountService.Stub.asInterface(service);
    238             } else {
    239                 Log.e(TAG, "Can't get mount service");
    240             }
    241         }
    242         return mMountService;
    243     }
    244 }
    245