ExternalStorageFormatter.java revision 004a4b20f8d3116e6a711525960d433fcfea4ee4
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    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