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