ExternalStorageFormatter.java revision b049e212ab7fe8967893c202efcb30fecfdb82fb
1package com.android.internal.os.storage;
2
3import android.app.ProgressDialog;
4import android.app.Service;
5import android.content.ComponentName;
6import android.content.Context;
7import android.content.DialogInterface;
8import android.content.Intent;
9import android.os.Environment;
10import android.os.IBinder;
11import android.os.PowerManager;
12import android.os.RemoteException;
13import android.os.ServiceManager;
14import android.os.storage.IMountService;
15import android.os.storage.StorageEventListener;
16import android.os.storage.StorageManager;
17import android.os.storage.StorageVolume;
18import android.util.Log;
19import android.view.WindowManager;
20import android.widget.Toast;
21
22import com.android.internal.R;
23
24/**
25 * Takes care of unmounting and formatting external storage.
26 */
27public 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.getLegacyExternalStorageDirectory().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.getLegacyExternalStorageDirectory().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.getLegacyExternalStorageDirectory().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