Notify the TrustAgent when the token is activated.

Previously, the agent will have to query the TrustManager to see if a
recently added escrow token has been activated.  If the agent asked too
soon about the token state, then the result could be inactive.  That
will be hard to distinguish from when the token is actually inactive
because activation failed.

This change pipes the call from LockSettings to the TrustAgentService
through the TrustManager when the token is activated.  So, the trust
agent doesn't have to query, it just gets notified.

Bug: 124312230
Test: Observe the onTokenStateReceived() callback received on the trust
agent.

Change-Id: I0ac674219fd7925ba36f50bb695c9998c18226f7
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 17ed2a0..c095376 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1687,8 +1687,21 @@
      *
      * @return a unique 64-bit token handle which is needed to refer to this token later.
      */
-    public long addEscrowToken(byte[] token, int userId) {
-        return getLockSettingsInternal().addEscrowToken(token, userId);
+    public long addEscrowToken(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback callback) {
+        return getLockSettingsInternal().addEscrowToken(token, userId, callback);
+    }
+
+    /**
+     * Callback interface to notify when an added escrow token has been activated.
+     */
+    public interface EscrowTokenStateChangeCallback {
+        /**
+         * The method to be called when the token is activated.
+         * @param handle 64 bit handle corresponding to the escrow token
+         * @param userid user for whom the escrow token has been added
+         */
+        void onEscrowTokenActivated(long handle, int userid);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 90397df..d459bfb 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -29,15 +29,18 @@
      * or change user password.
      *
      * After adding, if the user currently has lockscreen password, he will need to perform a
-     * confirm credential operation in order to activate the token for future use. If the user
+     * confirm credential operation in order to activate the token for future use.
+     * Once the token is activated, the callback that is passed here is called.   If the user
      * has no secure lockscreen, then the token is activated immediately.
      *
      * @return a unique 64-bit token handle which is needed to refer to this token later.
      */
-    public abstract long addEscrowToken(byte[] token, int userId);
+    public abstract long addEscrowToken(byte[] token, int userId,
+            LockPatternUtils.EscrowTokenStateChangeCallback callback);
 
     /**
      * Remove an escrow token.
+     *
      * @return true if the given handle refers to a valid token previously returned from
      * {@link #addEscrowToken}, whether it's active or not. return false otherwise.
      */
@@ -51,6 +54,7 @@
 
     /**
      * Set the lock credential.
+     *
      * @return true if password is set.
      */
     public abstract boolean setLockCredentialWithToken(byte[] credential, int type,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ee968c8..bd21f60 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -21,6 +21,7 @@
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -2648,7 +2649,8 @@
         }
     }
 
-    private long addEscrowToken(byte[] token, int userId) throws RemoteException {
+    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback)
+            throws RemoteException {
         if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
         synchronized (mSpManager) {
             enableSyntheticPasswordLocked();
@@ -2672,7 +2674,7 @@
                     throw new SecurityException("Escrow token is disabled on the current user");
                 }
             }
-            long handle = mSpManager.createTokenBasedSyntheticPassword(token, userId);
+            long handle = mSpManager.createTokenBasedSyntheticPassword(token, userId, callback);
             if (auth != null) {
                 mSpManager.activateTokenBasedSyntheticPassword(handle, auth, userId);
             }
@@ -2943,9 +2945,10 @@
     private final class LocalService extends LockSettingsInternal {
 
         @Override
-        public long addEscrowToken(byte[] token, int userId) {
+        public long addEscrowToken(byte[] token, int userId,
+                EscrowTokenStateChangeCallback callback) {
             try {
-                return LockSettingsService.this.addEscrowToken(token, userId);
+                return LockSettingsService.this.addEscrowToken(token, userId, callback);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index ea39dff..ec61d05 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.locksettings;
 
+import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
@@ -281,6 +283,7 @@
         byte[] secdiscardableOnDisk;
         byte[] weaverSecret;
         byte[] aggregatedSecret;
+        EscrowTokenStateChangeCallback mCallback;
     }
 
     private final Context mContext;
@@ -740,7 +743,12 @@
 
     private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
 
-    public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
+    /**
+     * Create a token based Synthetic password for the given user.
+     * @return
+     */
+    public long createTokenBasedSyntheticPassword(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback changeCallback) {
         long handle = generateHandle();
         if (!tokenMap.containsKey(userId)) {
             tokenMap.put(userId, new ArrayMap<>());
@@ -756,6 +764,7 @@
             tokenData.weaverSecret = null;
         }
         tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
+        tokenData.mCallback = changeCallback;
 
         tokenMap.get(userId).put(handle, tokenData);
         return handle;
@@ -803,6 +812,9 @@
         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
                 tokenData.aggregatedSecret, 0L, userId);
         tokenMap.get(userId).remove(handle);
+        if (tokenData.mCallback != null) {
+            tokenData.mCallback.onEscrowTokenActivated(handle, userId);
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 28fee4e..4614355 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -43,8 +43,6 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.policy.IKeyguardDismissCallback;
-
 import java.util.Collections;
 import java.util.List;
 
@@ -495,6 +493,21 @@
         }
     }
 
+    /**
+     * @see android.service.trust.TrustAgentService#onTokenStateReceived()
+     *
+     */
+    public void onEscrowTokenActivated(long handle, int userId) {
+        if (DEBUG) Slog.d(TAG, "onEscrowTokenActivated: " + handle + " user: " + userId);
+        if (mTrustAgentService != null) {
+            try {
+                mTrustAgentService.onTokenStateReceived(handle,
+                        TrustAgentService.TOKEN_STATE_ACTIVE);
+            } catch (RemoteException e) {
+                onError(e);
+            }
+        }
+    }
     private void setCallback(ITrustAgentServiceCallback callback) {
         try {
             if (mTrustAgentService != null) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index c7044a1..3a39053 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -409,7 +409,10 @@
     }
 
     public long addEscrowToken(byte[] token, int userId) {
-        return mLockPatternUtils.addEscrowToken(token, userId);
+        return mLockPatternUtils.addEscrowToken(token, userId,
+                (long handle, int userid) -> {
+                    dispatchEscrowTokenActivatedLocked(handle, userid);
+                });
     }
 
     public boolean removeEscrowToken(long handle, int userId) {
@@ -662,6 +665,15 @@
         }
     }
 
+    private void dispatchEscrowTokenActivatedLocked(long handle, int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo agent = mActiveAgents.valueAt(i);
+            if (agent.userId == userId) {
+                agent.agent.onEscrowTokenActivated(handle, userId);
+            }
+        }
+    }
+
     void updateDevicePolicyFeatures() {
         boolean changed = false;
         for (int i = 0; i < mActiveAgents.size(); i++) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 36251f5..d7922b15 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -13352,8 +13352,8 @@
                 if (policy.mPasswordTokenHandle != 0) {
                     mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, userHandle);
                 }
-
-                policy.mPasswordTokenHandle = mLockPatternUtils.addEscrowToken(token, userHandle);
+                policy.mPasswordTokenHandle = mLockPatternUtils.addEscrowToken(token,
+                        userHandle, /*EscrowTokenStateChangeCallback*/ null);
                 saveSettingsLocked(userHandle);
                 return policy.mPasswordTokenHandle != 0;
             } finally {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4293247..039a4b7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -29,6 +29,7 @@
 import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
 import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
 
+import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.server.testutils.TestUtils.assertExpectException;
 
 import static org.mockito.Matchers.any;
@@ -4157,8 +4158,9 @@
         final byte[] token = new byte[32];
         final long handle = 123456;
         final String password = "password";
-        when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM)))
-            .thenReturn(handle);
+        when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM),
+                nullable(EscrowTokenStateChangeCallback.class)))
+                .thenReturn(handle);
         assertTrue(dpm.setResetPasswordToken(admin1, token));
 
         // test password activation
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index e6e020d..8d5b720 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -338,7 +338,7 @@
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
 
-        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
@@ -367,7 +367,7 @@
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
 
-        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
@@ -393,7 +393,7 @@
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
 
-        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
@@ -417,7 +417,7 @@
             throws RemoteException {
         final String token = "some-high-entropy-secure-token";
         enableSyntheticPassword();
-        long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -427,7 +427,7 @@
             throws RemoteException {
         final String token = "some-high-entropy-secure-token";
         initializeCredentialUnderSP(null, PRIMARY_USER_ID);
-        long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -443,7 +443,7 @@
                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         enableSyntheticPassword();
 
-        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         // Token not activated immediately since user password exists
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         // Activate token (password gets migrated to SP at the same time)
@@ -461,7 +461,7 @@
 
         mHasSecureLockScreen = false;
         enableSyntheticPassword();
-        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
+        long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         try {