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 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 onCreate()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 onStartCommand(Intent intent, int flags, int startId)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 onDestroy()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 onBind(Intent intent)119 public IBinder onBind(Intent intent) { 120 return null; 121 } 122 123 @Override onCancel(DialogInterface dialog)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 fail(int msg)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 updateProgressState()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 updateProgressDialog(int msg)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 getMountService()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