Merge changes I04f6a54e,Ifb505f6d,Ia9ec3ce2,I97b201c6,Ia12e8cd6, ... into udc-dev am: 9c7f11991d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23120507 Change-Id: I53c40e2b80cd49d1050498f6eb5e04824aa4bea5 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2023 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License
|
||||||
|
-->
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<com.android.keyguard.BouncerKeyguardMessageArea
|
||||||
|
android:id="@+id/bouncer_primary_message_area"
|
||||||
|
style="@style/Keyguard.Bouncer.PrimaryMessage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/keyguard_lock_padding"
|
||||||
|
android:focusable="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.android.keyguard.BouncerKeyguardMessageArea
|
||||||
|
android:id="@+id/bouncer_secondary_message_area"
|
||||||
|
style="@style/Keyguard.Bouncer.SecondaryMessage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/secondary_message_padding"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
</merge>
|
||||||
@@ -29,6 +29,13 @@
|
|||||||
>
|
>
|
||||||
<include layout="@layout/keyguard_bouncer_message_area"/>
|
<include layout="@layout/keyguard_bouncer_message_area"/>
|
||||||
|
|
||||||
|
<com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
|
||||||
|
android:id="@+id/bouncer_message_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
/>
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
|||||||
@@ -33,6 +33,12 @@
|
|||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
<include layout="@layout/keyguard_bouncer_message_area"/>
|
<include layout="@layout/keyguard_bouncer_message_area"/>
|
||||||
|
|
||||||
|
<com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
|
||||||
|
android:id="@+id/bouncer_message_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/pattern_container"
|
android:id="@+id/pattern_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -29,6 +29,12 @@
|
|||||||
androidprv:layout_maxWidth="@dimen/keyguard_security_width">
|
androidprv:layout_maxWidth="@dimen/keyguard_security_width">
|
||||||
<include layout="@layout/keyguard_bouncer_message_area"/>
|
<include layout="@layout/keyguard_bouncer_message_area"/>
|
||||||
|
|
||||||
|
<com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
|
||||||
|
android:id="@+id/bouncer_message_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/pin_container"
|
android:id="@+id/pin_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -23,6 +23,26 @@
|
|||||||
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
|
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
|
||||||
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
|
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
|
||||||
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
|
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
|
||||||
|
<item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
|
||||||
|
</style>
|
||||||
|
<style name="Keyguard.Bouncer.PrimaryMessage" parent="Theme.SystemUI">
|
||||||
|
<item name="android:textSize">18sp</item>
|
||||||
|
<item name="android:lineHeight">24dp</item>
|
||||||
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
||||||
|
<item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
<item name="android:textAlignment">center</item>
|
||||||
|
<item name="android:ellipsize">marquee</item>
|
||||||
|
</style>
|
||||||
|
<style name="Keyguard.Bouncer.SecondaryMessage" parent="Theme.SystemUI">
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:lineHeight">20dp</item>
|
||||||
|
<item name="android:maxLines">@integer/bouncer_secondary_message_lines</item>
|
||||||
|
<item name="android:lines">@integer/bouncer_secondary_message_lines</item>
|
||||||
|
<item name="android:textAlignment">center</item>
|
||||||
|
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
|
||||||
|
<item name="android:ellipsize">end</item>
|
||||||
|
<item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
|
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
|
||||||
<item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
|
<item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
|
||||||
|
|||||||
@@ -577,6 +577,9 @@
|
|||||||
<!-- Whether to show the side fps hint while on bouncer -->
|
<!-- Whether to show the side fps hint while on bouncer -->
|
||||||
<bool name="config_show_sidefps_hint_on_bouncer">true</bool>
|
<bool name="config_show_sidefps_hint_on_bouncer">true</bool>
|
||||||
|
|
||||||
|
<!-- Max number of lines we want to show for the bouncer secondary message -->
|
||||||
|
<integer name="bouncer_secondary_message_lines">2</integer>
|
||||||
|
|
||||||
<!-- Whether to use the split 2-column notification shade -->
|
<!-- Whether to use the split 2-column notification shade -->
|
||||||
<bool name="config_use_split_notification_shade">false</bool>
|
<bool name="config_use_split_notification_shade">false</bool>
|
||||||
|
|
||||||
|
|||||||
@@ -807,6 +807,7 @@
|
|||||||
<!-- The width/height of the unlock icon view on keyguard. -->
|
<!-- The width/height of the unlock icon view on keyguard. -->
|
||||||
<dimen name="keyguard_lock_height">42dp</dimen>
|
<dimen name="keyguard_lock_height">42dp</dimen>
|
||||||
<dimen name="keyguard_lock_padding">20dp</dimen>
|
<dimen name="keyguard_lock_padding">20dp</dimen>
|
||||||
|
<dimen name="secondary_message_padding">8dp</dimen>
|
||||||
|
|
||||||
<dimen name="keyguard_security_container_padding_top">20dp</dimen>
|
<dimen name="keyguard_security_container_padding_top">20dp</dimen>
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import android.animation.AnimatorSet
|
|||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.TypedArray
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.android.app.animation.Interpolators
|
import com.android.app.animation.Interpolators
|
||||||
@@ -41,12 +39,28 @@ open class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
|
|||||||
protected open val SHOW_DURATION_MILLIS = 150L
|
protected open val SHOW_DURATION_MILLIS = 150L
|
||||||
protected open val HIDE_DURATION_MILLIS = 200L
|
protected open val HIDE_DURATION_MILLIS = 200L
|
||||||
|
|
||||||
|
override fun onFinishInflate() {
|
||||||
|
super.onFinishInflate()
|
||||||
|
mDefaultColorState = getColorInStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getColorInStyle(): ColorStateList? {
|
||||||
|
val styledAttributes =
|
||||||
|
context.obtainStyledAttributes(styleResId, intArrayOf(android.R.attr.textColor))
|
||||||
|
var colorStateList: ColorStateList? = null
|
||||||
|
if (styledAttributes != null) {
|
||||||
|
colorStateList = styledAttributes.getColorStateList(0)
|
||||||
|
}
|
||||||
|
styledAttributes.recycle()
|
||||||
|
return colorStateList
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateTextColor() {
|
override fun updateTextColor() {
|
||||||
var colorState = mDefaultColorState
|
var colorState = mDefaultColorState
|
||||||
mNextMessageColorState?.defaultColor?.let { color ->
|
mNextMessageColorState?.defaultColor?.let { color ->
|
||||||
if (color != DEFAULT_COLOR) {
|
if (color != DEFAULT_COLOR) {
|
||||||
colorState = mNextMessageColorState
|
colorState = mNextMessageColorState
|
||||||
mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR)
|
mNextMessageColorState = mDefaultColorState ?: ColorStateList.valueOf(DEFAULT_COLOR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTextColor(colorState)
|
setTextColor(colorState)
|
||||||
@@ -57,15 +71,12 @@ open class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onThemeChanged() {
|
override fun onThemeChanged() {
|
||||||
val array: TypedArray = mContext.obtainStyledAttributes(intArrayOf(TITLE))
|
mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
|
||||||
val newTextColors: ColorStateList = ColorStateList.valueOf(array.getColor(0, Color.RED))
|
|
||||||
array.recycle()
|
|
||||||
mDefaultColorState = newTextColors
|
|
||||||
super.onThemeChanged()
|
super.onThemeChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reloadColor() {
|
override fun reloadColor() {
|
||||||
mDefaultColorState = Utils.getColorAttr(context, TITLE)
|
mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
|
||||||
super.reloadColor()
|
super.reloadColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import android.view.HapticFeedbackConstants;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper;
|
||||||
|
|
||||||
import com.android.internal.widget.LockscreenCredential;
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
|
||||||
@@ -48,7 +50,9 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
|
|||||||
protected abstract void resetState();
|
protected abstract void resetState();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@CallSuper
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
|
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingClassifier;
|
import com.android.systemui.classifier.FalsingClassifier;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -77,9 +78,10 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
|
|||||||
KeyguardSecurityCallback keyguardSecurityCallback,
|
KeyguardSecurityCallback keyguardSecurityCallback,
|
||||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||||
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
|
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
|
||||||
EmergencyButtonController emergencyButtonController) {
|
EmergencyButtonController emergencyButtonController,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
|
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
|
||||||
messageAreaControllerFactory);
|
messageAreaControllerFactory, featureFlags);
|
||||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||||
mLockPatternUtils = lockPatternUtils;
|
mLockPatternUtils = lockPatternUtils;
|
||||||
mLatencyTracker = latencyTracker;
|
mLatencyTracker = latencyTracker;
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ import android.animation.AnimatorListenerAdapter;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.jank.InteractionJankMonitor;
|
import com.android.internal.jank.InteractionJankMonitor;
|
||||||
|
import com.android.systemui.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Base class for all Keyguard password/pattern/pin related inputs.
|
* A Base class for all Keyguard password/pattern/pin related inputs.
|
||||||
@@ -33,6 +36,9 @@ import com.android.internal.jank.InteractionJankMonitor;
|
|||||||
public abstract class KeyguardInputView extends LinearLayout {
|
public abstract class KeyguardInputView extends LinearLayout {
|
||||||
private Runnable mOnFinishImeAnimationRunnable;
|
private Runnable mOnFinishImeAnimationRunnable;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private View mBouncerMessageView;
|
||||||
|
|
||||||
public KeyguardInputView(Context context) {
|
public KeyguardInputView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
@@ -87,6 +93,18 @@ public abstract class KeyguardInputView extends LinearLayout {
|
|||||||
mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
|
mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
mBouncerMessageView = findViewById(R.id.bouncer_message_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public final View getBouncerMessageView() {
|
||||||
|
return mBouncerMessageView;
|
||||||
|
}
|
||||||
|
|
||||||
public void runOnFinishImeAnimationRunnable() {
|
public void runOnFinishImeAnimationRunnable() {
|
||||||
if (mOnFinishImeAnimationRunnable != null) {
|
if (mOnFinishImeAnimationRunnable != null) {
|
||||||
mOnFinishImeAnimationRunnable.run();
|
mOnFinishImeAnimationRunnable.run();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.keyguard;
|
package com.android.keyguard;
|
||||||
|
|
||||||
|
import android.annotation.CallSuper;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@@ -31,6 +32,7 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.statusbar.policy.DevicePostureController;
|
import com.android.systemui.statusbar.policy.DevicePostureController;
|
||||||
import com.android.systemui.util.ViewController;
|
import com.android.systemui.util.ViewController;
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||||
@@ -53,16 +55,19 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
|||||||
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
|
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
|
||||||
// state for the current security method.
|
// state for the current security method.
|
||||||
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
|
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
|
||||||
|
private final FeatureFlags mFeatureFlags;
|
||||||
|
|
||||||
protected KeyguardInputViewController(T view, SecurityMode securityMode,
|
protected KeyguardInputViewController(T view, SecurityMode securityMode,
|
||||||
KeyguardSecurityCallback keyguardSecurityCallback,
|
KeyguardSecurityCallback keyguardSecurityCallback,
|
||||||
EmergencyButtonController emergencyButtonController,
|
EmergencyButtonController emergencyButtonController,
|
||||||
@Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
|
@Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
super(view);
|
super(view);
|
||||||
mSecurityMode = securityMode;
|
mSecurityMode = securityMode;
|
||||||
mKeyguardSecurityCallback = keyguardSecurityCallback;
|
mKeyguardSecurityCallback = keyguardSecurityCallback;
|
||||||
mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
|
mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
|
||||||
mEmergencyButtonController = emergencyButtonController;
|
mEmergencyButtonController = emergencyButtonController;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
if (messageAreaControllerFactory != null) {
|
if (messageAreaControllerFactory != null) {
|
||||||
try {
|
try {
|
||||||
BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
|
BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
|
||||||
@@ -82,9 +87,21 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@CallSuper
|
||||||
protected void onViewAttached() {
|
protected void onViewAttached() {
|
||||||
|
updateMessageAreaVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMessageAreaVisibility() {
|
||||||
|
if (mMessageAreaController == null) return;
|
||||||
|
if (mFeatureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
|
||||||
|
mMessageAreaController.disable();
|
||||||
|
} else {
|
||||||
|
mMessageAreaController.setIsVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onViewDetached() {
|
protected void onViewDetached() {
|
||||||
}
|
}
|
||||||
@@ -208,14 +225,14 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
|||||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||||
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
|
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
|
||||||
emergencyButtonController, mMessageAreaControllerFactory,
|
emergencyButtonController, mMessageAreaControllerFactory,
|
||||||
mDevicePostureController);
|
mDevicePostureController, mFeatureFlags);
|
||||||
} else if (keyguardInputView instanceof KeyguardPasswordView) {
|
} else if (keyguardInputView instanceof KeyguardPasswordView) {
|
||||||
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
|
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
|
||||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||||
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
||||||
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
|
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
|
||||||
mFalsingCollector, mKeyguardViewController);
|
mFalsingCollector, mKeyguardViewController,
|
||||||
|
mFeatureFlags);
|
||||||
} else if (keyguardInputView instanceof KeyguardPINView) {
|
} else if (keyguardInputView instanceof KeyguardPINView) {
|
||||||
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
|
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
|
||||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||||
@@ -227,13 +244,13 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
|||||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||||
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
||||||
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
|
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
|
||||||
emergencyButtonController);
|
emergencyButtonController, mFeatureFlags);
|
||||||
} else if (keyguardInputView instanceof KeyguardSimPukView) {
|
} else if (keyguardInputView instanceof KeyguardSimPukView) {
|
||||||
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
|
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
|
||||||
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
|
||||||
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
|
||||||
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
|
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
|
||||||
emergencyButtonController);
|
emergencyButtonController, mFeatureFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
|
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
|
||||||
|
|||||||
@@ -44,11 +44,18 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
|
|||||||
private ViewGroup mContainer;
|
private ViewGroup mContainer;
|
||||||
private int mTopMargin;
|
private int mTopMargin;
|
||||||
protected boolean mAnimate;
|
protected boolean mAnimate;
|
||||||
|
private final int mStyleResId;
|
||||||
|
private boolean mIsDisabled = false;
|
||||||
|
|
||||||
public KeyguardMessageArea(Context context, AttributeSet attrs) {
|
public KeyguardMessageArea(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
|
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
|
||||||
|
if (attrs != null) {
|
||||||
|
mStyleResId = attrs.getStyleAttribute();
|
||||||
|
} else {
|
||||||
|
// Set to default reference style if the component is used without setting "style" attr
|
||||||
|
mStyleResId = R.style.Keyguard_TextView;
|
||||||
|
}
|
||||||
onThemeChanged();
|
onThemeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,13 +89,17 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDensityOrFontScaleChanged() {
|
void onDensityOrFontScaleChanged() {
|
||||||
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
|
TypedArray array = mContext.obtainStyledAttributes(getStyleResId(), new int[] {
|
||||||
android.R.attr.textSize
|
android.R.attr.textSize
|
||||||
});
|
});
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, array.getDimensionPixelSize(0, 0));
|
setTextSize(TypedValue.COMPLEX_UNIT_PX, array.getDimensionPixelSize(0, 0));
|
||||||
array.recycle();
|
array.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int getStyleResId() {
|
||||||
|
return mStyleResId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessage(CharSequence msg, boolean animate) {
|
public void setMessage(CharSequence msg, boolean animate) {
|
||||||
if (!TextUtils.isEmpty(msg)) {
|
if (!TextUtils.isEmpty(msg)) {
|
||||||
@@ -118,6 +129,10 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
|
if (mIsDisabled) {
|
||||||
|
setVisibility(GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
CharSequence status = mMessage;
|
CharSequence status = mMessage;
|
||||||
setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
|
setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
|
||||||
setText(status);
|
setText(status);
|
||||||
@@ -136,4 +151,17 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
|
|||||||
|
|
||||||
/** Set the text color */
|
/** Set the text color */
|
||||||
protected abstract void updateTextColor();
|
protected abstract void updateTextColor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this view with {@link android.view.View#GONE} visibility to remove this from the layout
|
||||||
|
* of the view. Any calls to {@link #setIsVisible(boolean)} after this will be a no-op.
|
||||||
|
*/
|
||||||
|
public void disable() {
|
||||||
|
mIsDisabled = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisabled() {
|
||||||
|
return mIsDisabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,14 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
|
|||||||
mView.setIsVisible(isVisible);
|
mView.setIsVisible(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this view with {@link View#GONE} visibility to remove this from the layout of the view.
|
||||||
|
* Any calls to {@link #setIsVisible(boolean)} after this will be a no-op.
|
||||||
|
*/
|
||||||
|
public void disable() {
|
||||||
|
mView.disable();
|
||||||
|
}
|
||||||
|
|
||||||
public void setMessage(CharSequence s) {
|
public void setMessage(CharSequence s) {
|
||||||
setMessage(s, true);
|
setMessage(s, true);
|
||||||
}
|
}
|
||||||
@@ -141,6 +149,9 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
|
|||||||
* Sets a message to the underlying text view.
|
* Sets a message to the underlying text view.
|
||||||
*/
|
*/
|
||||||
public void setMessage(CharSequence s, boolean animate) {
|
public void setMessage(CharSequence s, boolean animate) {
|
||||||
|
if (mView.isDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mView.setMessage(s, animate);
|
mView.setMessage(s, animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
|
|||||||
private View[][] mViews;
|
private View[][] mViews;
|
||||||
private int mYTrans;
|
private int mYTrans;
|
||||||
private int mYTransOffset;
|
private int mYTransOffset;
|
||||||
private View mBouncerMessageView;
|
private View mBouncerMessageArea;
|
||||||
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
|
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
|
||||||
public static final long ANIMATION_DURATION = 650;
|
public static final long ANIMATION_DURATION = 650;
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
|
|||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
|
|
||||||
mContainer = findViewById(R.id.pin_container);
|
mContainer = findViewById(R.id.pin_container);
|
||||||
mBouncerMessageView = findViewById(R.id.bouncer_message_area);
|
mBouncerMessageArea = findViewById(R.id.bouncer_message_area);
|
||||||
mViews = new View[][]{
|
mViews = new View[][]{
|
||||||
new View[]{
|
new View[]{
|
||||||
findViewById(R.id.row0), null, null
|
findViewById(R.id.row0), null, null
|
||||||
@@ -221,9 +221,9 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
|
|||||||
Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
|
Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
|
||||||
float standardProgress = standardDecelerate.getInterpolation(progress);
|
float standardProgress = standardDecelerate.getInterpolation(progress);
|
||||||
|
|
||||||
mBouncerMessageView.setTranslationY(
|
mBouncerMessageArea.setTranslationY(
|
||||||
mYTrans - mYTrans * standardProgress);
|
mYTrans - mYTrans * standardProgress);
|
||||||
mBouncerMessageView.setAlpha(standardProgress);
|
mBouncerMessageArea.setAlpha(standardProgress);
|
||||||
|
|
||||||
for (int i = 0; i < mViews.length; i++) {
|
for (int i = 0; i < mViews.length; i++) {
|
||||||
View[] row = mViews[i];
|
View[] row = mViews[i];
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -104,10 +105,11 @@ public class KeyguardPasswordViewController
|
|||||||
@Main DelayableExecutor mainExecutor,
|
@Main DelayableExecutor mainExecutor,
|
||||||
@Main Resources resources,
|
@Main Resources resources,
|
||||||
FalsingCollector falsingCollector,
|
FalsingCollector falsingCollector,
|
||||||
KeyguardViewController keyguardViewController) {
|
KeyguardViewController keyguardViewController,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||||
messageAreaControllerFactory, latencyTracker, falsingCollector,
|
messageAreaControllerFactory, latencyTracker, falsingCollector,
|
||||||
emergencyButtonController);
|
emergencyButtonController, featureFlags);
|
||||||
mKeyguardSecurityCallback = keyguardSecurityCallback;
|
mKeyguardSecurityCallback = keyguardSecurityCallback;
|
||||||
mInputMethodManager = inputMethodManager;
|
mInputMethodManager = inputMethodManager;
|
||||||
mMainExecutor = mainExecutor;
|
mMainExecutor = mainExecutor;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingClassifier;
|
import com.android.systemui.classifier.FalsingClassifier;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.statusbar.policy.DevicePostureController;
|
import com.android.systemui.statusbar.policy.DevicePostureController;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -196,9 +197,9 @@ public class KeyguardPatternViewController
|
|||||||
FalsingCollector falsingCollector,
|
FalsingCollector falsingCollector,
|
||||||
EmergencyButtonController emergencyButtonController,
|
EmergencyButtonController emergencyButtonController,
|
||||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||||
DevicePostureController postureController) {
|
DevicePostureController postureController, FeatureFlags featureFlags) {
|
||||||
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
|
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
|
||||||
messageAreaControllerFactory);
|
messageAreaControllerFactory, featureFlags);
|
||||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||||
mLockPatternUtils = lockPatternUtils;
|
mLockPatternUtils = lockPatternUtils;
|
||||||
mLatencyTracker = latencyTracker;
|
mLatencyTracker = latencyTracker;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import android.util.AttributeSet;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper;
|
||||||
|
|
||||||
import com.android.app.animation.Interpolators;
|
import com.android.app.animation.Interpolators;
|
||||||
import com.android.internal.widget.LockscreenCredential;
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
@@ -147,7 +149,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@CallSuper
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
mPasswordEntry = findViewById(getPasswordTextViewId());
|
mPasswordEntry = findViewById(getPasswordTextViewId());
|
||||||
|
|
||||||
// Set selected property on so the view can send accessibility events.
|
// Set selected property on so the view can send accessibility events.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.android.internal.widget.LockPatternUtils;
|
|||||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
|
||||||
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
|
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
|
||||||
extends KeyguardAbsKeyInputViewController<T> {
|
extends KeyguardAbsKeyInputViewController<T> {
|
||||||
@@ -58,10 +59,11 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
|
|||||||
LatencyTracker latencyTracker,
|
LatencyTracker latencyTracker,
|
||||||
LiftToActivateListener liftToActivateListener,
|
LiftToActivateListener liftToActivateListener,
|
||||||
EmergencyButtonController emergencyButtonController,
|
EmergencyButtonController emergencyButtonController,
|
||||||
FalsingCollector falsingCollector) {
|
FalsingCollector falsingCollector,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||||
messageAreaControllerFactory, latencyTracker, falsingCollector,
|
messageAreaControllerFactory, latencyTracker, falsingCollector,
|
||||||
emergencyButtonController);
|
emergencyButtonController, featureFlags);
|
||||||
mLiftToActivateListener = liftToActivateListener;
|
mLiftToActivateListener = liftToActivateListener;
|
||||||
mFalsingCollector = falsingCollector;
|
mFalsingCollector = falsingCollector;
|
||||||
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
|
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class KeyguardPinViewController
|
|||||||
FeatureFlags featureFlags) {
|
FeatureFlags featureFlags) {
|
||||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||||
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
||||||
emergencyButtonController, falsingCollector);
|
emergencyButtonController, falsingCollector, featureFlags);
|
||||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||||
mPostureController = postureController;
|
mPostureController = postureController;
|
||||||
mLockPatternUtils = lockPatternUtils;
|
mLockPatternUtils = lockPatternUtils;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) :
|
|||||||
private var disableESimButton: KeyguardEsimArea? = null
|
private var disableESimButton: KeyguardEsimArea? = null
|
||||||
|
|
||||||
override fun onFinishInflate() {
|
override fun onFinishInflate() {
|
||||||
|
super.onFinishInflate()
|
||||||
simImageView = findViewById(R.id.keyguard_sim)
|
simImageView = findViewById(R.id.keyguard_sim)
|
||||||
disableESimButton = findViewById(R.id.keyguard_esim_area)
|
disableESimButton = findViewById(R.id.keyguard_esim_area)
|
||||||
super.onFinishInflate()
|
super.onFinishInflate()
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import com.android.internal.widget.LockPatternUtils;
|
|||||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
|
||||||
public class KeyguardSimPinViewController
|
public class KeyguardSimPinViewController
|
||||||
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
|
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
|
||||||
@@ -81,10 +82,10 @@ public class KeyguardSimPinViewController
|
|||||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||||
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
|
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
|
||||||
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
|
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
|
||||||
EmergencyButtonController emergencyButtonController) {
|
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
|
||||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||||
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
||||||
emergencyButtonController, falsingCollector);
|
emergencyButtonController, falsingCollector, featureFlags);
|
||||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||||
mTelephonyManager = telephonyManager;
|
mTelephonyManager = telephonyManager;
|
||||||
mSimImageView = mView.findViewById(R.id.keyguard_sim);
|
mSimImageView = mView.findViewById(R.id.keyguard_sim);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import com.android.internal.widget.LockPatternUtils;
|
|||||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
|
||||||
public class KeyguardSimPukViewController
|
public class KeyguardSimPukViewController
|
||||||
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
|
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
|
||||||
@@ -85,10 +86,10 @@ public class KeyguardSimPukViewController
|
|||||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||||
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
|
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
|
||||||
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
|
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
|
||||||
EmergencyButtonController emergencyButtonController) {
|
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
|
||||||
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
|
||||||
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
|
||||||
emergencyButtonController, falsingCollector);
|
emergencyButtonController, falsingCollector, featureFlags);
|
||||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||||
mTelephonyManager = telephonyManager;
|
mTelephonyManager = telephonyManager;
|
||||||
mSimImageView = mView.findViewById(R.id.keyguard_sim);
|
mSimImageView = mView.findViewById(R.id.keyguard_sim);
|
||||||
|
|||||||
@@ -2635,6 +2635,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
return mAuthController.isUdfpsSupported();
|
return mAuthController.isUdfpsSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the FP sensor is non-UDFPS and the device can be unlocked using fingerprint
|
||||||
|
* at this moment.
|
||||||
|
*/
|
||||||
|
public boolean isFingerprintAllowedInBouncer() {
|
||||||
|
return !isUdfpsSupported() && isUnlockingWithFingerprintAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if there's at least one sfps enrollment for the current user.
|
* @return true if there's at least one sfps enrollment for the current user.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,9 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.keyguard
|
package com.android.systemui.keyguard.bouncer.data.factory
|
||||||
|
|
||||||
import android.annotation.IntDef
|
import android.annotation.IntDef
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel
|
||||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
|
||||||
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
|
||||||
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
|
||||||
@@ -34,6 +35,7 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
|
|||||||
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
|
||||||
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
|
||||||
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
import com.android.systemui.R.string.bouncer_face_not_recognized
|
import com.android.systemui.R.string.bouncer_face_not_recognized
|
||||||
import com.android.systemui.R.string.keyguard_enter_password
|
import com.android.systemui.R.string.keyguard_enter_password
|
||||||
import com.android.systemui.R.string.keyguard_enter_pattern
|
import com.android.systemui.R.string.keyguard_enter_pattern
|
||||||
@@ -71,10 +73,86 @@ import com.android.systemui.R.string.kg_wrong_input_try_fp_suggestion
|
|||||||
import com.android.systemui.R.string.kg_wrong_password_try_again
|
import com.android.systemui.R.string.kg_wrong_password_try_again
|
||||||
import com.android.systemui.R.string.kg_wrong_pattern_try_again
|
import com.android.systemui.R.string.kg_wrong_pattern_try_again
|
||||||
import com.android.systemui.R.string.kg_wrong_pin_try_again
|
import com.android.systemui.R.string.kg_wrong_pin_try_again
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.Message
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
typealias BouncerMessage = Pair<Int, Int>
|
@SysUISingleton
|
||||||
|
class BouncerMessageFactory
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val updateMonitor: KeyguardUpdateMonitor,
|
||||||
|
private val securityModel: KeyguardSecurityModel,
|
||||||
|
) {
|
||||||
|
|
||||||
fun emptyBouncerMessage(): BouncerMessage = Pair(0, 0)
|
fun createFromPromptReason(
|
||||||
|
@BouncerPromptReason reason: Int,
|
||||||
|
userId: Int,
|
||||||
|
): BouncerMessageModel? {
|
||||||
|
val pair =
|
||||||
|
getBouncerMessage(
|
||||||
|
reason,
|
||||||
|
securityModel.getSecurityMode(userId),
|
||||||
|
updateMonitor.isFingerprintAllowedInBouncer
|
||||||
|
)
|
||||||
|
return pair?.let {
|
||||||
|
BouncerMessageModel(
|
||||||
|
message = Message(messageResId = pair.first),
|
||||||
|
secondaryMessage = Message(messageResId = pair.second)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFromString(
|
||||||
|
primaryMsg: String? = null,
|
||||||
|
secondaryMsg: String? = null
|
||||||
|
): BouncerMessageModel =
|
||||||
|
BouncerMessageModel(
|
||||||
|
message = primaryMsg?.let { Message(message = it) },
|
||||||
|
secondaryMessage = secondaryMsg?.let { Message(message = it) },
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method that provides the relevant bouncer message that should be shown for different
|
||||||
|
* scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
|
||||||
|
* provide a more specific message.
|
||||||
|
*/
|
||||||
|
private fun getBouncerMessage(
|
||||||
|
@BouncerPromptReason reason: Int,
|
||||||
|
securityMode: SecurityMode,
|
||||||
|
fpAllowedInBouncer: Boolean = false
|
||||||
|
): Pair<Int, Int>? {
|
||||||
|
return when (reason) {
|
||||||
|
PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
|
||||||
|
PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
|
||||||
|
PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
|
||||||
|
PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
|
||||||
|
PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
|
||||||
|
PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
|
||||||
|
PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
|
||||||
|
PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
|
||||||
|
if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
|
||||||
|
else incorrectSecurityInput(securityMode)
|
||||||
|
PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
|
||||||
|
if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
|
||||||
|
else nonStrongAuthTimeout(securityMode)
|
||||||
|
PROMPT_REASON_TRUSTAGENT_EXPIRED ->
|
||||||
|
if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
|
||||||
|
else trustAgentDisabled(securityMode)
|
||||||
|
PROMPT_REASON_INCORRECT_FACE_INPUT ->
|
||||||
|
if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
|
||||||
|
else incorrectFaceInput(securityMode)
|
||||||
|
PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
|
||||||
|
PROMPT_REASON_DEFAULT ->
|
||||||
|
if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
|
||||||
|
else defaultMessage(securityMode)
|
||||||
|
PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
@IntDef(
|
@IntDef(
|
||||||
@@ -97,48 +175,7 @@ fun emptyBouncerMessage(): BouncerMessage = Pair(0, 0)
|
|||||||
)
|
)
|
||||||
annotation class BouncerPromptReason
|
annotation class BouncerPromptReason
|
||||||
|
|
||||||
/**
|
private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
* Helper method that provides the relevant bouncer message that should be shown for different
|
|
||||||
* scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
|
|
||||||
* provide a more specific message.
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
fun getBouncerMessage(
|
|
||||||
@BouncerPromptReason reason: Int,
|
|
||||||
securityMode: SecurityMode,
|
|
||||||
fpAllowedInBouncer: Boolean = false
|
|
||||||
): BouncerMessage {
|
|
||||||
return when (reason) {
|
|
||||||
PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
|
|
||||||
PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
|
|
||||||
PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
|
|
||||||
PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
|
|
||||||
PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
|
|
||||||
PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
|
|
||||||
PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
|
|
||||||
PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
|
|
||||||
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
|
|
||||||
if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
|
|
||||||
else incorrectSecurityInput(securityMode)
|
|
||||||
PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
|
|
||||||
if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
|
|
||||||
else nonStrongAuthTimeout(securityMode)
|
|
||||||
PROMPT_REASON_TRUSTAGENT_EXPIRED ->
|
|
||||||
if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
|
|
||||||
else trustAgentDisabled(securityMode)
|
|
||||||
PROMPT_REASON_INCORRECT_FACE_INPUT ->
|
|
||||||
if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
|
|
||||||
else incorrectFaceInput(securityMode)
|
|
||||||
PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
|
|
||||||
PROMPT_REASON_DEFAULT ->
|
|
||||||
if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
|
|
||||||
else defaultMessage(securityMode)
|
|
||||||
PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
|
|
||||||
else -> emptyBouncerMessage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun defaultMessage(securityMode: SecurityMode): BouncerMessage {
|
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, 0)
|
SecurityMode.Password -> Pair(keyguard_enter_password, 0)
|
||||||
@@ -147,7 +184,7 @@ fun defaultMessage(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessage {
|
private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
|
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
|
||||||
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
|
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
|
||||||
@@ -156,7 +193,7 @@ fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incorrectSecurityInput(securityMode: SecurityMode): BouncerMessage {
|
private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
|
SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
|
||||||
SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
|
SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
|
||||||
@@ -165,7 +202,7 @@ fun incorrectSecurityInput(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessage {
|
private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
|
SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
|
||||||
SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
|
SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
|
||||||
@@ -174,7 +211,7 @@ fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessage {
|
private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
|
SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
|
||||||
SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
|
SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
|
||||||
@@ -183,7 +220,7 @@ fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incorrectFaceInput(securityMode: SecurityMode): BouncerMessage {
|
private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
|
SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
|
||||||
SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
|
SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
|
||||||
@@ -192,7 +229,7 @@ fun incorrectFaceInput(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
|
private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
|
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
|
||||||
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
|
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
|
||||||
@@ -201,7 +238,7 @@ fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Bounce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun biometricLockout(securityMode: SecurityMode): BouncerMessage {
|
private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
|
||||||
@@ -210,7 +247,7 @@ fun biometricLockout(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessage {
|
private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
|
||||||
@@ -219,7 +256,7 @@ fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessage {
|
private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
|
||||||
@@ -228,7 +265,7 @@ fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessage {
|
private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
|
||||||
SecurityMode.Password ->
|
SecurityMode.Password ->
|
||||||
@@ -238,7 +275,7 @@ fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessage {
|
private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
|
||||||
@@ -247,7 +284,7 @@ fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessage {
|
private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
|
||||||
@@ -256,7 +293,7 @@ fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nonStrongAuthTimeout(securityMode: SecurityMode): BouncerMessage {
|
private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
|
||||||
@@ -265,7 +302,7 @@ fun nonStrongAuthTimeout(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
|
private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
|
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
|
||||||
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
|
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
|
||||||
@@ -274,7 +311,7 @@ fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Boun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun faceUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
|
private fun faceUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
|
||||||
@@ -283,7 +320,7 @@ fun faceUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fingerprintUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
|
private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out)
|
||||||
@@ -292,7 +329,7 @@ fun fingerprintUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trustAgentDisabled(securityMode: SecurityMode): BouncerMessage {
|
private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
|
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
|
||||||
SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
|
SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
|
||||||
@@ -301,7 +338,7 @@ fun trustAgentDisabled(securityMode: SecurityMode): BouncerMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
|
private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
|
SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
|
||||||
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
|
SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
|
||||||
@@ -310,7 +347,7 @@ fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Bounce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessage {
|
private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
|
||||||
return when (securityMode) {
|
return when (securityMode) {
|
||||||
SecurityMode.Pattern ->
|
SecurityMode.Pattern ->
|
||||||
Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
|
Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.data.repository
|
||||||
|
|
||||||
|
import android.hardware.biometrics.BiometricSourceType
|
||||||
|
import android.hardware.biometrics.BiometricSourceType.FACE
|
||||||
|
import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||||
|
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.keyguard.bouncer.data.factory.BouncerMessageFactory
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.TrustRepository
|
||||||
|
import com.android.systemui.user.data.repository.UserRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
|
||||||
|
/** Provide different sources of messages that needs to be shown on the bouncer. */
|
||||||
|
interface BouncerMessageRepository {
|
||||||
|
/**
|
||||||
|
* Messages that are shown in response to the incorrect security attempts on the bouncer and
|
||||||
|
* primary authentication method being locked out, along with countdown messages before primary
|
||||||
|
* auth is active again.
|
||||||
|
*/
|
||||||
|
val primaryAuthMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help messages that are shown to the user on how to successfully perform authentication using
|
||||||
|
* face.
|
||||||
|
*/
|
||||||
|
val faceAcquisitionMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help messages that are shown to the user on how to successfully perform authentication using
|
||||||
|
* fingerprint.
|
||||||
|
*/
|
||||||
|
val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/** Custom message that is displayed when the bouncer is being shown to launch an app. */
|
||||||
|
val customMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messages that are shown in response to biometric authentication attempts through face or
|
||||||
|
* fingerprint.
|
||||||
|
*/
|
||||||
|
val biometricAuthMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/** Messages that are shown when certain auth flags are set. */
|
||||||
|
val authFlagsMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/** Messages that are show after biometrics are locked out temporarily or permanently */
|
||||||
|
val biometricLockedOutMessage: Flow<BouncerMessageModel?>
|
||||||
|
|
||||||
|
/** Set the value for [primaryAuthMessage] */
|
||||||
|
fun setPrimaryAuthMessage(value: BouncerMessageModel?)
|
||||||
|
|
||||||
|
/** Set the value for [faceAcquisitionMessage] */
|
||||||
|
fun setFaceAcquisitionMessage(value: BouncerMessageModel?)
|
||||||
|
/** Set the value for [fingerprintAcquisitionMessage] */
|
||||||
|
fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?)
|
||||||
|
|
||||||
|
/** Set the value for [customMessage] */
|
||||||
|
fun setCustomMessage(value: BouncerMessageModel?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage],
|
||||||
|
* [fingerprintAcquisitionMessage] & [customMessage]
|
||||||
|
*/
|
||||||
|
fun clearMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class BouncerMessageRepositoryImpl
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
trustRepository: TrustRepository,
|
||||||
|
biometricSettingsRepository: BiometricSettingsRepository,
|
||||||
|
updateMonitor: KeyguardUpdateMonitor,
|
||||||
|
private val bouncerMessageFactory: BouncerMessageFactory,
|
||||||
|
private val userRepository: UserRepository,
|
||||||
|
fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
|
||||||
|
) : BouncerMessageRepository {
|
||||||
|
|
||||||
|
private val isFaceEnrolledAndEnabled =
|
||||||
|
and(
|
||||||
|
biometricSettingsRepository.isFaceAuthenticationEnabled,
|
||||||
|
biometricSettingsRepository.isFaceEnrolled
|
||||||
|
)
|
||||||
|
|
||||||
|
private val isFingerprintEnrolledAndEnabled =
|
||||||
|
and(
|
||||||
|
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy,
|
||||||
|
biometricSettingsRepository.isFingerprintEnrolled
|
||||||
|
)
|
||||||
|
|
||||||
|
private val isAnyBiometricsEnabledAndEnrolled =
|
||||||
|
or(isFaceEnrolledAndEnabled, isFingerprintEnrolledAndEnabled)
|
||||||
|
|
||||||
|
private val authFlagsBasedPromptReason: Flow<Int> =
|
||||||
|
combine(
|
||||||
|
biometricSettingsRepository.authenticationFlags,
|
||||||
|
trustRepository.isCurrentUserTrustManaged,
|
||||||
|
isAnyBiometricsEnabledAndEnrolled,
|
||||||
|
::Triple
|
||||||
|
)
|
||||||
|
.map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) ->
|
||||||
|
val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled)
|
||||||
|
return@map if (
|
||||||
|
trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
|
||||||
|
) {
|
||||||
|
PROMPT_REASON_RESTART
|
||||||
|
} else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
|
||||||
|
PROMPT_REASON_TIMEOUT
|
||||||
|
} else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
|
||||||
|
PROMPT_REASON_DEVICE_ADMIN
|
||||||
|
} else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) {
|
||||||
|
PROMPT_REASON_TRUSTAGENT_EXPIRED
|
||||||
|
} else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
|
||||||
|
PROMPT_REASON_TRUSTAGENT_EXPIRED
|
||||||
|
} else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
|
||||||
|
PROMPT_REASON_USER_REQUEST
|
||||||
|
} else if (
|
||||||
|
trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
|
||||||
|
) {
|
||||||
|
PROMPT_REASON_PREPARE_FOR_UPDATE
|
||||||
|
} else if (
|
||||||
|
trustOrBiometricsAvailable &&
|
||||||
|
flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
|
||||||
|
) {
|
||||||
|
PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
|
||||||
|
} else {
|
||||||
|
PROMPT_REASON_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val biometricAuthReason: Flow<Int> =
|
||||||
|
conflatedCallbackFlow {
|
||||||
|
val callback =
|
||||||
|
object : KeyguardUpdateMonitorCallback() {
|
||||||
|
override fun onBiometricAuthFailed(
|
||||||
|
biometricSourceType: BiometricSourceType?
|
||||||
|
) {
|
||||||
|
val promptReason =
|
||||||
|
if (biometricSourceType == FINGERPRINT)
|
||||||
|
PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
|
||||||
|
else if (
|
||||||
|
biometricSourceType == FACE && !updateMonitor.isFaceLockedOut
|
||||||
|
) {
|
||||||
|
PROMPT_REASON_INCORRECT_FACE_INPUT
|
||||||
|
} else PROMPT_REASON_NONE
|
||||||
|
trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricsCleared() {
|
||||||
|
trySendWithFailureLogging(
|
||||||
|
PROMPT_REASON_NONE,
|
||||||
|
TAG,
|
||||||
|
"onBiometricsCleared"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricAcquired(
|
||||||
|
biometricSourceType: BiometricSourceType?,
|
||||||
|
acquireInfo: Int
|
||||||
|
) {
|
||||||
|
trySendWithFailureLogging(
|
||||||
|
PROMPT_REASON_NONE,
|
||||||
|
TAG,
|
||||||
|
"clearBiometricPrompt for new auth session."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricAuthenticated(
|
||||||
|
userId: Int,
|
||||||
|
biometricSourceType: BiometricSourceType?,
|
||||||
|
isStrongBiometric: Boolean
|
||||||
|
) {
|
||||||
|
trySendWithFailureLogging(
|
||||||
|
PROMPT_REASON_NONE,
|
||||||
|
TAG,
|
||||||
|
"onBiometricAuthenticated"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateMonitor.registerCallback(callback)
|
||||||
|
awaitClose { updateMonitor.removeCallback(callback) }
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage
|
||||||
|
|
||||||
|
private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage
|
||||||
|
|
||||||
|
private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> =
|
||||||
|
_fingerprintAcquisitionMessage
|
||||||
|
|
||||||
|
private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val customMessage: Flow<BouncerMessageModel?> = _customMessage
|
||||||
|
|
||||||
|
override val biometricAuthMessage: Flow<BouncerMessageModel?> =
|
||||||
|
biometricAuthReason
|
||||||
|
.map {
|
||||||
|
if (it == PROMPT_REASON_NONE) null
|
||||||
|
else
|
||||||
|
bouncerMessageFactory.createFromPromptReason(
|
||||||
|
it,
|
||||||
|
userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onStart { emit(null) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
override val authFlagsMessage: Flow<BouncerMessageModel?> =
|
||||||
|
authFlagsBasedPromptReason
|
||||||
|
.map {
|
||||||
|
if (it == PROMPT_REASON_NONE) null
|
||||||
|
else
|
||||||
|
bouncerMessageFactory.createFromPromptReason(
|
||||||
|
it,
|
||||||
|
userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onStart { emit(null) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
// TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system
|
||||||
|
// has been launched.
|
||||||
|
private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow {
|
||||||
|
val callback =
|
||||||
|
object : KeyguardUpdateMonitorCallback() {
|
||||||
|
override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
|
||||||
|
if (biometricSourceType == FACE) {
|
||||||
|
trySendWithFailureLogging(
|
||||||
|
updateMonitor.isFaceLockedOut,
|
||||||
|
TAG,
|
||||||
|
"face lock out state changed."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateMonitor.registerCallback(callback)
|
||||||
|
trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value")
|
||||||
|
awaitClose { updateMonitor.removeCallback(callback) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val biometricLockedOutMessage: Flow<BouncerMessageModel?> =
|
||||||
|
combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face ->
|
||||||
|
return@combine if (fp) {
|
||||||
|
bouncerMessageFactory.createFromPromptReason(
|
||||||
|
PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
|
||||||
|
userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
} else if (face) {
|
||||||
|
bouncerMessageFactory.createFromPromptReason(
|
||||||
|
PROMPT_REASON_FACE_LOCKED_OUT,
|
||||||
|
userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
|
||||||
|
_primaryAuthMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
|
||||||
|
_faceAcquisitionMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
|
||||||
|
_fingerprintAcquisitionMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCustomMessage(value: BouncerMessageModel?) {
|
||||||
|
_customMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearMessage() {
|
||||||
|
_fingerprintAcquisitionMessage.value = null
|
||||||
|
_faceAcquisitionMessage.value = null
|
||||||
|
_primaryAuthMessage.value = null
|
||||||
|
_customMessage.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "BouncerDetailedMessageRepository"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
|
||||||
|
flow.combine(anotherFlow) { a, b -> a && b }
|
||||||
|
|
||||||
|
private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
|
||||||
|
flow.combine(anotherFlow) { a, b -> a || b }
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.domain.interactor
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.systemui.CoreStartable
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
private val TAG = BouncerMessageAuditLogger::class.simpleName!!
|
||||||
|
|
||||||
|
/** Logger that echoes bouncer messages state to logcat in debuggable builds. */
|
||||||
|
@SysUISingleton
|
||||||
|
class BouncerMessageAuditLogger
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@Application private val scope: CoroutineScope,
|
||||||
|
private val repository: BouncerMessageRepository,
|
||||||
|
private val interactor: BouncerMessageInteractor,
|
||||||
|
) : CoreStartable {
|
||||||
|
override fun start() {
|
||||||
|
if (Build.isDebuggable()) {
|
||||||
|
collectAndLog(repository.biometricAuthMessage, "biometricMessage: ")
|
||||||
|
collectAndLog(repository.primaryAuthMessage, "primaryAuthMessage: ")
|
||||||
|
collectAndLog(repository.customMessage, "customMessage: ")
|
||||||
|
collectAndLog(repository.faceAcquisitionMessage, "faceAcquisitionMessage: ")
|
||||||
|
collectAndLog(
|
||||||
|
repository.fingerprintAcquisitionMessage,
|
||||||
|
"fingerprintAcquisitionMessage: "
|
||||||
|
)
|
||||||
|
collectAndLog(repository.authFlagsMessage, "authFlagsMessage: ")
|
||||||
|
collectAndLog(interactor.bouncerMessage, "interactor.bouncerMessage: ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectAndLog(flow: Flow<BouncerMessageModel?>, context: String) {
|
||||||
|
scope.launch { flow.collect { Log.d(TAG, context + it) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.domain.interactor
|
||||||
|
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.flags.FeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.user.data.repository.UserRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class BouncerMessageInteractor
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val repository: BouncerMessageRepository,
|
||||||
|
private val factory: BouncerMessageFactory,
|
||||||
|
private val userRepository: UserRepository,
|
||||||
|
private val countDownTimerUtil: CountDownTimerUtil,
|
||||||
|
private val featureFlags: FeatureFlags,
|
||||||
|
) {
|
||||||
|
fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
val callback =
|
||||||
|
object : CountDownTimerCallback {
|
||||||
|
override fun onFinish() {
|
||||||
|
repository.clearMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
val secondsRemaining = (millisUntilFinished / 1000.0).roundToInt()
|
||||||
|
val message =
|
||||||
|
factory.createFromPromptReason(
|
||||||
|
reason = PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
|
||||||
|
userId = userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
message?.message?.animate = false
|
||||||
|
message?.message?.formatterArgs =
|
||||||
|
mutableMapOf<String, Any>(Pair("count", secondsRemaining))
|
||||||
|
repository.setPrimaryAuthMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countDownTimerUtil.startNewTimer(secondsBeforeLockoutReset * 1000, 1000, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPrimaryAuthIncorrectAttempt() {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.setPrimaryAuthMessage(
|
||||||
|
factory.createFromPromptReason(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
userRepository.getSelectedUserInfo().id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFingerprintAcquisitionMessage(value: String?) {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.setFingerprintAcquisitionMessage(
|
||||||
|
if (value != null) {
|
||||||
|
factory.createFromString(secondaryMsg = value)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFaceAcquisitionMessage(value: String?) {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.setFaceAcquisitionMessage(
|
||||||
|
if (value != null) {
|
||||||
|
factory.createFromString(secondaryMsg = value)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCustomMessage(value: String?) {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.setCustomMessage(
|
||||||
|
if (value != null) {
|
||||||
|
factory.createFromString(secondaryMsg = value)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPrimaryBouncerUserInput() {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.clearMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBouncerBeingHidden() {
|
||||||
|
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
|
||||||
|
|
||||||
|
repository.clearMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun firstNonNullMessage(
|
||||||
|
oneMessageModel: Flow<BouncerMessageModel?>,
|
||||||
|
anotherMessageModel: Flow<BouncerMessageModel?>
|
||||||
|
): Flow<BouncerMessageModel?> {
|
||||||
|
return oneMessageModel.combine(anotherMessageModel) { a, b -> a ?: b }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null if feature flag is enabled which gets ignored always or empty bouncer message model that
|
||||||
|
// always maps to an empty string.
|
||||||
|
private fun nullOrEmptyMessage() =
|
||||||
|
flowOf(
|
||||||
|
if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null
|
||||||
|
else factory.createFromString("", "")
|
||||||
|
)
|
||||||
|
|
||||||
|
val bouncerMessage =
|
||||||
|
listOf(
|
||||||
|
nullOrEmptyMessage(),
|
||||||
|
repository.primaryAuthMessage,
|
||||||
|
repository.biometricAuthMessage,
|
||||||
|
repository.fingerprintAcquisitionMessage,
|
||||||
|
repository.faceAcquisitionMessage,
|
||||||
|
repository.customMessage,
|
||||||
|
repository.authFlagsMessage,
|
||||||
|
repository.biometricLockedOutMessage,
|
||||||
|
userRepository.selectedUserInfo.map {
|
||||||
|
factory.createFromPromptReason(PROMPT_REASON_DEFAULT, it.id)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.reduce(::firstNonNullMessage)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CountDownTimerCallback {
|
||||||
|
fun onFinish()
|
||||||
|
fun onTick(millisUntilFinished: Long)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
open class CountDownTimerUtil @Inject constructor() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new count down timer that runs for [millisInFuture] with a tick every
|
||||||
|
* [millisInterval]
|
||||||
|
*/
|
||||||
|
fun startNewTimer(
|
||||||
|
millisInFuture: Long,
|
||||||
|
millisInterval: Long,
|
||||||
|
callback: CountDownTimerCallback,
|
||||||
|
): CountDownTimer {
|
||||||
|
return object : CountDownTimer(millisInFuture, millisInterval) {
|
||||||
|
override fun onFinish() = callback.onFinish()
|
||||||
|
|
||||||
|
override fun onTick(millisUntilFinished: Long) =
|
||||||
|
callback.onTick(millisUntilFinished)
|
||||||
|
}
|
||||||
|
.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.shared.model
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the message displayed on the bouncer. It has two parts, primary and a secondary
|
||||||
|
* message
|
||||||
|
*/
|
||||||
|
data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a single message on the bouncer. It can be either a string or a string resource
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
data class Message(
|
||||||
|
val message: String? = null,
|
||||||
|
val messageResId: Int? = null,
|
||||||
|
val colorState: ColorStateList? = null,
|
||||||
|
/** Any plural formatter arguments that can used to format the [messageResId] */
|
||||||
|
var formatterArgs: Map<String, Any>? = null,
|
||||||
|
/** Specifies whether this text should be animated when it is shown. */
|
||||||
|
var animate: Boolean = true,
|
||||||
|
)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import com.android.keyguard.BouncerKeyguardMessageArea
|
||||||
|
import com.android.keyguard.KeyguardMessageArea
|
||||||
|
import com.android.keyguard.KeyguardMessageAreaController
|
||||||
|
import com.android.systemui.R
|
||||||
|
|
||||||
|
class BouncerMessageView : LinearLayout {
|
||||||
|
constructor(context: Context?) : super(context)
|
||||||
|
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||||
|
|
||||||
|
init {
|
||||||
|
inflate(context, R.layout.bouncer_message_view, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
var primaryMessageView: BouncerKeyguardMessageArea? = null
|
||||||
|
var secondaryMessageView: BouncerKeyguardMessageArea? = null
|
||||||
|
var primaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null
|
||||||
|
var secondaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null
|
||||||
|
override fun onFinishInflate() {
|
||||||
|
super.onFinishInflate()
|
||||||
|
primaryMessageView = findViewById(R.id.bouncer_primary_message_area)
|
||||||
|
secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(factory: KeyguardMessageAreaController.Factory) {
|
||||||
|
primaryMessage = factory.create(primaryMessageView)
|
||||||
|
primaryMessage?.init()
|
||||||
|
secondaryMessage = factory.create(secondaryMessageView)
|
||||||
|
secondaryMessage?.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
|
|||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.android.internal.widget.LockPatternUtils
|
import com.android.internal.widget.LockPatternUtils
|
||||||
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
|
|
||||||
import com.android.systemui.Dumpable
|
import com.android.systemui.Dumpable
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.biometrics.AuthController
|
import com.android.systemui.biometrics.AuthController
|
||||||
@@ -36,13 +35,14 @@ import com.android.systemui.dagger.SysUISingleton
|
|||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
import com.android.systemui.dump.DumpManager
|
import com.android.systemui.dump.DumpManager
|
||||||
import com.android.systemui.keyguard.TAG
|
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
|
||||||
import com.android.systemui.keyguard.shared.model.DevicePosture
|
import com.android.systemui.keyguard.shared.model.DevicePosture
|
||||||
import com.android.systemui.user.data.repository.UserRepository
|
import com.android.systemui.user.data.repository.UserRepository
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -108,10 +108,14 @@ interface BiometricSettingsRepository {
|
|||||||
* lockdown.
|
* lockdown.
|
||||||
*/
|
*/
|
||||||
val isCurrentUserInLockdown: Flow<Boolean>
|
val isCurrentUserInLockdown: Flow<Boolean>
|
||||||
|
|
||||||
|
/** Authentication flags set for the current user. */
|
||||||
|
val authenticationFlags: Flow<AuthenticationFlags>
|
||||||
}
|
}
|
||||||
|
|
||||||
const val TAG = "BiometricsRepositoryImpl"
|
private const val TAG = "BiometricsRepositoryImpl"
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
class BiometricSettingsRepositoryImpl
|
class BiometricSettingsRepositoryImpl
|
||||||
@Inject
|
@Inject
|
||||||
@@ -129,6 +133,8 @@ constructor(
|
|||||||
dumpManager: DumpManager,
|
dumpManager: DumpManager,
|
||||||
) : BiometricSettingsRepository, Dumpable {
|
) : BiometricSettingsRepository, Dumpable {
|
||||||
|
|
||||||
|
private val biometricsEnabledForUser = mutableMapOf<Int, Boolean>()
|
||||||
|
|
||||||
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
||||||
|
|
||||||
private val strongAuthTracker = StrongAuthTracker(userRepository, context)
|
private val strongAuthTracker = StrongAuthTracker(userRepository, context)
|
||||||
@@ -136,6 +142,9 @@ constructor(
|
|||||||
override val isCurrentUserInLockdown: Flow<Boolean> =
|
override val isCurrentUserInLockdown: Flow<Boolean> =
|
||||||
strongAuthTracker.currentUserAuthFlags.map { it.isInUserLockdown }
|
strongAuthTracker.currentUserAuthFlags.map { it.isInUserLockdown }
|
||||||
|
|
||||||
|
override val authenticationFlags: Flow<AuthenticationFlags> =
|
||||||
|
strongAuthTracker.currentUserAuthFlags
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Log.d(TAG, "Registering StrongAuthTracker")
|
Log.d(TAG, "Registering StrongAuthTracker")
|
||||||
lockPatternUtils.registerStrongAuthTracker(strongAuthTracker)
|
lockPatternUtils.registerStrongAuthTracker(strongAuthTracker)
|
||||||
@@ -231,9 +240,14 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val isFaceEnabledByBiometricsManagerForCurrentUser: Flow<Boolean> =
|
||||||
|
userRepository.selectedUserInfo.flatMapLatest { userInfo ->
|
||||||
|
isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false }
|
||||||
|
}
|
||||||
|
|
||||||
override val isFaceAuthenticationEnabled: Flow<Boolean>
|
override val isFaceAuthenticationEnabled: Flow<Boolean>
|
||||||
get() =
|
get() =
|
||||||
combine(isFaceEnabledByBiometricsManager, isFaceEnabledByDevicePolicy) {
|
combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) {
|
||||||
biometricsManagerSetting,
|
biometricsManagerSetting,
|
||||||
devicePolicySetting ->
|
devicePolicySetting ->
|
||||||
biometricsManagerSetting && devicePolicySetting
|
biometricsManagerSetting && devicePolicySetting
|
||||||
@@ -249,13 +263,13 @@ constructor(
|
|||||||
.flowOn(backgroundDispatcher)
|
.flowOn(backgroundDispatcher)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
private val isFaceEnabledByBiometricsManager =
|
private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> =
|
||||||
conflatedCallbackFlow {
|
conflatedCallbackFlow {
|
||||||
val callback =
|
val callback =
|
||||||
object : IBiometricEnabledOnKeyguardCallback.Stub() {
|
object : IBiometricEnabledOnKeyguardCallback.Stub() {
|
||||||
override fun onChanged(enabled: Boolean, userId: Int) {
|
override fun onChanged(enabled: Boolean, userId: Int) {
|
||||||
trySendWithFailureLogging(
|
trySendWithFailureLogging(
|
||||||
enabled,
|
Pair(userId, enabled),
|
||||||
TAG,
|
TAG,
|
||||||
"biometricsEnabled state changed"
|
"biometricsEnabled state changed"
|
||||||
)
|
)
|
||||||
@@ -264,9 +278,10 @@ constructor(
|
|||||||
biometricManager?.registerEnabledOnKeyguardCallback(callback)
|
biometricManager?.registerEnabledOnKeyguardCallback(callback)
|
||||||
awaitClose {}
|
awaitClose {}
|
||||||
}
|
}
|
||||||
|
.onEach { biometricsEnabledForUser[it.first] = it.second }
|
||||||
// This is because the callback is binder-based and we want to avoid multiple callbacks
|
// This is because the callback is binder-based and we want to avoid multiple callbacks
|
||||||
// being registered.
|
// being registered.
|
||||||
.stateIn(scope, SharingStarted.Eagerly, false)
|
.stateIn(scope, SharingStarted.Eagerly, Pair(0, false))
|
||||||
|
|
||||||
override val isStrongBiometricAllowed: StateFlow<Boolean> =
|
override val isStrongBiometricAllowed: StateFlow<Boolean> =
|
||||||
strongAuthTracker.isStrongBiometricAllowed.stateIn(
|
strongAuthTracker.isStrongBiometricAllowed.stateIn(
|
||||||
@@ -306,14 +321,13 @@ constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
|
private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
|
||||||
LockPatternUtils.StrongAuthTracker(context) {
|
LockPatternUtils.StrongAuthTracker(context) {
|
||||||
|
|
||||||
// Backing field for onStrongAuthRequiredChanged
|
// Backing field for onStrongAuthRequiredChanged
|
||||||
private val _strongAuthFlags =
|
private val _authFlags =
|
||||||
MutableStateFlow(
|
MutableStateFlow(AuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId)))
|
||||||
StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Backing field for onIsNonStrongBiometricAllowedChanged
|
// Backing field for onIsNonStrongBiometricAllowedChanged
|
||||||
private val _nonStrongBiometricAllowed =
|
private val _nonStrongBiometricAllowed =
|
||||||
@@ -321,17 +335,15 @@ private class StrongAuthTracker(private val userRepository: UserRepository, cont
|
|||||||
Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
|
Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
|
||||||
)
|
)
|
||||||
|
|
||||||
val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
|
val currentUserAuthFlags: Flow<AuthenticationFlags> =
|
||||||
userRepository.selectedUserInfo
|
userRepository.selectedUserInfo
|
||||||
.map { it.id }
|
.map { it.id }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.flatMapLatest { userId ->
|
.flatMapLatest { userId ->
|
||||||
_strongAuthFlags
|
_authFlags
|
||||||
.filter { it.userId == userId }
|
.map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
|
||||||
.onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
|
.onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
|
||||||
.onStart {
|
.onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
|
||||||
emit(StrongAuthenticationFlags(userId, getStrongAuthForUser(userId)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** isStrongBiometricAllowed for the current user. */
|
/** isStrongBiometricAllowed for the current user. */
|
||||||
@@ -356,7 +368,7 @@ private class StrongAuthTracker(private val userRepository: UserRepository, cont
|
|||||||
|
|
||||||
override fun onStrongAuthRequiredChanged(userId: Int) {
|
override fun onStrongAuthRequiredChanged(userId: Int) {
|
||||||
val newFlags = getStrongAuthForUser(userId)
|
val newFlags = getStrongAuthForUser(userId)
|
||||||
_strongAuthFlags.value = StrongAuthenticationFlags(userId, newFlags)
|
_authFlags.value = AuthenticationFlags(userId, newFlags)
|
||||||
Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
|
Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,11 +387,3 @@ private fun DevicePolicyManager.isFingerprintDisabled(userId: Int): Boolean =
|
|||||||
|
|
||||||
private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
|
private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
|
||||||
(getKeyguardDisabledFeatures(null, userId) and policy) == 0
|
(getKeyguardDisabledFeatures(null, userId) and policy) == 0
|
||||||
|
|
||||||
private data class StrongAuthenticationFlags(val userId: Int, val flag: Int) {
|
|
||||||
val isInUserLockdown = containsFlag(flag, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun containsFlag(haystack: Int, needle: Int): Boolean {
|
|
||||||
return haystack and needle != 0
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
|
|||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
|
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
|
||||||
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
||||||
import com.android.systemui.log.dagger.BouncerLog
|
import com.android.systemui.log.dagger.BouncerTableLog
|
||||||
import com.android.systemui.log.table.TableLogBuffer
|
import com.android.systemui.log.table.TableLogBuffer
|
||||||
import com.android.systemui.log.table.logDiffsForTable
|
import com.android.systemui.log.table.logDiffsForTable
|
||||||
import com.android.systemui.util.time.SystemClock
|
import com.android.systemui.util.time.SystemClock
|
||||||
@@ -105,7 +105,7 @@ class KeyguardBouncerRepositoryImpl
|
|||||||
constructor(
|
constructor(
|
||||||
private val clock: SystemClock,
|
private val clock: SystemClock,
|
||||||
@Application private val applicationScope: CoroutineScope,
|
@Application private val applicationScope: CoroutineScope,
|
||||||
@BouncerLog private val buffer: TableLogBuffer,
|
@BouncerTableLog private val buffer: TableLogBuffer,
|
||||||
) : KeyguardBouncerRepository {
|
) : KeyguardBouncerRepository {
|
||||||
/** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
|
/** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
|
||||||
private val _primaryBouncerShow = MutableStateFlow(false)
|
private val _primaryBouncerShow = MutableStateFlow(false)
|
||||||
|
|||||||
@@ -16,8 +16,14 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.repository
|
package com.android.systemui.keyguard.data.repository
|
||||||
|
|
||||||
|
import com.android.systemui.CoreStartable
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepositoryImpl
|
||||||
|
import com.android.systemui.keyguard.bouncer.domain.interactor.BouncerMessageAuditLogger
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.multibindings.ClassKey
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
interface KeyguardRepositoryModule {
|
interface KeyguardRepositoryModule {
|
||||||
@@ -46,5 +52,13 @@ interface KeyguardRepositoryModule {
|
|||||||
@Binds
|
@Binds
|
||||||
fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
|
fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
fun bouncerMessageRepository(impl: BouncerMessageRepositoryImpl): BouncerMessageRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(BouncerMessageAuditLogger::class)
|
||||||
|
fun bind(impl: BouncerMessageAuditLogger): CoreStartable
|
||||||
|
|
||||||
@Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
|
@Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared.model
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils
|
||||||
|
|
||||||
|
/** Authentication flags corresponding to a user. */
|
||||||
|
data class AuthenticationFlags(val userId: Int, val flag: Int) {
|
||||||
|
val isInUserLockdown =
|
||||||
|
containsFlag(
|
||||||
|
flag,
|
||||||
|
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
|
||||||
|
)
|
||||||
|
|
||||||
|
val isPrimaryAuthRequiredAfterReboot =
|
||||||
|
containsFlag(flag, LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT)
|
||||||
|
|
||||||
|
val isPrimaryAuthRequiredAfterTimeout =
|
||||||
|
containsFlag(flag, LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
|
||||||
|
|
||||||
|
val isPrimaryAuthRequiredAfterDpmLockdown =
|
||||||
|
containsFlag(
|
||||||
|
flag,
|
||||||
|
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
|
||||||
|
)
|
||||||
|
|
||||||
|
val someAuthRequiredAfterUserRequest =
|
||||||
|
containsFlag(flag, LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST)
|
||||||
|
|
||||||
|
val someAuthRequiredAfterTrustAgentExpired =
|
||||||
|
containsFlag(
|
||||||
|
flag,
|
||||||
|
LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
|
||||||
|
)
|
||||||
|
|
||||||
|
val primaryAuthRequiredForUnattendedUpdate =
|
||||||
|
containsFlag(
|
||||||
|
flag,
|
||||||
|
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Either Class 3 biometrics or primary auth can be used to unlock the device. */
|
||||||
|
val strongerAuthRequiredAfterNonStrongBiometricsTimeout =
|
||||||
|
containsFlag(
|
||||||
|
flag,
|
||||||
|
LockPatternUtils.StrongAuthTracker
|
||||||
|
.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containsFlag(haystack: Int, needle: Int): Boolean {
|
||||||
|
return haystack and needle != 0
|
||||||
|
}
|
||||||
@@ -38,7 +38,8 @@ data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationS
|
|||||||
object FailedAuthenticationStatus : AuthenticationStatus()
|
object FailedAuthenticationStatus : AuthenticationStatus()
|
||||||
|
|
||||||
/** Face authentication error message */
|
/** Face authentication error message */
|
||||||
data class ErrorAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus() {
|
data class ErrorAuthenticationStatus(val msgId: Int, val msg: String? = null) :
|
||||||
|
AuthenticationStatus() {
|
||||||
/**
|
/**
|
||||||
* Method that checks if [msgId] is a lockout error. A lockout error means that face
|
* Method that checks if [msgId] is a lockout error. A lockout error means that face
|
||||||
* authentication is locked out.
|
* authentication is locked out.
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.log
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.log.dagger.BouncerLog
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "BouncerLog"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for logging for classes in the [com.android.systemui.keyguard.bouncer] package.
|
||||||
|
*
|
||||||
|
* To enable logcat echoing for an entire buffer:
|
||||||
|
* ```
|
||||||
|
* adb shell settings put global systemui/buffer/BouncerLog <logLevel>
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class BouncerLogger @Inject constructor(@BouncerLog private val buffer: LogBuffer) {
|
||||||
|
fun startBouncerMessageInteractor() {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
"Starting BouncerMessageInteractor.bouncerMessage collector"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bouncerMessageUpdated(bouncerMsg: BouncerMessageModel?) {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
{
|
||||||
|
int1 = bouncerMsg?.message?.messageResId ?: -1
|
||||||
|
str1 = bouncerMsg?.message?.message
|
||||||
|
int2 = bouncerMsg?.secondaryMessage?.messageResId ?: -1
|
||||||
|
str2 = bouncerMsg?.secondaryMessage?.message
|
||||||
|
},
|
||||||
|
{ "Bouncer message update received: $int1, $str1, $int2, $str2" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bindingBouncerMessageView() {
|
||||||
|
buffer.log(TAG, LogLevel.DEBUG, "Binding BouncerMessageView")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.log.dagger
|
package com.android.systemui.log.dagger
|
||||||
|
|
||||||
import java.lang.annotation.Documented
|
|
||||||
import java.lang.annotation.Retention
|
|
||||||
import java.lang.annotation.RetentionPolicy
|
|
||||||
import javax.inject.Qualifier
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
/** Logger for the primary and alternative bouncers. */
|
/** A [com.android.systemui.log.LogBuffer] for bouncer and its child views. */
|
||||||
@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerLog
|
@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BouncerLog()
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.log.dagger
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
/** Logger for the primary and alternative bouncers. */
|
||||||
|
@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerTableLog
|
||||||
@@ -403,6 +403,17 @@ public class LogModule {
|
|||||||
return factory.create("DeviceEntryFaceAuthRepositoryLog", 300);
|
return factory.create("DeviceEntryFaceAuthRepositoryLog", 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a {@link LogBuffer} for use by classes in the
|
||||||
|
* {@link com.android.systemui.keyguard.bouncer} package.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@SysUISingleton
|
||||||
|
@BouncerLog
|
||||||
|
public static LogBuffer provideBouncerLog(LogBufferFactory factory) {
|
||||||
|
return factory.create("BouncerLog", 100);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a {@link LogBuffer} for Device State Auto-Rotation logs.
|
* Provides a {@link LogBuffer} for Device State Auto-Rotation logs.
|
||||||
*/
|
*/
|
||||||
@@ -426,9 +437,9 @@ public class LogModule {
|
|||||||
/** Provides a logging buffer for the primary bouncer. */
|
/** Provides a logging buffer for the primary bouncer. */
|
||||||
@Provides
|
@Provides
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@BouncerLog
|
@BouncerTableLog
|
||||||
public static TableLogBuffer provideBouncerLogBuffer(TableLogBufferFactory factory) {
|
public static TableLogBuffer provideBouncerLogBuffer(TableLogBufferFactory factory) {
|
||||||
return factory.create("BouncerLog", 250);
|
return factory.create("BouncerTableLog", 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides a table logging buffer for the Monitor. */
|
/** Provides a table logging buffer for the Monitor. */
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -77,6 +79,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
private EmergencyButtonController mEmergencyButtonController;
|
private EmergencyButtonController mEmergencyButtonController;
|
||||||
|
|
||||||
|
private FakeFeatureFlags mFeatureFlags;
|
||||||
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
|
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -90,10 +93,18 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
|
|||||||
when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
|
when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
|
||||||
.thenReturn(mKeyguardMessageArea);
|
.thenReturn(mKeyguardMessageArea);
|
||||||
when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
|
when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
|
||||||
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
|
mFeatureFlags = new FakeFeatureFlags();
|
||||||
|
mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false);
|
||||||
|
mKeyguardAbsKeyInputViewController = createTestObject();
|
||||||
|
mKeyguardAbsKeyInputViewController.init();
|
||||||
|
reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyguardAbsKeyInputViewController createTestObject() {
|
||||||
|
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
|
||||||
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
|
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
|
||||||
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
|
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
|
||||||
mEmergencyButtonController) {
|
mEmergencyButtonController, mFeatureFlags) {
|
||||||
@Override
|
@Override
|
||||||
void resetState() {
|
void resetState() {
|
||||||
}
|
}
|
||||||
@@ -108,8 +119,16 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mKeyguardAbsKeyInputViewController.init();
|
}
|
||||||
reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
|
|
||||||
|
@Test
|
||||||
|
public void withFeatureFlagOn_oldMessage_isHidden() {
|
||||||
|
mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
|
||||||
|
KeyguardAbsKeyInputViewController underTest = createTestObject();
|
||||||
|
|
||||||
|
underTest.init();
|
||||||
|
|
||||||
|
verify(mKeyguardMessageAreaController).disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import com.android.internal.widget.LockPatternUtils
|
|||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.classifier.FalsingCollector
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -76,6 +78,8 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
|
|||||||
Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry))
|
Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry))
|
||||||
.thenReturn(passwordEntry)
|
.thenReturn(passwordEntry)
|
||||||
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
|
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
|
||||||
|
val fakeFeatureFlags = FakeFeatureFlags()
|
||||||
|
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||||
keyguardPasswordViewController =
|
keyguardPasswordViewController =
|
||||||
KeyguardPasswordViewController(
|
KeyguardPasswordViewController(
|
||||||
keyguardPasswordView,
|
keyguardPasswordView,
|
||||||
@@ -90,7 +94,8 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
|
|||||||
mainExecutor,
|
mainExecutor,
|
||||||
mContext.resources,
|
mContext.resources,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
keyguardViewController)
|
keyguardViewController,
|
||||||
|
fakeFeatureFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import com.android.systemui.R
|
|||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.classifier.FalsingCollector
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
import com.android.systemui.classifier.FalsingCollectorFake
|
import com.android.systemui.classifier.FalsingCollectorFake
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.statusbar.policy.DevicePostureController
|
import com.android.systemui.statusbar.policy.DevicePostureController
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -72,6 +74,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var mPostureController: DevicePostureController
|
@Mock private lateinit var mPostureController: DevicePostureController
|
||||||
|
|
||||||
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
|
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
|
||||||
|
private lateinit var fakeFeatureFlags: FakeFeatureFlags
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@@ -86,6 +89,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
|
|||||||
`when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
|
`when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
|
||||||
.thenReturn(mKeyguardMessageAreaController)
|
.thenReturn(mKeyguardMessageAreaController)
|
||||||
`when`(mKeyguardPatternView.resources).thenReturn(context.resources)
|
`when`(mKeyguardPatternView.resources).thenReturn(context.resources)
|
||||||
|
fakeFeatureFlags = FakeFeatureFlags()
|
||||||
|
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
|
||||||
mKeyguardPatternViewController =
|
mKeyguardPatternViewController =
|
||||||
KeyguardPatternViewController(
|
KeyguardPatternViewController(
|
||||||
mKeyguardPatternView,
|
mKeyguardPatternView,
|
||||||
@@ -97,7 +102,17 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
|
|||||||
mFalsingCollector,
|
mFalsingCollector,
|
||||||
mEmergencyButtonController,
|
mEmergencyButtonController,
|
||||||
mKeyguardMessageAreaControllerFactory,
|
mKeyguardMessageAreaControllerFactory,
|
||||||
mPostureController)
|
mPostureController,
|
||||||
|
fakeFeatureFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun withFeatureFlagOn_oldMessage_isHidden() {
|
||||||
|
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||||
|
|
||||||
|
mKeyguardPatternViewController.init()
|
||||||
|
|
||||||
|
verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import com.android.systemui.SysuiTestCase;
|
|||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||||
import com.android.systemui.classifier.SingleTapClassifier;
|
import com.android.systemui.classifier.SingleTapClassifier;
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -98,10 +100,13 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
|
|||||||
.thenReturn(mDeleteButton);
|
.thenReturn(mDeleteButton);
|
||||||
when(mPinBasedInputView.findViewById(R.id.key_enter))
|
when(mPinBasedInputView.findViewById(R.id.key_enter))
|
||||||
.thenReturn(mOkButton);
|
.thenReturn(mOkButton);
|
||||||
|
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
|
||||||
|
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
|
||||||
|
|
||||||
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
|
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
|
||||||
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
|
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
|
||||||
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
|
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
|
||||||
mEmergencyButtonController, mFalsingCollector) {
|
mEmergencyButtonController, mFalsingCollector, featureFlags) {
|
||||||
@Override
|
@Override
|
||||||
public void onResume(int reason) {
|
public void onResume(int reason) {
|
||||||
super.onResume(reason);
|
super.onResume(reason);
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ import com.android.systemui.biometrics.SideFpsController;
|
|||||||
import com.android.systemui.biometrics.SideFpsUiRequestSource;
|
import com.android.systemui.biometrics.SideFpsUiRequestSource;
|
||||||
import com.android.systemui.classifier.FalsingA11yDelegate;
|
import com.android.systemui.classifier.FalsingA11yDelegate;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
|
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
|
||||||
import com.android.systemui.log.SessionTracker;
|
import com.android.systemui.log.SessionTracker;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
@@ -195,11 +197,15 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
|||||||
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
|
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
|
||||||
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
|
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
|
||||||
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
|
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
|
||||||
|
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
|
||||||
|
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
|
||||||
|
|
||||||
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
|
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
|
||||||
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
|
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
|
||||||
SecurityMode.Password, mLockPatternUtils, null,
|
SecurityMode.Password, mLockPatternUtils, null,
|
||||||
mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
|
mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
|
||||||
null, mock(Resources.class), null, mKeyguardViewController);
|
null, mock(Resources.class), null, mKeyguardViewController,
|
||||||
|
featureFlags);
|
||||||
|
|
||||||
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
|
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
|
||||||
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
|
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import com.android.internal.widget.LockPatternUtils
|
|||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.classifier.FalsingCollector
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -71,6 +73,9 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
|
|||||||
simPinView =
|
simPinView =
|
||||||
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
|
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
|
||||||
as KeyguardSimPinView
|
as KeyguardSimPinView
|
||||||
|
val fakeFeatureFlags = FakeFeatureFlags()
|
||||||
|
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||||
|
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardSimPinViewController(
|
KeyguardSimPinViewController(
|
||||||
simPinView,
|
simPinView,
|
||||||
@@ -83,7 +88,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
|
|||||||
liftToActivateListener,
|
liftToActivateListener,
|
||||||
telephonyManager,
|
telephonyManager,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
emergencyButtonController
|
emergencyButtonController,
|
||||||
|
fakeFeatureFlags,
|
||||||
)
|
)
|
||||||
underTest.init()
|
underTest.init()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import com.android.internal.widget.LockPatternUtils
|
|||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.classifier.FalsingCollector
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -70,6 +72,9 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() {
|
|||||||
simPukView =
|
simPukView =
|
||||||
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
|
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
|
||||||
as KeyguardSimPukView
|
as KeyguardSimPukView
|
||||||
|
val fakeFeatureFlags = FakeFeatureFlags()
|
||||||
|
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||||
|
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardSimPukViewController(
|
KeyguardSimPukViewController(
|
||||||
simPukView,
|
simPukView,
|
||||||
@@ -82,7 +87,8 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() {
|
|||||||
liftToActivateListener,
|
liftToActivateListener,
|
||||||
telephonyManager,
|
telephonyManager,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
emergencyButtonController
|
emergencyButtonController,
|
||||||
|
fakeFeatureFlags,
|
||||||
)
|
)
|
||||||
underTest.init()
|
underTest.init()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.data.factory
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
|
||||||
|
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.StringSubject
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class BouncerMessageFactoryTest : SysuiTestCase() {
|
||||||
|
private lateinit var underTest: BouncerMessageFactory
|
||||||
|
|
||||||
|
@Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
|
||||||
|
|
||||||
|
@Mock private lateinit var securityModel: KeyguardSecurityModel
|
||||||
|
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
testScope = TestScope()
|
||||||
|
underTest = BouncerMessageFactory(updateMonitor, securityModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
|
||||||
|
testScope.runTest {
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = false)
|
||||||
|
.isEqualTo("Enter PIN")
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = true)
|
||||||
|
.isEqualTo("Unlock with PIN or fingerprint")
|
||||||
|
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = false)
|
||||||
|
.isEqualTo("Enter password")
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = true)
|
||||||
|
.isEqualTo("Unlock with password or fingerprint")
|
||||||
|
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = false)
|
||||||
|
.isEqualTo("Draw pattern")
|
||||||
|
primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = true)
|
||||||
|
.isEqualTo("Unlock with pattern or fingerprint")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
|
||||||
|
testScope.runTest {
|
||||||
|
primaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = PIN,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Wrong PIN. Try again.")
|
||||||
|
secondaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = PIN,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Or unlock with fingerprint")
|
||||||
|
|
||||||
|
primaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = Password,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Wrong password. Try again.")
|
||||||
|
secondaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = Password,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Or unlock with fingerprint")
|
||||||
|
|
||||||
|
primaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = Pattern,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Wrong pattern. Try again.")
|
||||||
|
secondaryMessage(
|
||||||
|
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
|
||||||
|
mode = Pattern,
|
||||||
|
fpAllowedInBouncer = true
|
||||||
|
)
|
||||||
|
.isEqualTo("Or unlock with fingerprint")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun primaryMessage(
|
||||||
|
reason: Int,
|
||||||
|
mode: KeyguardSecurityModel.SecurityMode,
|
||||||
|
fpAllowedInBouncer: Boolean
|
||||||
|
): StringSubject {
|
||||||
|
return assertThat(
|
||||||
|
context.resources.getString(
|
||||||
|
bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!.message!!.messageResId!!
|
||||||
|
)
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun secondaryMessage(
|
||||||
|
reason: Int,
|
||||||
|
mode: KeyguardSecurityModel.SecurityMode,
|
||||||
|
fpAllowedInBouncer: Boolean
|
||||||
|
): StringSubject {
|
||||||
|
return assertThat(
|
||||||
|
context.resources.getString(
|
||||||
|
bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!
|
||||||
|
.secondaryMessage!!
|
||||||
|
.messageResId!!
|
||||||
|
)
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bouncerMessageModel(
|
||||||
|
mode: KeyguardSecurityModel.SecurityMode,
|
||||||
|
fpAllowedInBouncer: Boolean,
|
||||||
|
reason: Int
|
||||||
|
): BouncerMessageModel? {
|
||||||
|
whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
|
||||||
|
whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(fpAllowedInBouncer)
|
||||||
|
|
||||||
|
return underTest.createFromPromptReason(reason, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.data.repository
|
||||||
|
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.hardware.biometrics.BiometricSourceType
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
|
||||||
|
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.R.string.keyguard_enter_pin
|
||||||
|
import com.android.systemui.R.string.kg_prompt_after_dpm_lock
|
||||||
|
import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin
|
||||||
|
import com.android.systemui.R.string.kg_prompt_auth_timeout
|
||||||
|
import com.android.systemui.R.string.kg_prompt_pin_auth_timeout
|
||||||
|
import com.android.systemui.R.string.kg_prompt_reason_restart_pin
|
||||||
|
import com.android.systemui.R.string.kg_prompt_unattended_update
|
||||||
|
import com.android.systemui.R.string.kg_trust_agent_disabled
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.Message
|
||||||
|
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.shared.model.AuthenticationFlags
|
||||||
|
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runCurrent
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class BouncerMessageRepositoryTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
|
||||||
|
@Mock private lateinit var securityModel: KeyguardSecurityModel
|
||||||
|
@Captor
|
||||||
|
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
|
||||||
|
|
||||||
|
private lateinit var underTest: BouncerMessageRepository
|
||||||
|
private lateinit var trustRepository: FakeTrustRepository
|
||||||
|
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
|
||||||
|
private lateinit var userRepository: FakeUserRepository
|
||||||
|
private lateinit var fingerprintRepository: FakeDeviceEntryFingerprintAuthRepository
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
trustRepository = FakeTrustRepository()
|
||||||
|
biometricSettingsRepository = FakeBiometricSettingsRepository()
|
||||||
|
userRepository = FakeUserRepository()
|
||||||
|
userRepository.setUserInfos(listOf(PRIMARY_USER))
|
||||||
|
fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository()
|
||||||
|
testScope = TestScope()
|
||||||
|
|
||||||
|
whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
|
||||||
|
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
|
||||||
|
underTest =
|
||||||
|
BouncerMessageRepositoryImpl(
|
||||||
|
trustRepository = trustRepository,
|
||||||
|
biometricSettingsRepository = biometricSettingsRepository,
|
||||||
|
updateMonitor = updateMonitor,
|
||||||
|
bouncerMessageFactory = BouncerMessageFactory(updateMonitor, securityModel),
|
||||||
|
userRepository = userRepository,
|
||||||
|
fingerprintAuthRepository = fingerprintRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setCustomMessage_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
underTest.setCustomMessage(message("not empty"))
|
||||||
|
|
||||||
|
val customMessage = collectLastValue(underTest.customMessage)
|
||||||
|
|
||||||
|
assertThat(customMessage()).isEqualTo(message("not empty"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setFaceMessage_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
underTest.setFaceAcquisitionMessage(message("not empty"))
|
||||||
|
|
||||||
|
val faceAcquisitionMessage = collectLastValue(underTest.faceAcquisitionMessage)
|
||||||
|
|
||||||
|
assertThat(faceAcquisitionMessage()).isEqualTo(message("not empty"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setFpMessage_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
underTest.setFingerprintAcquisitionMessage(message("not empty"))
|
||||||
|
|
||||||
|
val fpAcquisitionMsg = collectLastValue(underTest.fingerprintAcquisitionMessage)
|
||||||
|
|
||||||
|
assertThat(fpAcquisitionMsg()).isEqualTo(message("not empty"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setPrimaryAuthMessage_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
underTest.setPrimaryAuthMessage(message("not empty"))
|
||||||
|
|
||||||
|
val primaryAuthMessage = collectLastValue(underTest.primaryAuthMessage)
|
||||||
|
|
||||||
|
assertThat(primaryAuthMessage()).isEqualTo(message("not empty"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun biometricAuthMessage_propagatesBiometricAuthMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
val biometricAuthMessage = collectLastValue(underTest.biometricAuthMessage)
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
|
||||||
|
|
||||||
|
updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT)
|
||||||
|
|
||||||
|
assertThat(biometricAuthMessage())
|
||||||
|
.isEqualTo(message(R.string.kg_fp_not_recognized, R.string.kg_bio_try_again_or_pin))
|
||||||
|
|
||||||
|
updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FACE)
|
||||||
|
|
||||||
|
assertThat(biometricAuthMessage())
|
||||||
|
.isEqualTo(
|
||||||
|
message(R.string.bouncer_face_not_recognized, R.string.kg_bio_try_again_or_pin)
|
||||||
|
)
|
||||||
|
|
||||||
|
updateMonitorCallback.value.onBiometricAcquired(BiometricSourceType.FACE, 0)
|
||||||
|
|
||||||
|
assertThat(biometricAuthMessage()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onFaceLockout_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
val lockoutMessage = collectLastValue(underTest.biometricLockedOutMessage)
|
||||||
|
runCurrent()
|
||||||
|
verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
|
||||||
|
|
||||||
|
whenever(updateMonitor.isFaceLockedOut).thenReturn(true)
|
||||||
|
updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
|
||||||
|
|
||||||
|
assertThat(lockoutMessage())
|
||||||
|
.isEqualTo(message(keyguard_enter_pin, R.string.kg_face_locked_out))
|
||||||
|
|
||||||
|
whenever(updateMonitor.isFaceLockedOut).thenReturn(false)
|
||||||
|
updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
|
||||||
|
assertThat(lockoutMessage()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onFingerprintLockout_propagatesState() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
val lockedOutMessage = collectLastValue(underTest.biometricLockedOutMessage)
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
fingerprintRepository.setLockedOut(true)
|
||||||
|
|
||||||
|
assertThat(lockedOutMessage())
|
||||||
|
.isEqualTo(message(keyguard_enter_pin, R.string.kg_fp_locked_out))
|
||||||
|
|
||||||
|
fingerprintRepository.setLockedOut(false)
|
||||||
|
assertThat(lockedOutMessage()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
trustRepository.setCurrentUserTrustManaged(false)
|
||||||
|
biometricSettingsRepository.setFaceEnrolled(false)
|
||||||
|
biometricSettingsRepository.setFingerprintEnrolled(false)
|
||||||
|
|
||||||
|
verifyMessagesForAuthFlag(
|
||||||
|
STRONG_AUTH_NOT_REQUIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_BOOT to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
biometricSettingsRepository.setFaceEnrolled(false)
|
||||||
|
biometricSettingsRepository.setFingerprintEnrolled(false)
|
||||||
|
|
||||||
|
trustRepository.setCurrentUserTrustManaged(true)
|
||||||
|
|
||||||
|
verifyMessagesForAuthFlag(
|
||||||
|
STRONG_AUTH_NOT_REQUIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_BOOT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
|
||||||
|
Pair(keyguard_enter_pin, kg_trust_agent_disabled),
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
|
||||||
|
Pair(keyguard_enter_pin, kg_trust_agent_disabled),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_unattended_update),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
trustRepository.setCurrentUserTrustManaged(false)
|
||||||
|
biometricSettingsRepository.setFingerprintEnrolled(false)
|
||||||
|
|
||||||
|
biometricSettingsRepository.setIsFaceAuthEnabled(true)
|
||||||
|
biometricSettingsRepository.setFaceEnrolled(true)
|
||||||
|
|
||||||
|
verifyMessagesForAuthFlag(
|
||||||
|
STRONG_AUTH_NOT_REQUIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_BOOT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_unattended_update),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
trustRepository.setCurrentUserTrustManaged(false)
|
||||||
|
biometricSettingsRepository.setIsFaceAuthEnabled(false)
|
||||||
|
biometricSettingsRepository.setFaceEnrolled(false)
|
||||||
|
|
||||||
|
biometricSettingsRepository.setFingerprintEnrolled(true)
|
||||||
|
biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
|
||||||
|
|
||||||
|
verifyMessagesForAuthFlag(
|
||||||
|
STRONG_AUTH_NOT_REQUIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
|
||||||
|
SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_BOOT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
|
||||||
|
STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_unattended_update),
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
|
||||||
|
Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.verifyMessagesForAuthFlag(
|
||||||
|
vararg authFlagToExpectedMessages: Pair<Int, Pair<Int, Int>?>
|
||||||
|
) {
|
||||||
|
val authFlagsMessage = collectLastValue(underTest.authFlagsMessage)
|
||||||
|
|
||||||
|
authFlagToExpectedMessages.forEach { (flag, messagePair) ->
|
||||||
|
biometricSettingsRepository.setAuthenticationFlags(
|
||||||
|
AuthenticationFlags(PRIMARY_USER_ID, flag)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(authFlagsMessage())
|
||||||
|
.isEqualTo(messagePair?.let { message(it.first, it.second) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel {
|
||||||
|
return BouncerMessageModel(
|
||||||
|
message = Message(messageResId = primaryResId),
|
||||||
|
secondaryMessage = Message(messageResId = secondaryResId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
private fun message(value: String): BouncerMessageModel {
|
||||||
|
return BouncerMessageModel(message = Message(message = value))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PRIMARY_USER_ID = 0
|
||||||
|
private val PRIMARY_USER =
|
||||||
|
UserInfo(
|
||||||
|
/* id= */ PRIMARY_USER_ID,
|
||||||
|
/* name= */ "primary user",
|
||||||
|
/* flags= */ UserInfo.FLAG_PRIMARY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* 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.bouncer.domain.interactor
|
||||||
|
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel
|
||||||
|
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
|
import com.android.systemui.R.string.keyguard_enter_pin
|
||||||
|
import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.FlowValue
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.Message
|
||||||
|
import com.android.systemui.keyguard.data.repository.FakeBouncerMessageRepository
|
||||||
|
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||||
|
import com.android.systemui.util.mockito.KotlinArgumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.eq
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class BouncerMessageInteractorTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var securityModel: KeyguardSecurityModel
|
||||||
|
@Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
|
||||||
|
@Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
|
||||||
|
private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback>
|
||||||
|
private lateinit var underTest: BouncerMessageInteractor
|
||||||
|
private lateinit var repository: FakeBouncerMessageRepository
|
||||||
|
private lateinit var userRepository: FakeUserRepository
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
private lateinit var bouncerMessage: FlowValue<BouncerMessageModel?>
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
repository = FakeBouncerMessageRepository()
|
||||||
|
userRepository = FakeUserRepository()
|
||||||
|
userRepository.setUserInfos(listOf(PRIMARY_USER))
|
||||||
|
testScope = TestScope()
|
||||||
|
countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
|
||||||
|
|
||||||
|
allowTestableLooperAsMainThread()
|
||||||
|
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
|
||||||
|
whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun TestScope.init() {
|
||||||
|
userRepository.setSelectedUserInfo(PRIMARY_USER)
|
||||||
|
val featureFlags = FakeFeatureFlags()
|
||||||
|
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||||
|
underTest =
|
||||||
|
BouncerMessageInteractor(
|
||||||
|
repository = repository,
|
||||||
|
factory = BouncerMessageFactory(updateMonitor, securityModel),
|
||||||
|
userRepository = userRepository,
|
||||||
|
countDownTimerUtil = countDownTimerUtil,
|
||||||
|
featureFlags = featureFlags
|
||||||
|
)
|
||||||
|
bouncerMessage = collectLastValue(underTest.bouncerMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onIncorrectSecurityInput_setsTheBouncerModelInTheRepository() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
underTest.onPrimaryAuthIncorrectAttempt()
|
||||||
|
|
||||||
|
assertThat(repository.primaryAuthMessage).isNotNull()
|
||||||
|
assertThat(
|
||||||
|
context.resources.getString(
|
||||||
|
repository.primaryAuthMessage.value!!.message!!.messageResId!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.isEqualTo("Wrong PIN. Try again.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onUserStartsPrimaryAuthInput_clearsAllSetBouncerMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
repository.setCustomMessage(message("not empty"))
|
||||||
|
repository.setFaceAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setFingerprintAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setPrimaryAuthMessage(message("not empty"))
|
||||||
|
|
||||||
|
underTest.onPrimaryBouncerUserInput()
|
||||||
|
|
||||||
|
assertThat(repository.customMessage.value).isNull()
|
||||||
|
assertThat(repository.faceAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.primaryAuthMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onBouncerBeingHidden_clearsAllSetBouncerMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
repository.setCustomMessage(message("not empty"))
|
||||||
|
repository.setFaceAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setFingerprintAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setPrimaryAuthMessage(message("not empty"))
|
||||||
|
|
||||||
|
underTest.onBouncerBeingHidden()
|
||||||
|
|
||||||
|
assertThat(repository.customMessage.value).isNull()
|
||||||
|
assertThat(repository.faceAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.primaryAuthMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setCustomMessage_setsRepositoryValue() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
|
||||||
|
underTest.setCustomMessage("not empty")
|
||||||
|
|
||||||
|
assertThat(repository.customMessage.value)
|
||||||
|
.isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
|
||||||
|
|
||||||
|
underTest.setCustomMessage(null)
|
||||||
|
assertThat(repository.customMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setFaceMessage_setsRepositoryValue() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
|
||||||
|
underTest.setFaceAcquisitionMessage("not empty")
|
||||||
|
|
||||||
|
assertThat(repository.faceAcquisitionMessage.value)
|
||||||
|
.isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
|
||||||
|
|
||||||
|
underTest.setFaceAcquisitionMessage(null)
|
||||||
|
assertThat(repository.faceAcquisitionMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setFingerprintMessage_setsRepositoryValue() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
|
||||||
|
underTest.setFingerprintAcquisitionMessage("not empty")
|
||||||
|
|
||||||
|
assertThat(repository.fingerprintAcquisitionMessage.value)
|
||||||
|
.isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
|
||||||
|
|
||||||
|
underTest.setFingerprintAcquisitionMessage(null)
|
||||||
|
assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onPrimaryAuthLockout_startsTimerForSpecifiedNumberOfSeconds() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
|
||||||
|
underTest.onPrimaryAuthLockedOut(3)
|
||||||
|
|
||||||
|
verify(countDownTimerUtil)
|
||||||
|
.startNewTimer(eq(3000L), eq(1000L), countDownTimerCallback.capture())
|
||||||
|
|
||||||
|
countDownTimerCallback.value.onTick(2000L)
|
||||||
|
|
||||||
|
val primaryMessage = repository.primaryAuthMessage.value!!.message!!
|
||||||
|
assertThat(primaryMessage.messageResId!!)
|
||||||
|
.isEqualTo(kg_too_many_failed_attempts_countdown)
|
||||||
|
assertThat(primaryMessage.formatterArgs).isEqualTo(mapOf(Pair("count", 2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onPrimaryAuthLockout_timerComplete_resetsRepositoryMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
repository.setCustomMessage(message("not empty"))
|
||||||
|
repository.setFaceAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setFingerprintAcquisitionMessage(message("not empty"))
|
||||||
|
repository.setPrimaryAuthMessage(message("not empty"))
|
||||||
|
|
||||||
|
underTest.onPrimaryAuthLockedOut(3)
|
||||||
|
|
||||||
|
verify(countDownTimerUtil)
|
||||||
|
.startNewTimer(eq(3000L), eq(1000L), countDownTimerCallback.capture())
|
||||||
|
|
||||||
|
countDownTimerCallback.value.onFinish()
|
||||||
|
|
||||||
|
assertThat(repository.customMessage.value).isNull()
|
||||||
|
assertThat(repository.faceAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
|
||||||
|
assertThat(repository.primaryAuthMessage.value).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bouncerMessage_hasPriorityOrderOfMessages() =
|
||||||
|
testScope.runTest {
|
||||||
|
init()
|
||||||
|
repository.setBiometricAuthMessage(message("biometric message"))
|
||||||
|
repository.setFaceAcquisitionMessage(message("face acquisition message"))
|
||||||
|
repository.setFingerprintAcquisitionMessage(message("fingerprint acquisition message"))
|
||||||
|
repository.setPrimaryAuthMessage(message("primary auth message"))
|
||||||
|
repository.setAuthFlagsMessage(message("auth flags message"))
|
||||||
|
repository.setBiometricLockedOutMessage(message("biometrics locked out"))
|
||||||
|
repository.setCustomMessage(message("custom message"))
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("primary auth message"))
|
||||||
|
|
||||||
|
repository.setPrimaryAuthMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("biometric message"))
|
||||||
|
|
||||||
|
repository.setBiometricAuthMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("fingerprint acquisition message"))
|
||||||
|
|
||||||
|
repository.setFingerprintAcquisitionMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("face acquisition message"))
|
||||||
|
|
||||||
|
repository.setFaceAcquisitionMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("custom message"))
|
||||||
|
|
||||||
|
repository.setCustomMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("auth flags message"))
|
||||||
|
|
||||||
|
repository.setAuthFlagsMessage(null)
|
||||||
|
|
||||||
|
assertThat(bouncerMessage()).isEqualTo(message("biometrics locked out"))
|
||||||
|
|
||||||
|
repository.setBiometricLockedOutMessage(null)
|
||||||
|
|
||||||
|
// sets the default message if everything else is null
|
||||||
|
assertThat(bouncerMessage()!!.message!!.messageResId).isEqualTo(keyguard_enter_pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun message(value: String): BouncerMessageModel {
|
||||||
|
return BouncerMessageModel(message = Message(message = value))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PRIMARY_USER_ID = 0
|
||||||
|
private val PRIMARY_USER =
|
||||||
|
UserInfo(
|
||||||
|
/* id= */ PRIMARY_USER_ID,
|
||||||
|
/* name= */ "primary user",
|
||||||
|
/* flags= */ UserInfo.FLAG_PRIMARY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -310,19 +310,38 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
|||||||
createBiometricSettingsRepository()
|
createBiometricSettingsRepository()
|
||||||
verify(biometricManager)
|
verify(biometricManager)
|
||||||
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
|
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
|
||||||
|
|
||||||
whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
|
whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
|
||||||
.thenReturn(0)
|
.thenReturn(0)
|
||||||
broadcastDPMStateChange()
|
broadcastDPMStateChange()
|
||||||
|
|
||||||
biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
|
|
||||||
val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
|
val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
|
||||||
|
|
||||||
assertThat(isFaceAuthEnabled()).isTrue()
|
assertThat(isFaceAuthEnabled()).isFalse()
|
||||||
|
|
||||||
biometricManagerCallback.value.onChanged(false, PRIMARY_USER_ID)
|
// Value changes for another user
|
||||||
|
biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
|
||||||
|
|
||||||
assertThat(isFaceAuthEnabled()).isFalse()
|
assertThat(isFaceAuthEnabled()).isFalse()
|
||||||
|
|
||||||
|
// Value changes for current user.
|
||||||
|
biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
|
||||||
|
|
||||||
|
assertThat(isFaceAuthEnabled()).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun userChange_biometricEnabledChange_handlesRaceCondition() =
|
||||||
|
testScope.runTest {
|
||||||
|
createBiometricSettingsRepository()
|
||||||
|
verify(biometricManager)
|
||||||
|
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
|
||||||
|
val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
|
||||||
|
biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
userRepository.setSelectedUserInfo(ANOTHER_USER)
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
assertThat(isFaceAuthEnabled()).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -382,7 +401,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun userInLockdownUsesStrongAuthFlagsToDetermineValue() =
|
fun userInLockdownUsesAuthFlagsToDetermineValue() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
createBiometricSettingsRepository()
|
createBiometricSettingsRepository()
|
||||||
|
|
||||||
@@ -405,6 +424,38 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
|||||||
assertThat(isUserInLockdown()).isTrue()
|
assertThat(isUserInLockdown()).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun authFlagChangesForCurrentUserArePropagated() =
|
||||||
|
testScope.runTest {
|
||||||
|
createBiometricSettingsRepository()
|
||||||
|
|
||||||
|
val authFlags = collectLastValue(underTest.authenticationFlags)
|
||||||
|
// has default value.
|
||||||
|
val defaultStrongAuthValue = STRONG_AUTH_REQUIRED_AFTER_BOOT
|
||||||
|
assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
|
||||||
|
|
||||||
|
// change strong auth flags for another user.
|
||||||
|
// Combine with one more flag to check if we do the bitwise and
|
||||||
|
val inLockdown =
|
||||||
|
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
|
||||||
|
onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
|
||||||
|
|
||||||
|
// Still false.
|
||||||
|
assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
|
||||||
|
|
||||||
|
// change strong auth flags for current user.
|
||||||
|
onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
|
||||||
|
|
||||||
|
assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
|
||||||
|
|
||||||
|
onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, ANOTHER_USER_ID)
|
||||||
|
|
||||||
|
assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
|
||||||
|
|
||||||
|
userRepository.setSelectedUserInfo(ANOTHER_USER)
|
||||||
|
assertThat(authFlags()!!.flag).isEqualTo(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
|
||||||
|
}
|
||||||
|
|
||||||
private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
|
private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
|
||||||
authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
|
authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,13 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.repository
|
package com.android.systemui.keyguard.data.repository
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils
|
||||||
|
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
||||||
|
|
||||||
@@ -50,9 +53,12 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
|||||||
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
||||||
get() = _isFaceAuthSupportedInCurrentPosture
|
get() = _isFaceAuthSupportedInCurrentPosture
|
||||||
|
|
||||||
private val _isCurrentUserInLockdown = MutableStateFlow(false)
|
|
||||||
override val isCurrentUserInLockdown: Flow<Boolean>
|
override val isCurrentUserInLockdown: Flow<Boolean>
|
||||||
get() = _isCurrentUserInLockdown
|
get() = _authFlags.map { it.isInUserLockdown }
|
||||||
|
|
||||||
|
private val _authFlags = MutableStateFlow(AuthenticationFlags(0, 0))
|
||||||
|
override val authenticationFlags: Flow<AuthenticationFlags>
|
||||||
|
get() = _authFlags
|
||||||
|
|
||||||
fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
|
fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
|
||||||
_isFingerprintEnrolled.value = isFingerprintEnrolled
|
_isFingerprintEnrolled.value = isFingerprintEnrolled
|
||||||
@@ -66,6 +72,10 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
|||||||
_isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy
|
_isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAuthenticationFlags(value: AuthenticationFlags) {
|
||||||
|
_authFlags.value = value
|
||||||
|
}
|
||||||
|
|
||||||
fun setFaceEnrolled(isFaceEnrolled: Boolean) {
|
fun setFaceEnrolled(isFaceEnrolled: Boolean) {
|
||||||
_isFaceEnrolled.value = isFaceEnrolled
|
_isFaceEnrolled.value = isFaceEnrolled
|
||||||
}
|
}
|
||||||
@@ -79,7 +89,22 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setIsUserInLockdown(value: Boolean) {
|
fun setIsUserInLockdown(value: Boolean) {
|
||||||
_isCurrentUserInLockdown.value = value
|
if (value) {
|
||||||
|
setAuthenticationFlags(
|
||||||
|
AuthenticationFlags(
|
||||||
|
_authFlags.value.userId,
|
||||||
|
_authFlags.value.flag or
|
||||||
|
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
setAuthenticationFlags(
|
||||||
|
AuthenticationFlags(
|
||||||
|
_authFlags.value.userId,
|
||||||
|
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIsNonStrongBiometricAllowed(value: Boolean) {
|
fun setIsNonStrongBiometricAllowed(value: Boolean) {
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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.data.repository
|
||||||
|
|
||||||
|
import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
|
||||||
|
import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
class FakeBouncerMessageRepository : BouncerMessageRepository {
|
||||||
|
private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val primaryAuthMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _primaryAuthMessage
|
||||||
|
|
||||||
|
private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val faceAcquisitionMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _faceAcquisitionMessage
|
||||||
|
private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val fingerprintAcquisitionMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _fingerprintAcquisitionMessage
|
||||||
|
private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val customMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _customMessage
|
||||||
|
private val _biometricAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val biometricAuthMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _biometricAuthMessage
|
||||||
|
private val _authFlagsMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val authFlagsMessage: StateFlow<BouncerMessageModel?>
|
||||||
|
get() = _authFlagsMessage
|
||||||
|
|
||||||
|
private val _biometricLockedOutMessage = MutableStateFlow<BouncerMessageModel?>(null)
|
||||||
|
override val biometricLockedOutMessage: Flow<BouncerMessageModel?>
|
||||||
|
get() = _biometricLockedOutMessage
|
||||||
|
|
||||||
|
override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
|
||||||
|
_primaryAuthMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
|
||||||
|
_faceAcquisitionMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
|
||||||
|
_fingerprintAcquisitionMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCustomMessage(value: BouncerMessageModel?) {
|
||||||
|
_customMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBiometricAuthMessage(value: BouncerMessageModel?) {
|
||||||
|
_biometricAuthMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAuthFlagsMessage(value: BouncerMessageModel?) {
|
||||||
|
_authFlagsMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBiometricLockedOutMessage(value: BouncerMessageModel?) {
|
||||||
|
_biometricLockedOutMessage.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearMessage() {
|
||||||
|
_primaryAuthMessage.value = null
|
||||||
|
_faceAcquisitionMessage.value = null
|
||||||
|
_fingerprintAcquisitionMessage.value = null
|
||||||
|
_customMessage.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user