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     private String mReason = null;
     54 
     55     StorageEventListener mStorageListener = new StorageEventListener() {
     56         @Override
     57         public void onStorageStateChanged(String path, String oldState, String newState) {
     58             Log.i(TAG, "Received storage state changed notification that " +
     59                     path + " changed state from " + oldState +
     60                     " to " + newState);
     61             updateProgressState();
     62         }
     63     };
     64 
     65     @Override
     66     public void onCreate() {
     67         super.onCreate();
     68 
     69         if (mStorageManager == null) {
     70             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
     71             mStorageManager.registerListener(mStorageListener);
     72         }
     73 
     74         mWakeLock = ((PowerManager)getSystemService(Context.POWER_SERVICE))
     75                 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExternalStorageFormatter");
     76         mWakeLock.acquire();
     77     }
     78 
     79     @Override
     80     public int onStartCommand(Intent intent, int flags, int startId) {
     81         if (FORMAT_AND_FACTORY_RESET.equals(intent.getAction())) {
     82             mFactoryReset = true;
     83         }
     84         if (intent.getBooleanExtra(EXTRA_ALWAYS_RESET, false)) {
     85             mAlwaysReset = true;
     86         }
     87 
     88         mReason = intent.getStringExtra(Intent.EXTRA_REASON);
     89         mStorageVolume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
     90 
     91         if (mProgressDialog == null) {
     92             mProgressDialog = new ProgressDialog(this);
     93             mProgressDialog.setIndeterminate(true);
     94             mProgressDialog.setCancelable(true);
     95             mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
     96             if (!mAlwaysReset) {
     97                 mProgressDialog.setOnCancelListener(this);
     98             }
     99             updateProgressState();
    100             mProgressDialog.show();
    101         }
    102 
    103         return Service.START_REDELIVER_INTENT;
    104     }
    105 
    106     @Override
    107     public void onDestroy() {
    108         if (mStorageManager != null) {
    109             mStorageManager.unregisterListener(mStorageListener);
    110         }
    111         if (mProgressDialog != null) {
    112             mProgressDialog.dismiss();
    113         }
    114         mWakeLock.release();
    115         super.onDestroy();
    116     }
    117 
    118     @Override
    119     public IBinder onBind(Intent intent) {
    120         return null;
    121     }
    122 
    123     @Override
    124     public void onCancel(DialogInterface dialog) {
    125         IMountService mountService = getMountService();
    126         String extStoragePath = mStorageVolume == null ?
    127                 Environment.getLegacyExternalStorageDirectory().toString() :
    128                 mStorageVolume.getPath();
    129         try {
    130             mountService.mountVolume(extStoragePath);
    131         } catch (RemoteException e) {
    132             Log.w(TAG, "Failed talking with mount service", e);
    133         }
    134         stopSelf();
    135     }
    136 
    137     void fail(int msg) {
    138         Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
    139         if (mAlwaysReset) {
    140             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    141             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    142             intent.putExtra(Intent.EXTRA_REASON, mReason);
    143             sendBroadcast(intent);
    144         }
    145         stopSelf();
    146     }
    147 
    148     void updateProgressState() {
    149         String status = mStorageVolume == null ?
    150                 Environment.getExternalStorageState() :
    151                 mStorageManager.getVolumeState(mStorageVolume.getPath());
    152         if (Environment.MEDIA_MOUNTED.equals(status)
    153                 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)) {
    154             updateProgressDialog(R.string.progress_unmounting);
    155             IMountService mountService = getMountService();
    156             final String extStoragePath = mStorageVolume == null ?
    157                     Environment.getLegacyExternalStorageDirectory().toString() :
    158                     mStorageVolume.getPath();
    159             try {
    160                 // Remove encryption mapping if this is an unmount for a factory reset.
    161                 mountService.unmountVolume(extStoragePath, true, mFactoryReset);
    162             } catch (RemoteException e) {
    163                 Log.w(TAG, "Failed talking with mount service", e);
    164             }
    165         } else if (Environment.MEDIA_NOFS.equals(status)
    166                 || Environment.MEDIA_UNMOUNTED.equals(status)
    167                 || Environment.MEDIA_UNMOUNTABLE.equals(status)) {
    168             updateProgressDialog(R.string.progress_erasing);
    169             final IMountService mountService = getMountService();
    170             final String extStoragePath = mStorageVolume == null ?
    171                     Environment.getLegacyExternalStorageDirectory().toString() :
    172                     mStorageVolume.getPath();
    173             if (mountService != null) {
    174                 new Thread() {
    175                     @Override
    176                     public void run() {
    177                         boolean success = false;
    178                         try {
    179                             mountService.formatVolume(extStoragePath);
    180                             success = true;
    181                         } catch (Exception e) {
    182                             Toast.makeText(ExternalStorageFormatter.this,
    183                                     R.string.format_error, Toast.LENGTH_LONG).show();
    184                         }
    185                         if (success) {
    186                             if (mFactoryReset) {
    187                                 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    188                                 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    189                                 intent.putExtra(Intent.EXTRA_REASON, mReason);
    190                                 sendBroadcast(intent);
    191                                 // Intent handling is asynchronous -- assume it will happen soon.
    192                                 stopSelf();
    193                                 return;
    194                             }
    195                         }
    196                         // If we didn't succeed, or aren't doing a full factory
    197                         // reset, then it is time to remount the storage.
    198                         if (!success && mAlwaysReset) {
    199                             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    200                             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    201                             intent.putExtra(Intent.EXTRA_REASON, mReason);
    202                             sendBroadcast(intent);
    203                         } else {
    204                             try {
    205                                 mountService.mountVolume(extStoragePath);
    206                             } catch (RemoteException e) {
    207                                 Log.w(TAG, "Failed talking with mount service", e);
    208                             }
    209                         }
    210                         stopSelf();
    211                         return;
    212                     }
    213                 }.start();
    214             } else {
    215                 Log.w(TAG, "Unable to locate IMountService");
    216             }
    217         } else if (Environment.MEDIA_BAD_REMOVAL.equals(status)) {
    218             fail(R.string.media_bad_removal);
    219         } else if (Environment.MEDIA_CHECKING.equals(status)) {
    220             fail(R.string.media_checking);
    221         } else if (Environment.MEDIA_REMOVED.equals(status)) {
    222             fail(R.string.media_removed);
    223         } else if (Environment.MEDIA_SHARED.equals(status)) {
    224             fail(R.string.media_shared);
    225         } else {
    226             fail(R.string.media_unknown_state);
    227             Log.w(TAG, "Unknown storage state: " + status);
    228             stopSelf();
    229         }
    230     }
    231 
    232     public void updateProgressDialog(int msg) {
    233         if (mProgressDialog == null) {
    234             mProgressDialog = new ProgressDialog(this);
    235             mProgressDialog.setIndeterminate(true);
    236             mProgressDialog.setCancelable(false);
    237             mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    238             mProgressDialog.show();
    239         }
    240 
    241         mProgressDialog.setMessage(getText(msg));
    242     }
    243 
    244     IMountService getMountService() {
    245         if (mMountService == null) {
    246             IBinder service = ServiceManager.getService("mount");
    247             if (service != null) {
    248                 mMountService = IMountService.Stub.asInterface(service);
    249             } else {
    250                 Log.e(TAG, "Can't get mount service");
    251             }
    252         }
    253         return mMountService;
    254     }
    255 }
    256