blob: 68e49c0a1d4b6fe2782564ab2884d8e4b1031223 [file] [log] [blame]
/*
* Copyright (C) 2020 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.keyguard;
import static android.view.WindowInsets.Type.ime;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.SidefpsController;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
private KeyguardSecurityContainer mView;
@Mock
private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory;
@Mock
private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock
private MetricsLogger mMetricsLogger;
@Mock
private UiEventLogger mUiEventLogger;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
private KeyguardInputViewController mInputViewController;
@Mock
private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ConfigurationController mConfigurationController;
@Mock
private EmergencyButtonController mEmergencyButtonController;
@Mock
private Resources mResources;
@Mock
private FalsingCollector mFalsingCollector;
@Mock
private FalsingManager mFalsingManager;
@Mock
private GlobalSettings mGlobalSettings;
@Mock
private FeatureFlags mFeatureFlags;
@Mock
private UserSwitcherController mUserSwitcherController;
@Mock
private SessionTracker mSessionTracker;
@Mock
private KeyguardViewController mKeyguardViewController;
@Mock
private SidefpsController mSidefpsController;
@Mock
private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
private KeyguardPasswordViewController mKeyguardPasswordViewController;
private KeyguardPasswordView mKeyguardPasswordView;
@Before
public void setup() {
mConfiguration = new Configuration();
mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
when(mResources.getConfiguration()).thenReturn(mConfiguration);
when(mView.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
.thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
SecurityMode.Password, mLockPatternUtils, null,
mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
null, mock(Resources.class), null, mKeyguardViewController);
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
mUserSwitcherController, mFeatureFlags, mGlobalSettings,
mSessionTracker, Optional.of(mSidefpsController)).create(mSecurityCallback);
}
@Test
public void showSecurityScreen_canInflateAllModes() {
SecurityMode[] modes = SecurityMode.values();
for (SecurityMode mode : modes) {
when(mInputViewController.getSecurityMode()).thenReturn(mode);
mKeyguardSecurityContainerController.showSecurityScreen(mode);
if (mode == SecurityMode.Invalid) {
verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView(
any(SecurityMode.class), any(KeyguardSecurityCallback.class));
} else {
verify(mKeyguardSecurityViewFlipperController).getSecurityView(
eq(mode), any(KeyguardSecurityCallback.class));
}
}
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
SecurityMode.Password);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
mKeyguardSecurityContainerController.startDisappearAnimation(null);
verify(mWindowInsetsController).controlWindowInsetsAnimation(
eq(ime()), anyLong(), any(), any(), any());
}
@Test
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
private void touchDown() {
mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
MotionEvent.obtain(
/* downTime= */0,
/* eventTime= */0,
MotionEvent.ACTION_DOWN,
/* x= */0,
/* y= */0,
/* metaState= */0));
}
@Test
public void onInterceptTap_inhibitsFalsingInSidedSecurityMode() {
when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(false);
touchDown();
verify(mFalsingCollector, never()).avoidGesture();
when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(true);
touchDown();
verify(mFalsingCollector).avoidGesture();
}
@Test
public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() {
when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(false);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
@Test
public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() {
when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
@Test
public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
setupGetSecurityView();
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
@Test
public void addUserSwitchCallback() {
mKeyguardSecurityContainerController.onViewAttached();
verify(mUserSwitcherController)
.addUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
mKeyguardSecurityContainerController.onViewDetached();
verify(mUserSwitcherController)
.removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
}
@Test
public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() {
setupConditionsToEnableSideFpsHint();
reset(mSidefpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController).show();
verify(mSidefpsController, never()).hide();
}
@Test
public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setFingerprintDetectionRunning(false);
reset(mSidefpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
@Test
public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setSidedSecurityMode(false);
reset(mSidefpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
@Test
public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setNeedsStrongAuth(true);
reset(mSidefpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
@Test
public void onBouncerVisibilityChanged_sideFpsHintShown_sideFpsHintHidden() {
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController, atLeastOnce()).show();
reset(mSidefpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
@Test
public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() {
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController, atLeastOnce()).show();
reset(mSidefpsController);
mKeyguardSecurityContainerController.onStartingToHide();
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
@Test
public void onPause_sideFpsHintShown_sideFpsHintHidden() {
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
verify(mSidefpsController, atLeastOnce()).show();
reset(mSidefpsController);
mKeyguardSecurityContainerController.onPause();
verify(mSidefpsController).hide();
verify(mSidefpsController, never()).show();
}
private void setupConditionsToEnableSideFpsHint() {
attachView();
setSidedSecurityMode(true);
setFingerprintDetectionRunning(true);
setNeedsStrongAuth(false);
}
private void attachView() {
mKeyguardSecurityContainerController.onViewAttached();
verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
}
private void setFingerprintDetectionRunning(boolean running) {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(running);
mKeyguardUpdateMonitorCallback.getValue().onBiometricRunningStateChanged(running,
BiometricSourceType.FINGERPRINT);
}
private void setSidedSecurityMode(boolean sided) {
when(mView.isSidedSecurityMode()).thenReturn(sided);
}
private void setNeedsStrongAuth(boolean needed) {
when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed);
mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0);
}
private void setupGetSecurityView() {
when(mKeyguardSecurityViewFlipperController.getSecurityView(
any(), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewControllerMock);
}
}