Trust: Make setEnabledFeatures asynchronous

Unsynchronizes the call into app code from setEnabledFeatures,
replacing it with a callback mechanism. Also makes this actually
work by fixing the check in TrustManagerService to take into account
whitelisting.

Change-Id: I0831752cd2d3158eda9c8404a5569498f11ac2ac
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 49eb7be..637d080 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,5 +25,5 @@
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
     oneway void setCallback(ITrustAgentServiceCallback callback);
-    boolean setTrustAgentFeaturesEnabled(in Bundle options);
+    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 193ac59..b107bcc 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -16,6 +16,7 @@
 package android.service.trust;
 
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.UserHandle;
 
 /**
@@ -26,4 +27,5 @@
     void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser);
     void revokeTrust();
     void setManagingTrust(boolean managingTrust);
+    void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 2609fce..51f07ec 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
@@ -94,6 +95,7 @@
     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 ITrustAgentServiceCallback mCallback;
 
@@ -110,8 +112,20 @@
                 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);
+                    try {
+                        synchronized (mLock) {
+                            mCallback.onSetTrustAgentFeaturesEnabledCompleted(result, token);
+                        }
+                    } catch (RemoteException e) {
+                        onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
+                    }
+                    break;
             }
-        };
+        }
     };
 
     @Override
@@ -278,10 +292,10 @@
         }
 
         @Override
-        public boolean setTrustAgentFeaturesEnabled(Bundle features) {
-            synchronized (mLock) {
-                return onSetTrustAgentFeaturesEnabled(features);
-            }
+        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/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index eee2a4d..ee20b3c 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -17,12 +17,11 @@
 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.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -51,7 +50,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;
+    private static final int MSG_SET_TRUST_AGENT_FEATURES_COMPLETED = 5;
     private static final int MSG_MANAGING_TRUST = 6;
 
     /**
@@ -79,6 +78,7 @@
     private CharSequence mMessage;
     private boolean mTrustDisabledByDpm;
     private boolean mManagingTrust;
+    private IBinder mSetTrustAgentFeaturesToken;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -121,8 +121,21 @@
                     unbind();
                     mTrustManagerService.resetAgent(mName, mUserId);
                     break;
-                case MSG_DPM_CHANGED:
-                    updateDevicePolicyFeatures(mName);
+                case MSG_SET_TRUST_AGENT_FEATURES_COMPLETED:
+                    IBinder token = (IBinder) msg.obj;
+                    boolean result = msg.arg1 != 0;
+                    if (mSetTrustAgentFeaturesToken == token) {
+                        mSetTrustAgentFeaturesToken = null;
+                        if (mTrustDisabledByDpm && result) {
+                            if (DEBUG) Log.v(TAG, "Re-enabling agent because it acknowledged "
+                                    + "enabled features: " + mName);
+                            mTrustDisabledByDpm = false;
+                            mTrustManagerService.updateTrust(mUserId);
+                        }
+                    } else {
+                        if (DEBUG) Log.w(TAG, "Ignoring MSG_SET_TRUST_AGENT_FEATURES_COMPLETED "
+                                + "with obsolete token: " + mName);
+                    }
                     break;
                 case MSG_MANAGING_TRUST:
                     mManagingTrust = msg.arg1 != 0;
@@ -161,6 +174,13 @@
             if (DEBUG) Slog.v(TAG, "managingTrust()");
             mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget();
         }
+
+        @Override
+        public void onSetTrustAgentFeaturesEnabledCompleted(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();
+        }
     };
 
     private final ServiceConnection mConnection = new ServiceConnection() {
@@ -171,8 +191,7 @@
             mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
             mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
-            updateDevicePolicyFeatures(name);
-            watchForDpmChanges(true);
+            updateDevicePolicyFeatures();
         }
 
         @Override
@@ -180,24 +199,13 @@
             if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString());
             mTrustAgentService = null;
             mManagingTrust = false;
+            mSetTrustAgentFeaturesToken = null;
             mTrustManagerService.mArchive.logAgentDied(mUserId, name);
             mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
             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);
-            }
         }
     };
 
@@ -241,48 +249,32 @@
         }
     }
 
-    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 updateDevicePolicyFeatures() {
         boolean trustDisabled = false;
-        if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + name + ")");
+        if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + mName + ")");
         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
+
+                if ((dpm.getKeyguardDisabledFeatures(null)
+                        & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
+                    List<String> features = dpm.getTrustAgentFeaturesEnabled(null, mName);
+                    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, "TrustAgent " + mName.flattenToShortString()
+                                    + " disabled until it acknowledges "+ features);
                         }
+                        mSetTrustAgentFeaturesToken = new Binder();
+                        mTrustAgentService.setTrustAgentFeaturesEnabled(bundle,
+                                mSetTrustAgentFeaturesToken);
                     }
-                } else {
-                    Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
-                            new IllegalStateException("Stack trace:"));
                 }
             }
         } catch (RemoteException e) {
@@ -316,6 +308,7 @@
         mContext.unbindService(mConnection);
         mBound = false;
         mTrustAgentService = null;
+        mSetTrustAgentFeaturesToken = null;
         mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
         mHandler.removeMessages(MSG_RESTART_TIMEOUT);
     }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 950d639..d3b8d5d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -157,7 +157,7 @@
         dispatchOnTrustChanged(aggregateIsTrusted(userId), userId);
     }
 
-    protected void refreshAgentList() {
+    void refreshAgentList() {
         if (DEBUG) Slog.d(TAG, "refreshAgentList()");
         PackageManager pm = mContext.getPackageManager();
 
@@ -168,13 +168,13 @@
         obsoleteAgents.addAll(mActiveAgents);
 
         for (UserInfo userInfo : userInfos) {
-            int disabledFeatures = lockPatternUtils.getDevicePolicyManager()
-                    .getKeyguardDisabledFeatures(null, userInfo.id);
+            DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
+            int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
             final boolean disableTrustAgents =
                     (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
 
             List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
-            if (disableTrustAgents || enabledAgents == null) {
+            if (enabledAgents == null) {
                 continue;
             }
             List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
@@ -193,6 +193,13 @@
                 ComponentName name = getComponentName(resolveInfo);
                 if (!enabledAgents.contains(name)) continue;
 
+                if (disableTrustAgents) {
+                    List<String> features =
+                            dpm.getTrustAgentFeaturesEnabled(null /* admin */, name);
+                    // Disable agent if no features are enabled.
+                    if (features == null || features.isEmpty()) continue;
+                }
+
                 AgentInfo agentInfo = new AgentInfo();
                 agentInfo.component = name;
                 agentInfo.userId = userInfo.id;
@@ -224,6 +231,15 @@
         }
     }
 
+    void updateDevicePolicyFeatures(int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.agent.isConnected()) {
+                info.agent.updateDevicePolicyFeatures();
+            }
+        }
+    }
+
     private void removeAgentsOfPackage(String packageName) {
         boolean trustMayHaveChanged = false;
         for (int i = mActiveAgents.size() - 1; i >= 0; i--) {
@@ -587,6 +603,7 @@
             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
                     intent.getAction())) {
                 refreshAgentList();
+                updateDevicePolicyFeatures(getSendingUserId());
             }
         }