1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.keyguard; 18 19 import static android.view.WindowInsets.Type.ime; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.ArgumentMatchers.anyInt; 24 import static org.mockito.ArgumentMatchers.anyLong; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.reset; 29 import static org.mockito.Mockito.spy; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.content.res.Configuration; 35 import android.content.res.Resources; 36 import android.testing.AndroidTestingRunner; 37 import android.testing.TestableLooper; 38 import android.view.MotionEvent; 39 import android.view.WindowInsetsController; 40 41 import androidx.test.filters.SmallTest; 42 43 import com.android.internal.logging.MetricsLogger; 44 import com.android.internal.logging.UiEventLogger; 45 import com.android.internal.widget.LockPatternUtils; 46 import com.android.keyguard.KeyguardSecurityModel.SecurityMode; 47 import com.android.systemui.R; 48 import com.android.systemui.SysuiTestCase; 49 import com.android.systemui.classifier.FalsingCollector; 50 import com.android.systemui.statusbar.policy.ConfigurationController; 51 import com.android.systemui.statusbar.policy.KeyguardStateController; 52 53 import org.junit.Before; 54 import org.junit.Rule; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 import org.mockito.Mock; 58 import org.mockito.junit.MockitoJUnit; 59 import org.mockito.junit.MockitoRule; 60 61 @SmallTest 62 @RunWith(AndroidTestingRunner.class) 63 @TestableLooper.RunWithLooper() 64 public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { 65 private static final int VIEW_WIDTH = 1600; 66 67 @Rule 68 public MockitoRule mRule = MockitoJUnit.rule(); 69 70 @Mock 71 private KeyguardSecurityContainer mView; 72 @Mock 73 private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory; 74 @Mock 75 private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; 76 @Mock 77 private LockPatternUtils mLockPatternUtils; 78 @Mock 79 private KeyguardUpdateMonitor mKeyguardUpdateMonitor; 80 @Mock 81 private KeyguardSecurityModel mKeyguardSecurityModel; 82 @Mock 83 private MetricsLogger mMetricsLogger; 84 @Mock 85 private UiEventLogger mUiEventLogger; 86 @Mock 87 private KeyguardStateController mKeyguardStateController; 88 @Mock 89 private KeyguardInputViewController mInputViewController; 90 @Mock 91 private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; 92 @Mock 93 private WindowInsetsController mWindowInsetsController; 94 @Mock 95 private KeyguardSecurityViewFlipper mSecurityViewFlipper; 96 @Mock 97 private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; 98 @Mock 99 private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; 100 @Mock 101 private KeyguardMessageArea mKeyguardMessageArea; 102 @Mock 103 private ConfigurationController mConfigurationController; 104 @Mock 105 private EmergencyButtonController mEmergencyButtonController; 106 @Mock 107 private Resources mResources; 108 @Mock 109 private FalsingCollector mFalsingCollector; 110 private Configuration mConfiguration; 111 112 private KeyguardSecurityContainerController mKeyguardSecurityContainerController; 113 private KeyguardPasswordViewController mKeyguardPasswordViewController; 114 private KeyguardPasswordView mKeyguardPasswordView; 115 116 @Before setup()117 public void setup() { 118 mConfiguration = new Configuration(); 119 mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED. 120 121 when(mResources.getConfiguration()).thenReturn(mConfiguration); 122 when(mView.getContext()).thenReturn(mContext); 123 when(mView.getResources()).thenReturn(mResources); 124 when(mView.getWidth()).thenReturn(VIEW_WIDTH); 125 when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) 126 .thenReturn(mAdminSecondaryLockScreenController); 127 when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); 128 mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext())); 129 when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper); 130 when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area)) 131 .thenReturn(mKeyguardMessageArea); 132 when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController); 133 mKeyguardPasswordViewController = new KeyguardPasswordViewController( 134 (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor, 135 SecurityMode.Password, mLockPatternUtils, null, 136 mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController, 137 null, mock(Resources.class), null); 138 139 mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( 140 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, 141 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, 142 mKeyguardStateController, mKeyguardSecurityViewFlipperController, 143 mConfigurationController, mFalsingCollector) 144 .create(mSecurityCallback); 145 } 146 147 @Test showSecurityScreen_canInflateAllModes()148 public void showSecurityScreen_canInflateAllModes() { 149 SecurityMode[] modes = SecurityMode.values(); 150 for (SecurityMode mode : modes) { 151 when(mInputViewController.getSecurityMode()).thenReturn(mode); 152 mKeyguardSecurityContainerController.showSecurityScreen(mode); 153 if (mode == SecurityMode.Invalid) { 154 verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView( 155 any(SecurityMode.class), any(KeyguardSecurityCallback.class)); 156 } else { 157 verify(mKeyguardSecurityViewFlipperController).getSecurityView( 158 eq(mode), any(KeyguardSecurityCallback.class)); 159 } 160 } 161 } 162 163 @Test startDisappearAnimation_animatesKeyboard()164 public void startDisappearAnimation_animatesKeyboard() { 165 when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( 166 SecurityMode.Password); 167 when(mKeyguardSecurityViewFlipperController.getSecurityView( 168 eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) 169 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 170 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); 171 172 mKeyguardSecurityContainerController.startDisappearAnimation(null); 173 verify(mWindowInsetsController).controlWindowInsetsAnimation( 174 eq(ime()), anyLong(), any(), any(), any()); 175 } 176 177 @Test onResourcesUpdate_callsThroughOnRotationChange()178 public void onResourcesUpdate_callsThroughOnRotationChange() { 179 // Rotation is the same, shouldn't cause an update 180 mKeyguardSecurityContainerController.updateResources(); 181 verify(mView, times(0)).setOneHandedMode(anyBoolean()); 182 183 // Update rotation. Should trigger update 184 mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; 185 186 mKeyguardSecurityContainerController.updateResources(); 187 verify(mView, times(1)).setOneHandedMode(anyBoolean()); 188 } 189 190 @Test updateKeyguardPosition_callsThroughToViewInOneHandedMode()191 public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() { 192 when(mView.isOneHandedMode()).thenReturn(true); 193 mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f); 194 verify(mView).setOneHandedModeLeftAligned(true, false); 195 196 mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2); 197 verify(mView).setOneHandedModeLeftAligned(false, false); 198 } 199 200 @Test updateKeyguardPosition_ignoredInTwoHandedMode()201 public void updateKeyguardPosition_ignoredInTwoHandedMode() { 202 when(mView.isOneHandedMode()).thenReturn(false); 203 mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f); 204 verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean()); 205 } 206 touchDownLeftSide()207 private void touchDownLeftSide() { 208 mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent( 209 MotionEvent.obtain( 210 /* downTime= */0, 211 /* eventTime= */0, 212 MotionEvent.ACTION_DOWN, 213 /* x= */VIEW_WIDTH / 3f, 214 /* y= */0, 215 /* metaState= */0)); 216 } 217 touchDownRightSide()218 private void touchDownRightSide() { 219 mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent( 220 MotionEvent.obtain( 221 /* downTime= */0, 222 /* eventTime= */0, 223 MotionEvent.ACTION_DOWN, 224 /* x= */(VIEW_WIDTH / 3f) * 2, 225 /* y= */0, 226 /* metaState= */0)); 227 } 228 229 @Test onInterceptTap_inhibitsFalsingInOneHandedMode()230 public void onInterceptTap_inhibitsFalsingInOneHandedMode() { 231 when(mView.isOneHandedMode()).thenReturn(true); 232 when(mView.isOneHandedModeLeftAligned()).thenReturn(true); 233 234 touchDownLeftSide(); 235 verify(mFalsingCollector, never()).avoidGesture(); 236 237 // Now on the right. 238 touchDownRightSide(); 239 verify(mFalsingCollector).avoidGesture(); 240 241 // Move and re-test 242 reset(mFalsingCollector); 243 when(mView.isOneHandedModeLeftAligned()).thenReturn(false); 244 245 // On the right... 246 touchDownRightSide(); 247 verify(mFalsingCollector, never()).avoidGesture(); 248 249 touchDownLeftSide(); 250 verify(mFalsingCollector).avoidGesture(); 251 } 252 253 @Test showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode()254 public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() { 255 setUpKeyguardFlags( 256 /* deviceConfigCanUseOneHandedKeyguard= */false, 257 /* sysuiResourceCanUseOneHandedKeyguard= */false); 258 259 when(mKeyguardSecurityViewFlipperController.getSecurityView( 260 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) 261 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 262 263 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); 264 verify(mView).setOneHandedMode(false); 265 } 266 267 @Test showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode()268 public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() { 269 setUpKeyguardFlags( 270 /* deviceConfigCanUseOneHandedKeyguard= */false, 271 /* sysuiResourceCanUseOneHandedKeyguard= */true); 272 273 when(mKeyguardSecurityViewFlipperController.getSecurityView( 274 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) 275 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 276 277 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); 278 verify(mView).setOneHandedMode(false); 279 } 280 281 @Test showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode()282 public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() { 283 setUpKeyguardFlags( 284 /* deviceConfigCanUseOneHandedKeyguard= */true, 285 /* sysuiResourceCanUseOneHandedKeyguard= */false); 286 287 when(mKeyguardSecurityViewFlipperController.getSecurityView( 288 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) 289 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 290 291 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); 292 verify(mView).setOneHandedMode(false); 293 } 294 295 @Test showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode()296 public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() { 297 setUpKeyguardFlags( 298 /* deviceConfigCanUseOneHandedKeyguard= */true, 299 /* sysuiResourceCanUseOneHandedKeyguard= */true); 300 301 when(mKeyguardSecurityViewFlipperController.getSecurityView( 302 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) 303 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 304 305 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); 306 verify(mView).setOneHandedMode(true); 307 } 308 309 @Test showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode()310 public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() { 311 setUpKeyguardFlags( 312 /* deviceConfigCanUseOneHandedKeyguard= */true, 313 /* sysuiResourceCanUseOneHandedKeyguard= */true); 314 315 when(mKeyguardSecurityViewFlipperController.getSecurityView( 316 eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) 317 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); 318 319 mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); 320 verify(mView).setOneHandedMode(false); 321 } 322 setUpKeyguardFlags( boolean deviceConfigCanUseOneHandedKeyguard, boolean sysuiResourceCanUseOneHandedKeyguard)323 private void setUpKeyguardFlags( 324 boolean deviceConfigCanUseOneHandedKeyguard, 325 boolean sysuiResourceCanUseOneHandedKeyguard) { 326 when(mResources.getBoolean( 327 com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) 328 .thenReturn(deviceConfigCanUseOneHandedKeyguard); 329 when(mResources.getBoolean( 330 R.bool.can_use_one_handed_bouncer)) 331 .thenReturn(sysuiResourceCanUseOneHandedKeyguard); 332 } 333 } 334