Loading packages/SystemUI/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,7 @@ android_library { "androidx.compose.material_material-icons-extended", "androidx.activity_activity-compose", "androidx.compose.animation_animation-graphics", "device_policy_aconfig_flags_lib", ], libs: [ "keepanno-annotations", Loading Loading @@ -622,6 +623,7 @@ android_app { "//frameworks/libs/systemui:compilelib", "SystemUI-tests-base", "androidx.compose.runtime_runtime", "SystemUI-core", ], libs: [ "keepanno-annotations", Loading packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +44 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ package com.android.keyguard import android.app.admin.DevicePolicyManager import android.app.admin.flags.Flags as DevicePolicyFlags import android.content.res.Configuration import android.media.AudioManager import android.platform.test.annotations.EnableFlags import android.telephony.TelephonyManager import android.testing.TestableLooper.RunWithLooper import android.testing.TestableResources Loading Loading @@ -148,6 +151,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var postureController: DevicePostureController @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Captor private lateinit var swipeListenerArgumentCaptor: Loading Loading @@ -273,6 +277,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { mSelectedUserInteractor, deviceProvisionedController, faceAuthAccessibilityDelegate, devicePolicyManager, keyguardTransitionInteractor, { primaryBouncerInteractor }, ) { Loading Loading @@ -934,6 +939,45 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { verify(viewFlipperController).asynchronouslyInflateView(any(), any(), any()) } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() { val mainUserId = 10 underTest.showMessageForFailedUnlockAttempt( /* userId = */ mainUserId, /* expiringUserId = */ mainUserId, /* mainUserId = */ mainUserId, /* remainingBeforeWipe = */ 1, /* failedAttempts = */ 1 ) verify(view) .showAlmostAtWipeDialog(any(), any(), eq(KeyguardSecurityContainer.USER_TYPE_PRIMARY)) } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() { val secondaryUserId = 10 val mainUserId = 0 underTest.showMessageForFailedUnlockAttempt( /* userId = */ secondaryUserId, /* expiringUserId = */ secondaryUserId, /* mainUserId = */ mainUserId, /* remainingBeforeWipe = */ 1, /* failedAttempts = */ 1 ) verify(view) .showAlmostAtWipeDialog( any(), any(), eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER) ) } private val registeredSwipeListener: KeyguardSecurityContainer.SwipeListener get() { underTest.onViewAttached() Loading packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +8 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.DevicePolicyManager import android.app.admin.flags.Flags as DevicePolicyFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils Loading @@ -32,6 +34,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -410,12 +414,16 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun upcomingWipe() = testScope.runTest { val upcomingWipe by collectLastValue(underTest.upcomingWipe) kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin) val correctPin = FakeAuthenticationRepository.DEFAULT_PIN val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 } kosmos.fakeUserRepository.asMainUser() kosmos.fakeAuthenticationRepository.profileWithMinFailedUnlockAttemptsForWipe = FakeUserRepository.MAIN_USER_ID underTest.authenticate(correctPin) assertThat(upcomingWipe).isNull() Loading packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +44 −23 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.app.admin.flags.Flags; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; Loading Loading @@ -134,6 +135,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final BouncerMessageInteractor mBouncerMessageInteractor; private int mTranslationY; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final DevicePolicyManager mDevicePolicyManager; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise // the audio service will bring up the volume dialog. Loading Loading @@ -460,6 +462,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard SelectedUserInteractor selectedUserInteractor, DeviceProvisionedController deviceProvisionedController, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, DevicePolicyManager devicePolicyManager, KeyguardTransitionInteractor keyguardTransitionInteractor, Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor, Provider<DeviceEntryInteractor> deviceEntryInteractor Loading Loading @@ -495,6 +498,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardTransitionInteractor = keyguardTransitionInteractor; mDeviceProvisionedController = deviceProvisionedController; mPrimaryBouncerInteractor = primaryBouncerInteractor; mDevicePolicyManager = devicePolicyManager; } @Override Loading Loading @@ -1105,45 +1109,62 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); final int failedAttemptsBeforeWipe = dpm.getMaximumFailedPasswordsForWipe(null, userId); mDevicePolicyManager.getMaximumFailedPasswordsForWipe(null, userId); final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? (failedAttemptsBeforeWipe - failedAttempts) : Integer.MAX_VALUE; // because DPM returns 0 if no restriction if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { // The user has installed a DevicePolicyManager that requests a user/profile to be wiped // N attempts. Once we get below the grace period, we post this dialog every time as a // clear warning until the deletion fires. // Check which profile has the strictest policy for failed password attempts final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); // The user has installed a DevicePolicyManager that requests a // user/profile to be wiped N attempts. Once we get below the grace period, // we post this dialog every time as a clear warning until the deletion // fires. Check which profile has the strictest policy for failed password // attempts. final int expiringUser = mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(userId); Integer mainUser = mSelectedUserInteractor.getMainUserId(); showMessageForFailedUnlockAttempt( userId, expiringUser, mainUser, remainingBeforeWipe, failedAttempts); } mLockPatternUtils.reportFailedPasswordAttempt(userId); if (timeoutMs > 0) { mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); if (!com.android.systemui.Flags.revampedBouncerMessages()) { mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, mSecurityModel.getSecurityMode(userId)); } } } @VisibleForTesting void showMessageForFailedUnlockAttempt(int userId, int expiringUserId, Integer mainUserId, int remainingBeforeWipe, int failedAttempts) { int userType = USER_TYPE_PRIMARY; if (expiringUser == userId) { if (expiringUserId == userId) { int primaryUser = UserHandle.USER_SYSTEM; if (Flags.headlessSingleUserFixes()) { if (mainUserId != null) { primaryUser = mainUserId; } } // TODO: http://b/23522538 if (expiringUser != UserHandle.USER_SYSTEM) { if (expiringUserId != primaryUser) { userType = USER_TYPE_SECONDARY_USER; } } else if (expiringUser != UserHandle.USER_NULL) { } else if (expiringUserId != UserHandle.USER_NULL) { userType = USER_TYPE_WORK_PROFILE; } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY if (remainingBeforeWipe > 0) { mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); } else { // Too many attempts. The device will be wiped shortly. Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); Slog.i(TAG, "Too many unlock attempts; user " + expiringUserId + " will be wiped!"); mView.showWipeDialog(failedAttempts, userType); } } mLockPatternUtils.reportFailedPasswordAttempt(userId); if (timeoutMs > 0) { mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); if (!com.android.systemui.Flags.revampedBouncerMessages()) { mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, mSecurityModel.getSecurityMode(userId)); } } } private void getCurrentSecurityController( KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback) { Loading packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +8 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.flags.Flags import android.os.UserHandle import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternView Loading Loading @@ -288,9 +289,15 @@ constructor( private suspend fun getWipeTarget(): WipeTarget { // Check which profile has the strictest policy for failed authentication attempts. val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe() val primaryUser = if (Flags.headlessSingleUserFixes()) { selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM } else { UserHandle.USER_SYSTEM } return when (userToBeWiped) { selectedUserInteractor.getSelectedUserId() -> if (userToBeWiped == UserHandle.USER_SYSTEM) { if (userToBeWiped == primaryUser) { WipeTarget.WholeDevice } else { WipeTarget.User Loading Loading
packages/SystemUI/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,7 @@ android_library { "androidx.compose.material_material-icons-extended", "androidx.activity_activity-compose", "androidx.compose.animation_animation-graphics", "device_policy_aconfig_flags_lib", ], libs: [ "keepanno-annotations", Loading Loading @@ -622,6 +623,7 @@ android_app { "//frameworks/libs/systemui:compilelib", "SystemUI-tests-base", "androidx.compose.runtime_runtime", "SystemUI-core", ], libs: [ "keepanno-annotations", Loading
packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +44 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ package com.android.keyguard import android.app.admin.DevicePolicyManager import android.app.admin.flags.Flags as DevicePolicyFlags import android.content.res.Configuration import android.media.AudioManager import android.platform.test.annotations.EnableFlags import android.telephony.TelephonyManager import android.testing.TestableLooper.RunWithLooper import android.testing.TestableResources Loading Loading @@ -148,6 +151,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var postureController: DevicePostureController @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Captor private lateinit var swipeListenerArgumentCaptor: Loading Loading @@ -273,6 +277,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { mSelectedUserInteractor, deviceProvisionedController, faceAuthAccessibilityDelegate, devicePolicyManager, keyguardTransitionInteractor, { primaryBouncerInteractor }, ) { Loading Loading @@ -934,6 +939,45 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { verify(viewFlipperController).asynchronouslyInflateView(any(), any(), any()) } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() { val mainUserId = 10 underTest.showMessageForFailedUnlockAttempt( /* userId = */ mainUserId, /* expiringUserId = */ mainUserId, /* mainUserId = */ mainUserId, /* remainingBeforeWipe = */ 1, /* failedAttempts = */ 1 ) verify(view) .showAlmostAtWipeDialog(any(), any(), eq(KeyguardSecurityContainer.USER_TYPE_PRIMARY)) } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() { val secondaryUserId = 10 val mainUserId = 0 underTest.showMessageForFailedUnlockAttempt( /* userId = */ secondaryUserId, /* expiringUserId = */ secondaryUserId, /* mainUserId = */ mainUserId, /* remainingBeforeWipe = */ 1, /* failedAttempts = */ 1 ) verify(view) .showAlmostAtWipeDialog( any(), any(), eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER) ) } private val registeredSwipeListener: KeyguardSecurityContainer.SwipeListener get() { underTest.onViewAttached() Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +8 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.DevicePolicyManager import android.app.admin.flags.Flags as DevicePolicyFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils Loading @@ -32,6 +34,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -410,12 +414,16 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES) fun upcomingWipe() = testScope.runTest { val upcomingWipe by collectLastValue(underTest.upcomingWipe) kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin) val correctPin = FakeAuthenticationRepository.DEFAULT_PIN val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 } kosmos.fakeUserRepository.asMainUser() kosmos.fakeAuthenticationRepository.profileWithMinFailedUnlockAttemptsForWipe = FakeUserRepository.MAIN_USER_ID underTest.authenticate(correctPin) assertThat(upcomingWipe).isNull() Loading
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +44 −23 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.app.admin.flags.Flags; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; Loading Loading @@ -134,6 +135,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final BouncerMessageInteractor mBouncerMessageInteractor; private int mTranslationY; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final DevicePolicyManager mDevicePolicyManager; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise // the audio service will bring up the volume dialog. Loading Loading @@ -460,6 +462,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard SelectedUserInteractor selectedUserInteractor, DeviceProvisionedController deviceProvisionedController, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, DevicePolicyManager devicePolicyManager, KeyguardTransitionInteractor keyguardTransitionInteractor, Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor, Provider<DeviceEntryInteractor> deviceEntryInteractor Loading Loading @@ -495,6 +498,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardTransitionInteractor = keyguardTransitionInteractor; mDeviceProvisionedController = deviceProvisionedController; mPrimaryBouncerInteractor = primaryBouncerInteractor; mDevicePolicyManager = devicePolicyManager; } @Override Loading Loading @@ -1105,45 +1109,62 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); final int failedAttemptsBeforeWipe = dpm.getMaximumFailedPasswordsForWipe(null, userId); mDevicePolicyManager.getMaximumFailedPasswordsForWipe(null, userId); final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? (failedAttemptsBeforeWipe - failedAttempts) : Integer.MAX_VALUE; // because DPM returns 0 if no restriction if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { // The user has installed a DevicePolicyManager that requests a user/profile to be wiped // N attempts. Once we get below the grace period, we post this dialog every time as a // clear warning until the deletion fires. // Check which profile has the strictest policy for failed password attempts final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); // The user has installed a DevicePolicyManager that requests a // user/profile to be wiped N attempts. Once we get below the grace period, // we post this dialog every time as a clear warning until the deletion // fires. Check which profile has the strictest policy for failed password // attempts. final int expiringUser = mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(userId); Integer mainUser = mSelectedUserInteractor.getMainUserId(); showMessageForFailedUnlockAttempt( userId, expiringUser, mainUser, remainingBeforeWipe, failedAttempts); } mLockPatternUtils.reportFailedPasswordAttempt(userId); if (timeoutMs > 0) { mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); if (!com.android.systemui.Flags.revampedBouncerMessages()) { mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, mSecurityModel.getSecurityMode(userId)); } } } @VisibleForTesting void showMessageForFailedUnlockAttempt(int userId, int expiringUserId, Integer mainUserId, int remainingBeforeWipe, int failedAttempts) { int userType = USER_TYPE_PRIMARY; if (expiringUser == userId) { if (expiringUserId == userId) { int primaryUser = UserHandle.USER_SYSTEM; if (Flags.headlessSingleUserFixes()) { if (mainUserId != null) { primaryUser = mainUserId; } } // TODO: http://b/23522538 if (expiringUser != UserHandle.USER_SYSTEM) { if (expiringUserId != primaryUser) { userType = USER_TYPE_SECONDARY_USER; } } else if (expiringUser != UserHandle.USER_NULL) { } else if (expiringUserId != UserHandle.USER_NULL) { userType = USER_TYPE_WORK_PROFILE; } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY if (remainingBeforeWipe > 0) { mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); } else { // Too many attempts. The device will be wiped shortly. Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); Slog.i(TAG, "Too many unlock attempts; user " + expiringUserId + " will be wiped!"); mView.showWipeDialog(failedAttempts, userType); } } mLockPatternUtils.reportFailedPasswordAttempt(userId); if (timeoutMs > 0) { mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); if (!com.android.systemui.Flags.revampedBouncerMessages()) { mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, mSecurityModel.getSecurityMode(userId)); } } } private void getCurrentSecurityController( KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback) { Loading
packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +8 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.flags.Flags import android.os.UserHandle import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternView Loading Loading @@ -288,9 +289,15 @@ constructor( private suspend fun getWipeTarget(): WipeTarget { // Check which profile has the strictest policy for failed authentication attempts. val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe() val primaryUser = if (Flags.headlessSingleUserFixes()) { selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM } else { UserHandle.USER_SYSTEM } return when (userToBeWiped) { selectedUserInteractor.getSelectedUserId() -> if (userToBeWiped == UserHandle.USER_SYSTEM) { if (userToBeWiped == primaryUser) { WipeTarget.WholeDevice } else { WipeTarget.User Loading