Define trust agent framework components

Adds the TrustManager system service that allows
registering for changes to the trust status and
reporting events that are important to trust agents.

Bug: 13723878
Change-Id: I7d0d2ea86fd755702d31aa5d49cac038a6cd4301
diff --git a/Android.mk b/Android.mk
index 442c716..e1c15470 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,8 @@
 	core/java/android/app/IWallpaperManager.aidl \
 	core/java/android/app/IWallpaperManagerCallback.aidl \
 	core/java/android/app/admin/IDevicePolicyManager.aidl \
+	core/java/android/app/trust/ITrustManager.aidl \
+	core/java/android/app/trust/ITrustListener.aidl \
 	core/java/android/app/backup/IBackupManager.aidl \
 	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreObserver.aidl \
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b2b2571..d1f94f0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -119,6 +119,7 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
@@ -621,6 +622,12 @@
                 return new MediaSessionManager(ctx);
             }
         });
+        registerService(TRUST_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(TRUST_SERVICE);
+                return new TrustManager(b);
+            }
+        });
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
new file mode 100644
index 0000000..4680043
--- /dev/null
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package android.app.trust;
+
+/**
+ * Private API to be notified about trust changes.
+ *
+ * {@hide}
+ */
+oneway interface ITrustListener {
+    void onTrustChanged(boolean enabled, int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
new file mode 100644
index 0000000..ad4ccbb
--- /dev/null
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -0,0 +1,31 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package android.app.trust;
+
+import android.app.trust.ITrustListener;
+
+/**
+ * System private API to comunicate with trust service.
+ *
+ * {@hide}
+ */
+interface ITrustManager {
+    void reportUnlockAttempt(boolean successful, int userId);
+    void reportEnabledTrustAgentsChanged(int userId);
+    void registerTrustListener(in ITrustListener trustListener);
+    void unregisterTrustListener(in ITrustListener trustListener);
+}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
new file mode 100644
index 0000000..e31c624
--- /dev/null
+++ b/core/java/android/app/trust/TrustManager.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.trust;
+
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+/**
+ * See {@link com.android.server.trust.TrustManagerService}
+ * @hide
+ */
+public class TrustManager {
+
+    private static final int MSG_TRUST_CHANGED = 1;
+
+    private static final String TAG = "TrustManager";
+
+    private final ITrustManager mService;
+    private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
+
+    public TrustManager(IBinder b) {
+        mService = ITrustManager.Stub.asInterface(b);
+        mTrustListeners = new ArrayMap<TrustListener, ITrustListener>();
+    }
+
+    /**
+     * Reports that user {@param userId} has tried to unlock the device.
+     *
+     * @param successful if true, the unlock attempt was successful.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public void reportUnlockAttempt(boolean successful, int userId) {
+        try {
+            mService.reportUnlockAttempt(successful, userId);
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
+     * Reports that the list of enabled trust agents changed for user {@param userId}.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public void reportEnabledTrustAgentsChanged(int userId) {
+        try {
+            mService.reportEnabledTrustAgentsChanged(userId);
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
+     * Registers a listener for trust events.
+     *
+     * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
+     */
+    public void registerTrustListener(final TrustListener trustListener) {
+        try {
+            ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
+                @Override
+                public void onTrustChanged(boolean enabled, int userId) throws RemoteException {
+                    mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId,
+                            trustListener).sendToTarget();
+                }
+            };
+            mService.registerTrustListener(iTrustListener);
+            mTrustListeners.put(trustListener, iTrustListener);
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
+     * Unregisters a listener for trust events.
+     *
+     * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
+     */
+    public void unregisterTrustListener(final TrustListener trustListener) {
+        ITrustListener iTrustListener = mTrustListeners.remove(trustListener);
+        if (iTrustListener != null) {
+            try {
+                mService.unregisterTrustListener(iTrustListener);
+            } catch (RemoteException e) {
+                onError(e);
+            }
+        }
+    }
+
+    private void onError(Exception e) {
+        Log.e(TAG, "Error while calling TrustManagerService", e);
+    }
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_TRUST_CHANGED:
+                    ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2);
+                    break;
+            }
+        }
+    };
+
+    public interface TrustListener {
+
+        /**
+         * Reports that the trust state has changed.
+         * @param enabled if true, the system believes the environment to be trusted.
+         * @param userId the user, for which the trust changed.
+         */
+        void onTrustChanged(boolean enabled, int userId);
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cd91f3b..3fdaef2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2595,6 +2595,14 @@
     public static final String CONSUMER_IR_SERVICE = "consumer_ir";
 
     /**
+     * {@link android.app.trust.TrustManager} for managing trust agents.
+     * @see #getSystemService
+     * @see android.app.trust.TrustManager
+     * @hide
+     */
+    public static final String TRUST_SERVICE = "trust";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2d79491..4752da5 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -19,11 +19,14 @@
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
 import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -46,6 +49,8 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -145,6 +150,8 @@
     private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
             Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
 
+    private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private DevicePolicyManager mDevicePolicyManager;
@@ -167,6 +174,15 @@
         return mDevicePolicyManager;
     }
 
+    private TrustManager getTrustManager() {
+        TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+        if (trust == null) {
+            Log.e(TAG, "Can't get TrustManagerService: is it running?",
+                    new IllegalStateException("Stack trace:"));
+        }
+        return trust;
+    }
+
     /**
      * @param contentResolver Used to look up and save settings.
      */
@@ -242,10 +258,14 @@
      */
     public void reportFailedPasswordAttempt() {
         getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId());
+        getTrustManager().reportUnlockAttempt(false /* authenticated */,
+                getCurrentOrCallingUserId());
     }
 
     public void reportSuccessfulPasswordAttempt() {
         getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId());
+        getTrustManager().reportUnlockAttempt(true /* authenticated */,
+                getCurrentOrCallingUserId());
     }
 
     public void setCurrentUser(int userId) {
@@ -496,11 +516,12 @@
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
         try {
-            getLockSettings().setLockPattern(patternToString(pattern), getCurrentOrCallingUserId());
+            int userId = getCurrentOrCallingUserId();
+            getLockSettings().setLockPattern(patternToString(pattern), userId);
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (pattern != null) {
 
-                int userHandle = getCurrentOrCallingUserId();
+                int userHandle = userId;
                 if (userHandle == UserHandle.USER_OWNER) {
                     String stringPattern = patternToString(pattern);
                     updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
@@ -511,18 +532,18 @@
                     deleteGallery();
                     setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
                     dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
-                            pattern.size(), 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+                            pattern.size(), 0, 0, 0, 0, 0, 0, userId);
                 } else {
                     setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
                     setLong(PASSWORD_TYPE_ALTERNATE_KEY,
                             DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
                     finishBiometricWeak();
                     dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
-                            0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+                            0, 0, 0, 0, 0, 0, 0, userId);
                 }
             } else {
                 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
-                        0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+                        0, 0, 0, 0, 0, userId);
             }
         } catch (RemoteException re) {
             Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -1374,4 +1395,38 @@
         setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
     }
 
+    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
+        setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId());
+    }
+
+    public List<ComponentName> getEnabledTrustAgents() {
+        return getEnabledTrustAgents(getCurrentOrCallingUserId());
+    }
+
+    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
+        StringBuilder sb = new StringBuilder();
+        for (ComponentName cn : activeTrustAgents) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            sb.append(cn.flattenToShortString());
+        }
+        setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
+        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
+    }
+
+    public List<ComponentName> getEnabledTrustAgents(int userId) {
+        String serialized = getString(ENABLED_TRUST_AGENTS, userId);
+        if (TextUtils.isEmpty(serialized)) {
+            return null;
+        }
+        String[] split = serialized.split(",");
+        ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
+        for (String s : split) {
+            if (!TextUtils.isEmpty(s)) {
+                activeTrustAgents.add(ComponentName.unflattenFromString(s));
+            }
+        }
+        return activeTrustAgents;
+    }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2e0ac08..2a4d872 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2547,6 +2547,13 @@
         android:label="@string/permlab_control_keyguard"
         android:description="@string/permdesc_control_keyguard" />
 
+    <!-- Allows an application to listen to trust changes.  Only allowed for system processes.
+        @hide -->
+    <permission android:name="android.permission.TRUST_LISTENER"
+                android:protectionLevel="signature"
+                android:label="@string/permlab_trust_listener"
+                android:description="@string/permdesc_trust_listener" />
+
     <!-- Must be required by an {@link
          android.service.trust.TrustAgentService},
          to ensure that only the system can bind to it. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eb16bbd..3a4f059 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3651,6 +3651,11 @@
     <!-- Description of an application permission that lets it control keyguard. -->
     <string name="permdesc_control_keyguard">Allows an application to control keguard.</string>
 
+    <!-- Title of an application permission that lets it listen to trust state changes. -->
+    <string name="permlab_trust_listener">Listen to trust state changes.</string>
+    <!-- Description of an application permission that lets it listen to trust state changes. -->
+    <string name="permdesc_trust_listener">Allows an application to listen for changes in trust state.</string>
+
     <!-- Title of an application permission that lets it bind to a trust agent service. -->
     <string name="permlab_bind_trust_agent_service">Bind to a trust agent service</string>
     <!-- Description of an application permission that lets it bind to a trust agent service. -->
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
new file mode 100644
index 0000000..a83fa87
--- /dev/null
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.trust;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import android.service.trust.ITrustAgentService;
+import android.service.trust.ITrustAgentServiceCallback;
+
+/**
+ * A wrapper around a TrustAgentService interface. Coordinates communication between
+ * TrustManager and the actual TrustAgent.
+ */
+public class TrustAgentWrapper {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TrustAgentWrapper";
+
+    private static final int MSG_ENABLE_TRUST = 1;
+    private static final int MSG_REVOKE_TRUST = 2;
+    private static final int MSG_TRUST_TIMEOUT = 3;
+
+    private final TrustManagerService mTrustManagerService;
+    private final int mUserId;
+    private final Context mContext;
+    private final ComponentName mName;
+
+    private ITrustAgentService mTrustAgentService;
+
+    // Trust state
+    private boolean mTrusted;
+    private String mMessage;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ENABLE_TRUST:
+                    mTrusted = true;
+                    mMessage = (String) msg.obj;
+                    boolean initiatedByUser = msg.arg1 != 0;
+                    // TODO: Handle handle user initiated trust changes.
+                    mTrustManagerService.updateTrust(mUserId);
+                    break;
+                case MSG_TRUST_TIMEOUT:
+                    if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString());
+                    // Fall through.
+                case MSG_REVOKE_TRUST:
+                    mTrusted = false;
+                    mMessage = null;
+                    mTrustManagerService.updateTrust(mUserId);
+                    break;
+            }
+        }
+    };
+
+    private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
+
+        public void enableTrust(String userMessage, long durationMs, boolean initiatedByUser) {
+            if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
+                        + ", initiatedByUser = " + initiatedByUser + ")");
+
+            mHandler.obtainMessage(MSG_ENABLE_TRUST, initiatedByUser ? 1 : 0, 0, userMessage)
+                    .sendToTarget();
+            if (durationMs > 0) {
+                mHandler.removeMessages(MSG_TRUST_TIMEOUT);
+                mHandler.sendEmptyMessageDelayed(MSG_TRUST_TIMEOUT, durationMs);
+            }
+        }
+
+        public void revokeTrust() {
+            if (DEBUG) Slog.v(TAG, "revokeTrust()");
+            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
+        }
+    };
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
+            mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
+            setCallback(mCallback);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString());
+            mTrustAgentService = null;
+            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
+        }
+    };
+
+
+    public TrustAgentWrapper(Context context, TrustManagerService trustManagerService,
+            Intent intent, UserHandle user) {
+        mContext = context;
+        mTrustManagerService = trustManagerService;
+        mUserId = user.getIdentifier();
+        mName = intent.getComponent();
+        if (!context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user)) {
+            if (DEBUG) Log.v(TAG, "can't bind to TrustAgent " + mName.flattenToShortString());
+            // TODO: retry somehow?
+        }
+    }
+
+    private void onError(Exception e) {
+        Slog.w(TAG , "Remote Exception", e);
+    }
+
+    /**
+     * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean)
+     */
+    public void onUnlockAttempt(boolean successful) {
+        try {
+            if (mTrustAgentService != null) mTrustAgentService.onUnlockAttempt(successful);
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    private void setCallback(ITrustAgentServiceCallback callback) {
+        try {
+            if (mTrustAgentService != null) {
+                mTrustAgentService.setCallback(callback);
+            }
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    public boolean isTrusted() {
+        return mTrusted;
+    }
+
+    public String getMessage() {
+        return mMessage;
+    }
+
+    public void unbind() {
+        if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
+        mContext.unbindService(mConnection);
+    }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
new file mode 100644
index 0000000..a2a49c9
--- /dev/null
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.trust;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.Manifest;
+import android.app.trust.ITrustListener;
+import android.app.trust.ITrustManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.trust.TrustAgentService;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages trust agents and trust listeners.
+ *
+ * It is responsible for binding to the enabled {@link android.service.trust.TrustAgentService}s
+ * of each user and notifies them about events that are relevant to them.
+ * It start and stops them based on the value of
+ * {@link com.android.internal.widget.LockPatternUtils#getEnabledTrustAgents(int)}.
+ *
+ * It also keeps a set of {@link android.app.trust.ITrustListener}s that are notified whenever the
+ * trust state changes for any user.
+ *
+ * Trust state and the setting of enabled agents is kept per user and each user has its own
+ * instance of a {@link android.service.trust.TrustAgentService}.
+ */
+public class TrustManagerService extends SystemService {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TrustManagerService";
+
+    private static final Intent TRUST_AGENT_INTENT =
+            new Intent(TrustAgentService.SERVICE_INTERFACE);
+
+    private static final int MSG_REGISTER_LISTENER = 1;
+    private static final int MSG_UNREGISTER_LISTENER = 2;
+    private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
+    private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
+
+    private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
+    private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
+    private final Context mContext;
+
+    private UserManager mUserManager;
+
+    /**
+     * Cache for {@link #refreshAgentList()}
+     */
+    private final ArraySet<AgentInfo> mObsoleteAgents = new ArraySet<AgentInfo>();
+
+
+    public TrustManagerService(Context context) {
+        super(context);
+        mContext = context;
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.TRUST_SERVICE, mService);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) {
+            // Listen for package changes
+            mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
+            refreshAgentList();
+        }
+    }
+
+    // Agent management
+
+    private static final class AgentInfo {
+        CharSequence label;
+        Drawable icon;
+        ComponentName component; // service that implements ITrustAgent
+        ComponentName settings; // setting to launch to modify agent.
+        TrustAgentWrapper agent;
+        int userId;
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof AgentInfo)) {
+                return false;
+            }
+            AgentInfo o = (AgentInfo) other;
+            return component.equals(o.component) && userId == o.userId;
+        }
+
+        @Override
+        public int hashCode() {
+            return component.hashCode() * 31 + userId;
+        }
+    }
+
+    private void updateTrustAll() {
+        List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
+        for (UserInfo userInfo : userInfos) {
+            updateTrust(userInfo.id);
+        }
+    }
+
+    public void updateTrust(int userId) {
+        dispatchOnTrustChanged(aggregateIsTrusted(userId), userId);
+    }
+
+    protected void refreshAgentList() {
+        if (DEBUG) Slog.d(TAG, "refreshAgentList()");
+        PackageManager pm = mContext.getPackageManager();
+
+        List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+
+        mObsoleteAgents.clear();
+        mObsoleteAgents.addAll(mActiveAgents);
+
+        for (UserInfo userInfo : userInfos) {
+            List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
+            if (enabledAgents == null) {
+                continue;
+            }
+            List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
+                    PackageManager.GET_META_DATA, userInfo.id);
+            for (ResolveInfo resolveInfo : resolveInfos) {
+                if (resolveInfo.serviceInfo == null) continue;
+                ComponentName name = getComponentName(resolveInfo);
+                if (!enabledAgents.contains(name)) continue;
+
+                AgentInfo agentInfo = new AgentInfo();
+                agentInfo.component = name;
+                agentInfo.userId = userInfo.id;
+                if (!mActiveAgents.contains(agentInfo)) {
+                    agentInfo.label = resolveInfo.loadLabel(pm);
+                    agentInfo.icon = resolveInfo.loadIcon(pm);
+                    agentInfo.settings = getSettingsComponentName(pm, resolveInfo);
+                    agentInfo.agent = new TrustAgentWrapper(mContext, this,
+                            new Intent().setComponent(name), userInfo.getUserHandle());
+                    mActiveAgents.add(agentInfo);
+                } else {
+                    mObsoleteAgents.remove(agentInfo);
+                }
+            }
+        }
+
+        boolean trustMayHaveChanged = false;
+        for (int i = 0; i < mObsoleteAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.agent.isTrusted()) {
+                trustMayHaveChanged = true;
+            }
+            info.agent.unbind();
+            mActiveAgents.remove(info);
+        }
+
+        if (trustMayHaveChanged) {
+            updateTrustAll();
+        }
+    }
+
+    private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
+        if (resolveInfo == null || resolveInfo.serviceInfo == null
+                || resolveInfo.serviceInfo.metaData == null) return null;
+        String cn = null;
+        XmlResourceParser parser = null;
+        Exception caughtException = null;
+        try {
+            parser = resolveInfo.serviceInfo.loadXmlMetaData(pm,
+                    TrustAgentService.TRUST_AGENT_META_DATA);
+            if (parser == null) {
+                Slog.w(TAG, "Can't find " + TrustAgentService.TRUST_AGENT_META_DATA + " meta-data");
+                return null;
+            }
+            Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // Drain preamble.
+            }
+            String nodeName = parser.getName();
+            if (!"trust_agent".equals(nodeName)) {
+                Slog.w(TAG, "Meta-data does not start with trust_agent tag");
+                return null;
+            }
+            TypedArray sa = res
+                    .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
+            cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
+            sa.recycle();
+        } catch (PackageManager.NameNotFoundException e) {
+            caughtException = e;
+        } catch (IOException e) {
+            caughtException = e;
+        } catch (XmlPullParserException e) {
+            caughtException = e;
+        } finally {
+            if (parser != null) parser.close();
+        }
+        if (caughtException != null) {
+            Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
+            return null;
+        }
+        if (cn == null) {
+            return null;
+        }
+        if (cn.indexOf('/') < 0) {
+            cn = resolveInfo.serviceInfo.packageName + "/" + cn;
+        }
+        return ComponentName.unflattenFromString(cn);
+    }
+
+    private ComponentName getComponentName(ResolveInfo resolveInfo) {
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) return null;
+        return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
+    }
+
+    // Agent dispatch and aggregation
+
+    private boolean aggregateIsTrusted(int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId) {
+                if (info.agent.isTrusted()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void dispatchUnlockAttempt(boolean successful, int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId) {
+                info.agent.onUnlockAttempt(successful);
+            }
+        }
+    }
+
+    // Listeners
+
+    private void addListener(ITrustListener listener) {
+        for (int i = 0; i < mTrustListeners.size(); i++) {
+            if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
+                return;
+            }
+        }
+        mTrustListeners.add(listener);
+    }
+
+    private void removeListener(ITrustListener listener) {
+        for (int i = 0; i < mTrustListeners.size(); i++) {
+            if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
+                mTrustListeners.get(i);
+                return;
+            }
+        }
+    }
+
+    private void dispatchOnTrustChanged(boolean enabled, int userId) {
+        for (int i = 0; i < mTrustListeners.size(); i++) {
+            try {
+                mTrustListeners.get(i).onTrustChanged(enabled, userId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
+                mTrustListeners.get(i);
+                i--;
+            }
+        }
+    }
+
+    // Plumbing
+
+    private final IBinder mService = new ITrustManager.Stub() {
+        @Override
+        public void reportUnlockAttempt(boolean authenticated, int userId) throws RemoteException {
+            enforceReportPermission();
+            mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_ATTEMPT, authenticated ? 1 : 0, userId)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
+            enforceReportPermission();
+            // coalesce refresh messages.
+            mHandler.removeMessages(MSG_ENABLED_AGENTS_CHANGED);
+            mHandler.sendEmptyMessage(MSG_ENABLED_AGENTS_CHANGED);
+        }
+
+        @Override
+        public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
+            enforceListenerPermission();
+            mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
+        }
+
+        @Override
+        public void unregisterTrustListener(ITrustListener trustListener) throws RemoteException {
+            enforceListenerPermission();
+            mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
+        }
+
+        private void enforceReportPermission() {
+            mContext.enforceCallingPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+                    "reporting trust events");
+        }
+
+        private void enforceListenerPermission() {
+            mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
+                    "register trust listener");
+        }
+    };
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_REGISTER_LISTENER:
+                    addListener((ITrustListener) msg.obj);
+                    break;
+                case MSG_UNREGISTER_LISTENER:
+                    removeListener((ITrustListener) msg.obj);
+                    break;
+                case MSG_DISPATCH_UNLOCK_ATTEMPT:
+                    dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
+                    break;
+                case MSG_ENABLED_AGENTS_CHANGED:
+                    refreshAgentList();
+                    break;
+            }
+        }
+    };
+
+    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override
+        public void onSomePackagesChanged() {
+            refreshAgentList();
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            // We're interested in all changes, even if just some components get enabled / disabled.
+            return true;
+        }
+    };
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d98139f..ab4c89e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -76,6 +76,7 @@
 import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.trust.TrustManagerService;
 import com.android.server.twilight.TwilightService;
 import com.android.server.usb.UsbService;
 import com.android.server.wallpaper.WallpaperManagerService;
@@ -913,6 +914,13 @@
                 } catch (Throwable e) {
                     reportWtf("starting MediaRouterService", e);
                 }
+
+                try {
+                    Slog.i(TAG, "Trust Manager");
+                    mSystemServiceManager.startService(TrustManagerService.class);
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Failure starting TrustManagerService", e);
+                }
             }
         }