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