Update TrustAgentService API after review

This change incorporates API council feedback and enables the
TrustAgent whitelisting API.

It also contains a minor cleanup of DPM's use of UserHandle
to eliminate unnecessary object creation.

Fixes bug 17008504

Change-Id: I63cc50169fde54b34406845818bcaf6aadc1a3db
diff --git a/api/current.txt b/api/current.txt
index 6851bbf..4993e99 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5317,6 +5317,7 @@
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
+    method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
     method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
     method public boolean hasGrantedPolicy(android.content.ComponentName, int);
     method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5365,6 +5366,7 @@
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
+    method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
     method public void uninstallAllUserCaCerts(android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 74502fc..d3ff79d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -31,6 +31,7 @@
 import android.net.ProxyInfo;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -40,6 +41,7 @@
 import android.provider.Settings;
 import android.security.Credentials;
 import android.service.restrictions.RestrictionsReceiver;
+import android.service.trust.TrustAgentService;
 import android.util.Log;
 
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -2604,25 +2606,29 @@
     }
 
     /**
-     * Sets a list of features to enable for a TrustAgent component. This is meant to be
-     * used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which will disable all
-     * trust agents but those with features enabled by this function call.
+     * Sets a list of configuration features to enable for a TrustAgent component. This is meant
+     * to be used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which disables all
+     * trust agents but those enabled by this function call. If flag
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is not set, then this call has no effect.
      *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call
-     * this method; if it has not, a security exception will be thrown.
+     * this method; if not, a security exception will be thrown.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param agent Which component to enable features for.
-     * @param features List of features to enable. Consult specific TrustAgent documentation for
-     * the feature list.
-     * @hide
+     * @param target Component name of the agent to be enabled.
+     * @param options TrustAgent-specific feature bundle. If null for any admin, agent
+     * will be strictly disabled according to the state of the
+     *  {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag.
+     * <p>If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all admins,
+     * then it's up to the TrustAgent itself to aggregate the values from all device admins.
+     * <p>Consult documentation for the specific TrustAgent to determine legal options parameters.
      */
-    public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
-            List<String> features) {
+    public void setTrustAgentConfiguration(ComponentName admin, ComponentName target,
+            PersistableBundle options) {
         if (mService != null) {
             try {
-                mService.setTrustAgentFeaturesEnabled(admin, agent, features, UserHandle.myUserId());
+                mService.setTrustAgentConfiguration(admin, target, options, UserHandle.myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2630,24 +2636,30 @@
     }
 
     /**
-     * Gets list of enabled features for the given TrustAgent component. If admin is
-     * null, this will return the intersection of all features enabled for the given agent by all
-     * admins.
+     * Gets configuration for the given trust agent based on aggregating all calls to
+     * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for
+     * all device admins.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param agent Which component to get enabled features for.
-     * @return List of enabled features.
-     * @hide
+     * @return configuration for the given trust agent.
      */
-    public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent) {
+    public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+            ComponentName agent) {
+        return getTrustAgentConfiguration(admin, agent, UserHandle.myUserId());
+    }
+
+    /** @hide per-user version */
+    public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+            ComponentName agent, int userHandle) {
         if (mService != null) {
             try {
-                return mService.getTrustAgentFeaturesEnabled(admin, agent, UserHandle.myUserId());
+                return mService.getTrustAgentConfiguration(admin, agent, userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
         }
-        return new ArrayList<String>(); // empty list
+        return new ArrayList<PersistableBundle>(); // empty list
     }
 
     /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c8e1780..07aa800 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.net.ProxyInfo;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
 import java.util.List;
@@ -183,8 +184,10 @@
     boolean getCrossProfileCallerIdDisabled(in ComponentName who);
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
 
-    void setTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, in List<String> features, int userId);
-    List<String> getTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, int userId);
+    void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
+            in PersistableBundle args, int userId);
+    List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
+            in ComponentName agent, int userId);
 
     boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
     boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index bd80a3f..bb0c2b2 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -15,7 +15,7 @@
  */
 package android.service.trust;
 
-import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.service.trust.ITrustAgentServiceCallback;
 
 /**
@@ -25,6 +25,6 @@
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
     oneway void onTrustTimeout();
+    oneway void onConfigure(in List<PersistableBundle> options, IBinder token);
     oneway void setCallback(ITrustAgentServiceCallback callback);
-    oneway void setTrustAgentFeaturesEnabled(in Bundle options, IBinder token);
 }
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index b107bcc..76b2be0 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -27,5 +27,5 @@
     void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser);
     void revokeTrust();
     void setManagingTrust(boolean managingTrust);
-    void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token);
+    void onConfigureCompleted(boolean result, IBinder token);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 3ef5b37..00d60c0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -29,11 +29,14 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
 
+import java.util.List;
+
 /**
  * A service that notifies the system about whether it believes the environment of the device
  * to be trusted.
@@ -86,17 +89,47 @@
      */
     public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
 
-    /**
-     * A white list of features that the given trust agent should support when otherwise disabled
-     * by device policy.
-     * @hide
-     */
-    public static final String KEY_FEATURES = "trust_agent_features";
-
     private static final int MSG_UNLOCK_ATTEMPT = 1;
-    private static final int MSG_SET_TRUST_AGENT_FEATURES_ENABLED = 2;
+    private static final int MSG_CONFIGURE = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
 
+    /**
+     * Container class for a list of configuration options and helper methods
+     */
+    public static final class Configuration {
+        public final List<PersistableBundle> options;
+        public Configuration(List<PersistableBundle> opts) {
+            options = opts;
+        }
+
+        /**
+         * Very basic method to determine if all bundles have the given feature, regardless
+         * of type.
+         * @param option String to search for.
+         * @return true if found in all bundles.
+         */
+        public boolean hasOption(String option) {
+            if (options == null || options.size() == 0) return false;
+            final int N = options.size();
+            for (int i = 0; i < N; i++) {
+                if (!options.get(i).containsKey(option)) return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Class containing raw data for a given configuration request.
+     */
+    private static final class ConfigurationData {
+        final IBinder token;
+        final List<PersistableBundle> options;
+        ConfigurationData(List<PersistableBundle> opts, IBinder t) {
+            options = opts;
+            token = t;
+        }
+    }
+
     private ITrustAgentServiceCallback mCallback;
 
     private Runnable mPendingGrantTrustTask;
@@ -112,13 +145,12 @@
                 case MSG_UNLOCK_ATTEMPT:
                     onUnlockAttempt(msg.arg1 != 0);
                     break;
-                case MSG_SET_TRUST_AGENT_FEATURES_ENABLED:
-                    Bundle features = msg.peekData();
-                    IBinder token = (IBinder) msg.obj;
-                    boolean result = onSetTrustAgentFeaturesEnabled(features);
+                case MSG_CONFIGURE:
+                    ConfigurationData data = (ConfigurationData) msg.obj;
+                    boolean result = onConfigure(new Configuration(data.options));
                     try {
                         synchronized (mLock) {
-                            mCallback.onSetTrustAgentFeaturesEnabledCompleted(result, token);
+                            mCallback.onConfigureCompleted(result, data.token);
                         }
                     } catch (RemoteException e) {
                         onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
@@ -171,23 +203,16 @@
     }
 
     /**
-     * Called when device policy wants to restrict features in the agent in response to
-     * {@link DevicePolicyManager#setTrustAgentFeaturesEnabled(ComponentName, ComponentName, java.util.List) }.
-     * Agents that support this feature should overload this method and return 'true'.
+     * Called when device policy admin wants to enable specific options for agent in response to
+     * {@link DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)} and
+     * {@link DevicePolicyManager#setTrustAgentConfiguration(ComponentName, ComponentName,
+     * PersistableBundle)}.
+     * <p>Agents that support configuration options should overload this method and return 'true'.
      *
-     * The list of options can be obtained by calling
-     * options.getStringArrayList({@link #KEY_FEATURES}). Presence of a feature string in the list
-     * means it should be enabled ("white-listed"). Absence of the feature means it should be
-     * disabled. An empty list means all features should be disabled.
-     *
-     * This function is only called if {@link DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} is
-     * set.
-     *
-     * @param options Option feature bundle.
-     * @return true if the {@link TrustAgentService} supports this feature.
-     * @hide
+     * @param options bundle containing all options or null if none.
+     * @return true if the {@link TrustAgentService} supports configuration options.
      */
-    public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
+    public boolean onConfigure(Configuration options) {
         return false;
     }
 
@@ -295,6 +320,12 @@
         }
 
         @Override /* Binder API */
+        public void onConfigure(List<PersistableBundle> args, IBinder token) {
+            mHandler.obtainMessage(MSG_CONFIGURE, new ConfigurationData(args, token))
+                    .sendToTarget();
+        }
+
+        @Override /* Binder API */
         public void setCallback(ITrustAgentServiceCallback callback) {
             synchronized (mLock) {
                 mCallback = callback;
@@ -313,13 +344,6 @@
                 }
             }
         }
-
-        @Override /* Binder API */
-        public void setTrustAgentFeaturesEnabled(Bundle features, IBinder token) {
-            Message msg = mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_ENABLED, token);
-            msg.setData(features);
-            msg.sendToTarget();
-        }
     }
 
 }
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index c650a0f..09c7165 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -21,7 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.preference.PreferenceManager;
 import android.service.trust.TrustAgentService;
 import android.support.v4.content.LocalBroadcastManager;
@@ -90,8 +90,15 @@
     }
 
     @Override
-    public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
-        Log.v(TAG, "Policy options received: " + options.getStringArrayList(KEY_FEATURES));
+    public boolean onConfigure(Configuration config) {
+        if (config != null && config.options != null) {
+           for (int i = 0; i < config.options.size(); i++) {
+               PersistableBundle options = config.options.get(i);
+               Log.v(TAG, "Policy options received: " + options.toString());
+           }
+        } else {
+            Log.w(TAG, "onConfigure() called with no options");
+        }
         // TODO: Handle options
         return true; // inform DPM that we support it
     }
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index b1c918d..b2bcf75 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -27,11 +27,11 @@
 import android.content.ServiceConnection;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PatternMatcher;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -218,7 +218,7 @@
         }
 
         @Override
-        public void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token) {
+        public void onConfigureCompleted(boolean result, IBinder token) {
             if (DEBUG) Slog.v(TAG, "onSetTrustAgentFeaturesEnabledCompleted(result=" + result);
             mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED,
                     result ? 1 : 0, 0, token).sendToTarget();
@@ -318,23 +318,19 @@
                 DevicePolicyManager dpm =
                     (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
 
-                if ((dpm.getKeyguardDisabledFeatures(null)
+                if ((dpm.getKeyguardDisabledFeatures(null, mUserId)
                         & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
-                    List<String> features = dpm.getTrustAgentFeaturesEnabled(null, mName);
+                    List<PersistableBundle> config = dpm.getTrustAgentConfiguration(
+                            null, mName, mUserId);
                     trustDisabled = true;
-                    if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Features = "
-                            + features);
-                    if (features != null && features.size() > 0) {
-                        Bundle bundle = new Bundle();
-                        bundle.putStringArrayList(TrustAgentService.KEY_FEATURES,
-                                (ArrayList<String>)features);
+                    if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Config = " + config);
+                    if (config != null && config.size() > 0) {
                         if (DEBUG) {
                             Slog.v(TAG, "TrustAgent " + mName.flattenToShortString()
-                                    + " disabled until it acknowledges "+ features);
+                                    + " disabled until it acknowledges "+ config);
                         }
                         mSetTrustAgentFeaturesToken = new Binder();
-                        mTrustAgentService.setTrustAgentFeaturesEnabled(bundle,
-                                mSetTrustAgentFeaturesToken);
+                        mTrustAgentService.onConfigure(config, mSetTrustAgentFeaturesToken);
                     }
                 }
                 final long maxTimeToLock = dpm.getMaximumTimeToLock(null);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4437e12..fe5cb33 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -48,6 +48,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -228,10 +229,10 @@
 
                 if (!enabledAgents.contains(name)) continue;
                 if (disableTrustAgents) {
-                    List<String> features =
-                            dpm.getTrustAgentFeaturesEnabled(null /* admin */, name);
+                    List<PersistableBundle> config =
+                            dpm.getTrustAgentConfiguration(null /* admin */, name, userInfo.id);
                     // Disable agent if no features are enabled.
-                    if (features == null || features.isEmpty()) continue;
+                    if (config == null || config.isEmpty()) continue;
                 }
 
                 AgentInfo agentInfo = new AgentInfo();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 308fcd8..2c6a222 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -60,6 +60,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IPowerManager;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RecoverySystem;
@@ -96,6 +97,7 @@
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -322,7 +324,7 @@
                 = "permitted-accessiblity-services";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
         private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features";
-        private static final String TAG_TRUST_AGENT_FEATURE = "feature";
+        private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS = "trust-agent-component-options";
         private static final String TAG_TRUST_AGENT_COMPONENT = "component";
         private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
         private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
@@ -389,6 +391,7 @@
         long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
 
         static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none
+
         int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
 
         boolean encryptionRequested = false;
@@ -397,6 +400,13 @@
         boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
         boolean requireAutoTime = false; // Can only be set by a device owner.
 
+        static class TrustAgentInfo {
+            public PersistableBundle options;
+            TrustAgentInfo(PersistableBundle bundle) {
+                options = bundle;
+            }
+        }
+
         Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // The list of permitted accessibility services package namesas set by a profile
@@ -413,7 +423,8 @@
         boolean specifiesGlobalProxy = false;
         String globalProxySpec = null;
         String globalProxyExclusionList = null;
-        HashMap<String, List<String>> trustAgentFeatures = new HashMap<String, List<String>>();
+
+        HashMap<String, TrustAgentInfo> trustAgentInfos = new HashMap<String, TrustAgentInfo>();
 
         List<String> crossProfileWidgetProviders;
 
@@ -551,16 +562,21 @@
                 }
                 out.endTag(null,  TAG_DISABLE_ACCOUNT_MANAGEMENT);
             }
-            if (!trustAgentFeatures.isEmpty()) {
-                Set<Entry<String, List<String>>> set = trustAgentFeatures.entrySet();
+            if (!trustAgentInfos.isEmpty()) {
+                Set<Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
                 out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
-                for (Entry<String, List<String>> component : set) {
+                for (Entry<String, TrustAgentInfo> entry : set) {
+                    TrustAgentInfo trustAgentInfo = entry.getValue();
                     out.startTag(null, TAG_TRUST_AGENT_COMPONENT);
-                    out.attribute(null, ATTR_VALUE, component.getKey());
-                    for (String feature : component.getValue()) {
-                        out.startTag(null, TAG_TRUST_AGENT_FEATURE);
-                        out.attribute(null, ATTR_VALUE, feature);
-                        out.endTag(null, TAG_TRUST_AGENT_FEATURE);
+                    out.attribute(null, ATTR_VALUE, entry.getKey());
+                    if (trustAgentInfo.options != null) {
+                        out.startTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
+                        try {
+                            trustAgentInfo.options.saveToXml(out);
+                        } catch (XmlPullParserException e) {
+                            Log.e(LOG_TAG, "Failed to save TrustAgent options", e);
+                        }
+                        out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
                     }
                     out.endTag(null, TAG_TRUST_AGENT_COMPONENT);
                 }
@@ -679,7 +695,7 @@
                 } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
                     accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag);
                 } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
-                    trustAgentFeatures = getAllTrustAgentFeatures(parser, tag);
+                    trustAgentInfos = getAllTrustAgentInfos(parser, tag);
                 } else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
                     crossProfileWidgetProviders = getCrossProfileWidgetProviders(parser, tag);
                 } else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) {
@@ -738,11 +754,11 @@
             return result;
         }
 
-        private HashMap<String, List<String>> getAllTrustAgentFeatures(XmlPullParser parser,
-                String tag) throws XmlPullParserException, IOException {
+        private HashMap<String, TrustAgentInfo> getAllTrustAgentInfos(
+                XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
             int outerDepthDAM = parser.getDepth();
             int typeDAM;
-            HashMap<String, List<String>> result = new HashMap<String, List<String>>();
+            HashMap<String, TrustAgentInfo> result = new HashMap<String, TrustAgentInfo>();
             while ((typeDAM=parser.next()) != END_DOCUMENT
                     && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
                 if (typeDAM == END_TAG || typeDAM == TEXT) {
@@ -751,7 +767,8 @@
                 String tagDAM = parser.getName();
                 if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) {
                     final String component = parser.getAttributeValue(null, ATTR_VALUE);
-                    result.put(component, getTrustAgentFeatures(parser, tag));
+                    final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
+                    result.put(component, trustAgentInfo);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
                 }
@@ -759,20 +776,21 @@
             return result;
         }
 
-        private List<String> getTrustAgentFeatures(XmlPullParser parser, String tag)
+        private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag)
                 throws XmlPullParserException, IOException  {
             int outerDepthDAM = parser.getDepth();
             int typeDAM;
-            ArrayList<String> result = new ArrayList<String>();
+            TrustAgentInfo result = new TrustAgentInfo(null);
             while ((typeDAM=parser.next()) != END_DOCUMENT
                     && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
                 if (typeDAM == END_TAG || typeDAM == TEXT) {
                     continue;
                 }
                 String tagDAM = parser.getName();
-                if (TAG_TRUST_AGENT_FEATURE.equals(tagDAM)) {
-                    final String feature = parser.getAttributeValue(null, ATTR_VALUE);
-                    result.add(feature);
+                if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
+                    PersistableBundle bundle = new PersistableBundle();
+                    bundle.restoreFromXml(parser);
+                    result.options = bundle;
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
                 }
@@ -1174,7 +1192,7 @@
             int userHandle) {
         List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
         for (UserInfo ui : profiles) {
-            int id = ui.getUserHandle().getIdentifier();
+            int id = ui.id;
             sendAdminCommandLocked(action, reqPolicy, id);
         }
     }
@@ -1591,7 +1609,7 @@
 
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo ui : profiles) {
-                int profileUserHandle = ui.getUserHandle().getIdentifier();
+                int profileUserHandle = ui.id;
                 final DevicePolicyData policy = getUserData(profileUserHandle);
                 final int count = policy.mAdminList.size();
                 if (count > 0) {
@@ -1878,7 +1896,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -1925,7 +1943,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -1972,7 +1990,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i = 0; i < N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2033,7 +2051,7 @@
 
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i = 0; i < N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2131,7 +2149,7 @@
 
         List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
         for (UserInfo userInfo : profiles) {
-            DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+            DevicePolicyData policy = getUserData(userInfo.id);
             final int N = policy.mAdminList.size();
             for (int i = 0; i < N; i++) {
                 ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2188,7 +2206,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2232,7 +2250,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2279,7 +2297,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2326,7 +2344,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i = 0; i < N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2373,7 +2391,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2420,7 +2438,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2526,7 +2544,7 @@
         int count = 0;
         ActiveAdmin strictestAdmin = null;
         for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
-            DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+            DevicePolicyData policy = getUserData(userInfo.id);
             for (ActiveAdmin admin : policy.mAdminList) {
                 if (admin.maximumFailedPasswordsForWipe ==
                         ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
@@ -2738,7 +2756,7 @@
             // Return strictest policy for this user and profiles that are visible from this user.
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
@@ -3055,7 +3073,7 @@
     private void updatePasswordExpirationsLocked(int userHandle) {
             List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             for (UserInfo userInfo : profiles) {
-                int profileId = userInfo.getUserHandle().getIdentifier();
+                int profileId = userInfo.id;
                 DevicePolicyData policy = getUserData(profileId);
                 final int N = policy.mAdminList.size();
                 if (N > 0) {
@@ -4106,13 +4124,13 @@
         }
     }
 
-    public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
-            List<String>features, int userHandle) {
+    public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
+            PersistableBundle args, int userHandle) {
         if (!mHasFeature) {
             return;
         }
         enforceCrossUserPermission(userHandle);
-        enforceNotManagedProfile(userHandle, "manage trust agent features");
+        enforceNotManagedProfile(userHandle, "set trust agent configuration");
         synchronized (this) {
             if (admin == null) {
                 throw new NullPointerException("admin is null");
@@ -4122,57 +4140,68 @@
             }
             ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
                     DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
-            ap.trustAgentFeatures.put(agent.flattenToString(), features);
+            ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
             saveSettingsLocked(userHandle);
             syncDeviceCapabilitiesLocked(getUserData(userHandle));
         }
     }
 
-    public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
-            int userHandle) {
+    public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+            ComponentName agent, int userHandle) {
         if (!mHasFeature) {
             return null;
         }
         enforceCrossUserPermission(userHandle);
+        if (agent == null) {
+            throw new NullPointerException("agent is null");
+        }
+
         synchronized (this) {
-            if (agent == null) {
-                throw new NullPointerException("agent is null");
-            }
             final String componentName = agent.flattenToString();
             if (admin != null) {
                 final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle);
-                return (ap != null) ? ap.trustAgentFeatures.get(componentName) : null;
+                if (ap == null) return null;
+                TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
+                if (trustAgentInfo == null || trustAgentInfo.options == null) return null;
+                List<PersistableBundle> result = new ArrayList<PersistableBundle>();
+                result.add(trustAgentInfo.options);
+                return result;
             }
 
             // Return strictest policy for this user and profiles that are visible from this user.
-            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
-            List<String> result = null;
+            final List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            List<PersistableBundle> result = null;
+
+            // Search through all admins that use KEYGUARD_DISABLE_TRUST_AGENTS and keep track
+            // of the options. If any admin doesn't have options, discard options for the rest
+            // and return null.
+            boolean allAdminsHaveOptions = true;
             for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                DevicePolicyData policy = getUserData(userInfo.id);
                 final int N = policy.mAdminList.size();
-                for (int i=0; i<N; i++) {
-                    ActiveAdmin ap = policy.mAdminList.get(i);
-                    // Compute the intersection of all features for active admins that disable
-                    // trust agents:
-                    if ((ap.disabledKeyguardFeatures
-                            & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
-                        final List<String> features = ap.trustAgentFeatures.get(componentName);
-                        if (result == null) {
-                            if (features == null || features.size() == 0) {
-                                result = new ArrayList<String>();
-                                Slog.w(LOG_TAG, "admin " + ap.info.getPackageName()
-                                    + " has null trust agent feature set; all will be disabled");
-                            } else {
-                                result = new ArrayList<String>(features.size());
-                                result.addAll(features);
+                for (int i=0; i < N; i++) {
+                    final ActiveAdmin active = policy.mAdminList.get(i);
+                    final boolean disablesTrust = (active.disabledKeyguardFeatures
+                            & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+                    final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
+                    if (info != null && info.options != null && !info.options.isEmpty()) {
+                        if (disablesTrust) {
+                            if (result == null) {
+                                result = new ArrayList<PersistableBundle>();
                             }
+                            result.add(info.options);
                         } else {
-                            result.retainAll(features);
+                            Log.w(LOG_TAG, "Ignoring admin " + active.info
+                                    + " because it has trust options but doesn't declare "
+                                    + "KEYGUARD_DISABLE_TRUST_AGENTS");
                         }
+                    } else if (disablesTrust) {
+                        allAdminsHaveOptions = false;
+                        break;
                     }
                 }
             }
-            return result;
+            return allAdminsHaveOptions ? result : null;
         }
     }