Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50f9f5c6 authored by Kholoud Mohamed's avatar Kholoud Mohamed Committed by Android (Google) Code Review
Browse files

Merge "Fix incorrect password attempts messaging on headless" into main

parents 85ba7948 02438edd
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -362,6 +362,7 @@ android_library {
        "device_state_flags_lib",
        "kotlinx_coroutines_android",
        "kotlinx_coroutines",
        "kotlinx_coroutines_guava",
        "//frameworks/libs/systemui:iconloader_base",
        "SystemUI-tags",
        "SystemUI-proto",
@@ -382,6 +383,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",
@@ -541,6 +543,7 @@ android_library {
        "androidx.activity_activity-compose",
        "androidx.compose.animation_animation-graphics",
        "TraceurCommon",
        "kotlinx_coroutines_guava",
    ],
}

@@ -622,6 +625,7 @@ android_app {
        "//frameworks/libs/systemui:compilelib",
        "SystemUI-tests-base",
        "androidx.compose.runtime_runtime",
        "SystemUI-core",
    ],
    libs: [
        "keepanno-annotations",
+43 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@

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.telephony.TelephonyManager
@@ -148,6 +150,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:
@@ -273,6 +276,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
                mSelectedUserInteractor,
                deviceProvisionedController,
                faceAuthAccessibilityDelegate,
                devicePolicyManager,
                keyguardTransitionInteractor,
                { primaryBouncerInteractor },
            ) {
@@ -934,6 +938,45 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
        verify(viewFlipperController).asynchronouslyInflateView(any(), any(), any())
    }

    @Test
    fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() {
        mSetFlagsRule.enableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
        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
    fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() {
        mSetFlagsRule.enableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
        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()
+8 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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()
+60 −22
Original line number Diff line number Diff line
@@ -35,12 +35,14 @@ 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;
import android.content.res.Resources;
import android.media.AudioManager;
import android.metrics.LogMaker;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
@@ -96,12 +98,15 @@ import com.android.systemui.util.ViewController;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;

import com.google.common.util.concurrent.ListenableFuture;

import dagger.Lazy;

import kotlinx.coroutines.Job;

import java.io.File;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;

import javax.inject.Inject;
import javax.inject.Provider;
@@ -134,6 +139,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.
@@ -460,6 +466,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
            SelectedUserInteractor selectedUserInteractor,
            DeviceProvisionedController deviceProvisionedController,
            FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
            DevicePolicyManager devicePolicyManager,
            KeyguardTransitionInteractor keyguardTransitionInteractor,
            Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
            Provider<DeviceEntryInteractor> deviceEntryInteractor
@@ -495,6 +502,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
        mDeviceProvisionedController = deviceProvisionedController;
        mPrimaryBouncerInteractor = primaryBouncerInteractor;
        mDevicePolicyManager = devicePolicyManager;
    }

    @Override
@@ -1105,45 +1113,75 @@ 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);
            ListenableFuture<Integer> getMainUserIdFuture =
                    mSelectedUserInteractor.getMainUserIdAsync();
            getMainUserIdFuture.addListener(() -> {
                Looper.prepare();
                Integer mainUser;
                try {
                    mainUser = getMainUserIdFuture.get();
                } catch (InterruptedException | ExecutionException e) {
                    // Nothing we can, keep using the system user as the primary
                    // user.
                    mainUser = null;
                }
                showMessageForFailedUnlockAttempt(
                        userId, expiringUser, mainUser, remainingBeforeWipe, failedAttempts);
                Looper.loop();
            }, ThreadUtils.getBackgroundExecutor());
        }
        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) {
+8 −1
Original line number Diff line number Diff line
@@ -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
@@ -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