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 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