Add API to disable account management for certain types
of accounts.

The account types with disabled account management are stored in a
blacklist in the active admin object, editable by profile owners.

Change-Id: I57dc5f709ad79674fa28dd006969283585daea24
diff --git a/api/current.txt b/api/current.txt
index bc7bbcc..ba8c78a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5027,6 +5027,7 @@
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+    method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5056,6 +5057,7 @@
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean resetPassword(java.lang.String, int);
+    method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 209c536..61ff60a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2062,6 +2062,28 @@
     }
 
     /**
+     * Called by a profile owner to disable account management for a specific type of account.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param accountType For which account management is disabled or enabled.
+     * @param disabled The boolean indicating that account management will be disabled (true) or
+     * enabled (false).
+     */
+    public void setAccountManagementDisabled(ComponentName admin, String accountType,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setAccountManagementDisabled(admin, accountType, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Called by profile or device owner to re-enable system apps by intent that were disabled
      * by default when the managed profile was created. This should only be called from a profile
      * or device owner running within a managed profile.
@@ -2081,4 +2103,26 @@
         }
         return 0;
     }
+
+    /**
+     * Gets the array of accounts for which account management is disabled by the profile owner.
+     *
+     * <p> Account management can be disabled/enabled by calling
+     * {@link #setAccountManagementDisabled}.
+     *
+     * @return a list of account types for which account management has been disabled.
+     *
+     * @see #setAccountManagementDisabled
+     */
+    public String[] getAccountTypesWithManagementDisabled() {
+        if (mService != null) {
+            try {
+                return mService.getAccountTypesWithManagementDisabled();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b30f1b9..0096580 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -126,4 +126,7 @@
 
     void enableSystemApp(in ComponentName admin, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
+
+    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
+    String[] getAccountTypesWithManagementDisabled();
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dcca837..9a9f1c8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -100,6 +100,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -232,6 +233,8 @@
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
+        private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+        private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
         private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
         private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
@@ -297,6 +300,7 @@
 
         boolean encryptionRequested = false;
         boolean disableCamera = false;
+        Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
         boolean specifiesGlobalProxy = false;
@@ -413,6 +417,15 @@
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
                 out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
             }
+            if (!accountTypesWithManagementDisabled.isEmpty()) {
+                out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+                for (String ac : accountTypesWithManagementDisabled) {
+                    out.startTag(null, TAG_ACCOUNT_TYPE);
+                    out.attribute(null, ATTR_VALUE, ac);
+                    out.endTag(null, TAG_ACCOUNT_TYPE);
+                }
+                out.endTag(null,  TAG_DISABLE_ACCOUNT_MANAGEMENT);
+            }
         }
 
         void readFromXml(XmlPullParser parser)
@@ -484,6 +497,23 @@
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
+                    int outerDepthDAM = parser.getDepth();
+                    int typeDAM;
+                    while ((typeDAM=parser.next()) != XmlPullParser.END_DOCUMENT
+                            && (typeDAM != XmlPullParser.END_TAG
+                                    || parser.getDepth() > outerDepthDAM)) {
+                        if (typeDAM == XmlPullParser.END_TAG || typeDAM == XmlPullParser.TEXT) {
+                            continue;
+                        }
+                        String tagDAM = parser.getName();
+                        if (TAG_ACCOUNT_TYPE.equals(tagDAM)) {
+                            accountTypesWithManagementDisabled.add(
+                                    parser.getAttributeValue(null, ATTR_VALUE));
+                        } else {
+                            Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                        }
+                    }
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                 }
@@ -3178,7 +3208,6 @@
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
             }
-
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
@@ -3277,4 +3306,42 @@
         ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
         return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
     }
+
+    @Override
+    public void setAccountManagementDisabled(ComponentName who, String accountType,
+            boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (disabled) {
+                ap.accountTypesWithManagementDisabled.add(accountType);
+            } else {
+                ap.accountTypesWithManagementDisabled.remove(accountType);
+            }
+            saveSettingsLocked(UserHandle.getCallingUserId());
+        }
+    }
+
+    @Override
+    public String[] getAccountTypesWithManagementDisabled() {
+        if (!mHasFeature) {
+            return null;
+        }
+        synchronized (this) {
+            DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+            final int N = policy.mAdminList.size();
+            HashSet<String> resultSet = new HashSet<String>();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = policy.mAdminList.get(i);
+                resultSet.addAll(admin.accountTypesWithManagementDisabled);
+            }
+            return resultSet.toArray(new String[resultSet.size()]);
+        }
+    }
 }