Remove strings from low level onError(...) calls

In order to get a correctly translated error messages, getString(...)
should be called on the application context, as opposed to the system
context. This is because the system context is unaware of the user's
locale.

Bug: 141025588
Test: Face Unlock works E2E
Test: Works with BiometricPromptDemo
Test: atest BiometricServiceTest
Test: atest AuthControllerTest
Test: atest CommandQueueTest

Change-Id: Ic228bb7ebb0d6a4ebaf96b9f1d2d70ed4e9dd79a
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index cdc2623..b758731 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.biometrics;
 
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
@@ -27,6 +30,8 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,6 +39,7 @@
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.SystemUI;
@@ -229,15 +235,8 @@
     }
 
     @Override
-    public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
-        if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
-                + " reason: " + failureReason);
-
-        if (authenticated) {
-            mCurrentDialog.onAuthenticationSucceeded();
-        } else {
-            mCurrentDialog.onAuthenticationFailed(failureReason);
-        }
+    public void onBiometricAuthenticated() {
+        mCurrentDialog.onAuthenticationSucceeded();
     }
 
     @Override
@@ -247,16 +246,45 @@
         mCurrentDialog.onHelp(message);
     }
 
-    @Override
-    public void onBiometricError(int errorCode, String error) {
-        if (DEBUG) Log.d(TAG, "onBiometricError: " + errorCode + ", " + error);
+    private String getErrorString(int modality, int error, int vendorCode) {
+        switch (modality) {
+            case TYPE_FACE:
+                return FaceManager.getErrorString(mContext, error, vendorCode);
 
-        final boolean isLockout = errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
-                || errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+            case TYPE_FINGERPRINT:
+                return FingerprintManager.getErrorString(mContext, error, vendorCode);
+
+            default:
+                return "";
+        }
+    }
+
+    @Override
+    public void onBiometricError(int modality, int error, int vendorCode) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
+        }
+
+        final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
+                || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
+
+        // TODO(b/141025588): Create separate methods for handling hard and soft errors.
+        final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
+                || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
+
         if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
+            if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
             mCurrentDialog.animateToCredentialUI();
+        } else if (isSoftError) {
+            final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED)
+                    ? mContext.getString(R.string.biometric_not_recognized)
+                    : getErrorString(modality, error, vendorCode);
+            if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
+            mCurrentDialog.onAuthenticationFailed(errorMessage);
         } else {
-            mCurrentDialog.onError(error);
+            final String errorMessage = getErrorString(modality, error, vendorCode);
+            if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
+            mCurrentDialog.onError(errorMessage);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d6a8f90..2b1c807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -273,9 +273,9 @@
         default void showAuthenticationDialog(Bundle bundle,
                 IBiometricServiceReceiverInternal receiver, int biometricModality,
                 boolean requireConfirmation, int userId, String opPackageName) { }
-        default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
+        default void onBiometricAuthenticated() { }
         default void onBiometricHelp(String message) { }
-        default void onBiometricError(int errorCode, String error) { }
+        default void onBiometricError(int modality, int error, int vendorCode) { }
         default void hideAuthenticationDialog() { }
 
         /**
@@ -757,12 +757,9 @@
     }
 
     @Override
-    public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
+    public void onBiometricAuthenticated() {
         synchronized (mLock) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = authenticated;
-            args.arg2 = failureReason;
-            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
         }
     }
 
@@ -774,9 +771,13 @@
     }
 
     @Override
-    public void onBiometricError(int errorCode, String error) {
+    public void onBiometricError(int modality, int error, int vendorCode) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, errorCode, 0, error).sendToTarget();
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = modality;
+            args.argi2 = error;
+            args.argi3 = vendorCode;
+            mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, args).sendToTarget();
         }
     }
 
@@ -1045,13 +1046,9 @@
                     break;
                 }
                 case MSG_BIOMETRIC_AUTHENTICATED: {
-                    SomeArgs someArgs = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onBiometricAuthenticated(
-                                (boolean) someArgs.arg1 /* authenticated */,
-                                (String) someArgs.arg2 /* failureReason */);
+                        mCallbacks.get(i).onBiometricAuthenticated();
                     }
-                    someArgs.recycle();
                     break;
                 }
                 case MSG_BIOMETRIC_HELP:
@@ -1060,9 +1057,15 @@
                     }
                     break;
                 case MSG_BIOMETRIC_ERROR:
+                    SomeArgs someArgs = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onBiometricError(msg.arg1, (String) msg.obj);
+                        mCallbacks.get(i).onBiometricError(
+                                someArgs.argi1 /* modality */,
+                                someArgs.argi2 /* error */,
+                                someArgs.argi3 /* vendorCode */
+                        );
                     }
+                    someArgs.recycle();
                     break;
                 case MSG_BIOMETRIC_HIDE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e1eb3b0..b089b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -38,15 +38,18 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
 import android.os.Bundle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
 import android.testing.TestableLooper.RunWithLooper;
 
+import com.android.internal.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -89,9 +92,9 @@
 
         when(context.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
-            .thenReturn(true);
+                .thenReturn(true);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
-            .thenReturn(true);
+                .thenReturn(true);
 
         when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
         when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
@@ -170,20 +173,34 @@
     @Test
     public void testOnAuthenticationSucceededInvoked_whenSystemRequested() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
-        mAuthController.onBiometricAuthenticated(true, null /* failureReason */);
+        mAuthController.onBiometricAuthenticated();
         verify(mDialog1).onAuthenticationSucceeded();
     }
 
     @Test
-    public void testOnAuthenticationFailedInvoked_whenSystemRequested() {
+    public void testOnAuthenticationFailedInvoked_whenBiometricRejected() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
-        final String failureReason = "failure reason";
-        mAuthController.onBiometricAuthenticated(false, failureReason);
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE,
+                BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
+                0 /* vendorCode */);
 
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         verify(mDialog1).onAuthenticationFailed(captor.capture());
 
-        assertEquals(captor.getValue(), failureReason);
+        assertEquals(captor.getValue(), mContext.getString(R.string.biometric_not_recognized));
+    }
+
+    @Test
+    public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() {
+        showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+        final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
+        final int vendorCode = 0;
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onAuthenticationFailed(captor.capture());
+
+        assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
     }
 
     @Test
@@ -199,27 +216,27 @@
     }
 
     @Test
-    public void testOnErrorInvoked_whenSystemRequested() {
+    public void testOnErrorInvoked_whenSystemRequested() throws Exception {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
         final int error = 1;
-        final String errMessage = "error message";
-        mAuthController.onBiometricError(error, errMessage);
+        final int vendorCode = 0;
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
 
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         verify(mDialog1).onError(captor.capture());
 
-        assertEquals(captor.getValue(), errMessage);
+        assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
     }
 
     @Test
     public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
         final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
-        final String errorString = "lockout";
+        final int vendorCode = 0;
 
         when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
 
-        mAuthController.onBiometricError(error, errorString);
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
         verify(mDialog1, never()).onError(anyString());
         verify(mDialog1).animateToCredentialUI();
     }
@@ -228,11 +245,11 @@
     public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
         final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-        final String errorString = "lockout_permanent";
+        final int vendorCode = 0;
 
         when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
 
-        mAuthController.onBiometricError(error, errorString);
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
         verify(mDialog1, never()).onError(anyString());
         verify(mDialog1).animateToCredentialUI();
     }
@@ -241,12 +258,12 @@
     public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
         final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
-        final String errorString = "lockout";
+        final int vendorCode = 0;
 
         when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
 
-        mAuthController.onBiometricError(error, errorString);
-        verify(mDialog1).onError(eq(errorString));
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+        verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
         verify(mDialog1, never()).animateToCredentialUI();
     }
 
@@ -254,12 +271,12 @@
     public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() {
         showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
         final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-        final String errorString = "lockout_permanent";
+        final int vendorCode = 0;
 
         when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
 
-        mAuthController.onBiometricError(error, errorString);
-        verify(mDialog1).onError(eq(errorString));
+        mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+        verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
         verify(mDialog1, never()).animateToCredentialUI();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1bd01e1..ccb754c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -379,10 +379,9 @@
 
     @Test
     public void testOnBiometricAuthenticated() {
-        String failureReason = "test_failure_reason";
-        mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason);
+        mCommandQueue.onBiometricAuthenticated();
         waitForIdleSync();
-        verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason));
+        verify(mCallbacks).onBiometricAuthenticated();
     }
 
     @Test
@@ -395,11 +394,12 @@
 
     @Test
     public void testOnBiometricError() {
-        final int errorCode = 1;
-        String errorMessage = "test_error_message";
-        mCommandQueue.onBiometricError(errorCode, errorMessage);
+        final int modality = 1;
+        final int error = 2;
+        final int vendorCode = 3;
+        mCommandQueue.onBiometricError(modality, error, vendorCode);
         waitForIdleSync();
-        verify(mCallbacks).onBiometricError(eq(errorCode), eq(errorMessage));
+        verify(mCallbacks).onBiometricError(eq(modality), eq(error), eq(vendorCode));
     }
 
     @Test