[DO NOT MERGE] Do not dismiss keyguard after SIM PUK unlock

After PUK unlock, multiple calls to
KeyguardSecurityContainerController#dismiss() were being called from
the KeyguardSimPukViewController, which begins the transition to the
next security screen, if any. At the same time, other parts of the
system, also listening to SIM events, recognize the PUK unlock and
call KeyguardSecurityContainer#showSecurityScreen, which updates which
security method comes next. After boot, this should be one of PIN,
Password, Pattern, assuming they have a security method. If one of the
first dismiss() calls comes AFTER the security method changes, this is
incorrectly recognized by the code as a successful
PIN/pattern/password unlock. This causes the keyguard to be marked as
done, causing screen flickers and incorrect system state.

The solution: every call to dismiss() should include a new parameter
for the security method used. If there is a difference between this
parameter and the current value in KeyguardSecurityContainerCallback,
ignore the request, as the system state has changed.

Fixes: 238804980
Bug: 218500036
Test: atest KeyguardSecurityContainerTest
AdminSecondaryLockScreenControllerTest KeyguardHostViewControllerTest
KeyguardSecurityContainerControllerTest

Change-Id: I7c8714a177bc85fbce92f6e8fe911f74ca2ac243
Merged-In: I7c8714a177bc85fbce92f6e8fe911f74ca2ac243
(cherry picked from commit 37aeb26b0a)
This commit is contained in:
Matt Pietal
2022-08-04 13:00:39 +00:00
parent 07afb47507
commit 1e7087f113
12 changed files with 102 additions and 26 deletions

View File

@@ -33,6 +33,7 @@ import android.view.SurfaceView;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
@@ -208,7 +209,7 @@ public class AdminSecondaryLockScreenController {
hide(); hide();
if (mKeyguardCallback != null) { if (mKeyguardCallback != null) {
mKeyguardCallback.dismiss(/* securityVerified= */ true, userId, mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
/* bypassSecondaryLockScreen= */true); /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid);
} }
} }
} }

View File

@@ -173,7 +173,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
if (dismissKeyguard) { if (dismissKeyguard) {
mDismissing = true; mDismissing = true;
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK); mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
getKeyguardSecurityCallback().dismiss(true, userId); getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
} }
} else { } else {
if (isValidPassword) { if (isValidPassword) {

View File

@@ -93,7 +93,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
Log.i(TAG, "TrustAgent dismissed Keyguard."); Log.i(TAG, "TrustAgent dismissed Keyguard.");
} }
mSecurityCallback.dismiss(false /* authenticated */, userId, mSecurityCallback.dismiss(false /* authenticated */, userId,
/* bypassSecondaryLockScreen */ false); /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid);
} else { } else {
mViewMediatorCallback.playTrustedSound(); mViewMediatorCallback.playTrustedSound();
} }
@@ -105,9 +105,9 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
@Override @Override
public boolean dismiss(boolean authenticated, int targetUserId, public boolean dismiss(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) { boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
authenticated, targetUserId, bypassSecondaryLockScreen); authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode);
} }
@Override @Override
@@ -215,7 +215,8 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
* @return True if the keyguard is done. * @return True if the keyguard is done.
*/ */
public boolean dismiss(int targetUserId) { public boolean dismiss(int targetUserId) {
return mSecurityCallback.dismiss(false, targetUserId, false); return mSecurityCallback.dismiss(false, targetUserId, false,
getCurrentSecurityMode());
} }
/** /**
@@ -352,10 +353,10 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
} }
public boolean handleBackKey() { public boolean handleBackKey() {
if (mKeyguardSecurityContainerController.getCurrentSecurityMode() SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode();
!= SecurityMode.None) { if (securityMode != SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss( mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser()); false, KeyguardUpdateMonitor.getCurrentUser(), securityMode);
return true; return true;
} }
return false; return false;

View File

@@ -58,10 +58,11 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return false; return false;
} }
@Override @Override
public void dismiss(boolean securityVerified, int targetUserId) { } public void dismiss(boolean securityVerified, int targetUserId,
SecurityMode expectedSecurityMode) { }
@Override @Override
public void dismiss(boolean authenticated, int targetId, public void dismiss(boolean authenticated, int targetId,
boolean bypassSecondaryLockScreen) { } boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { }
@Override @Override
public void onUserInput() { } public void onUserInput() { }
@Override @Override

View File

@@ -164,7 +164,7 @@ public class KeyguardPatternViewController
if (dismissKeyguard) { if (dismissKeyguard) {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK); mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
getKeyguardSecurityCallback().dismiss(true, userId); getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
} }
} else { } else {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);

View File

@@ -15,14 +15,17 @@
*/ */
package com.android.keyguard; package com.android.keyguard;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
public interface KeyguardSecurityCallback { public interface KeyguardSecurityCallback {
/** /**
* Dismiss the given security screen. * Dismiss the given security screen.
* @param securityVerified true if the user correctly entered credentials for the given screen. * @param securityVerified true if the user correctly entered credentials for the given screen.
* @param targetUserId a user that needs to be the foreground user at the dismissal completion. * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @param expectedSecurityMode The security mode that is invoking this dismiss.
*/ */
void dismiss(boolean securityVerified, int targetUserId); void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode);
/** /**
* Dismiss the given security screen. * Dismiss the given security screen.
@@ -30,8 +33,10 @@ public interface KeyguardSecurityCallback {
* @param targetUserId a user that needs to be the foreground user at the dismissal completion. * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen, * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
* if any, during this dismissal. * if any, during this dismissal.
* @param expectedSecurityMode The security mode that is invoking this dismiss.
*/ */
void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen); void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen,
SecurityMode expectedSecurityMode);
/** /**
* Manually report user activity to keep the device awake. * Manually report user activity to keep the device awake.

View File

@@ -173,7 +173,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
// Used to notify the container when something interesting happens. // Used to notify the container when something interesting happens.
public interface SecurityCallback { public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen); /**
* Potentially dismiss the current security screen, after validating that all device
* security has been unlocked. Otherwise show the next screen.
*/
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen,
SecurityMode expectedSecurityMode);
void userActivity(); void userActivity();

View File

@@ -120,14 +120,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
} }
@Override @Override
public void dismiss(boolean authenticated, int targetId) { public void dismiss(boolean authenticated, int targetId,
dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); SecurityMode expectedSecurityMode) {
dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false,
expectedSecurityMode);
} }
@Override @Override
public void dismiss(boolean authenticated, int targetId, public void dismiss(boolean authenticated, int targetId,
boolean bypassSecondaryLockScreen) { boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
expectedSecurityMode);
} }
public boolean isVerifyUnlockOnly() { public boolean isVerifyUnlockOnly() {
@@ -280,8 +283,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
return mCurrentSecurityMode; return mCurrentSecurityMode;
} }
public void dismiss(boolean authenticated, int targetUserId) { /**
mKeyguardSecurityCallback.dismiss(authenticated, targetUserId); * Potentially dismiss the current security screen, after validating that all device
* security has been unlocked. Otherwise show the next screen.
*/
public void dismiss(boolean authenticated, int targetUserId,
SecurityMode expectedSecurityMode) {
mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode);
} }
public void reset() { public void reset() {
@@ -332,12 +340,21 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
* completion. * completion.
* @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
* secondary lock screen requirement, if any. * secondary lock screen requirement, if any.
* @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
* indicates that no check should be done
* @return true if keyguard is done * @return true if keyguard is done
*/ */
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) { boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
if (expectedSecurityMode != SecurityMode.Invalid
&& expectedSecurityMode != getCurrentSecurityMode()) {
Log.w(TAG, "Attempted to invoke showNextSecurityScreenOrFinish with securityMode "
+ expectedSecurityMode + ", but current mode is " + getCurrentSecurityMode());
return false;
}
boolean finish = false; boolean finish = false;
boolean strongAuth = false; boolean strongAuth = false;
int eventSubtype = -1; int eventSubtype = -1;

View File

@@ -162,7 +162,8 @@ public class KeyguardSimPinViewController
mRemainingAttempts = -1; mRemainingAttempts = -1;
mShowDefaultMessage = true; mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss( getKeyguardSecurityCallback().dismiss(
true, KeyguardUpdateMonitor.getCurrentUser()); true, KeyguardUpdateMonitor.getCurrentUser(),
SecurityMode.SimPin);
} else { } else {
mShowDefaultMessage = false; mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) { if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {

View File

@@ -69,7 +69,8 @@ public class KeyguardSimPukViewController
if (simState == TelephonyManager.SIM_STATE_READY) { if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1; mRemainingAttempts = -1;
mShowDefaultMessage = true; mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
SecurityMode.SimPuk);
} else { } else {
resetState(); resetState();
} }
@@ -274,7 +275,8 @@ public class KeyguardSimPukViewController
mShowDefaultMessage = true; mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss( getKeyguardSecurityCallback().dismiss(
true, KeyguardUpdateMonitor.getCurrentUser()); true, KeyguardUpdateMonitor.getCurrentUser(),
SecurityMode.SimPuk);
} else { } else {
mShowDefaultMessage = false; mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) { if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {

View File

@@ -44,6 +44,7 @@ import android.view.SurfaceView;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import org.junit.After; import org.junit.After;
@@ -190,7 +191,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private void verifyViewDismissed(SurfaceView v) throws Exception { private void verifyViewDismissed(SurfaceView v) throws Exception {
verify(mKeyguardSecurityContainer).removeView(v); verify(mKeyguardSecurityContainer).removeView(v);
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true); verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true, SecurityMode.Invalid);
assertThat(mContext.isBound(mComponentName)).isFalse(); assertThat(mContext.isBound(mComponentName)).isFalse();
} }
} }

View File

@@ -18,7 +18,10 @@ package com.android.keyguard;
import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.ime;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@@ -58,6 +61,7 @@ import org.mockito.junit.MockitoRule;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper() @TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private static final int TARGET_USER_ID = 100;
@Rule @Rule
public MockitoRule mRule = MockitoJUnit.rule(); public MockitoRule mRule = MockitoJUnit.rule();
@@ -183,4 +187,42 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f); mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
verify(mView).updateKeyguardPosition(1.0f); verify(mView).updateKeyguardPosition(1.0f);
} }
@Test
public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
// GIVEN the current security method is SimPin
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
// WHEN a request is made from the SimPin screens to show the next security method
when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN);
mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
/* authenticated= */true,
TARGET_USER_ID,
/* bypassSecondaryLockScreen= */true,
SecurityMode.SimPin);
// THEN the next security method of PIN is set, and the keyguard is not marked as done
verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt());
assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode())
.isEqualTo(SecurityMode.PIN);
}
@Test
public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
//GIVEN current security mode has been set to PIN
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN);
//WHEN a request comes from SimPin to dismiss the security screens
boolean keyguardDone = mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
/* authenticated= */true,
TARGET_USER_ID,
/* bypassSecondaryLockScreen= */true,
SecurityMode.SimPin);
//THEN no action has happened, which will not dismiss the security screens
assertThat(keyguardDone).isEqualTo(false);
verify(mKeyguardUpdateMonitor, never()).getUserHasTrust(anyInt());
}
} }