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
onCreate()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
onStartCommand(Intent intent, int flags, int startId)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
onDestroy()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
onBind(Intent intent)119     public IBinder onBind(Intent intent) {
120         return null;
121     }
122 
123     @Override
onCancel(DialogInterface dialog)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 
fail(int msg)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 
updateProgressState()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 
updateProgressDialog(int msg)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 
getMountService()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