Merge "[Audiosharing] Support new start broadcast API." into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4d085a4..e92564b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13702,6 +13702,7 @@
method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
method public int describeContents();
method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_restriction_status") public int getCarrierRestrictionStatus();
method public int getDefaultCarrierRestriction();
method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
method public int getMultiSimPolicy();
diff --git a/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl
new file mode 100644
index 0000000..73ac333
--- /dev/null
+++ b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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 android.hardware.biometrics;
+
+/**
+ * Low-level callback interface between <Biometric>Manager and <Auth>Service. Allows core system
+ * services (e.g. SystemUI) to register a listener for updates about the current state of biometric
+ * authentication.
+ * @hide
+ */
+oneway interface AuthenticationStateListener {
+ /**
+ * Defines behavior in response to authentication starting
+ * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting
+ * authentication starting
+ */
+ void onAuthenticationStarted(int requestReason);
+
+ /**
+ * Defines behavior in response to authentication stopping
+ */
+ void onAuthenticationStopped();
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index f82f79e..d7d1d1a 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -552,6 +552,44 @@
}
/**
+ * Registers listener for changes to biometric authentication state.
+ * Only sends callbacks for events that occur after the callback has been registered.
+ * @param listener Listener for changes to biometric authentication state
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void registerAuthenticationStateListener(AuthenticationStateListener listener) {
+ if (mService != null) {
+ try {
+ mService.registerAuthenticationStateListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "registerAuthenticationStateListener(): Service not connected");
+ }
+ }
+
+ /**
+ * Unregisters listener for changes to biometric authentication state.
+ * @param listener Listener for changes to biometric authentication state
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void unregisterAuthenticationStateListener(AuthenticationStateListener listener) {
+ if (mService != null) {
+ try {
+ mService.unregisterAuthenticationStateListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "unregisterAuthenticationStateListener(): Service not connected");
+ }
+ }
+
+
+ /**
* Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
* authenticatorId invalidated for the specified user. This happens when enrollments have been
* added on devices with multiple biometric sensors.
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricRequestConstants.java
similarity index 69%
rename from core/java/android/hardware/biometrics/BiometricOverlayConstants.java
rename to core/java/android/hardware/biometrics/BiometricRequestConstants.java
index 065ae64a..b036f30 100644
--- a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricRequestConstants.java
@@ -22,24 +22,27 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Common constants for biometric overlays.
+ * Common constants for biometric requests.
* @hide
*/
-public interface BiometricOverlayConstants {
+public class BiometricRequestConstants {
+
+ private BiometricRequestConstants() {}
+
/** Unknown usage. */
- int REASON_UNKNOWN = 0;
+ public static final int REASON_UNKNOWN = 0;
/** User is about to enroll. */
- int REASON_ENROLL_FIND_SENSOR = 1;
+ public static final int REASON_ENROLL_FIND_SENSOR = 1;
/** User is enrolling. */
- int REASON_ENROLL_ENROLLING = 2;
+ public static final int REASON_ENROLL_ENROLLING = 2;
/** Usage from BiometricPrompt. */
- int REASON_AUTH_BP = 3;
- /** Usage from Keyguard. */
- int REASON_AUTH_KEYGUARD = 4;
+ public static final int REASON_AUTH_BP = 3;
+ /** Usage from Device Entry. */
+ public static final int REASON_AUTH_KEYGUARD = 4;
/** Non-specific usage (from FingerprintManager). */
- int REASON_AUTH_OTHER = 5;
+ public static final int REASON_AUTH_OTHER = 5;
/** Usage from Settings. */
- int REASON_AUTH_SETTINGS = 6;
+ public static final int REASON_AUTH_SETTINGS = 6;
@IntDef({REASON_UNKNOWN,
REASON_ENROLL_FIND_SENSOR,
@@ -49,5 +52,5 @@
REASON_AUTH_OTHER,
REASON_AUTH_SETTINGS})
@Retention(RetentionPolicy.SOURCE)
- @interface ShowReason {}
+ public @interface RequestReason {}
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 5bdbe2b5..8514f98 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -16,6 +16,7 @@
package android.hardware.biometrics;
+import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
@@ -66,6 +67,12 @@
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
+ // Register listener for changes to authentication state.
+ void registerAuthenticationStateListener(AuthenticationStateListener listener);
+
+ // Unregister listener for changes to authentication state.
+ void unregisterAuthenticationStateListener(AuthenticationStateListener listener);
+
// Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
// specified user. This happens when enrollments have been added on devices with multiple
// biometric sensors.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 935157a..fe7de83 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -983,6 +983,7 @@
}
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/**
* @hide
*/
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0100660..f594c00 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IBiometricStateListener;
@@ -203,6 +204,14 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void setSidefpsController(in ISidefpsController controller);
+ // Registers AuthenticationStateListener.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerAuthenticationStateListener(AuthenticationStateListener listener);
+
+ // Unregisters AuthenticationStateListener.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void unregisterAuthenticationStateListener(AuthenticationStateListener listener);
+
// Registers BiometricStateListener.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerBiometricStateListener(IBiometricStateListener listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 77427d9..96e57e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -223,6 +223,7 @@
private boolean mExitSplitScreenOnHide;
private boolean mIsDividerRemoteAnimating;
private boolean mIsDropEntering;
+ private boolean mSkipEvictingMainStageChildren;
private boolean mIsExiting;
private boolean mIsRootTranslucent;
@VisibleForTesting
@@ -468,6 +469,7 @@
}
// Due to drag already pip task entering split by this method so need to reset flag here.
mIsDropEntering = false;
+ mSkipEvictingMainStageChildren = false;
return true;
}
@@ -572,6 +574,15 @@
return;
}
+ // Don't evict the main stage children as this can race and happen after the activity is
+ // started into that stage
+ if (!isSplitScreenVisible()) {
+ mSkipEvictingMainStageChildren = true;
+ // Starting the split task without evicting children will bring the single root task
+ // container forward, so ensure that we hide the divider before we start animate it
+ setDividerVisibility(false, null);
+ }
+
// If split screen is not activated, we're expecting to open a pair of apps to split.
final int extraTransitType = mMainStage.isActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -600,6 +611,15 @@
return;
}
+ // Don't evict the main stage children as this can race and happen after the activity is
+ // started into that stage
+ if (!isSplitScreenVisible()) {
+ mSkipEvictingMainStageChildren = true;
+ // Starting the split task without evicting children will bring the single root task
+ // container forward, so ensure that we hide the divider before we start animate it
+ setDividerVisibility(false, null);
+ }
+
// If split screen is not activated, we're expecting to open a pair of apps to split.
final int extraTransitType = mMainStage.isActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -1618,7 +1638,7 @@
// Ensure to evict old splitting tasks because the new split pair might be composed by
// one of the splitting tasks, evicting the task when finishing entering transition
// won't guarantee to put the task to the indicated new position.
- if (!mIsDropEntering) {
+ if (!mSkipEvictingMainStageChildren) {
mMainStage.evictAllChildren(wct);
}
mMainStage.reparentTopTask(wct);
@@ -1680,6 +1700,7 @@
finishT.show(mRootTaskLeash);
setSplitsVisible(true);
mIsDropEntering = false;
+ mSkipEvictingMainStageChildren = false;
mSplitRequest = null;
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
@@ -1929,6 +1950,7 @@
if (mIsDropEntering) {
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
mIsDropEntering = false;
+ mSkipEvictingMainStageChildren = false;
} else {
mShowDecorImmediately = true;
mSplitLayout.flingDividerToCenter();
@@ -2123,6 +2145,7 @@
if (mIsDropEntering) {
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
mIsDropEntering = false;
+ mSkipEvictingMainStageChildren = false;
} else {
mShowDecorImmediately = true;
mSplitLayout.flingDividerToCenter();
@@ -3245,6 +3268,7 @@
public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
if (!isSplitScreenVisible()) {
mIsDropEntering = true;
+ mSkipEvictingMainStageChildren = true;
}
if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 901ea46..a8ffd2b 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -110,6 +110,10 @@
void pauseRecording(in IBinder sessionToken, in Bundle params, int userId);
void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
+ // For playback control
+ void startPlayback(in IBinder sessionToken, int userId);
+ void stopPlayback(in IBinder sessionToken, int mode, int userId);
+
// For broadcast info
void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
void removeBroadcastInfo(in IBinder sessionToken, int id, int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 5246f5c4..e37ee6e 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,6 +63,9 @@
void timeShiftSetMode(int mode);
void timeShiftEnablePositionTracking(boolean enable);
+ void startPlayback();
+ void stopPlayback(int mode);
+
// For the recording session
void startRecording(in Uri programUri, in Bundle params);
void stopRecording();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index d749b91..ae3ee65 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -79,6 +79,8 @@
private static final int DO_TIME_SHIFT_SET_MODE = 30;
private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
private static final int DO_NOTIFY_TV_MESSAGE = 32;
+ private static final int DO_STOP_PLAYBACK = 33;
+ private static final int DO_START_PLAYBACK = 34;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -286,6 +288,14 @@
mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2);
break;
}
+ case DO_STOP_PLAYBACK: {
+ mTvInputSessionImpl.stopPlayback(msg.arg1);
+ break;
+ }
+ case DO_START_PLAYBACK: {
+ mTvInputSessionImpl.startPlayback();
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -483,6 +493,17 @@
enabled));
}
+ @Override
+ public void stopPlayback(int mode) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode));
+ }
+
+ @Override
+ public void startPlayback() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
+ }
+
+
private final class TvInputEventReceiver extends InputEventReceiver {
TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 631ab9a..c685a5a 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -339,6 +339,14 @@
*/
public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END;
+ /**
+ * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
+ * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
+ * it has been stopped by stopPlayback.
+ * @hide
+ */
+ public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED,
@@ -3302,6 +3310,30 @@
}
}
+ void stopPlayback(int mode) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.stopPlayback(mToken, mode, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void startPlayback() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.startPlayback(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sends TV messages to the service for testing purposes
*/
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 720d9a6..55fa517 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -34,6 +34,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioPresentation;
import android.media.PlaybackParams;
+import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -1531,6 +1532,32 @@
}
/**
+ * Called when the application requests playback of the Audio, Video, and CC streams to be
+ * stopped, but the metadata should continue to be filtered.
+ *
+ * <p>The metadata that will continue to be filtered includes the PSI
+ * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
+ *
+ * <p> Note that this is different form {@link #timeShiftPause()} as should release the
+ * stream, making it impossible to resume from this position again.
+ * @param mode
+ * @hide
+ */
+ public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
+ }
+
+ /**
+ * Starts playback of the Audio, Video, and CC streams.
+ *
+ * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
+ * used after stopping playback. This is used to restart playback from the current position
+ * in the live broadcast.
+ * @hide
+ */
+ public void onStartPlayback() {
+ }
+
+ /**
* Called when the application requests to play a given recorded TV program.
*
* @param recordedProgramUri The URI of a recorded TV program.
@@ -1993,6 +2020,20 @@
}
/**
+ * Calls {@link #onStopPlayback(int)}.
+ */
+ void stopPlayback(int mode) {
+ onStopPlayback(mode);
+ }
+
+ /**
+ * Calls {@link #onStartPlayback()}.
+ */
+ void startPlayback() {
+ onStartPlayback();
+ }
+
+ /**
* Calls {@link #onTimeShiftPlay(Uri)}.
*/
void timeShiftPlay(Uri recordedProgramUri) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 196b5c3..233f966 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -37,6 +37,7 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
+import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -643,6 +644,35 @@
}
}
+ /**
+ * Stops playback of the Audio, Video, and CC streams, but continue filtering the metadata.
+ *
+ * <p>The metadata that will continue to be filtered includes the PSI
+ * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
+ *
+ * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
+ * the stream, making it impossible to resume from this position again.
+ * @hide
+ */
+ public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
+ if (mSession != null) {
+ mSession.stopPlayback(mode);
+ }
+ }
+
+ /**
+ * Starts playback of the Audio, Video, and CC streams.
+ *
+ * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
+ * used after stopping playback. This is used to restart playback from the current position
+ * in the live broadcast.
+ * @hide
+ */
+ public void startPlayback() {
+ if (mSession != null) {
+ mSession.startPlayback();
+ }
+ }
/**
* Sends TV messages to the session for testing purposes
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 0abf285..66282dc 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -527,6 +527,15 @@
final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
+ // No need to show permission consent dialog if it is a isSkipPrompt(true)
+ // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+ if (mRequest.isSkipPrompt()) {
+ Log.d(TAG, "Skipping the permission consent dialog.");
+ mSingleDeviceSpinner.setVisibility(View.GONE);
+ onUserSelectedDevice(mSelectedDevice);
+ return;
+ }
+
updatePermissionUi();
mProfileIcon.setImageDrawable(profileIcon);
@@ -598,6 +607,14 @@
Log.d(TAG, "onDeviceClicked(): " + mSelectedDevice.toShortString());
+ // No need to show permission consent dialog if it is a isSkipPrompt(true)
+ // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+ if (mRequest.isSkipPrompt()) {
+ Log.d(TAG, "Skipping the permission consent dialog.");
+ onUserSelectedDevice(mSelectedDevice);
+ return;
+ }
+
updatePermissionUi();
mSummary.setVisibility(View.VISIBLE);
@@ -615,14 +632,6 @@
this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName);
final Spanned summary;
- // No need to show permission consent dialog if it is a isSkipPrompt(true)
- // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
- if (mRequest.isSkipPrompt()) {
- mSingleDeviceSpinner.setVisibility(View.GONE);
- onUserSelectedDevice(mSelectedDevice);
- return;
- }
-
if (deviceProfile == null && mRequest.isSingleDevice()) {
summary = getHtmlFromResources(this, summaryResourceId, remoteDeviceName);
mConstraintList.setVisibility(View.GONE);
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index f216abb..66bd6f5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -28,7 +28,14 @@
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -41,14 +48,14 @@
import com.android.settingslib.spa.widget.card.CardButton
import com.android.settingslib.spa.widget.card.CardModel
import com.android.settingslib.spa.widget.card.SettingsCard
-import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
import com.android.settingslib.spa.widget.card.SettingsCardContent
+import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
object CardPageProvider : SettingsPageProvider {
- override val name = "CardPage"
+ override val name = "Card"
override fun getTitle(arguments: Bundle?) = TITLE
@@ -79,10 +86,13 @@
@Composable
private fun SettingsCardWithoutIcon() {
+ var isVisible by rememberSaveable { mutableStateOf(true) }
SettingsCard(
CardModel(
title = stringResource(R.string.sample_title),
text = stringResource(R.string.sample_text),
+ isVisible = { isVisible },
+ onDismiss = { isVisible = false },
buttons = listOf(
CardButton(text = "Action") {},
),
@@ -92,21 +102,23 @@
@Composable
fun SampleSettingsCollapsibleCard() {
- SettingsCollapsibleCard(
- title = "More alerts",
- imageVector = Icons.Outlined.Error,
- models = listOf(
+ val context = LocalContext.current
+ var isVisible0 by rememberSaveable { mutableStateOf(true) }
+ val cards = remember {
+ mutableStateListOf(
CardModel(
- title = stringResource(R.string.sample_title),
- text = stringResource(R.string.sample_text),
+ title = context.getString(R.string.sample_title),
+ text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.PowerOff,
+ isVisible = { isVisible0 },
+ onDismiss = { isVisible0 = false },
buttons = listOf(
CardButton(text = "Action") {},
)
),
CardModel(
- title = stringResource(R.string.sample_title),
- text = stringResource(R.string.sample_text),
+ title = context.getString(R.string.sample_title),
+ text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.Shield,
buttons = listOf(
CardButton(text = "Action") {},
@@ -114,6 +126,11 @@
)
)
)
+ }
+ SettingsCollapsibleCard(
+ title = "More alerts",
+ imageVector = Icons.Outlined.Error,
+ models = cards.toList()
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index c143390..993cb4a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -37,6 +37,7 @@
val itemPaddingAround = 8.dp
val itemDividerHeight = 32.dp
+ val iconSmall = 16.dp
val iconLarge = 48.dp
/** The size when app icon is displayed in list. */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index c113f43..b18a1bc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -28,5 +28,14 @@
val title: String,
val text: String,
val imageVector: ImageVector? = null,
+ val isVisible: () -> Boolean = { true },
+
+ /**
+ * A dismiss button will be displayed if this is not null.
+ *
+ * And this callback will be called when user clicks the button.
+ */
+ val onDismiss: (() -> Unit)? = null,
+
val buttons: List<CardButton> = emptyList(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 4379278..7eec888 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -16,28 +16,35 @@
package com.android.settingslib.spa.widget.card
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.debug.UiModePreviews
import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -87,20 +94,31 @@
@Composable
internal fun SettingsCardImpl(model: CardModel) {
- SettingsCardContent {
- Column(
- modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
- verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
- ) {
- CardIcon(model.imageVector)
- SettingsTitle(model.title)
- SettingsBody(model.text)
- Buttons(model.buttons)
+ AnimatedVisibility(visible = model.isVisible()) {
+ SettingsCardContent {
+ Column(
+ modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+ verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
+ ) {
+ CardHeader(model.imageVector, model.onDismiss)
+ SettingsTitle(model.title)
+ SettingsBody(model.text)
+ Buttons(model.buttons)
+ }
}
}
}
@Composable
+fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) {
+ Row(Modifier.fillMaxWidth()) {
+ CardIcon(imageVector)
+ Spacer(modifier = Modifier.weight(1f))
+ DismissButton(onDismiss)
+ }
+}
+
+@Composable
private fun CardIcon(imageVector: ImageVector?) {
if (imageVector != null) {
Icon(
@@ -113,6 +131,28 @@
}
@Composable
+private fun DismissButton(onDismiss: (() -> Unit)?) {
+ if (onDismiss == null) return
+ Surface(
+ shape = CircleShape,
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ ) {
+ IconButton(
+ onClick = onDismiss,
+ modifier = Modifier.size(SettingsDimension.itemIconSize)
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Close,
+ contentDescription = stringResource(
+ androidx.compose.material3.R.string.m3c_snackbar_dismiss
+ ),
+ modifier = Modifier.size(SettingsDimension.iconSmall),
+ )
+ }
+ }
+}
+
+@Composable
private fun Buttons(buttons: List<CardButton>) {
if (buttons.isNotEmpty()) {
Row(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
index bf192a1..6e36490 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -58,7 +58,7 @@
var expanded by rememberSaveable { mutableStateOf(false) }
SettingsCard {
SettingsCardContent {
- Header(title, imageVector, models.size, expanded) { expanded = it }
+ Header(title, imageVector, models.count { it.isVisible() }, expanded) { expanded = it }
}
AnimatedVisibility(expanded) {
Column {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
index fd3ae49..beb9433 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -16,10 +16,18 @@
package com.android.settingslib.spa.widget.card
+import android.content.Context
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -31,6 +39,8 @@
@get:Rule
val composeTestRule = createComposeRule()
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
@Test
fun settingsCard_titleDisplayed() {
composeTestRule.setContent {
@@ -96,6 +106,27 @@
assertThat(buttonClicked).isTrue()
}
+ @Test
+ fun settingsCard_dismiss() {
+ composeTestRule.setContent {
+ var isVisible by remember { mutableStateOf(true) }
+ SettingsCard(
+ CardModel(
+ title = TITLE,
+ text = "",
+ isVisible = { isVisible },
+ onDismiss = { isVisible = false },
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithContentDescription(
+ context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+ ).performClick()
+
+ composeTestRule.onNodeWithText(TEXT).isNotDisplayed()
+ }
+
private companion object {
const val TITLE = "Title"
const val TEXT = "Text"
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
index efe1c70..aba9d7b 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
@@ -16,12 +16,20 @@
package com.android.settingslib.spa.widget.card
+import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Error
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
@@ -32,6 +40,8 @@
@get:Rule
val composeTestRule = createComposeRule()
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
@Test
fun settingsCollapsibleCard_titleDisplayed() {
setContent()
@@ -62,8 +72,22 @@
composeTestRule.onNodeWithText(CARD_TEXT).assertIsDisplayed()
}
+ @Test
+ fun settingsCollapsibleCard_dismiss() {
+ setContent()
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onNodeWithContentDescription(
+ context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+ ).performClick()
+
+ composeTestRule.onNodeWithText(CARD_TEXT).isNotDisplayed()
+ composeTestRule.onNodeWithText("0").assertIsDisplayed()
+ }
+
private fun setContent() {
composeTestRule.setContent {
+ var isVisible by rememberSaveable { mutableStateOf(true) }
SettingsCollapsibleCard(
title = TITLE,
imageVector = Icons.Outlined.Error,
@@ -71,6 +95,8 @@
CardModel(
title = "",
text = CARD_TEXT,
+ isVisible = { isVisible },
+ onDismiss = { isVisible = false },
)
),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 695d888..f170135 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -18,7 +18,7 @@
package com.android.keyguard
import android.content.res.Configuration
-import android.hardware.biometrics.BiometricOverlayConstants
+import android.hardware.biometrics.BiometricRequestConstants
import android.media.AudioManager
import android.telephony.TelephonyManager
import android.testing.TestableLooper.RunWithLooper
@@ -59,6 +59,7 @@
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -235,6 +236,7 @@
sceneInteractor = sceneInteractor,
)
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest =
KeyguardSecurityContainerController(
view,
@@ -763,16 +765,18 @@
@Test
fun sideFpsControllerShow() {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest.updateSideFpsVisibility(/* isVisible= */ true)
verify(sideFpsController)
.show(
SideFpsUiRequestSource.PRIMARY_BOUNCER,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD
)
}
@Test
fun sideFpsControllerHide() {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest.updateSideFpsVisibility(/* isVisible= */ false)
verify(sideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index a1b801c..f8321b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -22,9 +22,9 @@
import android.content.ComponentName
import android.graphics.Insets
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManager
@@ -65,6 +65,7 @@
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -144,6 +145,7 @@
@Before
fun setup() {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
displayRepository = FakeDisplayRepository()
displayStateRepository = FakeDisplayStateRepository()
keyguardBouncerRepository = FakeKeyguardBouncerRepository()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 90d36e7..a726b7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -17,12 +17,12 @@
package com.android.systemui.biometrics
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.RequestReason
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
@@ -135,7 +135,7 @@
}
private fun withReason(
- @ShowReason reason: Int,
+ @RequestReason reason: Int,
isDebuggable: Boolean = false,
enableDeviceEntryUdfpsRefactor: Boolean = false,
block: () -> Unit,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 97ee526..dddcf18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -43,7 +43,7 @@
import android.graphics.Rect;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
@@ -359,7 +359,7 @@
@Test
public void dozeTimeTick() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
mUdfpsController.dozeTimeTick();
verify(mUdfpsView).dozeTimeTick();
@@ -455,7 +455,7 @@
public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// WHEN the overlay is hidden
@@ -469,7 +469,7 @@
@Test
public void showUdfpsOverlay_callsListener() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mFingerprintManager).onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN,
@@ -479,7 +479,7 @@
@Test
public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
@@ -520,7 +520,7 @@
reset(mWindowManager);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
+ BiometricRequestConstants.REASON_ENROLL_ENROLLING,
mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(any(), any());
@@ -555,7 +555,7 @@
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(any(), any());
@@ -637,7 +637,7 @@
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -720,7 +720,7 @@
initUdfpsController(testParams.sensorProps);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -778,7 +778,7 @@
// GIVEN that the overlay is showing and screen is on and fp is running
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
@@ -808,7 +808,7 @@
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -886,7 +886,7 @@
// GIVEN overlay is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
@@ -917,7 +917,7 @@
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -958,7 +958,7 @@
final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the ACTION_UP event
@@ -1019,7 +1019,7 @@
// GIVEN screen off
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
@@ -1041,7 +1041,7 @@
// GIVEN showing overlay
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
@@ -1126,7 +1126,7 @@
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
if (a11y) {
@@ -1148,7 +1148,7 @@
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -1197,7 +1197,7 @@
-1 /* pointerId */, touchData);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -1289,7 +1289,7 @@
public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
// GIVEN UDFPS overlay is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// GIVEN there's been an AoD interrupt
@@ -1316,7 +1316,7 @@
throws RemoteException {
// GIVEN UDFPS overlay is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// GIVEN there's been an AoD interrupt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 90e0c19..a3bf3f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
@@ -105,8 +106,10 @@
job.cancel()
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun shouldUpdateSideFps_show() = runTest {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
var count = 0
val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this)
repository.setPrimaryShow(true)
@@ -116,8 +119,10 @@
job.cancel()
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun shouldUpdateSideFps_hide() = runTest {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
repository.setPrimaryShow(true)
var count = 0
val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this)
@@ -128,8 +133,10 @@
job.cancel()
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun sideFpsShowing() = runTest {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
var sideFpsIsShowing = false
val job = underTest.sideFpsShowing.onEach { sideFpsIsShowing = it }.launchIn(this)
repository.setSideFpsShowing(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 2b7221e..6b7d263 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -304,4 +304,34 @@
)
assertThat(authenticationStatus).isNull()
}
+
+ @Test
+ fun onBiometricRunningStateChanged_shouldUpdateIndicatorVisibility() =
+ testScope.runTest {
+ val shouldUpdateIndicatorVisibility by
+ collectLastValue(underTest.shouldUpdateIndicatorVisibility)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ assertThat(shouldUpdateIndicatorVisibility).isFalse()
+
+ invokeOnCallback {
+ it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT)
+ }
+ assertThat(shouldUpdateIndicatorVisibility).isTrue()
+ }
+
+ @Test
+ fun onStrongAuthStateChanged_shouldUpdateIndicatorVisibility() =
+ testScope.runTest {
+ val shouldUpdateIndicatorVisibility by
+ collectLastValue(underTest.shouldUpdateIndicatorVisibility)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ assertThat(shouldUpdateIndicatorVisibility).isFalse()
+
+ invokeOnCallback { it.onStrongAuthStateChanged(0) }
+ assertThat(shouldUpdateIndicatorVisibility).isTrue()
+ }
}
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index 4d95220..fc4bf8a 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.SideFpsLottieViewWrapper
+<com.android.systemui.biometrics.SideFpsIndicatorView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sidefps_animation"
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
index 80f70a0..3064829 100644
--- 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
@@ -37,6 +37,10 @@
val hasSfps: Boolean
get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType
+ /** If UDFPS authentication is available. */
+ val hasUdfps: Boolean
+ get() = hasFingerprint && fingerprintProperties!!.isAnyUdfpsType
+
/** If fingerprint authentication is available (and [faceProperties] is non-null). */
val hasFace: Boolean
get() = faceProperties != null
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0a4378e..cce2018 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -38,7 +38,7 @@
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.media.AudioManager;
import android.metrics.LogMaker;
import android.os.SystemClock;
@@ -74,6 +74,7 @@
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.biometrics.SideFpsUiRequestSource;
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingA11yDelegate;
@@ -486,7 +487,11 @@
mSceneContainerFlags = sceneContainerFlags;
mGlobalSettings = globalSettings;
mSessionTracker = sessionTracker;
- mSideFpsController = sideFpsController;
+ if (SideFpsControllerRefactor.isEnabled()) {
+ mSideFpsController = Optional.empty();
+ } else {
+ mSideFpsController = sideFpsController;
+ }
mFalsingA11yDelegate = falsingA11yDelegate;
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
@@ -569,12 +574,14 @@
mView.clearFocus();
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/**
* Shows and hides the side finger print sensor animation.
*
* @param isVisible sets whether we show or hide the side fps animation
*/
public void updateSideFpsVisibility(boolean isVisible) {
+ SideFpsControllerRefactor.assertInLegacyMode();
if (!mSideFpsController.isPresent()) {
return;
}
@@ -582,7 +589,7 @@
if (isVisible) {
mSideFpsController.get().show(
SideFpsUiRequestSource.PRIMARY_BOUNCER,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD
);
} else {
mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 877afce..5fba761 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -70,6 +70,7 @@
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
@@ -88,6 +89,8 @@
import dagger.Lazy;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -101,7 +104,6 @@
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
import kotlinx.coroutines.CoroutineScope;
/**
@@ -317,7 +319,9 @@
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
- mSideFpsController = mSidefpsControllerFactory.get();
+ if (!SideFpsControllerRefactor.isEnabled()) {
+ mSideFpsController = mSidefpsControllerFactory.get();
+ }
}
mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
@@ -1194,7 +1198,7 @@
* Whether the passed userId has enrolled SFPS.
*/
public boolean isSfpsEnrolled(int userId) {
- if (mSideFpsController == null) {
+ if (mSidefpsProps == null) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 91cee9e..ac99fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -23,9 +23,9 @@
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.display.DisplayManager
import android.hardware.fingerprint.FingerprintManager
@@ -58,6 +58,7 @@
import com.android.keyguard.KeyguardPINView
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -91,7 +92,7 @@
@Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
- @Application private val scope: CoroutineScope,
+ @Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
fpsUnlockTracker: FpsUnlockTracker
) : Dumpable {
@@ -110,7 +111,7 @@
handler,
sensorProps,
{ reason -> onOrientationChanged(reason) },
- BiometricOverlayConstants.REASON_UNKNOWN
+ BiometricRequestConstants.REASON_UNKNOWN
)
@VisibleForTesting val orientationListener = orientationReasonListener.orientationListener
@@ -169,25 +170,27 @@
}
init {
- fpsUnlockTracker.startTracking()
- fingerprintManager?.setSidefpsController(
- object : ISidefpsController.Stub() {
- override fun show(
- sensorId: Int,
- @BiometricOverlayConstants.ShowReason reason: Int
- ) =
- if (reason.isReasonToAutoShow(activityTaskManager)) {
- show(SideFpsUiRequestSource.AUTO_SHOW, reason)
- } else {
- hide(SideFpsUiRequestSource.AUTO_SHOW)
- }
+ if (!SideFpsControllerRefactor.isEnabled) {
+ fpsUnlockTracker.startTracking()
+ fingerprintManager?.setSidefpsController(
+ object : ISidefpsController.Stub() {
+ override fun show(
+ sensorId: Int,
+ @BiometricRequestConstants.RequestReason reason: Int
+ ) =
+ if (reason.isReasonToAutoShow(activityTaskManager)) {
+ show(SideFpsUiRequestSource.AUTO_SHOW, reason)
+ } else {
+ hide(SideFpsUiRequestSource.AUTO_SHOW)
+ }
- override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
- }
- )
- listenForAlternateBouncerVisibility()
+ override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
+ }
+ )
+ listenForAlternateBouncerVisibility()
- dumpManager.registerDumpable(this)
+ dumpManager.registerDumpable(this)
+ }
}
private fun listenForAlternateBouncerVisibility() {
@@ -195,7 +198,7 @@
alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController")
}
- scope.launch {
+ applicationScope.launch {
alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
if (isVisible) {
show(SideFpsUiRequestSource.ALTERNATE_BOUNCER, REASON_AUTH_KEYGUARD)
@@ -209,8 +212,10 @@
/** Shows the side fps overlay if not already shown. */
fun show(
request: SideFpsUiRequestSource,
- @BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN
+ @BiometricRequestConstants.RequestReason
+ reason: Int = BiometricRequestConstants.REASON_UNKNOWN
) {
+ SideFpsControllerRefactor.assertInLegacyMode()
if (!displayStateInteractor.isInRearDisplayMode.value) {
requests.add(request)
mainExecutor.execute {
@@ -229,6 +234,7 @@
/** Hides the fps overlay if shown. */
fun hide(request: SideFpsUiRequestSource) {
+ SideFpsControllerRefactor.assertInLegacyMode()
requests.remove(request)
mainExecutor.execute {
if (requests.isEmpty()) {
@@ -239,6 +245,7 @@
/** Hide the arrow indicator. */
fun hideIndicator() {
+ SideFpsControllerRefactor.assertInLegacyMode()
val lottieAnimationView =
overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
lottieAnimationView?.visibility = INVISIBLE
@@ -246,6 +253,7 @@
/** Show the arrow indicator. */
fun showIndicator() {
+ SideFpsControllerRefactor.assertInLegacyMode()
val lottieAnimationView =
overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
lottieAnimationView?.visibility = VISIBLE
@@ -279,13 +287,13 @@
pw.println("currentRotation=${displayInfo.rotation}")
}
- private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) {
+ private fun onOrientationChanged(@BiometricRequestConstants.RequestReason reason: Int) {
if (overlayView != null) {
createOverlayForDisplay(reason)
}
}
- private fun createOverlayForDisplay(@BiometricOverlayConstants.ShowReason reason: Int) {
+ private fun createOverlayForDisplay(@BiometricRequestConstants.RequestReason reason: Int) {
val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
overlayView = view
val display = context.display!!
@@ -395,7 +403,7 @@
/** Returns [True] when the device has a side fingerprint sensor. */
fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
-@BiometricOverlayConstants.ShowReason
+@BiometricRequestConstants.RequestReason
private fun Int.isReasonToAutoShow(activityTaskManager: ActivityTaskManager): Boolean =
when (this) {
REASON_AUTH_KEYGUARD -> false
@@ -434,7 +442,7 @@
private fun LottieAnimationView.addOverlayDynamicColor(
context: Context,
- @BiometricOverlayConstants.ShowReason reason: Int
+ @BiometricRequestConstants.RequestReason reason: Int
) {
fun update() {
val isKeyguard = reason == REASON_AUTH_KEYGUARD
@@ -501,7 +509,7 @@
handler: Handler,
sensorProps: FingerprintSensorPropertiesInternal,
onOrientationChanged: (reason: Int) -> Unit,
- @BiometricOverlayConstants.ShowReason var reason: Int
+ @BiometricRequestConstants.RequestReason var reason: Int
) {
val orientationListener =
BiometricDisplayListener(
@@ -516,7 +524,7 @@
/**
* The source of a request to show the side fps visual indicator. This is distinct from
- * [BiometricOverlayConstants] which corrresponds with the reason fingerprint authentication is
+ * [BiometricRequestConstants] which corresponds with the reason fingerprint authentication is
* requested.
*/
enum class SideFpsUiRequestSource {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
rename to packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
index e98f6db..d5e25ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
@@ -19,6 +19,6 @@
import android.util.AttributeSet
import com.android.systemui.util.wrapper.LottieViewWrapper
-class SideFpsLottieViewWrapper
+class SideFpsIndicatorView
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index bb6ef41..65668b5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -19,10 +19,10 @@
import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR;
import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -106,6 +106,8 @@
import dagger.Lazy;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -115,8 +117,6 @@
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
-
import kotlinx.coroutines.ExperimentalCoroutinesApi;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 452cd6a..dae6d08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -21,13 +21,13 @@
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.hardware.biometrics.BiometricRequestConstants.RequestReason
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.os.Build
import android.os.RemoteException
@@ -96,7 +96,7 @@
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
val requestId: Long,
- @ShowReason val requestReason: Int,
+ @RequestReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
private val activityLaunchAnimator: ActivityLaunchAnimator,
@@ -461,7 +461,7 @@
}
}
-@ShowReason
+@RequestReason
private fun Int.isImportantForAccessibility() =
this == REASON_ENROLL_FIND_SENSOR ||
this == REASON_ENROLL_ENROLLING ||
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
index e3dbcb5..88b9e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
@@ -18,13 +18,13 @@
import android.content.Context
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.util.Log
import android.view.LayoutInflater
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 72fcfe7..8ae6f87 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -18,8 +18,11 @@
import android.content.res.Resources
import com.android.internal.R
+import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepositoryImpl
import com.android.systemui.biometrics.data.repository.DisplayStateRepository
import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
@@ -33,11 +36,14 @@
import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import java.util.concurrent.Executor
import javax.inject.Qualifier
@@ -46,6 +52,11 @@
interface BiometricsModule {
@Binds
+ @IntoMap
+ @ClassKey(SideFpsOverlayViewBinder::class)
+ fun bindsSideFpsOverlayViewBinder(viewBinder: SideFpsOverlayViewBinder): CoreStartable
+
+ @Binds
@SysUISingleton
fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository
@@ -57,6 +68,10 @@
@Binds
@SysUISingleton
+ fun biometricStatusRepository(impl: BiometricStatusRepositoryImpl): BiometricStatusRepository
+
+ @Binds
+ @SysUISingleton
fun fingerprintRepository(
impl: FingerprintPropertyRepositoryImpl
): FingerprintPropertyRepository
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
new file mode 100644
index 0000000..ad2136a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.data.repository
+
+import android.hardware.biometrics.AuthenticationStateListener
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.shareIn
+
+/** A repository for the state of biometric authentication. */
+interface BiometricStatusRepository {
+ /**
+ * The logical reason for the current fingerprint auth operation if one is on-going, otherwise
+ * [NotRunning].
+ */
+ val fingerprintAuthenticationReason: Flow<AuthenticationReason>
+}
+
+@SysUISingleton
+class BiometricStatusRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val biometricManager: BiometricManager?
+) : BiometricStatusRepository {
+
+ override val fingerprintAuthenticationReason: Flow<AuthenticationReason> =
+ conflatedCallbackFlow {
+ val updateFingerprintAuthenticateReason = { reason: AuthenticationReason ->
+ trySendWithFailureLogging(
+ reason,
+ TAG,
+ "Error sending fingerprintAuthenticateReason reason"
+ )
+ }
+
+ val authenticationStateListener =
+ object : AuthenticationStateListener.Stub() {
+ override fun onAuthenticationStarted(requestReason: Int) {
+ val authenticationReason =
+ when (requestReason) {
+ REASON_AUTH_BP ->
+ AuthenticationReason.BiometricPromptAuthentication
+ REASON_AUTH_KEYGUARD ->
+ AuthenticationReason.DeviceEntryAuthentication
+ REASON_AUTH_OTHER -> AuthenticationReason.OtherAuthentication
+ REASON_AUTH_SETTINGS ->
+ AuthenticationReason.SettingsAuthentication(
+ SettingsOperations.OTHER
+ )
+ REASON_ENROLL_ENROLLING ->
+ AuthenticationReason.SettingsAuthentication(
+ SettingsOperations.ENROLL_ENROLLING
+ )
+ REASON_ENROLL_FIND_SENSOR ->
+ AuthenticationReason.SettingsAuthentication(
+ SettingsOperations.ENROLL_FIND_SENSOR
+ )
+ else -> AuthenticationReason.Unknown
+ }
+ updateFingerprintAuthenticateReason(authenticationReason)
+ }
+
+ override fun onAuthenticationStopped() {
+ updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning)
+ }
+ }
+
+ updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning)
+ biometricManager?.registerAuthenticationStateListener(authenticationStateListener)
+ awaitClose {
+ biometricManager?.unregisterAuthenticationStateListener(
+ authenticationStateListener
+ )
+ }
+ }
+ .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
+
+ companion object {
+ private const val TAG = "BiometricStatusRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index b9b2fd8..ec3fd9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -15,6 +15,8 @@
*/
package com.android.systemui.biometrics.domain
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -38,6 +40,12 @@
@Binds
@SysUISingleton
+ fun providesBiometricStatusInteractor(
+ impl: BiometricStatusInteractorImpl
+ ): BiometricStatusInteractor
+
+ @Binds
+ @SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
new file mode 100644
index 0000000..55a2d3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.domain.interactor
+
+import android.app.ActivityTaskManager
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for interacting with biometric authentication state. */
+interface BiometricStatusInteractor {
+ /**
+ * The logical reason for the current side fingerprint sensor auth operation if one is on-going,
+ * filtered for when the overlay should be shown, otherwise [NotRunning].
+ */
+ val sfpsAuthenticationReason: Flow<AuthenticationReason>
+}
+
+class BiometricStatusInteractorImpl
+@Inject
+constructor(
+ private val activityTaskManager: ActivityTaskManager,
+ biometricStatusRepository: BiometricStatusRepository,
+) : BiometricStatusInteractor {
+
+ override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
+ biometricStatusRepository.fingerprintAuthenticationReason.map { reason: AuthenticationReason
+ ->
+ if (reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) {
+ reason
+ } else {
+ AuthenticationReason.NotRunning
+ }
+ }
+
+ companion object {
+ private const val TAG = "BiometricStatusInteractor"
+ }
+}
+
+/** True if the sfps overlay should always be updated for this request source, false otherwise. */
+private fun AuthenticationReason.isReasonToAlwaysUpdateSfpsOverlay(
+ activityTaskManager: ActivityTaskManager
+): Boolean =
+ when (this) {
+ AuthenticationReason.DeviceEntryAuthentication -> false
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER) ->
+ when (activityTaskManager.topClass()) {
+ // TODO(b/186176653): exclude fingerprint overlays from this list view
+ "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false
+ else -> true
+ }
+ else -> true
+ }
+
+internal fun ActivityTaskManager.topClass(): String =
+ getTasks(1).firstOrNull()?.topActivity?.className ?: ""
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index f513799..f4231ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -141,7 +141,6 @@
}
}
}
-
SideFpsSensorLocation(
left = sensorLeft,
top = sensorTop,
@@ -149,7 +148,15 @@
isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation
)
}
- .distinctUntilChanged()
+ .distinctUntilChanged(
+ areEquivalent = { old: SideFpsSensorLocation, new: SideFpsSensorLocation ->
+ old.left == new.left &&
+ old.top == new.top &&
+ old.length == new.length &&
+ old.isSensorVerticalInDefaultOrientation ==
+ new.isSensorVerticalInDefaultOrientation
+ }
+ )
.onEach {
logger.sensorLocationStateChanged(
it.left,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt
new file mode 100644
index 0000000..899b07e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+import com.android.systemui.shared.Flags
+
+/** Helper for reading or using the sidefps controller refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object SideFpsControllerRefactor {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.sidefpsControllerRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt
new file mode 100644
index 0000000..0c3debbe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt
@@ -0,0 +1,48 @@
+/*
+ * 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
+
+/**
+ * The logical reason for a fingerprint auth operation if one is on-going, otherwise [NotRunning].
+ */
+sealed interface AuthenticationReason {
+ /** Device entry requested authentication */
+ data object DeviceEntryAuthentication : AuthenticationReason
+
+ /** Settings requested authentication */
+ data class SettingsAuthentication(val settingsOperation: SettingsOperations) :
+ AuthenticationReason
+
+ /** App requested authentication */
+ data object BiometricPromptAuthentication : AuthenticationReason
+
+ /** Authentication requested for other reason */
+ data object OtherAuthentication : AuthenticationReason
+
+ /** Authentication requested for unknown reason */
+ data object Unknown : AuthenticationReason
+
+ /** Authentication is not running */
+ data object NotRunning : AuthenticationReason
+
+ /** Settings operations that request biometric authentication */
+ enum class SettingsOperations {
+ ENROLL_ENROLLING,
+ ENROLL_FIND_SENSOR,
+ OTHER
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
index e98f6db..0b30055 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
@@ -13,12 +13,10 @@
* 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.util.AttributeSet
-import com.android.systemui.util.wrapper.LottieViewWrapper
+package com.android.systemui.biometrics.shared.model
-class SideFpsLottieViewWrapper
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
+import com.airbnb.lottie.model.KeyPath
+
+/** Represents properties of a LottieAnimationView callback */
+data class LottieCallback(val keypath: KeyPath, val color: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 32d9067..90e4a38 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -376,6 +376,22 @@
}
}
+ // Talkback directional guidance
+ backgroundView.setOnHoverListener { _, event ->
+ launch {
+ viewModel.onAnnounceAccessibilityHint(
+ event,
+ accessibilityManager.isTouchExplorationEnabled
+ )
+ }
+ false
+ }
+ launch {
+ viewModel.accessibilityHint.collect { message ->
+ if (message.isNotBlank()) view.announceForAccessibility(message)
+ }
+ }
+
// Play haptics
launch {
viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
new file mode 100644
index 0000000..a8c9446
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -0,0 +1,224 @@
+/*
+ * 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.ui.binder
+
+import android.content.Context
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieProperty
+import com.android.app.animation.Interpolators
+import com.android.keyguard.KeyguardPINView
+import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.FpsUnlockTracker
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+/** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
+@SysUISingleton
+class SideFpsOverlayViewBinder
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Application private val applicationContext: Context,
+ private val biometricStatusInteractor: BiometricStatusInteractor,
+ private val displayStateInteractor: DisplayStateInteractor,
+ private val deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
+ private val fpsUnlockTracker: FpsUnlockTracker,
+ private val layoutInflater: LayoutInflater,
+ private val sideFpsProgressBarViewModel: SideFpsProgressBarViewModel,
+ private val sfpsSensorInteractor: SideFpsSensorInteractor,
+ private val windowManager: WindowManager
+) : CoreStartable {
+
+ override fun start() {
+ if (!SideFpsControllerRefactor.isEnabled) {
+ return
+ }
+ applicationScope
+ .launch {
+ combine(
+ biometricStatusInteractor.sfpsAuthenticationReason,
+ deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
+ sideFpsProgressBarViewModel.isVisible,
+ ::Triple
+ )
+ .sample(displayStateInteractor.isInRearDisplayMode, ::Pair)
+ .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
+ val (
+ systemServerAuthReason,
+ showIndicatorForDeviceEntry,
+ progressBarIsVisible) =
+ combinedFlows
+ if (!isInRearDisplayMode) {
+ if (progressBarIsVisible) {
+ hide()
+ } else if (systemServerAuthReason != NotRunning) {
+ show()
+ } else if (showIndicatorForDeviceEntry) {
+ show()
+ } else {
+ hide()
+ }
+ }
+ }
+ }
+ .invokeOnCompletion { fpsUnlockTracker.stopTracking() }
+ }
+
+ private var overlayView: View? = null
+ private var lottie: LottieAnimationView? = null
+
+ /** Show the side fingerprint sensor indicator */
+ private fun show() {
+ overlayView?.let {
+ if (it.isAttachedToWindow) {
+ lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
+ lottie?.pauseAnimation()
+ windowManager.removeView(it)
+ }
+ }
+
+ overlayView = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+ val overlayViewModel =
+ SideFpsOverlayViewModel(
+ applicationContext,
+ biometricStatusInteractor,
+ deviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor,
+ sfpsSensorInteractor,
+ sideFpsProgressBarViewModel
+ )
+ bind(overlayView!!, overlayViewModel, fpsUnlockTracker, windowManager)
+ overlayView!!.visibility = View.INVISIBLE
+ windowManager.addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+ }
+
+ /** Hide the side fingerprint sensor indicator */
+ private fun hide() {
+ if (overlayView != null) {
+ windowManager.removeView(overlayView)
+ overlayView = null
+ }
+ }
+
+ companion object {
+ private const val TAG = "SideFpsOverlayViewBinder"
+
+ /** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */
+ fun bind(
+ overlayView: View,
+ viewModel: SideFpsOverlayViewModel,
+ fpsUnlockTracker: FpsUnlockTracker,
+ windowManager: WindowManager
+ ) {
+ overlayView.repeatWhenAttached {
+ fpsUnlockTracker.startTracking()
+
+ val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
+ lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition ->
+ viewModel.setLottieBounds(composition.bounds)
+ overlayView.visibility = View.VISIBLE
+ }
+ it.alpha = 0f
+ val overlayShowAnimator =
+ it.animate()
+ .alpha(1f)
+ .setDuration(KeyguardPINView.ANIMATION_DURATION)
+ .setInterpolator(Interpolators.ALPHA_IN)
+
+ overlayShowAnimator.start()
+
+ it.setAccessibilityDelegate(
+ object : View.AccessibilityDelegate() {
+ override fun dispatchPopulateAccessibilityEvent(
+ host: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ return if (
+ event.getEventType() ===
+ android.view.accessibility.AccessibilityEvent
+ .TYPE_WINDOW_STATE_CHANGED
+ ) {
+ true
+ } else {
+ super.dispatchPopulateAccessibilityEvent(host, event)
+ }
+ }
+ }
+ )
+
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.lottieCallbacks.collect { callbacks ->
+ lottie.addOverlayDynamicColor(callbacks)
+ }
+ }
+
+ launch {
+ viewModel.overlayViewParams.collect { params ->
+ windowManager.updateViewLayout(it, params)
+ lottie.resumeAnimation()
+ }
+ }
+
+ launch {
+ viewModel.overlayViewProperties.collect { properties ->
+ it.rotation = properties.overlayViewRotation
+ lottie.setAnimation(properties.indicatorAsset)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun LottieAnimationView.addOverlayDynamicColor(colorCallbacks: List<LottieCallback>) {
+ addLottieOnCompositionLoadedListener {
+ for (callback in colorCallbacks) {
+ addValueCallback(callback.keypath, LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(callback.color, PorterDuff.Mode.SRC_ATOP)
+ }
+ }
+ resumeAnimation()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 647aaf3..6d0a58e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -21,9 +21,12 @@
import android.util.Log
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import com.android.systemui.Flags.bpTalkback
+import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -35,7 +38,9 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -49,7 +54,9 @@
constructor(
displayStateInteractor: DisplayStateInteractor,
promptSelectorInteractor: PromptSelectorInteractor,
- @Application context: Context,
+ @Application private val context: Context,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ private val udfpsUtils: UdfpsUtils
) {
/** The set of modalities available for this prompt */
val modalities: Flow<BiometricModalities> =
@@ -69,6 +76,11 @@
val faceIconHeight: Int =
context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
+ private val _accessibilityHint = MutableSharedFlow<String>()
+
+ /** Hint for talkback directional guidance */
+ val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow()
+
private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)
/** If the user is currently authenticating (i.e. at least one biometric is scanning). */
@@ -516,6 +528,40 @@
return false
}
+ /** Sets the message used for UDFPS directional guidance */
+ suspend fun onAnnounceAccessibilityHint(
+ event: MotionEvent,
+ touchExplorationEnabled: Boolean,
+ ): Boolean {
+ if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) {
+ // TODO(b/315184924): Remove uses of UdfpsUtils
+ val scaledTouch =
+ udfpsUtils.getTouchInNativeCoordinates(
+ event.getPointerId(0),
+ event,
+ udfpsOverlayInteractor.udfpsOverlayParams.value
+ )
+ if (
+ !udfpsUtils.isWithinSensorArea(
+ event.getPointerId(0),
+ event,
+ udfpsOverlayInteractor.udfpsOverlayParams.value
+ )
+ ) {
+ _accessibilityHint.emit(
+ udfpsUtils.onTouchOutsideOfSensorArea(
+ touchExplorationEnabled,
+ context,
+ scaledTouch.x,
+ scaledTouch.y,
+ udfpsOverlayInteractor.udfpsOverlayParams.value
+ )
+ )
+ }
+ }
+ return false
+ }
+
/**
* Switch to the credential view.
*
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
new file mode 100644
index 0000000..ce72603
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.Point
+import android.graphics.Rect
+import android.view.Gravity
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import com.airbnb.lottie.model.KeyPath
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+
+/** Models UI of the side fingerprint sensor indicator view. */
+class SideFpsOverlayViewModel
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ biometricStatusInteractor: BiometricStatusInteractor,
+ deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor: DisplayStateInteractor,
+ sfpsSensorInteractor: SideFpsSensorInteractor,
+ sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+) {
+ /** Contains properties of the side fingerprint sensor indicator */
+ data class OverlayViewProperties(
+ /** The raw asset for the indicator animation */
+ val indicatorAsset: Int,
+ /** Rotation of the overlayView */
+ val overlayViewRotation: Float,
+ )
+
+ private val _lottieBounds: MutableStateFlow<Rect?> = MutableStateFlow(null)
+
+ /** Used for setting lottie bounds once the composition has loaded. */
+ fun setLottieBounds(bounds: Rect) {
+ _lottieBounds.value = bounds
+ }
+
+ private val displayRotation = displayStateInteractor.currentRotation
+ private val sensorLocation = sfpsSensorInteractor.sensorLocation
+
+ /** Default LayoutParams for the overlayView */
+ val defaultOverlayViewParams: WindowManager.LayoutParams
+ get() =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+ PixelFormat.TRANSLUCENT
+ )
+ .apply {
+ title = TAG
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = PRIVATE_FLAG_TRUSTED_OVERLAY or PRIVATE_FLAG_NO_MOVE_ANIMATION
+ }
+
+ private val indicatorAsset: Flow<Int> =
+ combine(displayRotation, sensorLocation) { rotation: DisplayRotation, sensorLocation ->
+ val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation
+ val newAsset: Int =
+ when (rotation) {
+ DisplayRotation.ROTATION_0 ->
+ if (yAligned) {
+ R.raw.sfps_pulse
+ } else {
+ R.raw.sfps_pulse_landscape
+ }
+ DisplayRotation.ROTATION_180 ->
+ if (yAligned) {
+ R.raw.sfps_pulse
+ } else {
+ R.raw.sfps_pulse_landscape
+ }
+ else ->
+ if (yAligned) {
+ R.raw.sfps_pulse_landscape
+ } else {
+ R.raw.sfps_pulse
+ }
+ }
+ newAsset
+ }
+ .distinctUntilChanged()
+
+ private val overlayViewRotation: Flow<Float> =
+ combine(
+ displayRotation,
+ sensorLocation,
+ ) { rotation: DisplayRotation, sensorLocation ->
+ val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation
+ when (rotation) {
+ DisplayRotation.ROTATION_90 -> if (yAligned) 0f else 180f
+ DisplayRotation.ROTATION_180 -> 180f
+ DisplayRotation.ROTATION_270 -> if (yAligned) 180f else 0f
+ else -> 0f
+ }
+ }
+ .distinctUntilChanged()
+
+ /** Contains properties (animation asset and view rotation) for overlayView */
+ val overlayViewProperties: Flow<OverlayViewProperties> =
+ combine(indicatorAsset, overlayViewRotation) { asset: Int, rotation: Float ->
+ OverlayViewProperties(asset, rotation)
+ }
+
+ /** LayoutParams for placement of overlayView (the side fingerprint sensor indicator view) */
+ val overlayViewParams: Flow<WindowManager.LayoutParams> =
+ combine(
+ _lottieBounds,
+ sensorLocation,
+ displayRotation,
+ ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation
+ ->
+ val topLeft = Point(sensorLocation.left, sensorLocation.top)
+
+ if (sensorLocation.isSensorVerticalInDefaultOrientation) {
+ if (displayRotation == DisplayRotation.ROTATION_0) {
+ topLeft.x -= bounds!!.width()
+ } else if (displayRotation == DisplayRotation.ROTATION_270) {
+ topLeft.y -= bounds!!.height()
+ }
+ } else {
+ if (displayRotation == DisplayRotation.ROTATION_180) {
+ topLeft.y -= bounds!!.height()
+ } else if (displayRotation == DisplayRotation.ROTATION_270) {
+ topLeft.x -= bounds!!.width()
+ }
+ }
+ defaultOverlayViewParams.apply {
+ x = topLeft.x
+ y = topLeft.y
+ }
+ }
+
+ /** List of LottieCallbacks use for adding dynamic color to the overlayView */
+ val lottieCallbacks: Flow<List<LottieCallback>> =
+ combine(
+ biometricStatusInteractor.sfpsAuthenticationReason,
+ deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry.distinctUntilChanged(),
+ sideFpsProgressBarViewModel.isVisible,
+ ) { reason: AuthenticationReason, showIndicatorForDeviceEntry: Boolean, progressBarIsVisible
+ ->
+ val callbacks = mutableListOf<LottieCallback>()
+ if (showIndicatorForDeviceEntry) {
+ val indicatorColor =
+ com.android.settingslib.Utils.getColorAttrDefaultColor(
+ applicationContext,
+ com.android.internal.R.attr.materialColorPrimaryFixed
+ )
+ val outerRimColor =
+ com.android.settingslib.Utils.getColorAttrDefaultColor(
+ applicationContext,
+ com.android.internal.R.attr.materialColorPrimaryFixedDim
+ )
+ val chevronFill =
+ com.android.settingslib.Utils.getColorAttrDefaultColor(
+ applicationContext,
+ com.android.internal.R.attr.materialColorOnPrimaryFixed
+ )
+ callbacks.add(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
+ callbacks.add(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
+ callbacks.add(LottieCallback(KeyPath(".black", "**"), chevronFill))
+ } else {
+ if (!isDarkMode(applicationContext)) {
+ callbacks.add(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
+ }
+ for (key in listOf(".blue600", ".blue400")) {
+ callbacks.add(
+ LottieCallback(
+ KeyPath(key, "**"),
+ applicationContext.getColor(
+ com.android.settingslib.color.R.color.settingslib_color_blue400
+ ),
+ )
+ )
+ }
+ }
+ callbacks
+ }
+
+ companion object {
+ private const val TAG = "SideFpsOverlayViewModel"
+ }
+}
+
+private fun isDarkMode(context: Context): Boolean {
+ val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ return darkMode == Configuration.UI_MODE_NIGHT_YES
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index 1e0e16c..c2a1d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -18,6 +18,7 @@
import android.os.Build
import android.util.Log
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.dagger.SysUISingleton
@@ -121,6 +122,7 @@
fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
fun setSideFpsShowing(isShowing: Boolean)
}
@@ -261,7 +263,9 @@
_isBackButtonEnabled.value = isBackButtonEnabled
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
override fun setSideFpsShowing(isShowing: Boolean) {
+ SideFpsControllerRefactor.assertInLegacyMode()
_sideFpsShowing.value = isShowing
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index aa7758f..621ca5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.DejankUtils
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -116,9 +117,11 @@
/** Allow for interaction when just about fully visible */
val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 }
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
val sideFpsShowing: Flow<Boolean> = repository.sideFpsShowing
private var currentUserActiveUnlockRunning = false
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/** This callback needs to be a class field so it does not get garbage collected. */
val keyguardUpdateMonitorCallback =
object : KeyguardUpdateMonitorCallback() {
@@ -135,7 +138,10 @@
}
init {
- keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ if (!SideFpsControllerRefactor.isEnabled) {
+ keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ }
applicationScope.launch {
trustRepository.isCurrentUserActiveUnlockRunning.collect {
currentUserActiveUnlockRunning = it
@@ -333,8 +339,10 @@
repository.setPrimaryStartDisappearAnimation(runnable)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/** Determine whether to show the side fps animation. */
fun updateSideFpsVisibility() {
+ SideFpsControllerRefactor.assertInLegacyMode()
val sfpsEnabled: Boolean =
context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
val fpsDetectionRunning: Boolean = keyguardUpdateMonitor.isFingerprintDetectionRunning
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index ac3d4b6..5dcd661 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -27,6 +27,7 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityView
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.bouncer.ui.BouncerViewDelegate
@@ -233,15 +234,21 @@
.collect { view.systemUiVisibility = it }
}
- launch {
- viewModel.shouldUpdateSideFps.collect {
- viewModel.updateSideFpsVisibility()
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ if (!SideFpsControllerRefactor.isEnabled) {
+ launch {
+ viewModel.shouldUpdateSideFps.collect {
+ viewModel.updateSideFpsVisibility()
+ }
}
}
- launch {
- viewModel.sideFpsShowing.collect {
- securityContainerController.updateSideFpsVisibility(it)
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ if (!SideFpsControllerRefactor.isEnabled) {
+ launch {
+ viewModel.sideFpsShowing.collect {
+ securityContainerController.updateSideFpsVisibility(it)
+ }
}
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 649ae2f..1c9d1f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.view.View
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.bouncer.ui.BouncerView
@@ -61,9 +62,11 @@
/** Observe whether keyguard is authenticated already. */
val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticatedBiometrics
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/** Observe whether the side fps is showing. */
val sideFpsShowing: Flow<Boolean> = interactor.sideFpsShowing
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/** Observe whether we should update fps is showing. */
val shouldUpdateSideFps: Flow<Unit> =
merge(
@@ -87,7 +90,9 @@
interactor.onMessageShown()
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
fun updateSideFpsVisibility() {
+ SideFpsControllerRefactor.assertInLegacyMode()
interactor.updateSideFpsVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 96386f3..9a13558d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -41,6 +41,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -61,6 +62,9 @@
/** Provide the current status of fingerprint authentication. */
val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+
+ /** Indicates whether to update the side fingerprint sensor indicator visibility. */
+ val shouldUpdateIndicatorVisibility: Flow<Boolean>
}
/**
@@ -256,6 +260,37 @@
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
+ override val shouldUpdateIndicatorVisibility: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val sendShouldUpdateIndicatorVisibility =
+ { shouldUpdateIndicatorVisibility: Boolean ->
+ trySendWithFailureLogging(
+ shouldUpdateIndicatorVisibility,
+ TAG,
+ "Error sending shouldUpdateIndicatorVisibility " +
+ "$shouldUpdateIndicatorVisibility"
+ )
+ }
+
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricRunningStateChanged(
+ running: Boolean,
+ biometricSourceType: BiometricSourceType?
+ ) {
+ sendShouldUpdateIndicatorVisibility(true)
+ }
+ override fun onStrongAuthStateChanged(userId: Int) {
+ sendShouldUpdateIndicatorVisibility(true)
+ }
+ }
+ sendShouldUpdateIndicatorVisibility(false)
+ keyguardUpdateMonitor.registerCallback(callback)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .flowOn(mainDispatcher)
+ .shareIn(scope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
new file mode 100644
index 0000000..de15fd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.Context
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Encapsulates business logic for device entry events that impact the side fingerprint sensor
+ * overlay.
+ */
+@SysUISingleton
+class DeviceEntrySideFpsOverlayInteractor
+@Inject
+constructor(
+ @Application private val context: Context,
+ deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+
+ init {
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+ }
+
+ private val showIndicatorForPrimaryBouncer: Flow<Boolean> =
+ merge(
+ primaryBouncerInteractor.isShowing,
+ primaryBouncerInteractor.startingToHide,
+ primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(),
+ deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }
+ )
+ .map { shouldShowIndicatorForPrimaryBouncer() }
+
+ private val showIndicatorForAlternateBouncer: Flow<Boolean> =
+ alternateBouncerInteractor.isVisible
+
+ /**
+ * Indicates whether the primary or alternate bouncers request showing the side fingerprint
+ * sensor indicator.
+ */
+ val showIndicatorForDeviceEntry: Flow<Boolean> =
+ combine(showIndicatorForPrimaryBouncer, showIndicatorForAlternateBouncer) {
+ showForPrimaryBouncer,
+ showForAlternateBouncer ->
+ showForPrimaryBouncer || showForAlternateBouncer
+ }
+
+ private fun shouldShowIndicatorForPrimaryBouncer(): Boolean {
+ val sfpsEnabled: Boolean =
+ context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
+ val sfpsDetectionRunning = keyguardUpdateMonitor.isFingerprintDetectionRunning
+ val isUnlockingWithFpAllowed = keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed
+ return primaryBouncerInteractor.isBouncerShowing() &&
+ sfpsEnabled &&
+ sfpsDetectionRunning &&
+ isUnlockingWithFpAllowed &&
+ !primaryBouncerInteractor.isAnimatingAway()
+ }
+
+ companion object {
+ private const val TAG = "DeviceEntrySideFpsOverlayInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index 560e4ad..9a6dca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -21,6 +21,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.Flags
import com.android.systemui.biometrics.SideFpsController
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
@@ -46,6 +47,7 @@
private val viewModel: SideFpsProgressBarViewModel,
private val view: SideFpsProgressBar,
@Application private val applicationScope: CoroutineScope,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
private val sfpsController: dagger.Lazy<SideFpsController>,
private val logger: SideFpsLogger,
private val commandRegistry: CommandRegistry,
@@ -109,12 +111,14 @@
view.updateView(visible, location, length, thickness, rotation)
// We have to hide the SFPS indicator as the progress bar will
// be shown at the same location
- if (visible) {
- logger.hidingSfpsIndicator()
- sfpsController.get().hideIndicator()
- } else if (fpDetectRunning) {
- logger.showingSfpsIndicator()
- sfpsController.get().showIndicator()
+ if (!SideFpsControllerRefactor.isEnabled) {
+ if (visible) {
+ logger.hidingSfpsIndicator()
+ sfpsController.get().hideIndicator()
+ } else if (fpDetectRunning) {
+ logger.showingSfpsIndicator()
+ sfpsController.get().showIndicator()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 2d0712c..1dbf1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Point
+import androidx.annotation.VisibleForTesting
import androidx.core.animation.doOnEnd
import com.android.systemui.Flags
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -213,4 +214,9 @@
}
}
}
+
+ @VisibleForTesting
+ fun setVisible(isVisible: Boolean) {
+ _visible.value = isVisible
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index ea20d29..91219f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -47,12 +47,14 @@
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -101,6 +103,12 @@
lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock
lateinit var vibrator: VibratorHelper
+ @Mock
+ lateinit var udfpsUtils: UdfpsUtils
+ @Mock
+ lateinit var authController: AuthController
+ @Mock
+ lateinit var selectedUserInteractor: SelectedUserInteractor
private val testScope = TestScope(StandardTestDispatcher())
private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -123,6 +131,7 @@
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
+ private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
@@ -140,6 +149,12 @@
displayStateRepository,
displayRepository,
)
+ udfpsOverlayInteractor =
+ UdfpsOverlayInteractor(
+ authController,
+ selectedUserInteractor,
+ testScope.backgroundScope,
+ )
}
@After
@@ -532,6 +547,8 @@
displayStateInteractor,
promptSelectorInteractor,
context,
+ udfpsOverlayInteractor,
+ udfpsUtils
),
{ credentialViewModel },
Handler(TestableLooper.get(this).looper),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
new file mode 100644
index 0000000..27d93eb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.data.repository
+
+import android.hardware.biometrics.AuthenticationStateListener
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BiometricStatusRepositoryTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var biometricManager: BiometricManager
+
+ private lateinit var underTest: BiometricStatusRepository
+
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ @Before
+ fun setUp() {
+ underTest = BiometricStatusRepositoryImpl(testScope.backgroundScope, biometricManager)
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenBiometricPromptAuthenticationStarted() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ listener.onAuthenticationStarted(REASON_AUTH_BP)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(AuthenticationReason.BiometricPromptAuthentication)
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenDeviceEntryAuthenticationStarted() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ listener.onAuthenticationStarted(REASON_AUTH_KEYGUARD)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(AuthenticationReason.DeviceEntryAuthentication)
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenOtherAuthenticationStarted() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ listener.onAuthenticationStarted(REASON_AUTH_OTHER)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(AuthenticationReason.OtherAuthentication)
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenSettingsAuthenticationStarted() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ listener.onAuthenticationStarted(REASON_AUTH_SETTINGS)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER))
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenEnrollmentAuthenticationStarted() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ listener.onAuthenticationStarted(REASON_ENROLL_FIND_SENSOR)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(
+ AuthenticationReason.SettingsAuthentication(
+ SettingsOperations.ENROLL_FIND_SENSOR
+ )
+ )
+
+ listener.onAuthenticationStarted(REASON_ENROLL_ENROLLING)
+ assertThat(fingerprintAuthenticationReason)
+ .isEqualTo(
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+ )
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() =
+ testScope.runTest {
+ val fingerprintAuthenticationReason by
+ collectLastValue(underTest.fingerprintAuthenticationReason)
+ runCurrent()
+
+ val listener = biometricManager.captureListener()
+
+ listener.onAuthenticationStarted(REASON_AUTH_BP)
+ listener.onAuthenticationStopped()
+ assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+ }
+}
+
+private fun BiometricManager.captureListener() =
+ withArgCaptor<AuthenticationStateListener> {
+ verify(this@captureListener).registerAuthenticationStateListener(capture())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
new file mode 100644
index 0000000..6978923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.domain.interactor
+
+import android.app.ActivityManager
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BiometricStatusInteractorImplTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
+
+ private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
+ private lateinit var underTest: BiometricStatusInteractorImpl
+
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ @Before
+ fun setup() {
+ biometricStatusRepository = FakeBiometricStatusRepository()
+ underTest = BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+ }
+
+ @Test
+ fun updatesSfpsAuthenticationReason_whenBiometricPromptAuthenticationStarted() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ assertThat(sfpsAuthenticationReason)
+ .isEqualTo(AuthenticationReason.BiometricPromptAuthentication)
+ }
+
+ @Test
+ fun doesNotUpdateSfpsAuthenticationReason_whenDeviceEntryAuthenticationStarted() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.DeviceEntryAuthentication
+ )
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+ }
+
+ @Test
+ fun updatesSfpsAuthenticationReason_whenOtherAuthenticationStarted() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.OtherAuthentication
+ )
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.OtherAuthentication)
+ }
+
+ @Test
+ fun doesNotUpdateSfpsAuthenticationReason_whenOtherSettingsAuthenticationStarted() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ `when`(activityTaskManager.getTasks(Mockito.anyInt()))
+ .thenReturn(listOf(fpSettingsTask()))
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER)
+ )
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+ }
+
+ @Test
+ fun updatesSfpsAuthenticationReason_whenEnrollmentAuthenticationStarted() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_FIND_SENSOR)
+ )
+ assertThat(sfpsAuthenticationReason)
+ .isEqualTo(
+ AuthenticationReason.SettingsAuthentication(
+ SettingsOperations.ENROLL_FIND_SENSOR
+ )
+ )
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+ )
+ assertThat(sfpsAuthenticationReason)
+ .isEqualTo(
+ AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+ )
+ }
+
+ @Test
+ fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() =
+ testScope.runTest {
+ val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+ runCurrent()
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+ }
+}
+
+private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings")
+
+private fun settingsTask(cls: String) =
+ ActivityManager.RunningTaskInfo().apply {
+ topActivity = ComponentName.createRelative("com.android.settings", cls)
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
index 22e3e7f..74c4313 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.shared.model
+import android.hardware.fingerprint.FingerprintSensorProperties
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -35,6 +36,46 @@
}
@Test
+ fun hasUdfps() {
+ with(
+ BiometricModalities(
+ fingerprintProperties = fingerprintSensorPropertiesInternal(
+ sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ ).first(),
+ )
+ ) {
+ assertThat(isEmpty).isFalse()
+ assertThat(hasUdfps).isTrue()
+ assertThat(hasSfps).isFalse()
+ assertThat(hasFace).isFalse()
+ assertThat(hasFaceOnly).isFalse()
+ assertThat(hasFingerprint).isTrue()
+ assertThat(hasFingerprintOnly).isTrue()
+ assertThat(hasFaceAndFingerprint).isFalse()
+ }
+ }
+
+ @Test
+ fun hasSfps() {
+ with(
+ BiometricModalities(
+ fingerprintProperties = fingerprintSensorPropertiesInternal(
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ ).first(),
+ )
+ ) {
+ assertThat(isEmpty).isFalse()
+ assertThat(hasUdfps).isFalse()
+ assertThat(hasSfps).isTrue()
+ assertThat(hasFace).isFalse()
+ assertThat(hasFaceOnly).isFalse()
+ assertThat(hasFingerprint).isTrue()
+ assertThat(hasFingerprintOnly).isTrue()
+ assertThat(hasFaceAndFingerprint).isFalse()
+ }
+ }
+
+ @Test
fun fingerprintOnly() {
with(
BiometricModalities(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
new file mode 100644
index 0000000..b4ae00d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -0,0 +1,484 @@
+/*
+ * 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.ui.binder
+
+import android.animation.Animator
+import android.app.ActivityTaskManager
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.testing.TestableLooper
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.FpsUnlockTracker
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class SideFpsOverlayViewBinderTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
+ @Mock private lateinit var displayManager: DisplayManager
+ @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock
+ private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+ @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var layoutInflater: LayoutInflater
+ @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var sideFpsView: View
+ @Mock private lateinit var windowManager: WindowManager
+
+ private val contextDisplayInfo = DisplayInfo()
+
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+ private val biometricStatusRepository = FakeBiometricStatusRepository()
+ private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ private val displayRepository = FakeDisplayRepository()
+ private val displayStateRepository = FakeDisplayStateRepository()
+ private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+
+ private lateinit var underTest: SideFpsOverlayViewBinder
+
+ private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+ private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor
+ private lateinit var displayStateInteractor: DisplayStateInteractorImpl
+ private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor
+
+ private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+
+ private lateinit var viewModel: SideFpsOverlayViewModel
+
+ private var displayWidth: Int = 0
+ private var displayHeight: Int = 0
+ private var boundsWidth: Int = 0
+ private var boundsHeight: Int = 0
+
+ private lateinit var deviceConfig: DeviceConfig
+ private lateinit var sensorLocation: SensorLocationInternal
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ enum class DeviceConfig {
+ X_ALIGNED,
+ Y_ALIGNED,
+ }
+
+ @Before
+ fun setup() {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+
+ allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
+
+ mContext = spy(mContext)
+
+ val resources = mContext.resources
+ whenever(mContext.display)
+ .thenReturn(
+ Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+ )
+
+ alternateBouncerInteractor =
+ AlternateBouncerInteractor(
+ mock(StatusBarStateController::class.java),
+ mock(KeyguardStateController::class.java),
+ bouncerRepository,
+ fingerprintPropertyRepository,
+ biometricSettingsRepository,
+ FakeSystemClock(),
+ keyguardUpdateMonitor,
+ testScope.backgroundScope,
+ )
+
+ biometricStatusInteractor =
+ BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+
+ displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ displayStateRepository,
+ displayRepository,
+ )
+ displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ mContext,
+ keyguardUpdateMonitor,
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ selectedUserInteractor,
+ faceAuthInteractor
+ )
+
+ deviceEntrySideFpsOverlayInteractor =
+ DeviceEntrySideFpsOverlayInteractor(
+ mContext,
+ deviceEntryFingerprintAuthRepository,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ keyguardUpdateMonitor
+ )
+
+ whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+ .thenReturn(MutableStateFlow(false))
+
+ sfpsSensorInteractor =
+ SideFpsSensorInteractor(
+ mContext,
+ fingerprintPropertyRepository,
+ windowManager,
+ displayStateInteractor,
+ Optional.of(fingerprintInteractiveToAuthProvider),
+ SideFpsLogger(logcatLogBuffer("SfpsLogger"))
+ )
+
+ sideFpsProgressBarViewModel =
+ SideFpsProgressBarViewModel(
+ mContext,
+ deviceEntryFingerprintAuthRepository,
+ sfpsSensorInteractor,
+ displayStateInteractor,
+ testScope.backgroundScope,
+ )
+
+ viewModel =
+ SideFpsOverlayViewModel(
+ mContext,
+ biometricStatusInteractor,
+ deviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor,
+ sfpsSensorInteractor,
+ sideFpsProgressBarViewModel
+ )
+
+ underTest =
+ SideFpsOverlayViewBinder(
+ testScope.backgroundScope,
+ mContext,
+ biometricStatusInteractor,
+ displayStateInteractor,
+ deviceEntrySideFpsOverlayInteractor,
+ fpsUnlockTracker,
+ layoutInflater,
+ sideFpsProgressBarViewModel,
+ sfpsSensorInteractor,
+ windowManager
+ )
+
+ context.addMockSystemService(DisplayManager::class.java, displayManager)
+ context.addMockSystemService(WindowManager::class.java, windowManager)
+
+ `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView)
+ `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+ .thenReturn(mock(LottieAnimationView::class.java))
+ with(mock(ViewPropertyAnimator::class.java)) {
+ `when`(sideFpsView.animate()).thenReturn(this)
+ `when`(alpha(Mockito.anyFloat())).thenReturn(this)
+ `when`(setStartDelay(Mockito.anyLong())).thenReturn(this)
+ `when`(setDuration(Mockito.anyLong())).thenReturn(this)
+ `when`(setListener(any())).thenAnswer {
+ (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd(
+ mock(Animator::class.java)
+ )
+ this
+ }
+ }
+ }
+
+ @Test
+ fun verifyIndicatorNotAdded_whenInRearDisplayMode() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = true
+ )
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+ }
+
+ @Test
+ fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+ // Show primary bouncer
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ verify(windowManager).addView(any(), any())
+
+ // Hide primary bouncer
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ verify(windowManager).removeView(any())
+ }
+ }
+
+ @Test
+ fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+ // Show alternate bouncer
+ bouncerRepository.setAlternateVisible(true)
+ runCurrent()
+
+ verify(windowManager).addView(any(), any())
+
+ // Hide alternate bouncer
+ bouncerRepository.setAlternateVisible(false)
+ runCurrent()
+
+ verify(windowManager).removeView(any())
+ }
+ }
+
+ @Test
+ fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ // System server authentication started
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ runCurrent()
+
+ verify(windowManager).addView(any(), any())
+
+ // System server authentication stopped
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ runCurrent()
+
+ verify(windowManager).removeView(any())
+ }
+ }
+
+ private fun updatePrimaryBouncer(
+ isShowing: Boolean,
+ isAnimatingAway: Boolean,
+ fpsDetectionRunning: Boolean,
+ isUnlockingWithFpAllowed: Boolean,
+ ) {
+ bouncerRepository.setPrimaryShow(isShowing)
+ bouncerRepository.setPrimaryStartingToHide(false)
+ val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+ bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+ whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+ .thenReturn(fpsDetectionRunning)
+ whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+ .thenReturn(isUnlockingWithFpAllowed)
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_show_sidefps_hint_on_bouncer,
+ true
+ )
+ }
+
+ private suspend fun TestScope.setupTestConfiguration(
+ deviceConfig: DeviceConfig,
+ rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode: Boolean,
+ ) {
+ this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig
+
+ when (deviceConfig) {
+ DeviceConfig.X_ALIGNED -> {
+ displayWidth = 3000
+ displayHeight = 1500
+ boundsWidth = 200
+ boundsHeight = 100
+ sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+ }
+ DeviceConfig.Y_ALIGNED -> {
+ displayWidth = 2500
+ displayHeight = 2000
+ boundsWidth = 100
+ boundsHeight = 200
+ sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+ }
+ }
+
+ whenever(windowManager.maximumWindowMetrics)
+ .thenReturn(
+ WindowMetrics(
+ Rect(0, 0, displayWidth, displayHeight),
+ mock(WindowInsets::class.java),
+ )
+ )
+
+ contextDisplayInfo.uniqueId = DISPLAY_ID
+
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 1,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.POWER_BUTTON,
+ sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+ )
+
+ displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+ displayStateRepository.setCurrentRotation(rotation)
+ displayRepository.emitDisplayChangeEvent(0)
+ underTest.start()
+ runCurrent()
+ }
+
+ companion object {
+ private const val DISPLAY_ID = "displayId"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index d06cbbb..7475235 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.viewmodel
import android.content.res.Configuration
+import android.graphics.Point
import android.hardware.biometrics.PromptInfo
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
@@ -25,7 +26,10 @@
import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags.FLAG_BP_TALKBACK
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -33,6 +37,7 @@
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
@@ -45,8 +50,10 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -77,7 +84,9 @@
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock private lateinit var lockPatternUtils: LockPatternUtils
- @Mock private lateinit var vibrator: VibratorHelper
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var udfpsUtils: UdfpsUtils
private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val testScope = TestScope()
@@ -87,6 +96,7 @@
private lateinit var displayStateRepository: FakeDisplayStateRepository
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
+ private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private lateinit var selector: PromptSelectorInteractor
private lateinit var viewModel: PromptViewModel
@@ -116,11 +126,24 @@
displayStateRepository,
displayRepository,
)
+ udfpsOverlayInteractor =
+ UdfpsOverlayInteractor(
+ authController,
+ selectedUserInteractor,
+ testScope.backgroundScope
+ )
selector =
PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
selector.resetPrompt()
- viewModel = PromptViewModel(displayStateInteractor, selector, mContext)
+ viewModel =
+ PromptViewModel(
+ displayStateInteractor,
+ selector,
+ mContext,
+ udfpsOverlayInteractor,
+ udfpsUtils
+ )
iconViewModel = viewModel.iconViewModel
}
@@ -1153,6 +1176,29 @@
assertThat(size).isEqualTo(PromptSize.LARGE)
}
+ @Test
+ fun hint_for_talkback_guidance() = runGenericTest {
+ mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK)
+ val hint by collectLastValue(viewModel.accessibilityHint)
+
+ // Touches should fall outside of sensor area
+ whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
+ .thenReturn(Point(0, 0))
+ whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
+ .thenReturn("Direction")
+
+ viewModel.onAnnounceAccessibilityHint(
+ obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
+ true
+ )
+
+ if (testCase.modalities.hasUdfps) {
+ assertThat(hint?.isNotBlank()).isTrue()
+ } else {
+ assertThat(hint.isNullOrBlank()).isTrue()
+ }
+ }
+
/** Asserts that the selected buttons are visible now. */
private suspend fun TestScope.assertButtonsVisible(
tryAgain: Boolean = false,
@@ -1220,14 +1266,19 @@
authenticatedModality = BiometricModality.Face,
),
TestCase(
- fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
authenticatedModality = BiometricModality.Fingerprint,
),
TestCase(
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1264,20 +1315,30 @@
TestCase(
face = faceSensorPropertiesInternal(strong = true).first(),
fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
- authenticatedModality = BiometricModality.Fingerprint,
- ),
- TestCase(
- face = faceSensorPropertiesInternal(strong = true).first(),
- fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
authenticatedModality = BiometricModality.Face,
confirmationRequested = true,
),
TestCase(
face = faceSensorPropertiesInternal(strong = true).first(),
- fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
authenticatedModality = BiometricModality.Fingerprint,
confirmationRequested = true,
),
+ TestCase(
+ face = faceSensorPropertiesInternal(strong = true).first(),
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ ),
)
}
}
@@ -1309,6 +1370,9 @@
else -> false
}
+ val modalities: BiometricModalities
+ get() = BiometricModalities(fingerprint, face)
+
val authenticatedByFingerprint: Boolean
get() = authenticatedModality == BiometricModality.Fingerprint
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
new file mode 100644
index 0000000..2267bdc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -0,0 +1,576 @@
+/*
+ * 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.ui.viewmodel
+
+import android.app.ActivityTaskManager
+import android.content.res.Configuration.UI_MODE_NIGHT_NO
+import android.content.res.Configuration.UI_MODE_NIGHT_YES
+import android.graphics.Color
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.model.KeyPath
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.Utils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class SideFpsOverlayViewModelTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
+ @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock
+ private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var windowManager: WindowManager
+
+ private val contextDisplayInfo = DisplayInfo()
+
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+ private val biometricStatusRepository = FakeBiometricStatusRepository()
+ private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ private val displayRepository = FakeDisplayRepository()
+ private val displayStateRepository = FakeDisplayStateRepository()
+ private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+
+ private val indicatorColor =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.materialColorPrimaryFixed
+ )
+ private val outerRimColor =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.materialColorPrimaryFixedDim
+ )
+ private val chevronFill =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.materialColorOnPrimaryFixed
+ )
+ private val color_blue400 =
+ context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400)
+
+ private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+ private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor
+ private lateinit var displayStateInteractor: DisplayStateInteractorImpl
+ private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor
+
+ private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+
+ private lateinit var underTest: SideFpsOverlayViewModel
+
+ private var displayWidth: Int = 0
+ private var displayHeight: Int = 0
+ private var boundsWidth: Int = 0
+ private var boundsHeight: Int = 0
+
+ private lateinit var deviceConfig: DeviceConfig
+ private lateinit var sensorLocation: SensorLocationInternal
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ enum class DeviceConfig {
+ X_ALIGNED,
+ Y_ALIGNED,
+ }
+
+ @Before
+ fun setup() {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+
+ mContext = spy(mContext)
+
+ val resources = mContext.resources
+ whenever(mContext.display)
+ .thenReturn(
+ Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+ )
+
+ alternateBouncerInteractor =
+ AlternateBouncerInteractor(
+ mock(StatusBarStateController::class.java),
+ mock(KeyguardStateController::class.java),
+ bouncerRepository,
+ fingerprintPropertyRepository,
+ biometricSettingsRepository,
+ FakeSystemClock(),
+ keyguardUpdateMonitor,
+ testScope.backgroundScope,
+ )
+
+ biometricStatusInteractor =
+ BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+
+ displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ displayStateRepository,
+ displayRepository,
+ )
+ displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ mContext,
+ keyguardUpdateMonitor,
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ selectedUserInteractor,
+ faceAuthInteractor
+ )
+
+ deviceEntrySideFpsOverlayInteractor =
+ DeviceEntrySideFpsOverlayInteractor(
+ mContext,
+ deviceEntryFingerprintAuthRepository,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ keyguardUpdateMonitor
+ )
+
+ whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+ .thenReturn(MutableStateFlow(false))
+
+ sfpsSensorInteractor =
+ SideFpsSensorInteractor(
+ mContext,
+ fingerprintPropertyRepository,
+ windowManager,
+ displayStateInteractor,
+ Optional.of(fingerprintInteractiveToAuthProvider),
+ SideFpsLogger(logcatLogBuffer("SfpsLogger"))
+ )
+
+ sideFpsProgressBarViewModel =
+ SideFpsProgressBarViewModel(
+ mContext,
+ deviceEntryFingerprintAuthRepository,
+ sfpsSensorInteractor,
+ displayStateInteractor,
+ testScope.backgroundScope,
+ )
+
+ underTest =
+ SideFpsOverlayViewModel(
+ mContext,
+ biometricStatusInteractor,
+ deviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor,
+ sfpsSensorInteractor,
+ sideFpsProgressBarViewModel,
+ )
+ }
+
+ @Test
+ fun updatesOverlayViewProperties_onDisplayRotationChange_xAlignedSensor() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+
+ val overlayViewProperties by collectLastValue(underTest.overlayViewProperties)
+
+ runCurrent()
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+ }
+ }
+
+ @Test
+ fun updatesOverlayViewProperties_onDisplayRotationChange_yAlignedSensor() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.Y_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+
+ val overlayViewProperties by collectLastValue(underTest.overlayViewProperties)
+
+ runCurrent()
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+ assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+ assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+ }
+ }
+
+ @Test
+ fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.X_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+
+ val overlayViewParams by collectLastValue(underTest.overlayViewParams)
+
+ underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+ runCurrent()
+
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationX)
+ assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(0)
+ assertThat(overlayViewParams!!.y)
+ .isEqualTo(
+ displayHeight - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+ )
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x)
+ .isEqualTo(
+ displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+ )
+ assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX)
+ }
+ }
+
+ @Test
+ fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() {
+ testScope.runTest {
+ setupTestConfiguration(
+ DeviceConfig.Y_ALIGNED,
+ rotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode = false
+ )
+
+ val overlayViewParams by collectLastValue(underTest.overlayViewParams)
+
+ underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+ runCurrent()
+
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationY)
+ assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x).isEqualTo(0)
+ assertThat(overlayViewParams!!.y)
+ .isEqualTo(
+ displayHeight - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+ )
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ assertThat(overlayViewParams).isNotNull()
+ assertThat(overlayViewParams!!.x)
+ .isEqualTo(
+ displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+ )
+ assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+ }
+ }
+
+ @Test
+ fun updatesLottieCallbacks_onShowIndicatorForDeviceEntry() {
+ testScope.runTest {
+ val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".black", "**"), chevronFill))
+ }
+ }
+
+ @Test
+ fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inDarkMode() {
+ testScope.runTest {
+ val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+ setDarkMode(true)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+ }
+ }
+
+ @Test
+ fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inLightMode() {
+ testScope.runTest {
+ val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+ setDarkMode(false)
+
+ biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ runCurrent()
+
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+ assertThat(lottieCallbacks)
+ .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+ }
+ }
+
+ private fun setDarkMode(inDarkMode: Boolean) {
+ val uiMode =
+ if (inDarkMode) {
+ UI_MODE_NIGHT_YES
+ } else {
+ UI_MODE_NIGHT_NO
+ }
+
+ mContext.resources.configuration.uiMode = uiMode
+ }
+
+ private fun updatePrimaryBouncer(
+ isShowing: Boolean,
+ isAnimatingAway: Boolean,
+ fpsDetectionRunning: Boolean,
+ isUnlockingWithFpAllowed: Boolean,
+ ) {
+ bouncerRepository.setPrimaryShow(isShowing)
+ bouncerRepository.setPrimaryStartingToHide(false)
+ val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+ bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+ whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+ .thenReturn(fpsDetectionRunning)
+ whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+ .thenReturn(isUnlockingWithFpAllowed)
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_show_sidefps_hint_on_bouncer,
+ true
+ )
+ }
+
+ private suspend fun TestScope.setupTestConfiguration(
+ deviceConfig: DeviceConfig,
+ rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+ isInRearDisplayMode: Boolean,
+ ) {
+ this@SideFpsOverlayViewModelTest.deviceConfig = deviceConfig
+
+ when (deviceConfig) {
+ DeviceConfig.X_ALIGNED -> {
+ displayWidth = 3000
+ displayHeight = 1500
+ boundsWidth = 200
+ boundsHeight = 100
+ sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+ }
+ DeviceConfig.Y_ALIGNED -> {
+ displayWidth = 2500
+ displayHeight = 2000
+ boundsWidth = 100
+ boundsHeight = 200
+ sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+ }
+ }
+
+ whenever(windowManager.maximumWindowMetrics)
+ .thenReturn(
+ WindowMetrics(
+ Rect(0, 0, displayWidth, displayHeight),
+ mock(WindowInsets::class.java),
+ )
+ )
+
+ contextDisplayInfo.uniqueId = DISPLAY_ID
+
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 1,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.POWER_BUTTON,
+ sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+ )
+
+ displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+
+ displayStateRepository.setCurrentRotation(rotation)
+
+ displayRepository.emitDisplayChangeEvent(0)
+ runCurrent()
+ }
+
+ companion object {
+ private const val DISPLAY_ID = "displayId"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 37a093e..dacf23a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
@@ -342,6 +343,7 @@
assertThat(underTest.willDismissWithAction()).isFalse()
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility() {
updateSideFpsVisibilityParameters(
@@ -355,6 +357,7 @@
verify(repository).setSideFpsShowing(true)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility_notVisible() {
updateSideFpsVisibilityParameters(
@@ -368,6 +371,7 @@
verify(repository).setSideFpsShowing(false)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility_sfpsNotEnabled() {
updateSideFpsVisibilityParameters(
@@ -381,6 +385,7 @@
verify(repository).setSideFpsShowing(false)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility_fpsDetectionNotRunning() {
updateSideFpsVisibilityParameters(
@@ -394,6 +399,7 @@
verify(repository).setSideFpsShowing(false)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility_UnlockingWithFpNotAllowed() {
updateSideFpsVisibilityParameters(
@@ -407,6 +413,7 @@
verify(repository).setSideFpsShowing(false)
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Test
fun testSideFpsVisibility_AnimatingAway() {
updateSideFpsVisibilityParameters(
@@ -492,6 +499,7 @@
isUnlockingWithFpAllowed: Boolean,
isAnimatingAway: Boolean
) {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
whenever(repository.primaryBouncerShow.value).thenReturn(isVisible)
resources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, sfpsEnabled)
whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
new file mode 100644
index 0000000..70d3f81
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -0,0 +1,238 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.os.Handler
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+
+ private lateinit var underTest: DeviceEntrySideFpsOverlayInteractor
+
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ @Before
+ fun setup() {
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ mContext,
+ keyguardUpdateMonitor,
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ mSelectedUserInteractor,
+ faceAuthInteractor
+ )
+ alternateBouncerInteractor =
+ AlternateBouncerInteractor(
+ mock(StatusBarStateController::class.java),
+ mock(KeyguardStateController::class.java),
+ bouncerRepository,
+ FakeFingerprintPropertyRepository(),
+ biometricSettingsRepository,
+ FakeSystemClock(),
+ keyguardUpdateMonitor,
+ testScope.backgroundScope,
+ )
+ underTest =
+ DeviceEntrySideFpsOverlayInteractor(
+ mContext,
+ FakeDeviceEntryFingerprintAuthRepository(),
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ keyguardUpdateMonitor
+ )
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerShowing() =
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(true)
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerHidden() =
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_whenFpsDetectionNotRunning() {
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = false,
+ isUnlockingWithFpAllowed = true
+ )
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+ }
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_onUnlockingWithFpDisallowed() {
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = false
+ )
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+ }
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerAnimatingAway() {
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = true,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+ }
+ }
+
+ @Test
+ fun updatesShowIndicatorForDeviceEntry_onAlternateBouncerRequest() =
+ testScope.runTest {
+ val showIndicatorForDeviceEntry by
+ collectLastValue(underTest.showIndicatorForDeviceEntry)
+ runCurrent()
+
+ bouncerRepository.setAlternateVisible(true)
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(true)
+
+ bouncerRepository.setAlternateVisible(false)
+ assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+ }
+
+ private fun updatePrimaryBouncer(
+ isShowing: Boolean,
+ isAnimatingAway: Boolean,
+ fpsDetectionRunning: Boolean,
+ isUnlockingWithFpAllowed: Boolean,
+ ) {
+ bouncerRepository.setPrimaryShow(isShowing)
+ bouncerRepository.setPrimaryStartingToHide(false)
+ val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+ bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+ whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+ .thenReturn(fpsDetectionRunning)
+ whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+ .thenReturn(isUnlockingWithFpAllowed)
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_show_sidefps_hint_on_bouncer,
+ true
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
new file mode 100644
index 0000000..1c8bd3b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeBiometricStatusRepository : BiometricStatusRepository {
+ private val _fingerprintAuthenticationReason =
+ MutableStateFlow<AuthenticationReason>(AuthenticationReason.NotRunning)
+ override val fingerprintAuthenticationReason: StateFlow<AuthenticationReason> =
+ _fingerprintAuthenticationReason.asStateFlow()
+
+ fun setFingerprintAuthenticationReason(reason: AuthenticationReason) {
+ _fingerprintAuthenticationReason.value = reason
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index f84481c..ff5179a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.bouncer.data.repository
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.dagger.SysUISingleton
@@ -113,7 +114,9 @@
_isBackButtonEnabled.value = isBackButtonEnabled
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
override fun setSideFpsShowing(isShowing: Boolean) {
+ SideFpsControllerRefactor.assertInLegacyMode()
_sideFpsShowing.value = isShowing
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index c9160ef..1d44929 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -54,6 +54,11 @@
private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
get() = _authenticationStatus.filterNotNull()
+
+ private var _shouldUpdateIndicatorVisibility = MutableStateFlow(false)
+ override val shouldUpdateIndicatorVisibility: Flow<Boolean>
+ get() = _shouldUpdateIndicatorVisibility
+
fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
_authenticationStatus.value = status
}
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index b8cf13b..e2488a5 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -19,6 +19,9 @@
defaults: [
"platform_service_defaults",
],
+ lint: {
+ error_checks: ["MissingPermissionAnnotation"],
+ },
srcs: [
":services.accessibility-sources",
"//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc",
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 97d36d4..1d73843 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -118,6 +118,7 @@
* This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
* It is responsible for behavior common to both types of clients.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter,
FingerprintGestureDispatcher.FingerprintGestureClient {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9ddc35a..abcd8e2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -57,6 +57,7 @@
*
* NOTE: This class has to be created and poked only from the main thread.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index edb41639..3d8d7b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -195,6 +195,7 @@
* event dispatch for {@link AccessibilityEvent}s generated across all processes
* on the device. Events are dispatched to {@link AccessibilityService}s.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
AccessibilityUserState.ServiceInfoChangeListener,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 40ca694..5ebe161 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -65,6 +65,7 @@
* passed to the service it represents as soon it is bound. It also serves as the
* connection for the service.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index a525e7c..b119d7d 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -34,6 +34,7 @@
* If we are stripping and/or replacing the actions from a window, we need to intercept the
* nodes heading back to the service and swap out the actions.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "ActionReplacingCallback";
diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
index c9ec16e..e10e87c 100644
--- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
@@ -33,6 +33,7 @@
/**
* Encapsulate fingerprint gesture logic
*/
+@SuppressWarnings("MissingPermissionAnnotation")
public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub
implements Handler.Callback{
private static final int MSG_REGISTER = 1;
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index ab01fc3..6aa4702 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -64,6 +64,7 @@
*
* TODO(241429275): Initialize this when a proxy is registered.
*/
+@SuppressWarnings("MissingPermissionAnnotation")
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 53c629a..f69104d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -245,6 +245,7 @@
}
}
+ @SuppressWarnings("MissingPermissionAnnotation")
private class UiAutomationService extends AbstractAccessibilityServiceConnection {
private final Handler mMainHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index e6af54b..e11c36a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -922,6 +922,7 @@
disableWindowMagnification(displayId, true);
}
+ @SuppressWarnings("MissingPermissionAnnotation")
private class ConnectionCallback extends IMagnificationConnectionCallback.Stub implements
IBinder.DeathRecipient {
private boolean mExpiredDeathRecipient = false;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index c63784a..db5b313 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -246,6 +246,7 @@
return new RemoteAnimationCallback(callback, trace);
}
+ @SuppressWarnings("MissingPermissionAnnotation")
private static class RemoteAnimationCallback extends
IRemoteMagnificationAnimationCallback.Stub {
private final MagnificationAnimationCallback mCallback;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 23e7ce6..9fdf5c2 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -92,9 +92,10 @@
int userId = getNextIntArgRequired();
String packageName = getNextArgRequired();
String address = getNextArgRequired();
+ String deviceProfile = getNextArg();
final MacAddress macAddress = MacAddress.fromString(address);
mService.createNewAssociation(userId, packageName, macAddress,
- null, null, false);
+ null, deviceProfile, false);
}
break;
@@ -350,7 +351,7 @@
pw.println(" Print this help text.");
pw.println(" list USER_ID");
pw.println(" List all Associations for a user.");
- pw.println(" associate USER_ID PACKAGE MAC_ADDRESS");
+ pw.println(" associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]");
pw.println(" Create a new Association.");
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
pw.println(" Remove an existing Association.");
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 8dc6537..0d5cdcb 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -439,6 +439,12 @@
if (associationInfo == null) {
throw new IllegalArgumentException("No association with ID " + associationId);
}
+ if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
+ .contains(associationInfo.getDeviceProfile())
+ && Flags.persistentDeviceIdApi()) {
+ throw new IllegalArgumentException("Unsupported CDM Association device profile "
+ + associationInfo.getDeviceProfile() + " for virtual device creation.");
+ }
Objects.requireNonNull(params);
Objects.requireNonNull(activityListener);
Objects.requireNonNull(soundEffectListener);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a0ccbf3..f5a80d8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -200,6 +200,7 @@
"notification_flags_lib",
"biometrics_flags_lib",
"am_flags_lib",
+ "com_android_systemui_shared_flags_lib",
"com_android_wm_shell_flags_lib",
"com.android.server.utils_aconfig-java",
"service-jobscheduler-deviceidle.flags-aconfig-java",
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0629e637..dafea9a 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -36,6 +36,7 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.ComponentInfoInternal;
@@ -385,6 +386,26 @@
}
@Override
+ public void registerAuthenticationStateListener(AuthenticationStateListener listener)
+ throws RemoteException {
+ checkInternalPermission();
+ final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+ if (fingerprintService != null) {
+ fingerprintService.registerAuthenticationStateListener(listener);
+ }
+ }
+
+ @Override
+ public void unregisterAuthenticationStateListener(AuthenticationStateListener listener)
+ throws RemoteException {
+ checkInternalPermission();
+ final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+ if (fingerprintService != null) {
+ fingerprintService.unregisterAuthenticationStateListener(listener);
+ }
+ }
+
+ @Override
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) throws RemoteException {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index a47135f..f9568ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -27,7 +27,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.os.IBinder;
import android.os.RemoteException;
import android.security.KeyStore;
@@ -430,19 +430,19 @@
return mLockoutTracker;
}
- protected int getShowOverlayReason() {
+ protected int getRequestReason() {
if (isKeyguard()) {
- return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+ return BiometricRequestConstants.REASON_AUTH_KEYGUARD;
} else if (isBiometricPrompt()) {
// BP reason always takes precedent over settings, since callers from within
// settings can always invoke BP.
- return BiometricOverlayConstants.REASON_AUTH_BP;
+ return BiometricRequestConstants.REASON_AUTH_BP;
} else if (isSettings()) {
// This is pretty much only for FingerprintManager#authenticate usage from
// FingerprintSettings.
- return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
+ return BiometricRequestConstants.REASON_AUTH_SETTINGS;
} else {
- return BiometricOverlayConstants.REASON_AUTH_OTHER;
+ return BiometricRequestConstants.REASON_AUTH_OTHER;
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java
new file mode 100644
index 0000000..5863535
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java
@@ -0,0 +1,108 @@
+/*
+ * 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.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.AuthenticationStateListener;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Low-level callback interface between BiometricManager and AuthService. Allows core system
+ * services (e.g. SystemUI) to register and unregister listeners for updates about the current
+ * state of biometric authentication.
+ * @hide */
+public class AuthenticationStateListeners implements IBinder.DeathRecipient {
+
+ private static final String TAG = "AuthenticationStateListeners";
+
+ @NonNull
+ private final CopyOnWriteArrayList<AuthenticationStateListener> mAuthenticationStateListeners =
+ new CopyOnWriteArrayList<>();
+
+ /**
+ * Enables clients to register an AuthenticationStateListener for updates about the current
+ * state of biometric authentication.
+ * @param listener listener to register
+ */
+ public void registerAuthenticationStateListener(
+ @NonNull AuthenticationStateListener listener) {
+ mAuthenticationStateListeners.add(listener);
+ try {
+ listener.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ /**
+ * Enables clients to unregister an AuthenticationStateListener.
+ * @param listener listener to register
+ */
+ public void unregisterAuthenticationStateListener(
+ @NonNull AuthenticationStateListener listener) {
+ mAuthenticationStateListeners.remove(listener);
+ }
+
+ /**
+ * Defines behavior in response to authentication starting
+ * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting
+ * authentication starting
+ */
+ public void onAuthenticationStarted(int requestReason) {
+ for (AuthenticationStateListener listener: mAuthenticationStateListeners) {
+ try {
+ listener.onAuthenticationStarted(requestReason);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in notifying listener that authentication "
+ + "started", e);
+ }
+ }
+ }
+
+ /**
+ * Defines behavior in response to authentication stopping
+ */
+ public void onAuthenticationStopped() {
+ for (AuthenticationStateListener listener: mAuthenticationStateListeners) {
+ try {
+ listener.onAuthenticationStopped();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in notifying listener that authentication "
+ + "stopped", e);
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ Slog.w(TAG, "Callback binder died: " + who);
+ if (mAuthenticationStateListeners.removeIf(listener -> listener.asBinder().equals(who))) {
+ Slog.w(TAG, "Removed dead listener for " + who);
+ } else {
+ Slog.w(TAG, "No dead listeners found");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 483ce75..2c4d30b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.fingerprint.FingerprintManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -135,14 +135,14 @@
return true;
}
- protected int getOverlayReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+ protected int getRequestReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
switch (reason) {
case FingerprintManager.ENROLL_FIND_SENSOR:
- return BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+ return BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR;
case FingerprintManager.ENROLL_ENROLL:
- return BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
+ return BiometricRequestConstants.REASON_ENROLL_ENROLLING;
default:
- return BiometricOverlayConstants.REASON_UNKNOWN;
+ return BiometricRequestConstants.REASON_UNKNOWN;
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
index aeb6b6e..3d20efc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
+++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
@@ -16,9 +16,11 @@
package com.android.server.biometrics.sensors;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
@@ -42,6 +44,8 @@
private static final String TAG = "SensorOverlays";
@NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController;
+
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@NonNull private final Optional<ISidefpsController> mSidefpsController;
/**
@@ -58,19 +62,32 @@
}
/**
+ * Create an overlay controller for each modality.
+ *
+ * @param udfpsOverlayController under display fps or null if not present on device
+ */
+ public SensorOverlays(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController);
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ mSidefpsController = Optional.empty();
+ }
+
+ /**
* Show the overlay.
*
* @param sensorId sensor id
* @param reason reason for showing
* @param client client performing operation
*/
- public void show(int sensorId, @BiometricOverlayConstants.ShowReason int reason,
+ public void show(int sensorId, @BiometricRequestConstants.RequestReason int reason,
@NonNull AcquisitionClient<?> client) {
- if (mSidefpsController.isPresent()) {
- try {
- mSidefpsController.get().show(sensorId, reason);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+ if (!sidefpsControllerRefactor()) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().show(sensorId, reason);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+ }
}
}
@@ -98,11 +115,13 @@
* @param sensorId sensor id
*/
public void hide(int sensorId) {
- if (mSidefpsController.isPresent()) {
- try {
- mSidefpsController.get().hide(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+ if (!sidefpsControllerRefactor()) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().hide(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 7695543..83b306b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -33,6 +33,7 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -81,6 +82,7 @@
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -128,6 +130,8 @@
private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
mBiometricStateCallback;
@NonNull
+ private final AuthenticationStateListeners mAuthenticationStateListeners;
+ @NonNull
private final Handler mHandler;
@NonNull
private final FingerprintServiceRegistry mRegistry;
@@ -891,6 +895,7 @@
return providers;
});
}
+
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void addAuthenticatorsRegisteredCallback(
@@ -902,6 +907,24 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
+ public void registerAuthenticationStateListener(
+ @NonNull AuthenticationStateListener listener) {
+ super.registerAuthenticationStateListener_enforcePermission();
+
+ mAuthenticationStateListeners.registerAuthenticationStateListener(listener);
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
+ public void unregisterAuthenticationStateListener(
+ @NonNull AuthenticationStateListener listener) {
+ super.unregisterAuthenticationStateListener_enforcePermission();
+
+ mAuthenticationStateListeners.unregisterAuthenticationStateListener(listener);
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
super.registerBiometricStateListener_enforcePermission();
@@ -957,6 +980,7 @@
}
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
@@ -1014,6 +1038,7 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+ mAuthenticationStateListeners = new AuthenticationStateListeners();
mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
(name) -> {
final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
@@ -1022,9 +1047,9 @@
if (fp != null) {
try {
return new FingerprintProvider(getContext(),
- mBiometricStateCallback, fp.getSensorProps(), name,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
- mBiometricContext);
+ mBiometricStateCallback, mAuthenticationStateListeners,
+ fp.getSensorProps(), name, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher, mBiometricContext);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
@@ -1086,13 +1111,13 @@
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
- mBiometricStateCallback, hidlSensor,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+ mBiometricStateCallback, mAuthenticationStateListeners,
+ hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
BiometricContext.getInstance(getContext()));
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
- mBiometricStateCallback, hidlSensor, mHandler,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ mBiometricStateCallback, mAuthenticationStateListeners, hidlSensor,
+ mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
providers.add(fingerprint21);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index a15d1a4..fc37d70 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -135,6 +135,7 @@
void onPowerPressed();
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
/**
* Sets side-fps controller
* @param controller side-fps controller
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 337c3c2..29c5a3d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskStackListener;
@@ -48,6 +50,7 @@
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -84,6 +87,7 @@
private final int mSkipWaitForPowerVendorAcquireMessage;
private final long mFingerUpIgnoresPower = 500;
private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
@Nullable
private ICancellationSignal mCancellationSignal;
private boolean mIsPointerDown;
@@ -110,7 +114,9 @@
boolean isStrongBiometric,
@Nullable TaskStackListener taskStackListener,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler,
@@ -136,7 +142,12 @@
false /* shouldVibrate */,
biometricStrength);
setRequestId(requestId);
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ }
+ mAuthenticationStateListeners = authenticationStateListeners;
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
mHandler = handler;
@@ -216,6 +227,9 @@
if (authenticated) {
mState = STATE_STOPPED;
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
}
@@ -241,6 +255,9 @@
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -266,11 +283,17 @@
}
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+ mSensorOverlays.show(getSensorId(), getRequestReason(), this);
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
+ }
try {
mCancellationSignal = doAuthenticate();
@@ -280,6 +303,9 @@
BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
mCallback.onClientFinished(this, false /* success */);
}
}
@@ -323,6 +349,9 @@
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
unsubscribeBiometricContext();
if (mCancellationSignal != null) {
@@ -423,6 +452,9 @@
}
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
mCallback.onClientFinished(this, false /* success */);
}
@@ -450,6 +482,9 @@
}
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
mCallback.onClientFinished(this, false /* success */);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index e2413ee..e58e5ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -16,10 +16,12 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -66,7 +68,12 @@
true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(
+ udfpsOverlayController, null /* sideFpsController */);
+ }
mOptions = options;
}
@@ -93,7 +100,7 @@
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+ mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 06550d8..c0761ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -42,6 +44,7 @@
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -65,6 +68,7 @@
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
private final @FingerprintManager.EnrollReason int mEnrollReason;
+ @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
@Nullable private ICancellationSignal mCancellationSignal;
private final int mMaxTemplatesPerUser;
private boolean mIsPointerDown;
@@ -80,15 +84,18 @@
}
}
- public FingerprintEnrollClient(@NonNull Context context,
- @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId,
+ public FingerprintEnrollClient(
+ @NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
@@ -96,7 +103,13 @@
logger, biometricContext);
setRequestId(requestId);
mSensorProps = sensorProps;
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ }
+ mAuthenticationStateListeners = authenticationStateListeners;
+
mMaxTemplatesPerUser = maxTemplatesPerUser;
mALSProbeCallback = getLogger().getAmbientLightProbe(true /* startWithClient */);
@@ -130,7 +143,11 @@
if (remaining == 0) {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
+
}
@Override
@@ -159,8 +176,10 @@
@Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
-
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
@Override
@@ -171,8 +190,12 @@
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+ mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
this);
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStarted(
+ getRequestReasonFromEnrollReason(mEnrollReason));
+ }
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
@@ -210,6 +233,10 @@
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
+
unsubscribeBiometricContext();
if (mCancellationSignal != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 9985b06..032ab87 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -64,6 +64,7 @@
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -108,6 +109,8 @@
@NonNull
private final BiometricStateCallback mBiometricStateCallback;
@NonNull
+ private final AuthenticationStateListeners mAuthenticationStateListeners;
+ @NonNull
private final String mHalInstanceName;
@NonNull
private final Handler mHandler;
@@ -122,6 +125,7 @@
@NonNull private final BiometricContext mBiometricContext;
@Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable private ISidefpsController mSidefpsController;
private AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
@@ -157,16 +161,19 @@
public FingerprintProvider(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext) {
- this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- gestureAvailabilityDispatcher, biometricContext, null /* daemon */);
+ this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
+ lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
+ null /* daemon */);
}
@VisibleForTesting FingerprintProvider(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@@ -174,6 +181,7 @@
IFingerprint daemon) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
+ mAuthenticationStateListeners = authenticationStateListeners;
mHalInstanceName = halInstanceName;
mFingerprintSensors = new SensorList<>(ActivityManager.getService());
mHandler = new Handler(Looper.getMainLooper());
@@ -434,7 +442,7 @@
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
- maxTemplatesPerUser, enrollReason);
+ mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
mBiometricStateCallback, new ClientMonitorCallback() {
@Override
@@ -498,7 +506,7 @@
mBiometricContext, isStrongBiometric,
mTaskStackListener,
mUdfpsOverlayController, mSidefpsController,
- allowBackgroundAuthentication,
+ mAuthenticationStateListeners, allowBackgroundAuthentication,
mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId),
SystemClock.elapsedRealtimeClock(),
@@ -732,6 +740,7 @@
}
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
mSidefpsController = controller;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 8bfa560..d3cecd0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -68,6 +68,7 @@
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -115,6 +116,7 @@
final Context mContext;
@NonNull private final BiometricStateCallback mBiometricStateCallback;
+ @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
private final ActivityTaskManager mActivityTaskManager;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
private final BiometricScheduler mScheduler;
@@ -128,6 +130,8 @@
@Nullable private IBiometricsFingerprint mDaemon;
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable private ISidefpsController mSidefpsController;
@NonNull private final BiometricContext mBiometricContext;
@Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
@@ -330,6 +334,7 @@
@VisibleForTesting
Fingerprint21(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler,
@NonNull Handler handler,
@@ -338,6 +343,7 @@
@NonNull BiometricContext biometricContext) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
+ mAuthenticationStateListeners = authenticationStateListeners;
mBiometricContext = biometricContext;
mSensorProperties = sensorProps;
@@ -378,6 +384,7 @@
public static Fingerprint21 newInstance(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@@ -388,8 +395,9 @@
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler, scheduler);
- return new Fingerprint21(context, biometricStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller, BiometricContext.getInstance(context));
+ return new Fingerprint21(context, biometricStateCallback, authenticationStateListeners,
+ sensorProps, scheduler, handler, lockoutResetDispatcher, controller,
+ BiometricContext.getInstance(context));
}
@Override
@@ -719,7 +727,10 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
+ mBiometricContext, mUdfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ mSidefpsController,
+ mAuthenticationStateListeners, enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -761,10 +772,14 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, null /* sensorProps */,
- mUdfpsOverlayController, mSidefpsController,
+ mUdfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+ mSidefpsController,
+ mAuthenticationStateListeners,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
enrollReason);
+
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -880,6 +895,7 @@
mBiometricContext, isStrongBiometric,
mTaskStackListener,
mUdfpsOverlayController, mSidefpsController,
+ mAuthenticationStateListeners,
allowBackgroundAuthentication, mSensorProperties, mHandler,
Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
@@ -898,6 +914,7 @@
mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
+ mAuthenticationStateListeners,
allowBackgroundAuthentication, mSensorProperties,
Utils.getCurrentStrength(mSensorId));
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
@@ -1123,6 +1140,7 @@
mUdfpsOverlayController = controller;
}
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
mSidefpsController = controller;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index c1a9370..88dae6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -41,6 +41,7 @@
import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -248,6 +249,7 @@
public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@@ -259,8 +261,9 @@
new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
- return new Fingerprint21UdfpsMock(context, biometricStateCallback, sensorProps, scheduler,
- handler, lockoutResetDispatcher, controller, biometricContext);
+ return new Fingerprint21UdfpsMock(context, biometricStateCallback,
+ authenticationStateListeners, sensorProps, scheduler, handler,
+ lockoutResetDispatcher, controller, biometricContext);
}
private static abstract class FakeFingerRunnable implements Runnable {
@@ -388,14 +391,15 @@
private Fingerprint21UdfpsMock(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull MockHalResultController controller,
@NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller, biometricContext);
+ super(context, biometricStateCallback, authenticationStateListeners, sensorProps, scheduler,
+ handler, lockoutResetDispatcher, controller, biometricContext);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 9966e91..4c1d4d6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskStackListener;
@@ -40,6 +42,7 @@
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -68,6 +71,7 @@
@NonNull private final SensorOverlays mSensorOverlays;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
+ @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
private boolean mIsPointerDown;
@@ -81,7 +85,9 @@
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@Authenticators.Types int sensorStrength) {
@@ -91,7 +97,12 @@
false /* shouldVibrate */, sensorStrength);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ }
+ mAuthenticationStateListeners = authenticationStateListeners;
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
}
@@ -128,6 +139,9 @@
mState = STATE_STOPPED;
resetFailedAttempts(getTargetUserId());
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
final @LockoutTracker.LockoutMode int lockoutMode =
@@ -141,6 +155,9 @@
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -156,6 +173,9 @@
}
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
private void resetFailedAttempts(int userId) {
@@ -205,7 +225,10 @@
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+ mSensorOverlays.show(getSensorId(), getRequestReason(), this);
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
+ }
try {
// GroupId was never used. In fact, groupId is always the same as userId.
@@ -215,6 +238,9 @@
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
mCallback.onClientFinished(this, false /* success */);
}
}
@@ -222,6 +248,9 @@
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
try {
getFreshDaemon().cancel();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 0d7f9f2..6e029d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -16,12 +16,14 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
@@ -71,7 +73,12 @@
options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(
+ udfpsOverlayController, null /* sideFpsController */);
+ }
mIsStrongBiometric = isStrongBiometric;
}
@@ -97,7 +104,7 @@
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+ mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 382e7e2..26332ff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -34,6 +36,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -58,22 +61,31 @@
@NonNull private final SensorOverlays mSensorOverlays;
private final @FingerprintManager.EnrollReason int mEnrollReason;
+ @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
private boolean mIsPointerDown;
- FingerprintEnrollClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ FingerprintEnrollClient(
+ @NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
biometricContext);
setRequestId(requestId);
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ if (sidefpsControllerRefactor()) {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+ } else {
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+ }
+ mAuthenticationStateListeners = authenticationStateListeners;
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -110,8 +122,12 @@
@Override
protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+ mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
this);
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStarted(
+ getRequestReasonFromEnrollReason(mEnrollReason));
+ }
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
@@ -122,6 +138,9 @@
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
mCallback.onClientFinished(this, false /* success */);
}
}
@@ -129,6 +148,9 @@
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
try {
getFreshDaemon().cancel();
@@ -149,6 +171,9 @@
if (remaining == 0) {
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
}
@@ -170,6 +195,9 @@
super.onError(errorCode, vendorCode);
mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 7b35589..7f58e75e 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -837,13 +837,11 @@
}
final String removedPackage = packageRemovedInfo.mRemovedPackage;
- final int removedAppId = packageRemovedInfo.mRemovedAppId;
- final int uid = packageRemovedInfo.mUid;
final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+ extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
@@ -888,8 +886,6 @@
boolean removedBySystem,
boolean isArchived) {
final String removedPackage = packageRemovedInfo.mRemovedPackage;
- final int removedAppId = packageRemovedInfo.mRemovedAppId;
- final int uid = packageRemovedInfo.mUid;
final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers;
final int[] instantUserIds = packageRemovedInfo.mInstantUserIds;
@@ -902,8 +898,7 @@
final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib;
Bundle extras = new Bundle();
- final int removedUid = removedAppId >= 0 ? removedAppId : uid;
- extras.putInt(Intent.EXTRA_UID, removedUid);
+ extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
@@ -940,10 +935,10 @@
sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED,
removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
- packageSender.notifyPackageRemoved(removedPackage, removedUid);
+ packageSender.notifyPackageRemoved(removedPackage, packageRemovedInfo.mUid);
}
}
- if (removedAppId >= 0) {
+ if (packageRemovedInfo.mIsAppIdRemoved) {
// If a system app's updates are uninstalled the UID is not actually removed. Some
// services need to know the package name affected.
if (isReplace) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index dcf921c..aa7f0d3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -510,7 +510,10 @@
}
if (clearPackageStateAndReturn) {
mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags);
- outInfo.mRemovedAppId = ps.getAppId();
+ // Legacy behavior to report appId as UID here.
+ // The final broadcasts will contain a per-user UID.
+ outInfo.mUid = ps.getAppId();
+ outInfo.mIsAppIdRemoved = true;
mPm.scheduleWritePackageRestrictions(user);
return;
}
@@ -555,6 +558,7 @@
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
@NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mPm.mLock) {
+ // Since the package is being deleted in all users, report appId as the uid
outInfo.mUid = ps.getAppId();
outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
mPm.snapshotComputer(), ps, allUserHandles,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 6480d64..0fa7aa5 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2912,7 +2912,8 @@
info.mInstallerPackageName = request.getInstallerPackageName();
info.mRemovedUsers = firstUserIds;
info.mBroadcastUsers = firstUserIds;
- info.mRemovedAppId = request.getAppId();
+ info.mUid = request.getAppId();
+ info.mIsAppIdRemoved = true;
info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode();
info.mRemovedForAllUsers = true;
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 9a51cc0..ee780d9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -818,7 +818,8 @@
public void setRemovedAppId(int appId) {
if (mRemovedInfo != null) {
- mRemovedInfo.mRemovedAppId = appId;
+ mRemovedInfo.mUid = appId;
+ mRemovedInfo.mIsAppIdRemoved = true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 215e952..322557b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2872,16 +2872,16 @@
UserHandle.USER_NULL, "runGrantRevokePermission"));
List<PackageInfo> packageInfos;
+ PackageManager pm = mContext.createContextAsUser(translatedUser, 0).getPackageManager();
if (pkg == null) {
- packageInfos = mContext.getPackageManager().getInstalledPackages(
- PackageManager.GET_PERMISSIONS);
+ packageInfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
} else {
try {
- packageInfos = Collections.singletonList(
- mContext.getPackageManager().getPackageInfo(pkg,
- PackageManager.GET_PERMISSIONS));
+ packageInfos = Collections.singletonList(pm.getPackageInfo(pkg,
+ PackageManager.GET_PERMISSIONS));
} catch (NameNotFoundException e) {
getErrPrintWriter().println("Error: package not found");
+ getOutPrintWriter().println("Failure [package not found]");
return 1;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 7ee1772..881b0b3 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -25,7 +25,7 @@
String mRemovedPackage;
String mInstallerPackageName;
int mUid = -1;
- int mRemovedAppId = -1;
+ boolean mIsAppIdRemoved = false;
int[] mOrigUsers;
int[] mRemovedUsers = null;
int[] mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 8ff4a6d..fefab3b 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -347,8 +347,7 @@
mPm.mAppsFilter.removePackage(snapshot,
snapshot.getPackageStateInternal(packageName));
removedAppId = mPm.mSettings.removePackageLPw(packageName);
- outInfo.mRemovedAppId = removedAppId;
-
+ outInfo.mIsAppIdRemoved = true;
if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
// If we don't have a disabled system package to reinstall, the package is
// really gone and its permission state should be removed.
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6f27507..d903ad4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -41,7 +41,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -2081,6 +2080,45 @@
}
@Override
+ public void stopPlayback(IBinder sessionToken, int mode, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "stopPlayback");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).stopPlayback(
+ mode);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in stopPlayback(mode)", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void startPlayback(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "stopPlayback");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in startPlayback()", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index a78f2dc..3b5cae3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -23,6 +23,8 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
@@ -41,6 +43,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -108,6 +111,7 @@
@Mock
IFaceService mFaceService;
@Mock
+
AppOpsManager mAppOpsManager;
@Mock
private VirtualDeviceManagerInternal mVdmInternal;
@@ -404,6 +408,23 @@
eq(TEST_OP_PACKAGE_NAME));
}
+ @Test
+ public void testRegisterAuthenticationStateListener_callsFingerprintService()
+ throws Exception {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final AuthenticationStateListener listener = mock(AuthenticationStateListener.class);
+
+ mAuthService.mImpl.registerAuthenticationStateListener(listener);
+
+ waitForIdle();
+ verify(mFingerprintService).registerAuthenticationStateListener(
+ eq(listener));
+ }
@Test
public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
index 5012335..94cb860 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -23,10 +25,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import androidx.test.filters.SmallTest;
@@ -40,6 +43,7 @@
import java.util.ArrayList;
import java.util.List;
+@RequiresFlagsDisabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
@Presubmit
@SmallTest
public class SensorOverlaysTest {
@@ -97,7 +101,7 @@
private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps)
throws Exception {
final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
- final int reason = BiometricOverlayConstants.REASON_UNKNOWN;
+ final int reason = BiometricRequestConstants.REASON_UNKNOWN;
sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient);
if (udfps != null) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 79a528c..c24227f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -18,6 +18,8 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -56,6 +58,7 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
@@ -68,6 +71,7 @@
import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -91,6 +95,8 @@
@SmallTest
public class FingerprintAuthenticationClientTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final int SENSOR_ID = 4;
private static final int USER_ID = 8;
private static final long OP_ID = 7;
@@ -128,6 +134,8 @@
@Mock
private ISidefpsController mSideFpsController;
@Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
private FingerprintSensorPropertiesInternal mSensorProps;
@Mock
private ClientMonitorCallback mCallback;
@@ -384,6 +392,7 @@
private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
throws RemoteException {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
@@ -398,6 +407,49 @@
}
@Test
+ public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_lockout_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutTimed(5000));
+ }
+
+ @Test
+ public void showHideOverlay_lockoutPerm_sidefpsControllerRemovalRefactor()
+ throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutPermanent());
+ }
+
+ private void showHideOverlay_sidefpsControllerRemovalRefactor(
+ Consumer<FingerprintAuthenticationClient> block) throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+ final FingerprintAuthenticationClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any());
+ verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mAuthenticationStateListeners).onAuthenticationStopped();
+ }
+
+ @Test
public void cancelsAuthWhenNotInForeground() throws Exception {
final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
topTask.topActivity = new ComponentName("other", "thing");
@@ -502,7 +554,8 @@
mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
null /* taskStackListener */,
- mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
+ mUdfpsOverlayController, mSideFpsController, mAuthenticationStateListeners,
+ allowBackgroundAuthentication,
mSensorProps,
new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock,
lockoutTracker) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index c7eb1db..e7d4a2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -38,6 +40,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
@@ -48,6 +51,7 @@
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -68,6 +72,8 @@
@SmallTest
public class FingerprintEnrollClientTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final byte[] HAT = new byte[69];
private static final int USER_ID = 8;
private static final long REQUEST_ID = 9;
@@ -98,6 +104,8 @@
@Mock
private ISidefpsController mSideFpsController;
@Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
private FingerprintSensorPropertiesInternal mSensorProps;
@Mock
private ClientMonitorCallback mCallback;
@@ -271,6 +279,7 @@
private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
throws RemoteException {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
final FingerprintEnrollClient client = createClient();
client.start(mCallback);
@@ -284,6 +293,44 @@
verify(mSideFpsController).hide(anyInt());
}
+ @Test
+ public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_result_sidefpsControllerRemovalRefactor() throws RemoteException {
+ showHideOverlay_sidefpsControllerRemovalRefactor(
+ c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
+ }
+
+ private void showHideOverlay_sidefpsControllerRemovalRefactor(
+ Consumer<FingerprintEnrollClient> block) throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+ final FingerprintEnrollClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any());
+ verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mAuthenticationStateListeners).onAuthenticationStopped();
+ }
+
private FingerprintEnrollClient createClient() throws RemoteException {
return createClient(500);
}
@@ -296,6 +343,7 @@
mClientMonitorCallbackConverter, 0 /* userId */,
HAT, "owner", mBiometricUtils, 8 /* sensorId */,
mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
- mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
+ mSideFpsController, mAuthenticationStateListeners, 6 /* maxTemplatesPerUser */,
+ FingerprintManager.ENROLL_ENROLL);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 8f6efff..4cfb83f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -43,6 +43,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -74,6 +75,8 @@
@Mock
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
private BiometricStateCallback mBiometricStateCallback;
@Mock
private BiometricContext mBiometricContext;
@@ -110,8 +113,9 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
mFingerprintProvider = new FingerprintProvider(mContext,
- mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext, mDaemon);
+ mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
+ mDaemon);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index b32b89a..0d3f192 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -40,6 +40,7 @@
import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -70,6 +71,8 @@
@Mock
private BiometricScheduler mScheduler;
@Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
private BiometricStateCallback mBiometricStateCallback;
@Mock
private BiometricContext mBiometricContext;
@@ -102,9 +105,10 @@
componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN,
resetLockoutRequiresHardwareAuthToken);
- mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback, sensorProps,
- mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
- mHalResultController, mBiometricContext);
+ mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback,
+ mAuthenticationStateListeners, sensorProps, mScheduler,
+ new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController,
+ mBiometricContext);
}
@Test
@@ -126,13 +130,14 @@
TestableFingerprint21(@NonNull Context context,
@NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler, @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller,
@NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller, biometricContext);
+ super(context, biometricStateCallback, authenticationStateListeners, sensorProps,
+ scheduler, handler, lockoutResetDispatcher, controller, biometricContext);
}
@Override
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index eac4d16..cc768bc 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -16,12 +16,16 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.carrier.CarrierIdentifier;
+import android.telephony.TelephonyManager.CarrierRestrictionStatus;
+
+import com.android.internal.telephony.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -104,7 +108,7 @@
private int mCarrierRestrictionDefault;
@MultiSimPolicy
private int mMultiSimPolicy;
- @TelephonyManager.CarrierRestrictionStatus
+ @CarrierRestrictionStatus
private int mCarrierRestrictionStatus;
private CarrierRestrictionRules() {
@@ -293,8 +297,22 @@
return true;
}
- /** @hide */
- public int getCarrierRestrictionStatus() {
+ /**
+ * Get the carrier restriction status of the device.
+ * The return value of the API is as follows.
+ * <ul>
+ * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER}
+ * if the caller and the device locked by the network are same</li>
+ * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED} if the
+ * caller and the device locked by the network are different</li>
+ * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the
+ * device is not locked</li>
+ * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device
+ * locking state is unavailable or radio does not supports the feature</li>
+ * </ul>
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_RESTRICTION_STATUS)
+ public @CarrierRestrictionStatus int getCarrierRestrictionStatus() {
return mCarrierRestrictionStatus;
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3e87872..8679bd4 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1866,7 +1866,7 @@
private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
private boolean mAlwaysOn;
- private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR;
+ private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
private boolean mEsimBootstrapProvisioning;
/**