Add escrow token APIs to TrustAgentService
Security review: b/31273740
Design doc: go/auto_login
Test: manual
Change-Id: Ib11d4146135a58f1dc451ae8e081977a8f8e6ace
diff --git a/api/system-current.txt b/api/system-current.txt
index fc78b4b..feea86e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39845,20 +39845,29 @@
 
   public class TrustAgentService extends android.app.Service {
     ctor public TrustAgentService();
+    method public final void addEscrowToken(byte[], android.os.UserHandle);
     method public final deprecated void grantTrust(java.lang.CharSequence, long, boolean);
     method public final void grantTrust(java.lang.CharSequence, long, int);
+    method public final void isEscrowTokenActive(long, android.os.UserHandle);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public boolean onConfigure(java.util.List<android.os.PersistableBundle>);
     method public void onDeviceLocked();
     method public void onDeviceUnlockLockout(long);
     method public void onDeviceUnlocked();
+    method public void onEscrowTokenAdded(byte[], long, android.os.UserHandle);
+    method public void onEscrowTokenRemoved(long, boolean);
+    method public void onEscrowTokenStateReceived(long, int);
     method public void onTrustTimeout();
     method public void onUnlockAttempt(boolean);
+    method public final void removeEscrowToken(long, android.os.UserHandle);
     method public final void revokeTrust();
     method public final void setManagingTrust(boolean);
+    method public final void unlockUserWithToken(long, byte[], android.os.UserHandle);
     field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2
     field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.trust.TrustAgentService";
+    field public static final int TOKEN_STATE_ACTIVE = 1; // 0x1
+    field public static final int TOKEN_STATE_INACTIVE = 0; // 0x0
     field public static final java.lang.String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
   }
 
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 22b4d09..21661db 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -16,6 +16,7 @@
 package android.service.trust;
 
 import android.os.PersistableBundle;
+import android.os.UserHandle;
 import android.service.trust.ITrustAgentServiceCallback;
 
 /**
@@ -30,4 +31,7 @@
     oneway void onDeviceUnlocked();
     oneway void onConfigure(in List<PersistableBundle> options, IBinder token);
     oneway void setCallback(ITrustAgentServiceCallback callback);
+    oneway void onEscrowTokenAdded(in byte[] token, long handle, in UserHandle user);
+    oneway void onTokenStateReceived(long handle, int tokenState);
+    oneway void onEscrowTokenRemoved(long handle, boolean successful);
 }
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index ec66cc8..14df7cb 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -28,4 +28,8 @@
     void revokeTrust();
     void setManagingTrust(boolean managingTrust);
     void onConfigureCompleted(boolean result, IBinder token);
+    void addEscrowToken(in byte[] token, int userId);
+    void isEscrowTokenActive(long handle, int userId);
+    void removeEscrowToken(long handle, int userId);
+    void unlockUserWithToken(long handle, in byte[] token, int userId);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 0d5177d..2b37a23 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -23,16 +23,20 @@
 import android.app.Service;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
+import android.content.Context;
 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.Message;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 import android.util.Slog;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
@@ -118,13 +122,44 @@
     public @interface GrantTrustFlags {}
 
 
+    /**
+     * Int enum indicating that escrow token is active.
+     * See {@link #onEscrowTokenStateReceived(long, int)}
+     *
+     */
+    public static final int TOKEN_STATE_ACTIVE = 1;
+
+    /**
+     * Int enum indicating that escow token is inactive.
+     * See {@link #onEscrowTokenStateReceived(long, int)}
+     *
+     */
+    public static final int TOKEN_STATE_INACTIVE = 0;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            value = {
+                TOKEN_STATE_ACTIVE,
+                TOKEN_STATE_INACTIVE,
+            })
+    public @interface TokenState {}
+
     private static final int MSG_UNLOCK_ATTEMPT = 1;
     private static final int MSG_CONFIGURE = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
     private static final int MSG_DEVICE_LOCKED = 4;
     private static final int MSG_DEVICE_UNLOCKED = 5;
     private static final int MSG_UNLOCK_LOCKOUT = 6;
+    private static final int MSG_ESCROW_TOKEN_ADDED = 7;
+    private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
+    private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
 
+    private static final String EXTRA_TOKEN = "token";
+    private static final String EXTRA_TOKEN_HANDLE = "token_handle";
+    private static final String EXTRA_USER_HANDLE = "user_handle";
+    private static final String EXTRA_TOKEN_STATE = "token_state";
+    private static final String EXTRA_TOKEN_REMOVED_RESULT = "token_removed_result";
     /**
      * Class containing raw data for a given configuration request.
      */
@@ -155,7 +190,7 @@
                 case MSG_UNLOCK_LOCKOUT:
                     onDeviceUnlockLockout(msg.arg1);
                     break;
-                case MSG_CONFIGURE:
+                case MSG_CONFIGURE: {
                     ConfigurationData data = (ConfigurationData) msg.obj;
                     boolean result = onConfigure(data.options);
                     if (data.token != null) {
@@ -168,6 +203,7 @@
                         }
                     }
                     break;
+                }
                 case MSG_TRUST_TIMEOUT:
                     onTrustTimeout();
                     break;
@@ -177,6 +213,28 @@
                 case MSG_DEVICE_UNLOCKED:
                     onDeviceUnlocked();
                     break;
+                case MSG_ESCROW_TOKEN_ADDED: {
+                    Bundle data = msg.getData();
+                    byte[] token = data.getByteArray(EXTRA_TOKEN);
+                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
+                    UserHandle user = (UserHandle) data.getParcelable(EXTRA_USER_HANDLE);
+                    onEscrowTokenAdded(token, handle, user);
+                    break;
+                }
+                case MSG_ESCROW_TOKEN_STATE_RECEIVED: {
+                    Bundle data = msg.getData();
+                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
+                    int tokenState = data.getInt(EXTRA_TOKEN_STATE, TOKEN_STATE_INACTIVE);
+                    onEscrowTokenStateReceived(handle, tokenState);
+                    break;
+                }
+                case MSG_ESCROW_TOKEN_REMOVED: {
+                    Bundle data = msg.getData();
+                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
+                    boolean success = data.getBoolean(EXTRA_TOKEN_REMOVED_RESULT);
+                    onEscrowTokenRemoved(handle, success);
+                    break;
+                }
             }
         }
     };
@@ -245,6 +303,38 @@
     public void onDeviceUnlockLockout(long timeoutMs) {
     }
 
+  /**
+     * Called when an escrow token is added for user userId.
+     *
+     * @param token the added token
+     * @param handle the handle to the corresponding internal synthetic password. A user is unlocked
+     * by presenting both handle and escrow token.
+     * @param user the user to which the escrow token is added.
+     *
+     */
+    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
+    }
+
+    /**
+     * Called when an escrow token state is received upon request.
+     *
+     * @param handle the handle to the internal synthetic password.
+     * @param state the state of the requested escrow token, see {@link TokenState}.
+     *
+     */
+    public void onEscrowTokenStateReceived(long handle, @TokenState int tokenState) {
+    }
+
+    /**
+     * Called when an escrow token is removed.
+     *
+     * @param handle the handle to the removed the synthetic password.
+     * @param successful whether the removing operaiton is achieved.
+     *
+     */
+    public void onEscrowTokenRemoved(long handle, boolean successful) {
+    }
+
     private void onError(String msg) {
         Slog.v(TAG, "Remote exception while " + msg);
     }
@@ -257,7 +347,7 @@
      * <p>Agents that support configuration options should overload this method and return 'true'.
      *
      * @param options The aggregated list of options or an empty list if no restrictions apply.
-     * @return true if the {@link TrustAgentService} supports configuration options.
+     * @return true if the {@link } supports configuration options.
      */
     public boolean onConfigure(List<PersistableBundle> options) {
         return false;
@@ -373,6 +463,106 @@
         }
     }
 
+    /**
+     * Call to add an escrow token to derive a synthetic password. A synthetic password is an
+     * alternaive to the user-set password/pin/pattern in order to unlock encrypted disk. An escrow
+     * token can be taken and internally derive the synthetic password. The new added token will not
+     * be acivated until the user input the correct PIN/Passcode/Password once.
+     *
+     * Result will be return by callback {@link #onEscrowTokenAdded(long, int)}
+     *
+     * @param token an escrow token of high entropy.
+     * @param user the user which the escrow token will be added to.
+     *
+     */
+    public final void addEscrowToken(byte[] token, UserHandle user) {
+        synchronized (mLock) {
+            if (mCallback == null) {
+                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
+                throw new IllegalStateException("Trust agent is not connected");
+            }
+            try {
+                mCallback.addEscrowToken(token, user.getIdentifier());
+            } catch (RemoteException e) {
+                onError("calling addEscrowToken");
+            }
+        }
+    }
+
+    /**
+     * Call to check the active state of an escrow token.
+     *
+     * Result will be return in callback {@link #onEscrowTokenStateReceived(long, boolean)}
+     *
+     * @param handle the handle of escrow token to the internal synthetic password.
+     * @param user the user which the escrow token is added to.
+     *
+     */
+    public final void isEscrowTokenActive(long handle, UserHandle user) {
+        synchronized (mLock) {
+            if (mCallback == null) {
+                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
+                throw new IllegalStateException("Trust agent is not connected");
+            }
+            try {
+                mCallback.isEscrowTokenActive(handle, user.getIdentifier());
+            } catch (RemoteException e) {
+                onError("calling isEscrowTokenActive");
+            }
+        }
+    }
+
+    /**
+     * Call to remove the escrow token.
+     *
+     * Result will be return in callback {@link #onEscrowTokenRemoved(long, boolean)}
+     *
+     * @param handle the handle of escrow tokent to the internal synthetic password.
+     * @param user the user id which the escrow token is added to.
+     *
+     */
+    public final void removeEscrowToken(long handle, UserHandle user) {
+        synchronized (mLock) {
+            if (mCallback == null) {
+                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
+                throw new IllegalStateException("Trust agent is not connected");
+            }
+            try {
+                mCallback.removeEscrowToken(handle, user.getIdentifier());
+            } catch (RemoteException e) {
+                onError("callling removeEscrowToken");
+            }
+        }
+    }
+
+    /**
+     * Call to unlock user's FBE.
+     *
+     * @param handle the handle of escrow tokent to the internal synthetic password.
+     * @param token the escrow token
+     * @param user the user about to be unlocked.
+     *
+     */
+    public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) {
+        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+        if (um.isUserUnlocked()) {
+            Slog.i(TAG, "User already unlocked");
+            return;
+        }
+
+        synchronized (mLock) {
+            if (mCallback == null) {
+                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
+                throw new IllegalStateException("Trust agent is not connected");
+            }
+            try {
+                mCallback.unlockUserWithToken(handle, token, user.getIdentifier());
+            } catch (RemoteException e) {
+                onError("calling unlockUserWithToken");
+            }
+        }
+    }
+
     @Override
     public final IBinder onBind(Intent intent) {
         if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
@@ -430,6 +620,28 @@
                 }
             }
         }
-    }
 
+        @Override
+        public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
+            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_ADDED);
+            msg.getData().putByteArray(EXTRA_TOKEN, token);
+            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
+            msg.getData().putParcelable(EXTRA_USER_HANDLE, user);
+            msg.sendToTarget();
+        }
+
+        public void onTokenStateReceived(long handle, int tokenState) {
+            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_STATE_RECEIVED);
+            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
+            msg.getData().putInt(EXTRA_TOKEN_STATE, tokenState);
+            msg.sendToTarget();
+        }
+
+        public void onEscrowTokenRemoved(long handle, boolean successful) {
+            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_REMOVED);
+            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
+            msg.getData().putBoolean(EXTRA_TOKEN_REMOVED_RESULT, successful);
+            msg.sendToTarget();
+        }
+    }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fcb0e08..9c2d8c0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2751,4 +2751,13 @@
          without explicit consent of the user. If no accessibility service with the specified name
          exists on the device, the accessibility shortcut will be disabled by default. -->
     <string name="config_defaultAccessibilityService" translatable="false"></string>
+
+    <!-- Flag indicates that whether escrow token API is enabled for TrustAgent -->
+    <!-- Warning: This API can be dangerous when not implemented properly. In particular,
+         escrow token must NOT be retrievable from device storage. In other words, either
+         escrow token is not stored on device or its ciphertext is stored on device while
+         the decryption key is not. Before enabling this feature, please ensure you've read
+         and followed the pertinent sections of the escrow tokens section of the CDD <link>-->
+    <!-- TODO(b/35230407) complete the link field -->
+    <bool name="config_allowEscrowTokenForTrustAgent">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0d63a1e..0ad9ea0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2848,6 +2848,9 @@
   <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
   <java-symbol type="string" name="capability_desc_canCaptureFingerprintGestures" />
 
+  <!-- android.service.trust -->
+  <java-symbol type="bool" name="config_allowEscrowTokenForTrustAgent"/>
+  
   <!-- Time picker -->
   <java-symbol type="id" name="toggle_mode"/>
   <java-symbol type="id" name="input_mode"/>
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 6c1648c..996a3d2 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.trust;
 
+import android.annotation.TargetApi;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
@@ -27,6 +28,7 @@
 import android.content.ServiceConnection;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -35,11 +37,11 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.util.Log;
-import android.util.Slog;
 import android.service.trust.ITrustAgentService;
 import android.service.trust.ITrustAgentServiceCallback;
-
+import android.service.trust.TrustAgentService;
+import android.util.Log;
+import android.util.Slog;
 import java.util.Collections;
 import java.util.List;
 
@@ -47,6 +49,7 @@
  * A wrapper around a TrustAgentService interface. Coordinates communication between
  * TrustManager and the actual TrustAgent.
  */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class TrustAgentWrapper {
     private static final String EXTRA_COMPONENT_NAME = "componentName";
     private static final String TRUST_EXPIRED_ACTION = "android.server.trust.TRUST_EXPIRED_ACTION";
@@ -60,6 +63,10 @@
     private static final int MSG_RESTART_TIMEOUT = 4;
     private static final int MSG_SET_TRUST_AGENT_FEATURES_COMPLETED = 5;
     private static final int MSG_MANAGING_TRUST = 6;
+    private static final int MSG_ADD_ESCROW_TOKEN = 7;
+    private static final int MSG_REMOVE_ESCROW_TOKEN = 8;
+    private static final int MSG_ESCROW_TOKEN_STATE = 9;
+    private static final int MSG_UNLOCK_USER = 10;
 
     /**
      * Time in uptime millis that we wait for the service connection, both when starting
@@ -71,6 +78,9 @@
      * Long extra for {@link #MSG_GRANT_TRUST}
      */
     private static final String DATA_DURATION = "duration";
+    private static final String DATA_ESCROW_TOKEN = "escrow_token";
+    private static final String DATA_HANDLE = "handle";
+    private static final String DATA_USER_ID = "user_id";
 
     private final TrustManagerService mTrustManagerService;
     private final int mUserId;
@@ -190,6 +200,49 @@
                     mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust);
                     mTrustManagerService.updateTrust(mUserId, 0);
                     break;
+                case MSG_ADD_ESCROW_TOKEN: {
+                    byte[] eToken = msg.getData().getByteArray(DATA_ESCROW_TOKEN);
+                    int userId = msg.getData().getInt(DATA_USER_ID);
+                    long handle = mTrustManagerService.addEscrowToken(eToken, userId);
+                    try {
+                        mTrustAgentService.onEscrowTokenAdded(
+                                eToken, handle, UserHandle.of(userId));
+                    } catch (RemoteException e) {
+                        onError(e);
+                    }
+                    break;
+                }
+                case MSG_ESCROW_TOKEN_STATE: {
+                    long handle = msg.getData().getLong(DATA_HANDLE);
+                    int userId = msg.getData().getInt(DATA_USER_ID);
+                    boolean active = mTrustManagerService.isEscrowTokenActive(handle, userId);
+                    try {
+                        mTrustAgentService.onTokenStateReceived(handle,
+                            active ? TrustAgentService.TOKEN_STATE_ACTIVE
+                                : TrustAgentService.TOKEN_STATE_INACTIVE);
+                    } catch (RemoteException e) {
+                        onError(e);
+                    }
+                    break;
+                }
+                case MSG_REMOVE_ESCROW_TOKEN: {
+                    long handle = msg.getData().getLong(DATA_HANDLE);
+                    int userId = msg.getData().getInt(DATA_USER_ID);
+                    boolean success = mTrustManagerService.removeEscrowToken(handle, userId);
+                    try {
+                        mTrustAgentService.onEscrowTokenRemoved(handle, success);
+                    } catch (RemoteException e) {
+                        onError(e);
+                    }
+                    break;
+                }
+                case MSG_UNLOCK_USER: {
+                    long handle = msg.getData().getLong(DATA_HANDLE);
+                    int userId = msg.getData().getInt(DATA_USER_ID);
+                    byte[] eToken = msg.getData().getByteArray(DATA_ESCROW_TOKEN);
+                    mTrustManagerService.unlockUserWithToken(handle, eToken, userId);
+                    break;
+                }
             }
         }
     };
@@ -225,6 +278,67 @@
             mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED,
                     result ? 1 : 0, 0, token).sendToTarget();
         }
+
+        @Override
+        public void addEscrowToken(byte[] token, int userId) {
+            if (mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
+                Slog.e(TAG, "Escrow token API is not allowed.");
+                return;
+            }
+
+            if (DEBUG) Slog.d(TAG, "adding escrow token for user " + userId);
+            Message msg = mHandler.obtainMessage(MSG_ADD_ESCROW_TOKEN);
+            msg.getData().putByteArray(DATA_ESCROW_TOKEN, token);
+            msg.getData().putInt(DATA_USER_ID, userId);
+            msg.sendToTarget();
+        }
+
+        @Override
+        public void isEscrowTokenActive(long handle, int userId) {
+            if (mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
+                Slog.e(TAG, "Escrow token API is not allowed.");
+                return;
+            }
+
+            if (DEBUG) Slog.d(TAG, "checking the state of escrow token on user " + userId);
+            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_STATE);
+            msg.getData().putLong(DATA_HANDLE, handle);
+            msg.getData().putInt(DATA_USER_ID, userId);
+            msg.sendToTarget();
+        }
+
+        @Override
+        public void removeEscrowToken(long handle, int userId) {
+            if (mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
+                Slog.e(TAG, "Escrow token API is not allowed.");
+                return;
+            }
+
+            if (DEBUG) Slog.d(TAG, "removing escrow token on user " + userId);
+            Message msg = mHandler.obtainMessage(MSG_REMOVE_ESCROW_TOKEN);
+            msg.getData().putLong(DATA_HANDLE, handle);
+            msg.getData().putInt(DATA_USER_ID, userId);
+            msg.sendToTarget();
+        }
+
+        @Override
+        public void unlockUserWithToken(long handle, byte[] token, int userId) {
+            if (mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
+                Slog.e(TAG, "Escrow token API is not allowed.");
+                return;
+            }
+
+            if (DEBUG) Slog.d(TAG, "unlocking user " + userId);
+            Message msg = mHandler.obtainMessage(MSG_UNLOCK_USER);
+            msg.getData().putInt(DATA_USER_ID, userId);
+            msg.getData().putLong(DATA_HANDLE, handle);
+            msg.getData().putByteArray(DATA_ESCROW_TOKEN, token);
+            msg.sendToTarget();
+        }
     };
 
     private final ServiceConnection mConnection = new ServiceConnection() {
@@ -294,7 +408,7 @@
     }
 
     private void onError(Exception e) {
-        Slog.w(TAG , "Remote Exception", e);
+        Slog.w(TAG , "Exception ", e);
     }
 
     private void onTrustTimeout() {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 71b725e..4570b0d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -16,14 +16,6 @@
 
 package com.android.server.trust;
 
-import com.android.internal.annotations.GuardedBy;
-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.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -65,12 +57,17 @@
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
-
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 /**
  * Manages trust agents and trust listeners.
@@ -229,6 +226,22 @@
                 TRUST_USUALLY_MANAGED_FLUSH_DELAY);
     }
 
+    public long addEscrowToken(byte[] token, int userId) {
+        return mLockPatternUtils.addEscrowToken(token, userId);
+    }
+
+    public boolean removeEscrowToken(long handle, int userId) {
+        return mLockPatternUtils.removeEscrowToken(handle, userId);
+    }
+
+    public boolean isEscrowTokenActive(long handle, int userId) {
+        return mLockPatternUtils.isEscrowTokenActive(handle, userId);
+    }
+
+    public void unlockUserWithToken(long handle, byte[] token, int userId) {
+        mLockPatternUtils.unlockUserWithToken(handle, token, userId);
+    }
+
     void refreshAgentList(int userIdOrAll) {
         if (DEBUG) Slog.d(TAG, "refreshAgentList(" + userIdOrAll + ")");
         if (!mTrustAgentsCanRun) {
@@ -329,7 +342,7 @@
                 if (!StorageManager.isUserKeyUnlocked(userInfo.id)
                         && !directUnlock) {
                     if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                            + "'s trust agent " + name + ": FDE still locked and "
+                            + "'s trust agent " + name + ": FBE still locked and "
                             + " the agent cannot unlock user profile.");
                     continue;
                 }