Extract some classes to BiometricsSharedLib.

Test: N/A

Change-Id: Ic41f684fe3e2e46fa3e540445015b8a3f3922fab
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index a6517c1..0415341 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -46,6 +46,7 @@
         ":wm_shell_util-sources",
     ],
     static_libs: [
+        "BiometricsSharedLib",
         "PluginCoreLib",
         "SystemUIAnimationLib",
         "SystemUIPluginLib",
diff --git a/packages/SystemUI/shared/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp
new file mode 100644
index 0000000..2bd7d97
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/Android.bp
@@ -0,0 +1,20 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+    name: "BiometricsSharedLib",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    min_sdk_version: "current",
+}
diff --git a/packages/SystemUI/shared/biometrics/AndroidManifest.xml b/packages/SystemUI/shared/biometrics/AndroidManifest.xml
new file mode 100644
index 0000000..861321b
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.systemui.shared.biometrics">
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/biometrics/res/values/strings.xml b/packages/SystemUI/shared/biometrics/res/values/strings.xml
new file mode 100644
index 0000000..c15c2b3
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/res/values/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- [CHAR LIMIT=NONE] Messages shown when users press outside udfps region during -->
+    <string name="udfps_accessibility_touch_hints_left"> Move left </string>
+    <!-- [CHAR LIMIT=NONE] Messages shown when users press outside udfps region during -->
+    <string name="udfps_accessibility_touch_hints_down"> Move down </string>
+    <!-- [CHAR LIMIT=NONE] Messages shown when users press outside udfps region during -->
+    <string name="udfps_accessibility_touch_hints_right"> Move right </string>
+    <!-- [CHAR LIMIT=NONE] Messages shown when users press outside udfps region during -->
+    <string name="udfps_accessibility_touch_hints_up"> Move up </string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/UdfpsUtils.java b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/UdfpsUtils.java
new file mode 100644
index 0000000..9574fba
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/UdfpsUtils.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.util.DisplayUtils;
+import android.util.Log;
+import android.util.RotationUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
+import com.android.systemui.shared.biometrics.R;
+
+/** Utility class for working with udfps. */
+public class UdfpsUtils {
+    private static final String TAG = "UdfpsUtils";
+
+    /**
+     * Gets the scale factor representing the user's current resolution / the stable (default)
+     * resolution.
+     *
+     * @param displayInfo The display information.
+     */
+    public float getScaleFactor(DisplayInfo displayInfo) {
+        Display.Mode maxDisplayMode =
+                DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes);
+        float scaleFactor =
+                DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+                        maxDisplayMode.getPhysicalWidth(),
+                        maxDisplayMode.getPhysicalHeight(),
+                        displayInfo.getNaturalWidth(),
+                        displayInfo.getNaturalHeight()
+                );
+        return (scaleFactor == Float.POSITIVE_INFINITY) ? 1f : scaleFactor;
+    }
+
+    /**
+     * Gets the touch in native coordinates. Map the touch to portrait mode if the device is in
+     * landscape mode.
+     *
+     * @param idx                The pointer identifier.
+     * @param event              The MotionEvent object containing full information about the event.
+     * @param udfpsOverlayParams The [UdfpsOverlayParams] used.
+     * @return The mapped touch event.
+     */
+    public Point getTouchInNativeCoordinates(int idx, MotionEvent event,
+            UdfpsOverlayParams udfpsOverlayParams) {
+        Point portraitTouch = getPortraitTouch(idx, event, udfpsOverlayParams);
+
+        // Scale the coordinates to native resolution.
+        float scale = udfpsOverlayParams.getScaleFactor();
+        portraitTouch.x = (int) (portraitTouch.x / scale);
+        portraitTouch.y = (int) (portraitTouch.y / scale);
+        return portraitTouch;
+    }
+
+    /**
+     * @param idx                The pointer identifier.
+     * @param event              The MotionEvent object containing full information about the event.
+     * @param udfpsOverlayParams The [UdfpsOverlayParams] used.
+     * @return Whether the touch event is within sensor area.
+     */
+    public boolean isWithinSensorArea(int idx, MotionEvent event,
+            UdfpsOverlayParams udfpsOverlayParams) {
+        Point portraitTouch = getPortraitTouch(idx, event, udfpsOverlayParams);
+        return udfpsOverlayParams.getSensorBounds().contains(portraitTouch.x, portraitTouch.y);
+    }
+
+    /**
+     * This function computes the angle of touch relative to the sensor and maps the angle to a list
+     * of help messages which are announced if accessibility is enabled.
+     *
+     * @return Whether the announcing string is null
+     */
+    public String onTouchOutsideOfSensorArea(boolean touchExplorationEnabled, Context context,
+            int scaledTouchX, int scaledTouchY, UdfpsOverlayParams udfpsOverlayParams) {
+        if (!touchExplorationEnabled) {
+            return null;
+        }
+
+        Resources resources = context.getResources();
+        String[] touchHints = new String[] {
+                resources.getString(R.string.udfps_accessibility_touch_hints_left),
+                resources.getString(R.string.udfps_accessibility_touch_hints_down),
+                resources.getString(R.string.udfps_accessibility_touch_hints_right),
+                resources.getString(R.string.udfps_accessibility_touch_hints_up),
+        };
+
+        // Scale the coordinates to native resolution.
+        float scale = udfpsOverlayParams.getScaleFactor();
+        float scaledSensorX = udfpsOverlayParams.getSensorBounds().centerX() / scale;
+        float scaledSensorY = udfpsOverlayParams.getSensorBounds().centerY() / scale;
+        String theStr =
+                onTouchOutsideOfSensorAreaImpl(
+                        touchHints,
+                        scaledTouchX,
+                        scaledTouchY,
+                        scaledSensorX,
+                        scaledSensorY,
+                        udfpsOverlayParams.getRotation()
+                );
+        Log.v(TAG, "Announcing touch outside : $theStr");
+        return theStr;
+    }
+
+    /**
+     * This function computes the angle of touch relative to the sensor and maps the angle to a list
+     * of help messages which are announced if accessibility is enabled.
+     *
+     * There are 4 quadrants of the circle (90 degree arcs)
+     *
+     * [315, 360] && [0, 45) -> touchHints[0] = "Move Fingerprint to the left" [45, 135) ->
+     * touchHints[1] = "Move Fingerprint down" And so on.
+     */
+    private String onTouchOutsideOfSensorAreaImpl(String[] touchHints, float touchX,
+            float touchY, float sensorX, float sensorY, int rotation) {
+        float xRelativeToSensor = touchX - sensorX;
+        // Touch coordinates are with respect to the upper left corner, so reverse
+        // this calculation
+        float yRelativeToSensor = sensorY - touchY;
+        double angleInRad = Math.atan2(yRelativeToSensor, xRelativeToSensor);
+        // If the radians are negative, that means we are counting clockwise.
+        // So we need to add 360 degrees
+        if (angleInRad < 0.0) {
+            angleInRad += 2.0 * Math.PI;
+        }
+        // rad to deg conversion
+        double degrees = Math.toDegrees(angleInRad);
+        double degreesPerBucket = 360.0 / touchHints.length;
+        double halfBucketDegrees = degreesPerBucket / 2.0;
+        // The mapping should be as follows
+        // [315, 360] && [0, 45] -> 0
+        // [45, 135]             -> 1
+        int index = (int) ((degrees + halfBucketDegrees) % 360 / degreesPerBucket);
+        index %= touchHints.length;
+
+        // A rotation of 90 degrees corresponds to increasing the index by 1.
+        if (rotation == Surface.ROTATION_90) {
+            index = (index + 1) % touchHints.length;
+        }
+        if (rotation == Surface.ROTATION_270) {
+            index = (index + 3) % touchHints.length;
+        }
+        return touchHints[index];
+    }
+
+    /**
+     * Map the touch to portrait mode if the device is in landscape mode.
+     */
+    private Point getPortraitTouch(int idx, MotionEvent event,
+            UdfpsOverlayParams udfpsOverlayParams) {
+        Point portraitTouch = new Point((int) event.getRawX(idx), (int) event.getRawY(idx));
+        int rot = udfpsOverlayParams.getRotation();
+        if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+            RotationUtils.rotatePoint(
+                    portraitTouch,
+                    RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+                    udfpsOverlayParams.getLogicalDisplayWidth(),
+                    udfpsOverlayParams.getLogicalDisplayHeight()
+            );
+        }
+        return portraitTouch;
+    }
+}
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
new file mode 100644
index 0000000..422f02f
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 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.systemui.biometrics
+
+import android.Manifest
+import android.annotation.IntDef
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.Insets
+import android.hardware.biometrics.BiometricManager.Authenticators
+import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.SensorPropertiesInternal
+import android.os.UserManager
+import android.util.DisplayMetrics
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import com.android.internal.widget.LockPatternUtils
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+
+object Utils {
+    const val CREDENTIAL_PIN = 1
+    const val CREDENTIAL_PATTERN = 2
+    const val CREDENTIAL_PASSWORD = 3
+
+    /** Base set of layout flags for fingerprint overlay widgets. */
+    const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
+        (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
+            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+
+    @JvmStatic
+    fun dpToPixels(context: Context, dp: Float): Float {
+        val density = context.resources.displayMetrics.densityDpi.toFloat()
+        return dp * (density / DisplayMetrics.DENSITY_DEFAULT)
+    }
+
+    /**
+     * Note: Talkback 14.0 has new rate-limitation design to reduce frequency of
+     * TYPE_WINDOW_CONTENT_CHANGED events to once every 30 seconds. (context: b/281765653#comment18)
+     * Using {@link View#announceForAccessibility} instead as workaround when sending events
+     * exceeding this frequency is required.
+     */
+    @JvmStatic
+    fun notifyAccessibilityContentChanged(am: AccessibilityManager, view: ViewGroup) {
+        if (!am.isEnabled) {
+            return
+        }
+        val event = AccessibilityEvent.obtain()
+        event.eventType = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+        event.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+        view.sendAccessibilityEventUnchecked(event)
+        view.notifySubtreeAccessibilityStateChanged(
+            view,
+            view,
+            AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+        )
+    }
+
+    @JvmStatic
+    fun isDeviceCredentialAllowed(promptInfo: PromptInfo): Boolean =
+        (promptInfo.authenticators and Authenticators.DEVICE_CREDENTIAL) != 0
+
+    @JvmStatic
+    fun isBiometricAllowed(promptInfo: PromptInfo): Boolean =
+        (promptInfo.authenticators and Authenticators.BIOMETRIC_WEAK) != 0
+
+    @JvmStatic
+    @CredentialType
+    fun getCredentialType(utils: LockPatternUtils, userId: Int): Int =
+        when (utils.getKeyguardStoredPasswordQuality(userId)) {
+            PASSWORD_QUALITY_SOMETHING -> CREDENTIAL_PATTERN
+            PASSWORD_QUALITY_NUMERIC,
+            PASSWORD_QUALITY_NUMERIC_COMPLEX -> CREDENTIAL_PIN
+            PASSWORD_QUALITY_ALPHABETIC,
+            PASSWORD_QUALITY_ALPHANUMERIC,
+            PASSWORD_QUALITY_COMPLEX,
+            PASSWORD_QUALITY_MANAGED -> CREDENTIAL_PASSWORD
+            else -> CREDENTIAL_PASSWORD
+        }
+
+    @JvmStatic
+    fun isManagedProfile(context: Context, userId: Int): Boolean =
+        context.getSystemService(UserManager::class.java)?.isManagedProfile(userId) ?: false
+
+    @JvmStatic
+    fun <T : SensorPropertiesInternal> findFirstSensorProperties(
+        properties: List<T>?,
+        sensorIds: IntArray
+    ): T? = properties?.firstOrNull { sensorIds.contains(it.sensorId) }
+
+    @JvmStatic
+    fun isSystem(context: Context, clientPackage: String?): Boolean {
+        val hasPermission =
+            (context.checkCallingOrSelfPermission(Manifest.permission.USE_BIOMETRIC_INTERNAL) ==
+                PackageManager.PERMISSION_GRANTED)
+        return hasPermission && "android" == clientPackage
+    }
+
+    @JvmStatic
+    fun getNavbarInsets(context: Context): Insets {
+        val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
+        val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics
+        return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
+            ?: Insets.NONE
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD)
+    annotation class CredentialType
+}
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
new file mode 100644
index 0000000..db46ccf
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.shared.model
+
+import android.hardware.biometrics.SensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+
+/** The available modalities for an operation. */
+data class BiometricModalities(
+    val fingerprintProperties: FingerprintSensorPropertiesInternal? = null,
+    val faceProperties: FaceSensorPropertiesInternal? = null,
+) {
+    /** If there are no available modalities. */
+    val isEmpty: Boolean
+        get() = !hasFingerprint && !hasFace
+
+    /** If fingerprint authentication is available (and [fingerprintProperties] is non-null). */
+    val hasFingerprint: Boolean
+        get() = fingerprintProperties != null
+
+    /** If fingerprint authentication is available (and [faceProperties] is non-null). */
+    val hasFace: Boolean
+        get() = faceProperties != null
+
+    /** If only face authentication is enabled. */
+    val hasFaceOnly: Boolean
+        get() = hasFace && !hasFingerprint
+
+    /** If only fingerprint authentication is enabled. */
+    val hasFingerprintOnly: Boolean
+        get() = hasFingerprint && !hasFace
+
+    /** If face & fingerprint authentication is enabled (coex). */
+    val hasFaceAndFingerprint: Boolean
+        get() = hasFingerprint && hasFace
+
+    /** If [hasFace] and it is configured as a STRONG class 3 biometric. */
+    val isFaceStrong: Boolean
+        get() = faceProperties?.sensorStrength == SensorProperties.STRENGTH_STRONG
+}
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt
new file mode 100644
index 0000000..fb580ca
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.shared.model
+
+import android.hardware.biometrics.BiometricAuthenticator
+
+/** Shadows [BiometricAuthenticator.Modality] for Kotlin use within SysUI. */
+enum class BiometricModality {
+    None,
+    Fingerprint,
+    Face,
+}
+
+/** Convert a framework [BiometricAuthenticator.Modality] to a SysUI [BiometricModality]. */
+@BiometricAuthenticator.Modality
+fun Int.asBiometricModality(): BiometricModality =
+    when (this) {
+        BiometricAuthenticator.TYPE_FINGERPRINT -> BiometricModality.Fingerprint
+        BiometricAuthenticator.TYPE_FACE -> BiometricModality.Face
+        else -> BiometricModality.None
+    }
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
new file mode 100644
index 0000000..39689ec
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
@@ -0,0 +1,12 @@
+package com.android.systemui.biometrics.shared.model
+
+/**
+ * Metadata about the current user BiometricPrompt is being shown to.
+ *
+ * If the user's fallback credential is owned by another profile user the [deviceCredentialOwnerId]
+ * will differ from the user's [userId].
+ */
+data class BiometricUserInfo(
+    val userId: Int,
+    val deviceCredentialOwnerId: Int = userId,
+)
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
new file mode 100644
index 0000000..6082fb9
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.shared.model
+
+import android.hardware.fingerprint.FingerprintSensorProperties
+
+/** Fingerprint sensor types. Represents [FingerprintSensorProperties.SensorType]. */
+enum class FingerprintSensorType {
+    UNKNOWN,
+    REAR,
+    UDFPS_ULTRASONIC,
+    UDFPS_OPTICAL,
+    POWER_BUTTON,
+    HOME_BUTTON
+}
+
+/** Convert [this] to corresponding [FingerprintSensorType] */
+fun Int.toSensorType(): FingerprintSensorType =
+    when (this) {
+        FingerprintSensorProperties.TYPE_UNKNOWN -> FingerprintSensorType.UNKNOWN
+        FingerprintSensorProperties.TYPE_REAR -> FingerprintSensorType.REAR
+        FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC -> FingerprintSensorType.UDFPS_ULTRASONIC
+        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL -> FingerprintSensorType.UDFPS_OPTICAL
+        FingerprintSensorProperties.TYPE_POWER_BUTTON -> FingerprintSensorType.POWER_BUTTON
+        FingerprintSensorProperties.TYPE_HOME_BUTTON -> FingerprintSensorType.HOME_BUTTON
+        else -> throw IllegalArgumentException("Invalid SensorType value: $this")
+    }
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
new file mode 100644
index 0000000..68bba32
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.shared.model
+
+import android.hardware.biometrics.BiometricConstants
+
+/** Lockout mode. Represents [BiometricConstants.LockoutMode]. */
+enum class LockoutMode {
+    NONE,
+    TIMED,
+    PERMANENT,
+}
+
+/** Convert [this] to corresponding [LockoutMode] */
+fun Int.toLockoutMode(): LockoutMode =
+    when (this) {
+        BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT -> LockoutMode.PERMANENT
+        BiometricConstants.BIOMETRIC_LOCKOUT_TIMED -> LockoutMode.TIMED
+        else -> LockoutMode.NONE
+    }
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
new file mode 100644
index 0000000..476daac
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.shared.model
+
+import android.hardware.biometrics.SensorProperties
+
+/** Sensor security strength. Represents [SensorProperties.Strength]. */
+enum class SensorStrength {
+    CONVENIENCE,
+    WEAK,
+    STRONG,
+}
+
+/** Convert [this] to corresponding [SensorStrength] */
+fun Int.toSensorStrength(): SensorStrength =
+    when (this) {
+        SensorProperties.STRENGTH_CONVENIENCE -> SensorStrength.CONVENIENCE
+        SensorProperties.STRENGTH_WEAK -> SensorStrength.WEAK
+        SensorProperties.STRENGTH_STRONG -> SensorStrength.STRONG
+        else -> throw IllegalArgumentException("Invalid SensorStrength value: $this")
+    }
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt
new file mode 100644
index 0000000..a9b4fe8
--- /dev/null
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt
@@ -0,0 +1,57 @@
+package com.android.systemui.biometrics.shared.model
+
+import android.graphics.Rect
+import android.view.Surface
+import android.view.Surface.Rotation
+
+/**
+ * Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
+ *
+ * [sensorBounds] coordinates of the bounding box around the sensor in natural orientation, in
+ * pixels, for the current resolution.
+ *
+ * [overlayBounds] coordinates of the UI overlay in natural orientation, in pixels, for the current
+ * resolution.
+ *
+ * [naturalDisplayWidth] width of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [naturalDisplayHeight] height of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [scaleFactor] ratio of a dimension in the current resolution to the corresponding dimension in
+ * the native resolution.
+ *
+ * [rotation] current rotation of the display.
+ */
+data class UdfpsOverlayParams(
+    val sensorBounds: Rect = Rect(),
+    val overlayBounds: Rect = Rect(),
+    val naturalDisplayWidth: Int = 0,
+    val naturalDisplayHeight: Int = 0,
+    val scaleFactor: Float = 1f,
+    @Rotation val rotation: Int = Surface.ROTATION_0
+) {
+
+    /** Same as [sensorBounds], but in native resolution. */
+    val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) }
+
+    /** Same as [overlayBounds], but in native resolution. */
+    val nativeOverlayBounds = Rect(overlayBounds).apply { scale(1f / scaleFactor) }
+
+    /** See [android.view.DisplayInfo.logicalWidth] */
+    val logicalDisplayWidth =
+        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+            naturalDisplayHeight
+        } else {
+            naturalDisplayWidth
+        }
+
+    /** See [android.view.DisplayInfo.logicalHeight] */
+    val logicalDisplayHeight =
+        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+            naturalDisplayWidth
+        } else {
+            naturalDisplayHeight
+        }
+}