1 /* 2 * Copyright (C) 2022 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 android.testing.TestableLooper 20 import android.view.KeyEvent 21 import android.view.View 22 import android.view.ViewGroup 23 import android.view.inputmethod.InputMethodManager 24 import android.widget.EditText 25 import android.widget.ImageView 26 import androidx.test.ext.junit.runners.AndroidJUnit4 27 import androidx.test.filters.SmallTest 28 import com.android.internal.util.LatencyTracker 29 import com.android.internal.widget.LockPatternUtils 30 import com.android.internal.widget.LockscreenCredential 31 import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor 32 import com.android.systemui.Flags as AconfigFlags 33 import com.android.systemui.SysuiTestCase 34 import com.android.systemui.classifier.FalsingCollector 35 import com.android.systemui.flags.FakeFeatureFlags 36 import com.android.systemui.flags.Flags 37 import com.android.systemui.haptics.msdl.bouncerHapticPlayer 38 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository 39 import com.android.systemui.res.R 40 import com.android.systemui.statusbar.policy.DevicePostureController 41 import com.android.systemui.testKosmos 42 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 43 import com.android.systemui.util.concurrency.DelayableExecutor 44 import com.android.systemui.util.mockito.mock 45 import com.android.systemui.util.mockito.whenever 46 import junit.framework.Assert.assertFalse 47 import junit.framework.Assert.assertTrue 48 import org.junit.Before 49 import org.junit.Test 50 import org.junit.runner.RunWith 51 import org.mockito.ArgumentCaptor 52 import org.mockito.ArgumentMatchers.anyBoolean 53 import org.mockito.ArgumentMatchers.anyString 54 import org.mockito.Captor 55 import org.mockito.Mock 56 import org.mockito.Mockito 57 import org.mockito.Mockito.mock 58 import org.mockito.Mockito.never 59 import org.mockito.Mockito.verify 60 import org.mockito.Mockito.`when` 61 import org.mockito.MockitoAnnotations 62 63 @SmallTest 64 @RunWith(AndroidJUnit4::class) 65 // collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow, 66 // which calls View.onRepeatWhenAttached, which requires being run on main thread. 67 @TestableLooper.RunWithLooper(setAsMainLooper = true) 68 class KeyguardPasswordViewControllerTest : SysuiTestCase() { 69 @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView 70 @Mock private lateinit var passwordEntry: EditText 71 private var passwordEntryLayoutParams = ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0) 72 @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor 73 @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode 74 @Mock lateinit var lockPatternUtils: LockPatternUtils 75 @Mock lateinit var keyguardSecurityCallback: KeyguardSecurityCallback 76 @Mock lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory 77 @Mock lateinit var latencyTracker: LatencyTracker 78 @Mock lateinit var inputMethodManager: InputMethodManager 79 @Mock lateinit var emergencyButtonController: EmergencyButtonController 80 @Mock lateinit var mainExecutor: DelayableExecutor 81 @Mock lateinit var falsingCollector: FalsingCollector 82 @Mock lateinit var keyguardViewController: KeyguardViewController 83 @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor 84 @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea 85 @Mock 86 private lateinit var mKeyguardMessageAreaController: 87 KeyguardMessageAreaController<BouncerKeyguardMessageArea> 88 @Mock private lateinit var postureController: DevicePostureController 89 @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier 90 @Captor private lateinit var keyListenerArgumentCaptor: ArgumentCaptor<View.OnKeyListener> 91 private val kosmos = testKosmos() 92 93 private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController 94 95 @Before setupnull96 fun setup() { 97 MockitoAnnotations.initMocks(this) 98 Mockito.`when`( 99 keyguardPasswordView.requireViewById<BouncerKeyguardMessageArea>( 100 R.id.bouncer_message_area 101 ) 102 ) 103 .thenReturn(mKeyguardMessageArea) 104 Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea)) 105 .thenReturn(mKeyguardMessageAreaController) 106 Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry) 107 Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) 108 .thenReturn(passwordEntry) 109 whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button)) 110 .thenReturn(mock(ImageView::class.java)) 111 `when`(keyguardPasswordView.resources).thenReturn(context.resources) 112 // TODO(b/362362385): No need to mock keyguardPasswordView.context once this bug is fixed. 113 `when`(keyguardPasswordView.context).thenReturn(context) 114 whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams) 115 val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository()) 116 val fakeFeatureFlags = FakeFeatureFlags() 117 fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) 118 mSetFlagsRule.enableFlags(AconfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES) 119 keyguardPasswordViewController = 120 KeyguardPasswordViewController( 121 keyguardPasswordView, 122 keyguardUpdateMonitor, 123 securityMode, 124 lockPatternUtils, 125 keyguardSecurityCallback, 126 messageAreaControllerFactory, 127 latencyTracker, 128 inputMethodManager, 129 emergencyButtonController, 130 mainExecutor, 131 mContext.resources, 132 falsingCollector, 133 keyguardViewController, 134 postureController, 135 fakeFeatureFlags, 136 mSelectedUserInteractor, 137 keyguardKeyboardInteractor, 138 kosmos.bouncerHapticPlayer, 139 mUserActivityNotifier, 140 ) 141 } 142 143 @Test testFocusWhenBouncerIsShownnull144 fun testFocusWhenBouncerIsShown() { 145 Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true) 146 Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) 147 keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) 148 keyguardPasswordView.post { 149 verify(keyguardPasswordView).requestFocus() 150 verify(keyguardPasswordView).showKeyboard() 151 } 152 } 153 154 @Test testDoNotFocusWhenBouncerIsHiddennull155 fun testDoNotFocusWhenBouncerIsHidden() { 156 Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false) 157 Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) 158 keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) 159 verify(keyguardPasswordView, never()).requestFocus() 160 } 161 162 @Test testHideKeyboardWhenOnPausenull163 fun testHideKeyboardWhenOnPause() { 164 keyguardPasswordViewController.onPause() 165 keyguardPasswordView.post { 166 verify(keyguardPasswordView).clearFocus() 167 verify(keyguardPasswordView).hideKeyboard() 168 } 169 } 170 171 @Test testOnViewAttachednull172 fun testOnViewAttached() { 173 keyguardPasswordViewController.onViewAttached() 174 verify(mKeyguardMessageAreaController) 175 .setMessage(context.resources.getString(R.string.keyguard_enter_your_password), false) 176 } 177 178 @Test testOnViewAttached_withExistingMessagenull179 fun testOnViewAttached_withExistingMessage() { 180 `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") 181 keyguardPasswordViewController.onViewAttached() 182 verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) 183 } 184 185 @Test testMessageIsSetWhenResetnull186 fun testMessageIsSetWhenReset() { 187 keyguardPasswordViewController.resetState() 188 verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password) 189 } 190 191 @Test testSpaceKeyDoesNotSubmitPasswordnull192 fun testSpaceKeyDoesNotSubmitPassword() { 193 keyguardPasswordViewController.onViewAttached() 194 verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture()) 195 196 val eventHandled = 197 keyListenerArgumentCaptor.value.onKey( 198 keyguardPasswordView, 199 KeyEvent.KEYCODE_SPACE, 200 KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE), 201 ) 202 203 assertFalse("Unlock attempted.", eventHandled) 204 } 205 206 @Test testEnterKeySubmitsPasswordnull207 fun testEnterKeySubmitsPassword() { 208 val password = mock<LockscreenCredential>() 209 `when`(keyguardPasswordView.enteredCredential).thenReturn(password) 210 `when`(password.size()).thenReturn(4) 211 `when`(password.duplicate()).thenReturn(password) 212 keyguardPasswordViewController.onViewAttached() 213 verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture()) 214 215 val eventHandled = 216 keyListenerArgumentCaptor.value.onKey( 217 keyguardPasswordView, 218 KeyEvent.KEYCODE_ENTER, 219 KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER), 220 ) 221 222 assertTrue("Unlock not attempted.", eventHandled) 223 } 224 } 225