Merge "Add new DevicePolicyManager API to allow fine-grained TrustAgent management" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index d89f2dd..68056b8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5332,6 +5332,7 @@
     method public android.os.UserHandle createUser(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 void setTrustAgentFeaturesEnabled(android.content.ComponentName, android.content.ComponentName, java.util.List<java.lang.String>);
     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);
@@ -5357,6 +5358,7 @@
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
+    method public java.util.List<java.lang.String> getTrustAgentFeaturesEnabled(android.content.ComponentName, android.content.ComponentName);
     method public boolean hasAnyCaCertsInstalled();
     method public boolean hasCaCertInstalled(byte[]);
     method public boolean hasGrantedPolicy(android.content.ComponentName, int);
@@ -27414,8 +27416,10 @@
     ctor public TrustAgentService();
     method public final void grantTrust(java.lang.CharSequence, long, boolean);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public boolean onSetTrustAgentFeaturesEnabled(android.os.Bundle);
     method public void onUnlockAttempt(boolean);
     method public final void revokeTrust();
+    field public static final java.lang.String KEY_FEATURES = "trust_agent_features";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.trust.TrustAgentService";
     field public static final java.lang.String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
   }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 76cf29a..20a0072 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,6 +54,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -2327,6 +2328,51 @@
     }
 
     /**
+     * Sets a list of features to enable for a TrustAgentService 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.
+     *
+     * <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.
+     *
+     * @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.
+     */
+    public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
+            List<String> features) {
+        if (mService != null) {
+            try {
+                mService.setTrustAgentFeaturesEnabled(admin, agent, features, UserHandle.myUserId());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Gets list of enabled features for the given {@link TrustAgentService} agent. If admin is
+     * null, this will return the intersection of all features enabled for the given agent by all
+     * 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.
+     */
+    public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent) {
+        if (mService != null) {
+            try {
+                return mService.getTrustAgentFeaturesEnabled(admin, agent, UserHandle.myUserId());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return new ArrayList<String>(); // empty list
+    }
+
+    /**
      * Called by a profile owner to set whether caller-Id information from the managed
      * profile will be shown for incoming calls.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6499ae4..a6544e6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -169,4 +169,8 @@
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
     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);
+
 }
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 863a249..49eb7be 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -22,7 +22,8 @@
  * Communication channel from TrustManagerService to the TrustAgent.
  * @hide
  */
-oneway interface ITrustAgentService {
-    void onUnlockAttempt(boolean successful);
-    void setCallback(ITrustAgentServiceCallback callback);
+interface ITrustAgentService {
+    oneway void onUnlockAttempt(boolean successful);
+    oneway void setCallback(ITrustAgentServiceCallback callback);
+    boolean setTrustAgentFeaturesEnabled(in Bundle options);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 8fa45e6..333a892 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -19,10 +19,12 @@
 import android.Manifest;
 import android.annotation.SdkConstant;
 import android.app.Service;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -74,6 +76,12 @@
      */
     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.
+     */
+    public static final String KEY_FEATURES = "trust_agent_features";
+
     private static final int MSG_UNLOCK_ATTEMPT = 1;
 
     private static final boolean DEBUG = false;
@@ -124,6 +132,26 @@
     }
 
     /**
+     * Called when device policy wants to restrict features in the TrustAgent in response to
+     * {@link DevicePolicyManager#setTrustAgentFeaturesEnabled(ComponentName, ComponentName, java.util.List) }.
+     * TrustAgents that support this feature 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.
+     */
+    public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
+        return false;
+    }
+
+    /**
      * Call to grant trust on the device.
      *
      * @param message describes why the device is trusted, e.g. "Trusted by location".
@@ -185,6 +213,7 @@
                     .sendToTarget();
         }
 
+        @Override
         public void setCallback(ITrustAgentServiceCallback callback) {
             synchronized (mLock) {
                 mCallback = callback;
@@ -194,6 +223,13 @@
                 }
             }
         }
+
+        @Override
+        public boolean setTrustAgentFeaturesEnabled(Bundle features) {
+            synchronized (mLock) {
+                return onSetTrustAgentFeaturesEnabled(features);
+            }
+        }
     }
 
 }
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 a51ea75..50a3f82 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
@@ -25,6 +25,7 @@
 import android.preference.PreferenceManager;
 import android.service.trust.TrustAgentService;
 import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
 import android.widget.Toast;
 
 public class SampleTrustAgent extends TrustAgentService {
@@ -41,6 +42,8 @@
     private static final String PREFERENCE_REPORT_UNLOCK_ATTEMPTS
             = "preference.report_unlock_attempts";
 
+    private static final String TAG = "SampleTrustAgent";
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -60,6 +63,13 @@
     }
 
     @Override
+    public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
+        Log.v(TAG, "Policy options received: " + options.getStringArrayList(KEY_FEATURES));
+        // TODO: Handle options
+        return true; // inform DPM that we support it
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         mLocalBroadcastManager.unregisterReceiver(mReceiver);
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 51009af..0acb82f 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -16,10 +16,14 @@
 
 package com.android.server.trust;
 
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -30,6 +34,10 @@
 import android.util.Slog;
 import android.service.trust.ITrustAgentService;
 import android.service.trust.ITrustAgentServiceCallback;
+import android.service.trust.TrustAgentService;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A wrapper around a TrustAgentService interface. Coordinates communication between
@@ -43,6 +51,7 @@
     private static final int MSG_REVOKE_TRUST = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
     private static final int MSG_RESTART_TIMEOUT = 4;
+    private static final int MSG_DPM_CHANGED = 5;
 
     /**
      * Time in uptime millis that we wait for the service connection, both when starting
@@ -67,6 +76,7 @@
     // Trust state
     private boolean mTrusted;
     private CharSequence mMessage;
+    private boolean mTrustDisabledByDpm;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -109,6 +119,9 @@
                     unbind();
                     mTrustManagerService.resetAgent(mName, mUserId);
                     break;
+                case MSG_DPM_CHANGED:
+                    updateDevicePolicyFeatures(mName);
+                    break;
             }
         }
     };
@@ -141,6 +154,8 @@
             mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
             mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
+            updateDevicePolicyFeatures(name);
+            watchForDpmChanges(true);
         }
 
         @Override
@@ -152,9 +167,21 @@
             if (mBound) {
                 scheduleRestart();
             }
+            // mTrustDisabledByDpm maintains state
+            watchForDpmChanges(false);
         }
     };
 
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
+                    .equals(intent.getAction())) {
+                mHandler.sendEmptyMessage(MSG_DPM_CHANGED);
+            }
+        }
+    };
 
     public TrustAgentWrapper(Context context, TrustManagerService trustManagerService,
             Intent intent, UserHandle user) {
@@ -196,8 +223,62 @@
         }
     }
 
+    private void watchForDpmChanges(boolean start) {
+        if (start) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+            filter.addAction(Intent.ACTION_USER_REMOVED);
+            mContext.registerReceiver(mBroadcastReceiver, filter);
+        } else {
+            mContext.unregisterReceiver(mBroadcastReceiver);
+        }
+    }
+
+    private boolean updateDevicePolicyFeatures(ComponentName name) {
+        boolean trustDisabled = false;
+        if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + name + ")");
+        try {
+            if (mTrustAgentService != null) {
+                DevicePolicyManager dpm =
+                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+                if (dpm != null) {
+                    // If trust disabled, only enable it if the options bundle is set and
+                    // accepted by the TrustAgent.
+                    if ((dpm.getKeyguardDisabledFeatures(null)
+                            & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
+                        List<String> features = dpm.getTrustAgentFeaturesEnabled(null, name);
+                        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, "TrustAgent " + name.flattenToShortString()
+                                        + " disabled except "+ features);
+                            }
+                            trustDisabled = mTrustAgentService.setTrustAgentFeaturesEnabled(bundle);
+                        } else {
+                            if (DEBUG) Slog.v(TAG, "TrustAgent " + name + " disabled by flag");
+                            trustDisabled = true; // trust agent should be disabled
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
+                            new IllegalStateException("Stack trace:"));
+                }
+            }
+        } catch (RemoteException e) {
+            onError(e);
+        }
+        if (mTrustDisabledByDpm != trustDisabled) {
+            mTrustDisabledByDpm = trustDisabled;
+            mTrustManagerService.updateTrust(mUserId);
+        }
+        return trustDisabled;
+    }
+
     public boolean isTrusted() {
-        return mTrusted;
+        return mTrusted && !mTrustDisabledByDpm;
     }
 
     public CharSequence getMessage() {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 14436aa..9c2df55 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -169,7 +169,7 @@
         for (UserInfo userInfo : userInfos) {
             int disabledFeatures = lockPatternUtils.getDevicePolicyManager()
                     .getKeyguardDisabledFeatures(null, userInfo.id);
-            boolean disableTrustAgents =
+            final boolean disableTrustAgents =
                     (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
 
             List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 25f9e9b..f49451e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -80,6 +80,7 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.service.trust.TrustAgentService;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -89,6 +90,11 @@
 import android.view.IWindowManager;
 
 import org.xmlpull.v1.XmlPullParser;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
@@ -111,6 +117,8 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -260,6 +268,9 @@
         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_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 = "component";
         private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
         private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
         private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list";
@@ -333,6 +344,7 @@
         boolean specifiesGlobalProxy = false;
         String globalProxySpec = null;
         String globalProxyExclusionList = null;
+        HashMap<String, List<String>> trustAgentFeatures = new HashMap<String, List<String>>();
 
         ActiveAdmin(DeviceAdminInfo _info) {
             info = _info;
@@ -463,15 +475,30 @@
                 }
                 out.endTag(null,  TAG_DISABLE_ACCOUNT_MANAGEMENT);
             }
+            if (!trustAgentFeatures.isEmpty()) {
+                Set<Entry<String, List<String>>> set = trustAgentFeatures.entrySet();
+                out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
+                for (Entry<String, List<String>> component : set) {
+                    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.endTag(null, TAG_TRUST_AGENT_COMPONENT);
+                }
+                out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
+            }
         }
 
         void readFromXml(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             int outerDepth = parser.getDepth();
             int type;
-            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+            while ((type=parser.next()) != END_DOCUMENT
+                   && (type != END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == END_TAG || type == TEXT) {
                     continue;
                 }
                 String tag = parser.getName();
@@ -541,22 +568,9 @@
                     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);
-                        }
-                    }
+                    accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag);
+                } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
+                    trustAgentFeatures = getAllTrustAgentFeatures(parser, tag);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                 }
@@ -564,6 +578,68 @@
             }
         }
 
+        private Set<String> readDisableAccountInfo(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException {
+            int outerDepthDAM = parser.getDepth();
+            int typeDAM;
+            Set<String> result = new HashSet<String>();
+            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_ACCOUNT_TYPE.equals(tagDAM)) {
+                    result.add(parser.getAttributeValue(null, ATTR_VALUE));
+                } else {
+                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                }
+            }
+            return result;
+        }
+
+        private HashMap<String, List<String>> getAllTrustAgentFeatures(XmlPullParser parser,
+                String tag) throws XmlPullParserException, IOException {
+            int outerDepthDAM = parser.getDepth();
+            int typeDAM;
+            HashMap<String, List<String>> result = new HashMap<String, List<String>>();
+            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_COMPONENT.equals(tagDAM)) {
+                    final String component = parser.getAttributeValue(null, ATTR_VALUE);
+                    result.put(component, getTrustAgentFeatures(parser, tag));
+                } else {
+                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                }
+            }
+            return result;
+        }
+
+        private List<String> getTrustAgentFeatures(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException  {
+            int outerDepthDAM = parser.getDepth();
+            int typeDAM;
+            ArrayList<String> result = new ArrayList<String>();
+            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);
+                } else {
+                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                }
+            }
+            return result;
+        }
+
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
             pw.print(prefix); pw.println("policies:");
@@ -3533,6 +3609,76 @@
         }
     }
 
+    public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
+            List<String>features, int userHandle) {
+        if (!mHasFeature) {
+            return;
+        }
+        enforceCrossUserPermission(userHandle);
+        enforceNotManagedProfile(userHandle, "manage trust agent features");
+        synchronized (this) {
+            if (admin == null) {
+                throw new NullPointerException("admin is null");
+            }
+            if (agent == null) {
+                throw new NullPointerException("agent is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+            ap.trustAgentFeatures.put(agent.flattenToString(), features);
+            saveSettingsLocked(userHandle);
+            syncDeviceCapabilitiesLocked(getUserData(userHandle));
+        }
+    }
+
+    public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
+            int userHandle) {
+        if (!mHasFeature) {
+            return null;
+        }
+        enforceCrossUserPermission(userHandle);
+        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;
+            }
+
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            List<String> result = null;
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                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);
+                            }
+                        } else {
+                            result.retainAll(features);
+                        }
+                    }
+                }
+            }
+            return result;
+        }
+    }
+
     @Override
     public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
         synchronized (this) {