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