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&lt;android.content.ComponentName&gt;"
+ 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;
         }
     }