diff options
-rw-r--r-- | api/current.xml | 11 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 7 | ||||
-rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 6 | ||||
-rw-r--r-- | core/java/com/android/internal/os/storage/ExternalStorageFormatter.java | 228 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 10 | ||||
-rwxr-xr-x | core/res/res/values/strings.xml | 29 | ||||
-rw-r--r-- | services/java/com/android/server/DevicePolicyManagerService.java | 20 | ||||
-rw-r--r-- | services/java/com/android/server/MasterClearReceiver.java | 29 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 3 |
9 files changed, 325 insertions, 18 deletions
diff --git a/api/current.xml b/api/current.xml index 6ab6b61..5611e0d 100644 --- a/api/current.xml +++ b/api/current.xml @@ -37902,6 +37902,17 @@ visibility="public" > </field> +<field name="WIPE_EXTERNAL_STORAGE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> </package> <package name="android.app.backup" diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4736404..5ae8a1f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -763,6 +763,13 @@ public class ActivityManager { public static final int FLAG_CANT_SAVE_STATE = 1<<0; /** + * Constant for {@link #flags}: this process is associated with a + * persistent system app. + * @hide + */ + public static final int FLAG_PERSISTENT = 1<<1; + + /** * Flags of information. May be any of * {@link #FLAG_CANT_SAVE_STATE}. * @hide diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2b7e427..ca27010 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -896,6 +896,12 @@ public class DevicePolicyManager { } /** + * Flag for {@link #wipeData(int)}: also erase the device's external + * storage. + */ + public static final int WIPE_EXTERNAL_STORAGE = 0x0001; + + /** * Ask the user date be wiped. This will cause the device to reboot, * erasing all user data while next booting up. External storage such * as SD cards will not be erased. diff --git a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java new file mode 100644 index 0000000..965022e --- /dev/null +++ b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java @@ -0,0 +1,228 @@ +package com.android.internal.os.storage; + +import android.app.ProgressDialog; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Environment; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.storage.IMountService; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; +import android.util.Log; +import android.view.WindowManager; +import android.widget.Toast; + +import com.android.internal.R; + +/** + * Takes care of unmounting and formatting external storage. + */ +public class ExternalStorageFormatter extends Service + implements DialogInterface.OnCancelListener { + static final String TAG = "ExternalStorageFormatter"; + + public static final String FORMAT_ONLY = "com.android.internal.os.storage.FORMAT_ONLY"; + public static final String FORMAT_AND_FACTORY_RESET = "com.android.internal.os.storage.FORMAT_AND_FACTORY_RESET"; + + public static final String EXTRA_ALWAYS_RESET = "always_reset"; + + public static final ComponentName COMPONENT_NAME + = new ComponentName("android", ExternalStorageFormatter.class.getName()); + + // Access using getMountService() + private IMountService mMountService = null; + + private StorageManager mStorageManager = null; + + private PowerManager.WakeLock mWakeLock; + + private ProgressDialog mProgressDialog = null; + + private boolean mFactoryReset = false; + private boolean mAlwaysReset = false; + + StorageEventListener mStorageListener = new StorageEventListener() { + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + Log.i(TAG, "Received storage state changed notification that " + + path + " changed state from " + oldState + + " to " + newState); + updateProgressState(); + } + }; + + @Override + public void onCreate() { + super.onCreate(); + + if (mStorageManager == null) { + mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); + mStorageManager.registerListener(mStorageListener); + } + + mWakeLock = ((PowerManager)getSystemService(Context.POWER_SERVICE)) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExternalStorageFormatter"); + mWakeLock.acquire(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (FORMAT_AND_FACTORY_RESET.equals(intent.getAction())) { + mFactoryReset = true; + } + if (intent.getBooleanExtra(EXTRA_ALWAYS_RESET, false)) { + mAlwaysReset = true; + } + + if (mProgressDialog == null) { + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setIndeterminate(true); + mProgressDialog.setCancelable(true); + mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + if (!mAlwaysReset) { + mProgressDialog.setOnCancelListener(this); + } + updateProgressState(); + mProgressDialog.show(); + } + + return Service.START_REDELIVER_INTENT; + } + + @Override + public void onDestroy() { + if (mStorageManager != null) { + mStorageManager.unregisterListener(mStorageListener); + } + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + mWakeLock.release(); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCancel(DialogInterface dialog) { + IMountService mountService = getMountService(); + String extStoragePath = Environment.getExternalStorageDirectory().toString(); + try { + mountService.mountVolume(extStoragePath); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with mount service", e); + } + stopSelf(); + } + + void fail(int msg) { + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + if (mAlwaysReset) { + sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); + } + stopSelf(); + } + + void updateProgressState() { + String status = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(status) + || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)) { + updateProgressDialog(R.string.progress_unmounting); + IMountService mountService = getMountService(); + String extStoragePath = Environment.getExternalStorageDirectory().toString(); + try { + mountService.unmountVolume(extStoragePath, true); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with mount service", e); + } + } else if (Environment.MEDIA_NOFS.equals(status) + || Environment.MEDIA_UNMOUNTED.equals(status) + || Environment.MEDIA_UNMOUNTABLE.equals(status)) { + updateProgressDialog(R.string.progress_erasing); + final IMountService mountService = getMountService(); + final String extStoragePath = Environment.getExternalStorageDirectory().toString(); + if (mountService != null) { + new Thread() { + public void run() { + boolean success = false; + try { + mountService.formatVolume(extStoragePath); + success = true; + } catch (Exception e) { + Toast.makeText(ExternalStorageFormatter.this, + R.string.format_error, Toast.LENGTH_LONG).show(); + } + if (success) { + if (mFactoryReset) { + sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); + // Intent handling is asynchronous -- assume it will happen soon. + stopSelf(); + return; + } + } + // If we didn't succeed, or aren't doing a full factory + // reset, then it is time to remount the storage. + if (!success && mAlwaysReset) { + sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); + } else { + try { + mountService.mountVolume(extStoragePath); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with mount service", e); + } + } + stopSelf(); + return; + } + }.start(); + } else { + Log.w("MediaFormat", "Unable to locate IMountService"); + } + } else if (Environment.MEDIA_BAD_REMOVAL.equals(status)) { + fail(R.string.media_bad_removal); + } else if (Environment.MEDIA_CHECKING.equals(status)) { + fail(R.string.media_checking); + } else if (Environment.MEDIA_REMOVED.equals(status)) { + fail(R.string.media_removed); + } else if (Environment.MEDIA_SHARED.equals(status)) { + fail(R.string.media_shared); + } else { + fail(R.string.media_unknown_state); + Log.w(TAG, "Unknown storage state: " + status); + stopSelf(); + } + } + + public void updateProgressDialog(int msg) { + if (mProgressDialog == null) { + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setIndeterminate(true); + mProgressDialog.setCancelable(false); + mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mProgressDialog.show(); + } + + mProgressDialog.setMessage(getText(msg)); + } + + IMountService getMountService() { + if (mMountService == null) { + IBinder service = ServiceManager.getService("mount"); + if (service != null) { + mMountService = IMountService.Stub.asInterface(service); + } else { + Log.e(TAG, "Can't get mount service"); + } + } + return mMountService; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1514b37..3e99541 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1389,7 +1389,8 @@ </receiver> <receiver android:name="com.android.server.MasterClearReceiver" - android:permission="android.permission.MASTER_CLEAR" > + android:permission="android.permission.MASTER_CLEAR" + android:priority="100" > <intent-filter> <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR --> <action android:name="android.intent.action.MASTER_CLEAR" /> @@ -1399,8 +1400,11 @@ <category android:name="android.intent.category.MASTER_CLEAR" /> </intent-filter> </receiver> - </application> -</manifest> + <service android:name="com.android.internal.os.storage.ExternalStorageFormatter" + android:permission="android.permission.MASTER_CLEAR" + android:exported="true" /> + </application> +</manifest> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 25757a8..1d2e780 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2404,4 +2404,33 @@ <!-- Label for the "Done" button on the far left of action mode toolbars. --> <string name="action_mode_done">Done</string> + + <!-- Strings for ExternalStorageFormatter service. --> + <!-- Text for progress dialog while unmounting USB storage volume [CHAR LIMIT=NONE] --> + <string name="progress_unmounting" product="nosdcard">Unmounting USB storage...</string> + <!-- Text for progress dialog while unmounting SD card [CHAR LIMIT=NONE] --> + <string name="progress_unmounting" product="default">Unmounting SD card...</string> + <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] --> + <string name="progress_erasing" product="nosdcard">Erasing USB storage...</string> + <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] --> + <string name="progress_erasing" product="default">Erasing SD card...</string> + <!-- Text for message to user that an error happened when formatting USB storage [CHAR LIMIT=NONE] --> + <string name="format_error" product="nosdcard">Failed to erase USB storage.</string> + <!-- Text for message to user that an error happened when formatting SD card [CHAR LIMIT=NONE] --> + <string name="format_error" product="default">Failed to erase SD card.</string> + <!-- Text for message to user that SD card has been removed while in use [CHAR LIMIT=NONE] --> + <string name="media_bad_removal">SD card was removed before being unmounted.</string> + <!-- Text for message to user USB storage is currently being checked [CHAR LIMIT=NONE] --> + <string name="media_checking" product="nosdcard">USB storage is currently being checked.</string> + <!-- Text for message to user SD card is currently being checked [CHAR LIMIT=NONE] --> + <string name="media_checking" product="default">SD card is currently being checked.</string> + <!-- Text for message to user SD card has been removed [CHAR LIMIT=NONE] --> + <string name="media_removed">SD card has been removed.</string> + <!-- Text for message to user USB storage is currently mounted on a computer [CHAR LIMIT=NONE] --> + <string name="media_shared" product="nosdcard">USB storage is currently in use by a computer.</string> + <!-- Text for message to user SD card is currently mounted on a computer [CHAR LIMIT=NONE] --> + <string name="media_shared" product="default">SD card is currently in use by a computer.</string> + <!-- Text for message for an unknown external media state [CHAR LIMIT=NONE] --> + <string name="media_unknown_state">External media in unknown state.</string> + </resources> diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 28126b9..081bd30 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.internal.content.PackageMonitor; +import com.android.internal.os.storage.ExternalStorageFormatter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; @@ -42,6 +43,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.IBinder; import android.os.IPowerManager; +import android.os.PowerManager; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.RemoteException; @@ -76,6 +78,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final Context mContext; final MyPackageMonitor mMonitor; + final PowerManager.WakeLock mWakeLock; IPowerManager mIPowerManager; @@ -343,6 +346,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext = context; mMonitor = new MyPackageMonitor(); mMonitor.register(context, true); + mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM"); } private IPowerManager getIPowerManager() { @@ -1345,10 +1350,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } void wipeDataLocked(int flags) { - try { - RecoverySystem.rebootWipeUserData(mContext); - } catch (IOException e) { - Slog.w(TAG, "Failed requesting data wipe", e); + if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) { + Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET); + intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME); + mWakeLock.acquire(10000); + mContext.startService(intent); + } else { + try { + RecoverySystem.rebootWipeUserData(mContext); + } catch (IOException e) { + Slog.w(TAG, "Failed requesting data wipe", e); + } } } diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java index 4d04cee..bdb5a24 100644 --- a/services/java/com/android/server/MasterClearReceiver.java +++ b/services/java/com/android/server/MasterClearReceiver.java @@ -29,7 +29,7 @@ public class MasterClearReceiver extends BroadcastReceiver { private static final String TAG = "MasterClear"; @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(final Context context, final Intent intent) { if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) { if (!"google.com".equals(intent.getStringExtra("from"))) { Slog.w(TAG, "Ignoring master clear request -- not from trusted server."); @@ -37,16 +37,23 @@ public class MasterClearReceiver extends BroadcastReceiver { } } - try { - Slog.w(TAG, "!!! FACTORY RESET !!!"); - if (intent.hasExtra("enableEFS")) { - RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false)); - } else { - RecoverySystem.rebootWipeUserData(context); + Slog.w(TAG, "!!! FACTORY RESET !!!"); + // The reboot call is blocking, so we need to do it on another thread. + Thread thr = new Thread("Reboot") { + @Override + public void run() { + try { + if (intent.hasExtra("enableEFS")) { + RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false)); + } else { + RecoverySystem.rebootWipeUserData(context); + } + Log.wtf(TAG, "Still running after master clear?!"); + } catch (IOException e) { + Slog.e(TAG, "Can't perform master clear/factory reset", e); + } } - Log.wtf(TAG, "Still running after master clear?!"); - } catch (IOException e) { - Slog.e(TAG, "Can't perform master clear/factory reset", e); - } + }; + thr.start(); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9c2c562..b0ace2f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -7137,6 +7137,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (mHeavyWeightProcess == app) { currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE; } + if (app.persistent) { + currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT; + } int adj = app.curAdj; if (adj >= EMPTY_APP_ADJ) { currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; |