Send AIDL face enrollment frame info to clients

Test: Manually tested grid and traffic light face enrollment

Bug: 181374571
Change-Id: If742c6c32547d56be417d4eb0cbb729ed90552a5
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
index 551139d..822a579 100644
--- a/core/java/android/hardware/face/FaceEnrollFrame.java
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.face.FaceEnrollStages.FaceEnrollStage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
deleted file mode 100644
index de717fb..0000000
--- a/core/java/android/hardware/face/FaceEnrollStage.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2021 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.hardware.face;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A stage that may occur during face enrollment.
- *
- * @hide
- */
-@Retention(RetentionPolicy.SOURCE)
-@IntDef({
-    FaceEnrollStage.UNKNOWN,
-    FaceEnrollStage.FIRST_FRAME_RECEIVED,
-    FaceEnrollStage.WAITING_FOR_CENTERING,
-    FaceEnrollStage.HOLD_STILL_IN_CENTER,
-    FaceEnrollStage.ENROLLING_MOVEMENT_1,
-    FaceEnrollStage.ENROLLING_MOVEMENT_2,
-    FaceEnrollStage.ENROLLMENT_FINISHED
-})
-public @interface FaceEnrollStage {
-    /**
-     * The current enrollment stage is not known.
-     */
-    int UNKNOWN = -1;
-
-    /**
-     * Enrollment has just begun. No action is needed from the user yet.
-     */
-    int FIRST_FRAME_RECEIVED = 0;
-
-    /**
-     * The user must center their face in the frame.
-     */
-    int WAITING_FOR_CENTERING = 1;
-
-    /**
-     * The user must keep their face centered in the frame.
-     */
-    int HOLD_STILL_IN_CENTER = 2;
-
-    /**
-     * The user must follow a first set of movement instructions.
-     */
-    int ENROLLING_MOVEMENT_1 = 3;
-
-    /**
-     * The user must follow a second set of movement instructions.
-     */
-    int ENROLLING_MOVEMENT_2 = 4;
-
-    /**
-     * Enrollment has completed. No more action is needed from the user.
-     */
-    int ENROLLMENT_FINISHED = 5;
-}
diff --git a/core/java/android/hardware/face/FaceEnrollStages.java b/core/java/android/hardware/face/FaceEnrollStages.java
new file mode 100644
index 0000000..de374b3
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStages.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A collection of constants representing different stages of face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollStages {
+    // Prevent instantiation.
+    private FaceEnrollStages() {}
+
+    /**
+     * A stage that may occur during face enrollment.
+     *
+     * @hide
+     */
+    @IntDef({
+        UNKNOWN,
+        FIRST_FRAME_RECEIVED,
+        WAITING_FOR_CENTERING,
+        HOLD_STILL_IN_CENTER,
+        ENROLLING_MOVEMENT_1,
+        ENROLLING_MOVEMENT_2,
+        ENROLLMENT_FINISHED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FaceEnrollStage {}
+
+    /**
+     * The current enrollment stage is not known.
+     */
+    public static final int UNKNOWN = 0;
+
+    /**
+     * Enrollment has just begun. No action is needed from the user yet.
+     */
+    public static final int FIRST_FRAME_RECEIVED = 1;
+
+    /**
+     * The user must center their face in the frame.
+     */
+    public static final int WAITING_FOR_CENTERING = 2;
+
+    /**
+     * The user must keep their face centered in the frame.
+     */
+    public static final int HOLD_STILL_IN_CENTER = 3;
+
+    /**
+     * The user must follow a first set of movement instructions.
+     */
+    public static final int ENROLLING_MOVEMENT_1 = 4;
+
+    /**
+     * The user must follow a second set of movement instructions.
+     */
+    public static final int ENROLLING_MOVEMENT_2 = 5;
+
+    /**
+     * Enrollment has completed. No more action is needed from the user.
+     */
+    public static final int ENROLLMENT_FINISHED = 6;
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 5f87899..9a27a99 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1023,6 +1023,34 @@
         }
 
         /**
+         * Called each time a single frame is captured during enrollment.
+         *
+         * <p>For older, non-AIDL implementations, only {@code helpCode} and {@code helpMessage} are
+         * supported. Sensible default values will be provided for all other arguments.
+         *
+         * @param helpCode    An integer identifying the capture status for this frame.
+         * @param helpMessage A human-readable help string that can be shown in UI.
+         * @param cell        The cell captured during this frame of enrollment, if any.
+         * @param stage       An integer representing the current stage of enrollment.
+         * @param pan         The horizontal pan of the detected face. Values in the range [-1, 1]
+         *                    indicate a good capture.
+         * @param tilt        The vertical tilt of the detected face. Values in the range [-1, 1]
+         *                    indicate a good capture.
+         * @param distance    The distance of the detected face from the device. Values in
+         *                    the range [-1, 1] indicate a good capture.
+         */
+        public void onEnrollmentFrame(
+                int helpCode,
+                @Nullable CharSequence helpMessage,
+                @Nullable FaceEnrollCell cell,
+                @FaceEnrollStages.FaceEnrollStage int stage,
+                float pan,
+                float tilt,
+                float distance) {
+            onEnrollmentHelp(helpCode, helpMessage);
+        }
+
+        /**
          * Called as each enrollment step progresses. Enrollment is considered complete when
          * remaining reaches 0. This function will not be called if enrollment fails. See
          * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
@@ -1305,7 +1333,7 @@
         } else if (mEnrollmentCallback != null) {
             final FaceEnrollFrame frame = new FaceEnrollFrame(
                     null /* cell */,
-                    FaceEnrollStage.UNKNOWN,
+                    FaceEnrollStages.UNKNOWN,
                     new FaceDataFrame(acquireInfo, vendorCode));
             sendEnrollmentFrame(frame);
         }
@@ -1333,12 +1361,19 @@
         if (frame == null) {
             Slog.w(TAG, "Received null enrollment frame");
         } else if (mEnrollmentCallback != null) {
-            // TODO(b/178414967): Send additional frame data to callback
-            final int acquireInfo = frame.getData().getAcquiredInfo();
-            final int vendorCode = frame.getData().getVendorCode();
+            final FaceDataFrame data = frame.getData();
+            final int acquireInfo = data.getAcquiredInfo();
+            final int vendorCode = data.getVendorCode();
             final int helpCode = getHelpCode(acquireInfo, vendorCode);
             final String helpMessage = getEnrollHelpMessage(mContext, acquireInfo, vendorCode);
-            mEnrollmentCallback.onEnrollmentHelp(helpCode, helpMessage);
+            mEnrollmentCallback.onEnrollmentFrame(
+                    helpCode,
+                    helpMessage,
+                    frame.getCell(),
+                    frame.getStage(),
+                    data.getPan(),
+                    data.getTilt(),
+                    data.getDistance());
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
index 5d713f3..043260c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -24,11 +24,14 @@
 import android.hardware.biometrics.face.BaseFrame;
 import android.hardware.biometrics.face.Cell;
 import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.biometrics.face.EnrollmentStage;
 import android.hardware.biometrics.face.Error;
 import android.hardware.face.FaceAuthenticationFrame;
 import android.hardware.face.FaceDataFrame;
 import android.hardware.face.FaceEnrollCell;
 import android.hardware.face.FaceEnrollFrame;
+import android.hardware.face.FaceEnrollStages;
+import android.hardware.face.FaceEnrollStages.FaceEnrollStage;
 
 /**
  * Utilities for converting from hardware to framework-defined AIDL models.
@@ -38,92 +41,107 @@
     private AidlConversionUtils() {
     }
 
-    public static @BiometricFaceConstants.FaceError int toFrameworkError(byte aidlError) {
-        if (aidlError == Error.UNKNOWN) {
-            // No framework constant available
-            return BiometricFaceConstants.FACE_ERROR_UNKNOWN;
-        } else if (aidlError == Error.HW_UNAVAILABLE) {
-            return BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
-        } else if (aidlError == Error.UNABLE_TO_PROCESS) {
-            return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
-        } else if (aidlError == Error.TIMEOUT) {
-            return BiometricFaceConstants.FACE_ERROR_TIMEOUT;
-        } else if (aidlError == Error.NO_SPACE) {
-            return BiometricFaceConstants.FACE_ERROR_NO_SPACE;
-        } else if (aidlError == Error.CANCELED) {
-            return BiometricFaceConstants.FACE_ERROR_CANCELED;
-        } else if (aidlError == Error.UNABLE_TO_REMOVE) {
-            return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_REMOVE;
-        } else if (aidlError == Error.VENDOR) {
-            return BiometricFaceConstants.FACE_ERROR_VENDOR;
-        } else if (aidlError == Error.REENROLL_REQUIRED) {
-            return BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL;
-        } else {
-            return BiometricFaceConstants.FACE_ERROR_UNKNOWN;
+    @BiometricFaceConstants.FaceError
+    public static int toFrameworkError(byte aidlError) {
+        switch (aidlError) {
+            case Error.HW_UNAVAILABLE:
+                return BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
+            case Error.UNABLE_TO_PROCESS:
+                return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
+            case Error.TIMEOUT:
+                return BiometricFaceConstants.FACE_ERROR_TIMEOUT;
+            case Error.NO_SPACE:
+                return BiometricFaceConstants.FACE_ERROR_NO_SPACE;
+            case Error.CANCELED:
+                return BiometricFaceConstants.FACE_ERROR_CANCELED;
+            case Error.UNABLE_TO_REMOVE:
+                return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_REMOVE;
+            case Error.VENDOR:
+                return BiometricFaceConstants.FACE_ERROR_VENDOR;
+            case Error.REENROLL_REQUIRED:
+                return BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL;
+            case Error.UNKNOWN:
+            default:
+                return BiometricFaceConstants.FACE_ERROR_UNKNOWN;
         }
     }
 
-    public static @BiometricFaceConstants.FaceAcquired int toFrameworkAcquiredInfo(
-            byte aidlAcquired) {
-        if (aidlAcquired == AcquiredInfo.UNKNOWN) {
-            return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
-        } else if (aidlAcquired == AcquiredInfo.GOOD) {
-            return BiometricFaceConstants.FACE_ACQUIRED_GOOD;
-        } else if (aidlAcquired == AcquiredInfo.INSUFFICIENT) {
-            return BiometricFaceConstants.FACE_ACQUIRED_INSUFFICIENT;
-        } else if (aidlAcquired == AcquiredInfo.TOO_BRIGHT) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT;
-        } else if (aidlAcquired == AcquiredInfo.TOO_DARK) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
-        } else if (aidlAcquired == AcquiredInfo.TOO_CLOSE) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE;
-        } else if (aidlAcquired == AcquiredInfo.TOO_FAR) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_FAR;
-        } else if (aidlAcquired == AcquiredInfo.FACE_TOO_HIGH) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH;
-        } else if (aidlAcquired == AcquiredInfo.FACE_TOO_LOW) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW;
-        } else if (aidlAcquired == AcquiredInfo.FACE_TOO_RIGHT) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_RIGHT;
-        } else if (aidlAcquired == AcquiredInfo.FACE_TOO_LEFT) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT;
-        } else if (aidlAcquired == AcquiredInfo.POOR_GAZE) {
-            return BiometricFaceConstants.FACE_ACQUIRED_POOR_GAZE;
-        } else if (aidlAcquired == AcquiredInfo.NOT_DETECTED) {
-            return BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED;
-        } else if (aidlAcquired == AcquiredInfo.TOO_MUCH_MOTION) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_MUCH_MOTION;
-        } else if (aidlAcquired == AcquiredInfo.RECALIBRATE) {
-            return BiometricFaceConstants.FACE_ACQUIRED_RECALIBRATE;
-        } else if (aidlAcquired == AcquiredInfo.TOO_DIFFERENT) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_DIFFERENT;
-        } else if (aidlAcquired == AcquiredInfo.TOO_SIMILAR) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TOO_SIMILAR;
-        } else if (aidlAcquired == AcquiredInfo.PAN_TOO_EXTREME) {
-            return BiometricFaceConstants.FACE_ACQUIRED_PAN_TOO_EXTREME;
-        } else if (aidlAcquired == AcquiredInfo.TILT_TOO_EXTREME) {
-            return BiometricFaceConstants.FACE_ACQUIRED_TILT_TOO_EXTREME;
-        } else if (aidlAcquired == AcquiredInfo.ROLL_TOO_EXTREME) {
-            return BiometricFaceConstants.FACE_ACQUIRED_ROLL_TOO_EXTREME;
-        } else if (aidlAcquired == AcquiredInfo.FACE_OBSCURED) {
-            return BiometricFaceConstants.FACE_ACQUIRED_FACE_OBSCURED;
-        } else if (aidlAcquired == AcquiredInfo.START) {
-            return BiometricFaceConstants.FACE_ACQUIRED_START;
-        } else if (aidlAcquired == AcquiredInfo.SENSOR_DIRTY) {
-            return BiometricFaceConstants.FACE_ACQUIRED_SENSOR_DIRTY;
-        } else if (aidlAcquired == AcquiredInfo.VENDOR) {
-            return BiometricFaceConstants.FACE_ACQUIRED_VENDOR;
-        } else if (aidlAcquired == AcquiredInfo.FIRST_FRAME_RECEIVED) {
-            // No framework constant available
-            return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
-        } else if (aidlAcquired == AcquiredInfo.DARK_GLASSES_DETECTED) {
-            // No framework constant available
-            return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
-        } else if (aidlAcquired == AcquiredInfo.MOUTH_COVERING_DETECTED) {
-            // No framework constant available
-            return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
-        } else {
-            return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
+    @BiometricFaceConstants.FaceAcquired
+    public static int toFrameworkAcquiredInfo(byte aidlAcquiredInfo) {
+        switch (aidlAcquiredInfo) {
+            case AcquiredInfo.GOOD:
+                return BiometricFaceConstants.FACE_ACQUIRED_GOOD;
+            case AcquiredInfo.INSUFFICIENT:
+                return BiometricFaceConstants.FACE_ACQUIRED_INSUFFICIENT;
+            case AcquiredInfo.TOO_BRIGHT:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT;
+            case AcquiredInfo.TOO_DARK:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
+            case AcquiredInfo.TOO_CLOSE:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE;
+            case AcquiredInfo.TOO_FAR:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_FAR;
+            case AcquiredInfo.FACE_TOO_HIGH:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH;
+            case AcquiredInfo.FACE_TOO_LOW:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW;
+            case AcquiredInfo.FACE_TOO_RIGHT:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_RIGHT;
+            case AcquiredInfo.FACE_TOO_LEFT:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT;
+            case AcquiredInfo.POOR_GAZE:
+                return BiometricFaceConstants.FACE_ACQUIRED_POOR_GAZE;
+            case AcquiredInfo.NOT_DETECTED:
+                return BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED;
+            case AcquiredInfo.TOO_MUCH_MOTION:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_MUCH_MOTION;
+            case AcquiredInfo.RECALIBRATE:
+                return BiometricFaceConstants.FACE_ACQUIRED_RECALIBRATE;
+            case AcquiredInfo.TOO_DIFFERENT:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_DIFFERENT;
+            case AcquiredInfo.TOO_SIMILAR:
+                return BiometricFaceConstants.FACE_ACQUIRED_TOO_SIMILAR;
+            case AcquiredInfo.PAN_TOO_EXTREME:
+                return BiometricFaceConstants.FACE_ACQUIRED_PAN_TOO_EXTREME;
+            case AcquiredInfo.TILT_TOO_EXTREME:
+                return BiometricFaceConstants.FACE_ACQUIRED_TILT_TOO_EXTREME;
+            case AcquiredInfo.ROLL_TOO_EXTREME:
+                return BiometricFaceConstants.FACE_ACQUIRED_ROLL_TOO_EXTREME;
+            case AcquiredInfo.FACE_OBSCURED:
+                return BiometricFaceConstants.FACE_ACQUIRED_FACE_OBSCURED;
+            case AcquiredInfo.START:
+                return BiometricFaceConstants.FACE_ACQUIRED_START;
+            case AcquiredInfo.SENSOR_DIRTY:
+                return BiometricFaceConstants.FACE_ACQUIRED_SENSOR_DIRTY;
+            case AcquiredInfo.VENDOR:
+                return BiometricFaceConstants.FACE_ACQUIRED_VENDOR;
+            case AcquiredInfo.UNKNOWN:
+            case AcquiredInfo.FIRST_FRAME_RECEIVED:
+            case AcquiredInfo.DARK_GLASSES_DETECTED:
+            case AcquiredInfo.MOUTH_COVERING_DETECTED:
+            default:
+                return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
+        }
+    }
+
+    @FaceEnrollStage
+    public static int toFrameworkEnrollmentStage(int aidlEnrollmentStage) {
+        switch (aidlEnrollmentStage) {
+            case EnrollmentStage.FIRST_FRAME_RECEIVED:
+                return FaceEnrollStages.FIRST_FRAME_RECEIVED;
+            case EnrollmentStage.WAITING_FOR_CENTERING:
+                return FaceEnrollStages.WAITING_FOR_CENTERING;
+            case EnrollmentStage.HOLD_STILL_IN_CENTER:
+                return FaceEnrollStages.HOLD_STILL_IN_CENTER;
+            case EnrollmentStage.ENROLLING_MOVEMENT_1:
+                return FaceEnrollStages.ENROLLING_MOVEMENT_1;
+            case EnrollmentStage.ENROLLING_MOVEMENT_2:
+                return FaceEnrollStages.ENROLLING_MOVEMENT_2;
+            case EnrollmentStage.ENROLLMENT_FINISHED:
+                return FaceEnrollStages.ENROLLMENT_FINISHED;
+            case EnrollmentStage.UNKNOWN:
+            default:
+                return FaceEnrollStages.UNKNOWN;
         }
     }
 
@@ -135,7 +153,9 @@
 
     @NonNull
     public static FaceEnrollFrame toFrameworkEnrollmentFrame(@NonNull EnrollmentFrame frame) {
-        return new FaceEnrollFrame(toFrameworkCell(frame.cell), frame.stage,
+        return new FaceEnrollFrame(
+                toFrameworkCell(frame.cell),
+                toFrameworkEnrollmentStage(frame.stage),
                 toFrameworkBaseFrame(frame.data));
     }