summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml11
-rw-r--r--core/java/android/app/ActivityManager.java7
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java6
-rw-r--r--core/java/com/android/internal/os/storage/ExternalStorageFormatter.java228
-rw-r--r--core/res/AndroidManifest.xml10
-rwxr-xr-xcore/res/res/values/strings.xml29
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java20
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java29
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java3
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;