Bite the bullet and add support for multiple device admins.
diff --git a/api/current.xml b/api/current.xml
index a32c757..d79b5cc 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20399,6 +20399,17 @@
deprecated="not deprecated"
visibility="public"
>
+<method name="getActiveAdmins"
+ return="java.util.List<android.content.ComponentName>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getCurrentFailedPasswordAttempts"
return="int"
abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 95142e3..05bbf3b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2518,7 +2518,7 @@
* and restored for you. Note that if the dialog is already created,
* {@link #onCreateDialog(int, Bundle)} will not be called with the new
* arguments but {@link #onPrepareDialog(int, Dialog, Bundle)} will be.
- * If you need to rebuild the dialog, call {@link #removeDialog(int)}Êfirst.
+ * If you need to rebuild the dialog, call {@link #removeDialog(int)} first.
* @return Returns true if the Dialog was created; false is returned if
* it is not created because {@link #onCreateDialog(int, Bundle)} returns false.
*
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index 9de7336..135851f 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -32,6 +32,7 @@
import android.util.Log;
import java.io.IOException;
+import java.util.List;
/**
* Public interface for managing policies enforced on a device. Most clients
@@ -65,10 +66,6 @@
* <p>You can optionally include the {@link #EXTRA_ADD_EXPLANATION}
* field to provide the user with additional explanation (in addition
* to your component's description) about what is being added.
- *
- * <p>Note: the current platform can only have one device administrator
- * active at a time. If you make this request while there is already
- * an active administrator, this new request will be canceled automatically.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_ADD_DEVICE_ADMIN
@@ -111,7 +108,7 @@
public boolean isAdminActive(ComponentName who) {
if (mService != null) {
try {
- return who.equals(mService.getActiveAdmin());
+ return mService.isAdminActive(who);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -120,6 +117,22 @@
}
/**
+ * Return a list of all currently active device administrator's component
+ * names. Note that if there are no administrators than null may be
+ * returned.
+ */
+ public List<ComponentName> getActiveAdmins() {
+ if (mService != null) {
+ try {
+ return mService.getActiveAdmins();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
* Remove a current administration component. This can only be called
* by the application that owns the administration component; if you
* try to remove someone else's component, a security exception will be
@@ -442,26 +455,7 @@
/**
* @hide
*/
- public ComponentName getActiveAdmin() {
- if (mService != null) {
- try {
- return mService.getActiveAdmin();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed talking with device policy service", e);
- }
- }
- return null;
- }
-
- /**
- * @hide
- */
- public DeviceAdminInfo getActiveAdminInfo() {
- ComponentName cn = getActiveAdmin();
- if (cn == null) {
- return null;
- }
-
+ public DeviceAdminInfo getAdminInfo(ComponentName cn) {
ActivityInfo ai;
try {
ai = mContext.getPackageManager().getReceiverInfo(cn,
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
index edb8603..a5508cd 100644
--- a/core/java/android/app/IDevicePolicyManager.aidl
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -45,7 +45,8 @@
void wipeData(int flags);
void setActiveAdmin(in ComponentName policyReceiver);
- ComponentName getActiveAdmin();
+ boolean isAdminActive(in ComponentName policyReceiver);
+ List<ComponentName> getActiveAdmins();
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index aafe453..bd41a13 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -121,11 +121,6 @@
}
- public boolean isDevicePolicyActive() {
- ComponentName admin = mDevicePolicyManager.getActiveAdmin();
- return admin != null ? mDevicePolicyManager.isAdminActive(admin) : false;
- }
-
public int getRequestedMinimumPasswordLength() {
return mDevicePolicyManager.getMinimumPasswordLength();
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index ebd6f3d..7b8645f 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -35,7 +35,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.RecoverySystem;
@@ -49,6 +48,8 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
/**
@@ -65,7 +66,10 @@
int mActivePasswordLength = 0;
int mFailedPasswordAttempts = 0;
- ActiveAdmin mActiveAdmin;
+ final HashMap<ComponentName, ActiveAdmin> mAdminMap
+ = new HashMap<ComponentName, ActiveAdmin>();
+ final ArrayList<ActiveAdmin> mAdminList
+ = new ArrayList<ActiveAdmin>();
static class ActiveAdmin {
final DeviceAdminInfo info;
@@ -148,7 +152,7 @@
}
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
- ActiveAdmin admin = mActiveAdmin;
+ ActiveAdmin admin = mAdminMap.get(who);
if (admin != null
&& who.getPackageName().equals(admin.info.getActivityInfo().packageName)
&& who.getClassName().equals(admin.info.getActivityInfo().name)) {
@@ -159,7 +163,7 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who)
throws SecurityException {
- ActiveAdmin admin = mActiveAdmin;
+ ActiveAdmin admin = mAdminMap.get(who);
if (admin != null && admin.getUid() == Binder.getCallingUid()) {
if (who != null) {
if (!who.getPackageName().equals(admin.info.getActivityInfo().packageName)
@@ -167,7 +171,7 @@
throw new SecurityException("Current admin is not " + who);
}
}
- return mActiveAdmin;
+ return admin;
}
throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
}
@@ -190,28 +194,25 @@
}
void sendAdminCommandLocked(String action, int reqPolicy) {
- if (mActiveAdmin != null) {
- if (mActiveAdmin.info.usesPolicy(reqPolicy)) {
- return;
+ final int N = mAdminList.size();
+ if (N > 0) {
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (admin.info.usesPolicy(reqPolicy)) {
+ sendAdminCommandLocked(admin, action);
+ }
}
- sendAdminCommandLocked(mActiveAdmin, action);
}
}
- ComponentName getActiveAdminLocked() {
- if (mActiveAdmin != null) {
- return mActiveAdmin.info.getComponent();
- }
- return null;
- }
-
void removeActiveAdminLocked(ComponentName adminReceiver) {
- ComponentName cur = getActiveAdminLocked();
- if (cur != null && cur.equals(adminReceiver)) {
- sendAdminCommandLocked(mActiveAdmin,
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
+ if (admin != null) {
+ sendAdminCommandLocked(admin,
DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
// XXX need to wait for it to complete.
- mActiveAdmin = null;
+ mAdminList.remove(admin);
+ mAdminMap.remove(adminReceiver);
}
}
@@ -251,13 +252,17 @@
out.startTag(null, "policies");
- ActiveAdmin ap = mActiveAdmin;
- if (ap != null) {
- out.startTag(null, "admin");
- out.attribute(null, "name", ap.info.getComponent().flattenToString());
- ap.writeToXml(out);
- out.endTag(null, "admin");
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin ap = mAdminList.get(i);
+ if (ap != null) {
+ out.startTag(null, "admin");
+ out.attribute(null, "name", ap.info.getComponent().flattenToString());
+ ap.writeToXml(out);
+ out.endTag(null, "admin");
+ }
}
+
out.endTag(null, "policies");
if (mFailedPasswordAttempts != 0) {
@@ -314,7 +319,8 @@
if (dai != null) {
ActiveAdmin ap = new ActiveAdmin(dai);
ap.readFromXml(parser);
- mActiveAdmin = ap;
+ mAdminMap.put(ap.info.getComponent(), ap);
+ mAdminList.add(ap);
}
} else if ("failed-password-attempts".equals(tag)) {
mFailedPasswordAttempts = Integer.parseInt(
@@ -369,16 +375,14 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- ComponentName cur = getActiveAdminLocked();
- if (cur != null && cur.equals(adminReceiver)) {
- throw new IllegalStateException("An admin is already set");
+ if (getActiveAdminUncheckedLocked(adminReceiver) != null) {
+ throw new IllegalArgumentException("Admin is already added");
}
- if (cur != null) {
- removeActiveAdminLocked(adminReceiver);
- }
- mActiveAdmin = new ActiveAdmin(info);
+ ActiveAdmin admin = new ActiveAdmin(info);
+ mAdminMap.put(adminReceiver, admin);
+ mAdminList.add(admin);
saveSettingsLocked();
- sendAdminCommandLocked(mActiveAdmin,
+ sendAdminCommandLocked(admin,
DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -386,15 +390,33 @@
}
}
- public ComponentName getActiveAdmin() {
+ public boolean isAdminActive(ComponentName adminReceiver) {
synchronized (this) {
- return getActiveAdminLocked();
+ return getActiveAdminUncheckedLocked(adminReceiver) != null;
+ }
+ }
+
+ public List<ComponentName> getActiveAdmins() {
+ synchronized (this) {
+ final int N = mAdminList.size();
+ if (N <= 0) {
+ return null;
+ }
+ ArrayList<ComponentName> res = new ArrayList<ComponentName>(N);
+ for (int i=0; i<N; i++) {
+ res.add(mAdminList.get(i).info.getComponent());
+ }
+ return res;
}
}
public void removeActiveAdmin(ComponentName adminReceiver) {
synchronized (this) {
- if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
+ if (admin == null) {
+ return;
+ }
+ if (admin.getUid() != Binder.getCallingUid()) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
}
@@ -423,8 +445,15 @@
public int getPasswordMode() {
synchronized (this) {
- return mActiveAdmin != null ? mActiveAdmin.passwordMode
- : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ final int N = mAdminList.size();
+ int mode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (mode < admin.passwordMode) {
+ mode = admin.passwordMode;
+ }
+ }
+ return mode;
}
}
@@ -444,7 +473,15 @@
public int getMinimumPasswordLength() {
synchronized (this) {
- return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
+ final int N = mAdminList.size();
+ int length = 0;
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordLength) {
+ length = admin.minimumPasswordLength;
+ }
+ }
+ return length;
}
}
@@ -486,7 +523,18 @@
public int getMaximumFailedPasswordsForWipe() {
synchronized (this) {
- return mActiveAdmin != null ? mActiveAdmin.maximumFailedPasswordsForWipe : 0;
+ final int N = mAdminList.size();
+ int count = 0;
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (count == 0) {
+ count = admin.maximumFailedPasswordsForWipe;
+ } else if (admin.maximumFailedPasswordsForWipe != 0
+ && count > admin.maximumFailedPasswordsForWipe) {
+ count = admin.maximumFailedPasswordsForWipe;
+ }
+ }
+ return count;
}
}
@@ -546,7 +594,18 @@
public long getMaximumTimeToLock() {
synchronized (this) {
- return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
+ final int N = mAdminList.size();
+ long time = 0;
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (time == 0) {
+ time = admin.maximumTimeToUnlock;
+ } else if (admin.maximumTimeToUnlock != 0
+ && time > admin.maximumTimeToUnlock) {
+ time = admin.maximumTimeToUnlock;
+ }
+ }
+ return time;
}
}