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
+ }
+}