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.server.input
18
19
20 import android.Manifest
21 import android.content.Context
22 import android.content.ContextWrapper
23 import android.content.PermissionChecker
24 import android.content.pm.PackageManager
25 import android.content.pm.PackageManagerInternal
26 import android.hardware.display.DisplayManager
27 import android.hardware.display.DisplayViewport
28 import android.hardware.display.VirtualDisplay
29 import android.hardware.input.InputManager
30 import android.hardware.input.InputManagerGlobal
31 import android.os.InputEventInjectionSync
32 import android.os.SystemClock
33 import android.os.test.TestLooper
34 import android.platform.test.annotations.EnableFlags
35 import android.platform.test.annotations.Presubmit
36 import android.platform.test.flag.junit.SetFlagsRule
37 import android.provider.Settings
38 import android.view.View.OnKeyListener
39 import android.view.InputDevice
40 import android.view.KeyCharacterMap
41 import android.view.KeyEvent
42 import android.view.SurfaceHolder
43 import android.view.SurfaceView
44 import android.view.WindowManager
45 import android.test.mock.MockContentResolver
46 import androidx.test.platform.app.InstrumentationRegistry
47 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
48 import com.android.dx.mockito.inline.extended.ExtendedMockito
49 import com.android.internal.policy.KeyInterceptionInfo
50 import com.android.internal.util.test.FakeSettingsProvider
51 import com.android.modules.utils.testing.ExtendedMockitoRule
52 import com.android.server.LocalServices
53 import com.android.server.wm.WindowManagerInternal
54 import com.google.common.truth.Truth.assertThat
55 import org.junit.After
56 import org.junit.Assert.assertEquals
57 import org.junit.Assert.assertNotEquals
58 import org.junit.Assert.assertTrue
59 import org.junit.Before
60 import org.junit.Rule
61 import org.junit.Test
62 import org.mockito.ArgumentMatchers.any
63 import org.mockito.ArgumentMatchers.anyBoolean
64 import org.mockito.ArgumentMatchers.anyFloat
65 import org.mockito.ArgumentMatchers.anyInt
66 import org.mockito.ArgumentMatchers.eq
67 import org.mockito.Mock
68 import org.mockito.Mockito.mock
69 import org.mockito.Mockito.never
70 import org.mockito.Mockito.spy
71 import org.mockito.Mockito.times
72 import org.mockito.Mockito.verify
73 import org.mockito.Mockito.verifyZeroInteractions
74 import org.mockito.Mockito.`when`
75 import org.mockito.stubbing.OngoingStubbing
76
77 /**
78 * Tests for {@link InputManagerService}.
79 *
80 * Build/Install/Run:
81 * atest InputTests:InputManagerServiceTests
82 */
83 @Presubmit
84 class InputManagerServiceTests {
85
86 companion object {
87 val ACTION_KEY_EVENTS = listOf(
88 KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
89 KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
90 KeyEvent( /* downTime= */0, /* eventTime= */0, /* action= */0, /* code= */0,
91 /* repeat= */0, KeyEvent.META_META_ON
92 )
93 )
94 }
95
96 @get:Rule
97 val extendedMockitoRule =
98 ExtendedMockitoRule.Builder(this)
99 .mockStatic(LocalServices::class.java)
100 .mockStatic(PermissionChecker::class.java)
101 .mockStatic(KeyCharacterMap::class.java)
102 .build()!!
103
104 @get:Rule
105 val setFlagsRule = SetFlagsRule()
106
107 @get:Rule
108 val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
109
110 @Mock
111 private lateinit var native: NativeInputManagerService
112
113 @Mock
114 private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
115
116 @Mock
117 private lateinit var windowManagerInternal: WindowManagerInternal
118
119 @Mock
120 private lateinit var packageManagerInternal: PackageManagerInternal
121
122 @Mock
123 private lateinit var uEventManager: UEventManager
124
125 @Mock
126 private lateinit var kbdController: InputManagerService.KeyboardBacklightControllerInterface
127
128 @Mock
129 private lateinit var kcm: KeyCharacterMap
130
131 private lateinit var service: InputManagerService
132 private lateinit var localService: InputManagerInternal
133 private lateinit var context: Context
134 private lateinit var testLooper: TestLooper
135 private lateinit var contentResolver: MockContentResolver
136 private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
137
138 @Before
setupnull139 fun setup() {
140 context = spy(ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()))
141 contentResolver = MockContentResolver(context)
142 contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider())
143 whenever(context.contentResolver).thenReturn(contentResolver)
144 testLooper = TestLooper()
145 service =
146 InputManagerService(object : InputManagerService.Injector(
147 context, testLooper.looper, uEventManager) {
148 override fun getNativeService(
149 service: InputManagerService?
150 ): NativeInputManagerService {
151 return native
152 }
153
154 override fun registerLocalService(service: InputManagerInternal?) {
155 localService = service!!
156 }
157
158 override fun getKeyboardBacklightController(
159 nativeService: NativeInputManagerService?
160 ): InputManagerService.KeyboardBacklightControllerInterface {
161 return kbdController
162 }
163 })
164 inputManagerGlobalSession = InputManagerGlobal.createTestSession(service)
165 val inputManager = InputManager(context)
166 whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
167 whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
168 whenever(context.checkCallingOrSelfPermission(Manifest.permission.MANAGE_KEY_GESTURES))
169 .thenReturn(
170 PackageManager.PERMISSION_GRANTED
171 )
172
173 ExtendedMockito.doReturn(windowManagerInternal).`when` {
174 LocalServices.getService(eq(WindowManagerInternal::class.java))
175 }
176 ExtendedMockito.doReturn(packageManagerInternal).`when` {
177 LocalServices.getService(eq(PackageManagerInternal::class.java))
178 }
179 ExtendedMockito.doReturn(kcm).`when` {
180 KeyCharacterMap.load(anyInt())
181 }
182
183 assertTrue("Local service must be registered", this::localService.isInitialized)
184 service.setWindowManagerCallbacks(wmCallbacks)
185 }
186
187 @After
tearDownnull188 fun tearDown() {
189 if (this::inputManagerGlobalSession.isInitialized) {
190 inputManagerGlobalSession.close()
191 }
192 }
193
194 @Test
testStartnull195 fun testStart() {
196 verifyZeroInteractions(native)
197
198 service.start()
199 verify(native).start()
200 }
201
202 @Test
testInputSettingsUpdatedOnSystemRunningnull203 fun testInputSettingsUpdatedOnSystemRunning() {
204 verifyZeroInteractions(native)
205
206 runWithShellPermissionIdentity {
207 service.systemRunning()
208 }
209
210 verify(native).setPointerSpeed(anyInt())
211 verify(native).setTouchpadPointerSpeed(anyInt())
212 verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean())
213 verify(native).setTouchpadTapToClickEnabled(anyBoolean())
214 verify(native).setTouchpadTapDraggingEnabled(anyBoolean())
215 verify(native).setShouldNotifyTouchpadHardwareState(anyBoolean())
216 verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
217 verify(native).setTouchpadThreeFingerTapShortcutEnabled(anyBoolean())
218 verify(native).setTouchpadSystemGesturesEnabled(anyBoolean())
219 verify(native).setShowTouches(anyBoolean())
220 verify(native).setMotionClassifierEnabled(anyBoolean())
221 verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
222 verify(native).setStylusPointerIconEnabled(anyBoolean())
223 // Called thrice at boot, since there are individual callbacks to update the
224 // key repeat timeout, the key repeat delay and whether key repeat enabled.
225 verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt(),
226 anyBoolean())
227 }
228
229 @Test
testPointerDisplayUpdatesWhenDisplayViewportsChangednull230 fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() {
231 val displayId = 123
232 whenever(wmCallbacks.pointerDisplayId).thenReturn(displayId)
233 val viewports = listOf<DisplayViewport>()
234 localService.setDisplayViewports(viewports)
235 verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java))
236 verify(native).setPointerDisplayId(displayId)
237 }
238
239 @Test
setDeviceTypeAssociation_setsDeviceTypeAssociationnull240 fun setDeviceTypeAssociation_setsDeviceTypeAssociation() {
241 val inputPort = "inputPort"
242 val type = "type"
243
244 localService.setTypeAssociation(inputPort, type)
245
246 assertThat(service.getDeviceTypeAssociations()).asList().containsExactly(inputPort, type)
247 .inOrder()
248 }
249
250 @Test
setAndUnsetDeviceTypeAssociation_deviceTypeAssociationIsMissingnull251 fun setAndUnsetDeviceTypeAssociation_deviceTypeAssociationIsMissing() {
252 val inputPort = "inputPort"
253 val type = "type"
254
255 localService.setTypeAssociation(inputPort, type)
256 localService.unsetTypeAssociation(inputPort)
257
258 assertTrue(service.getDeviceTypeAssociations().isEmpty())
259 }
260
261 @Test
testAddAndRemoveVirtualKeyboardLayoutAssociationnull262 fun testAddAndRemoveVirtualKeyboardLayoutAssociation() {
263 val inputPort = "input port"
264 val languageTag = "language"
265 val layoutType = "layoutType"
266 localService.addKeyboardLayoutAssociation(inputPort, languageTag, layoutType)
267 verify(native).changeKeyboardLayoutAssociation()
268
269 localService.removeKeyboardLayoutAssociation(inputPort)
270 verify(native, times(2)).changeKeyboardLayoutAssociation()
271 }
272
273 @Test
testActionKeyEventsForwardedToFocusedWindow_whenCorrectlyRequestednull274 fun testActionKeyEventsForwardedToFocusedWindow_whenCorrectlyRequested() {
275 service.systemRunning()
276 overrideSendActionKeyEventsToFocusedWindow(
277 /* hasPermission = */true,
278 /* hasPrivateFlag = */true
279 )
280 whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
281
282 for (event in ACTION_KEY_EVENTS) {
283 assertEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
284 }
285 }
286
287 @Test
testActionKeyEventsNotForwardedToFocusedWindow_whenNoPermissionsnull288 fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPermissions() {
289 service.systemRunning()
290 overrideSendActionKeyEventsToFocusedWindow(
291 /* hasPermission = */false,
292 /* hasPrivateFlag = */true
293 )
294 whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
295
296 for (event in ACTION_KEY_EVENTS) {
297 assertNotEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
298 }
299 }
300
301 @Test
testActionKeyEventsNotForwardedToFocusedWindow_whenNoPrivateFlagnull302 fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPrivateFlag() {
303 service.systemRunning()
304 overrideSendActionKeyEventsToFocusedWindow(
305 /* hasPermission = */true,
306 /* hasPrivateFlag = */false
307 )
308 whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
309
310 for (event in ACTION_KEY_EVENTS) {
311 assertNotEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
312 }
313 }
314
createVirtualDisplaysnull315 private fun createVirtualDisplays(count: Int): List<VirtualDisplay> {
316 val displayManager: DisplayManager = context.getSystemService(
317 DisplayManager::class.java
318 ) as DisplayManager
319 val virtualDisplays = mutableListOf<VirtualDisplay>()
320 for (i in 0 until count) {
321 virtualDisplays.add(displayManager.createVirtualDisplay(
322 /* displayName= */ "testVirtualDisplay$i",
323 /* width= */ 100,
324 /* height= */ 100,
325 /* densityDpi= */ 100,
326 /* surface= */ null,
327 /* flags= */ 0
328 ))
329 }
330 return virtualDisplays
331 }
332
333 // Helper function that creates a KeyEvent with Keycode A with the given action
createKeycodeAEventnull334 private fun createKeycodeAEvent(inputDevice: InputDevice, action: Int): KeyEvent {
335 val eventTime = SystemClock.uptimeMillis()
336 return KeyEvent(
337 /* downTime= */ eventTime,
338 /* eventTime= */ eventTime,
339 /* action= */ action,
340 /* code= */ KeyEvent.KEYCODE_A,
341 /* repeat= */ 0,
342 /* metaState= */ 0,
343 /* deviceId= */ inputDevice.id,
344 /* scanCode= */ 0,
345 /* flags= */ KeyEvent.FLAG_FROM_SYSTEM,
346 /* source= */ InputDevice.SOURCE_KEYBOARD
347 )
348 }
349
createInputDevicenull350 private fun createInputDevice(): InputDevice {
351 return InputDevice.Builder()
352 .setId(123)
353 .setName("abc")
354 .setDescriptor("def")
355 .setSources(InputDevice.SOURCE_KEYBOARD)
356 .build()
357 }
358
359 @Test
addUniqueIdAssociationByDescriptor_verifyAssociationsnull360 fun addUniqueIdAssociationByDescriptor_verifyAssociations() {
361 // Overall goal is to have 2 displays and verify that events from the InputDevice are
362 // sent only to the view that is on the associated display.
363 // So, associate the InputDevice with display 1, then send and verify KeyEvents.
364 // Then remove associations, then associate the InputDevice with display 2, then send
365 // and verify commands.
366
367 // Make 2 virtual displays with some mock SurfaceViews
368 val mockSurfaceView1 = mock(SurfaceView::class.java)
369 val mockSurfaceView2 = mock(SurfaceView::class.java)
370 val mockSurfaceHolder1 = mock(SurfaceHolder::class.java)
371 `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1)
372 val mockSurfaceHolder2 = mock(SurfaceHolder::class.java)
373 `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2)
374
375 val virtualDisplays = createVirtualDisplays(2)
376
377 // Simulate an InputDevice
378 val inputDevice = createInputDevice()
379
380 // Associate input device with display
381 service.addUniqueIdAssociationByDescriptor(
382 inputDevice.descriptor,
383 virtualDisplays[0].display.displayId.toString()
384 )
385
386 // Simulate 2 different KeyEvents
387 val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN)
388 val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP)
389
390 // Create a mock OnKeyListener object
391 val mockOnKeyListener = mock(OnKeyListener::class.java)
392
393 // Verify that the event went to Display 1 not Display 2
394 service.injectInputEvent(downEvent, InputEventInjectionSync.NONE)
395
396 // Call the onKey method on the mock OnKeyListener object
397 mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent)
398 mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent)
399
400 // Verify that the onKey method was called with the expected arguments
401 verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent)
402 verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent)
403
404 // Remove association
405 service.removeUniqueIdAssociationByDescriptor(inputDevice.descriptor)
406
407 // Associate with Display 2
408 service.addUniqueIdAssociationByDescriptor(
409 inputDevice.descriptor,
410 virtualDisplays[1].display.displayId.toString()
411 )
412
413 // Simulate a KeyEvent
414 service.injectInputEvent(upEvent, InputEventInjectionSync.NONE)
415
416 // Verify that the event went to Display 2 not Display 1
417 verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent)
418 verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
419 }
420
421 @Test
addUniqueIdAssociationByPort_verifyAssociationsnull422 fun addUniqueIdAssociationByPort_verifyAssociations() {
423 // Overall goal is to have 2 displays and verify that events from the InputDevice are
424 // sent only to the view that is on the associated display.
425 // So, associate the InputDevice with display 1, then send and verify KeyEvents.
426 // Then remove associations, then associate the InputDevice with display 2, then send
427 // and verify commands.
428
429 // Make 2 virtual displays with some mock SurfaceViews
430 val mockSurfaceView1 = mock(SurfaceView::class.java)
431 val mockSurfaceView2 = mock(SurfaceView::class.java)
432 val mockSurfaceHolder1 = mock(SurfaceHolder::class.java)
433 `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1)
434 val mockSurfaceHolder2 = mock(SurfaceHolder::class.java)
435 `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2)
436
437 val virtualDisplays = createVirtualDisplays(2)
438
439 // Simulate an InputDevice
440 val inputDevice = createInputDevice()
441
442 // Associate input device with display
443 service.addUniqueIdAssociationByPort(
444 inputDevice.name,
445 virtualDisplays[0].display.displayId.toString()
446 )
447
448 // Simulate 2 different KeyEvents
449 val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN)
450 val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP)
451
452 // Create a mock OnKeyListener object
453 val mockOnKeyListener = mock(OnKeyListener::class.java)
454
455 // Verify that the event went to Display 1 not Display 2
456 service.injectInputEvent(downEvent, InputEventInjectionSync.NONE)
457
458 // Call the onKey method on the mock OnKeyListener object
459 mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent)
460 mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent)
461
462 // Verify that the onKey method was called with the expected arguments
463 verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent)
464 verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent)
465
466 // Remove association
467 service.removeUniqueIdAssociationByPort(inputDevice.name)
468
469 // Associate with Display 2
470 service.addUniqueIdAssociationByPort(
471 inputDevice.name,
472 virtualDisplays[1].display.displayId.toString()
473 )
474
475 // Simulate a KeyEvent
476 service.injectInputEvent(upEvent, InputEventInjectionSync.NONE)
477
478 // Verify that the event went to Display 2 not Display 1
479 verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent)
480 verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
481 }
482
483 @Test
484 @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
handleKeyGestures_keyboardBacklightnull485 fun handleKeyGestures_keyboardBacklight() {
486 service.systemRunning()
487
488 val backlightDownEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN)
489 service.interceptKeyBeforeDispatching(null, backlightDownEvent, /* policyFlags = */0)
490 verify(kbdController).decrementKeyboardBacklight(anyInt())
491
492 val backlightUpEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP)
493 service.interceptKeyBeforeDispatching(null, backlightUpEvent, /* policyFlags = */0)
494 verify(kbdController).incrementKeyboardBacklight(anyInt())
495 }
496
497 @Test
498 @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
handleKeyGestures_toggleCapsLocknull499 fun handleKeyGestures_toggleCapsLock() {
500 service.systemRunning()
501
502 val metaDownEvent = createKeyEvent(KeyEvent.KEYCODE_META_LEFT)
503 service.interceptKeyBeforeDispatching(null, metaDownEvent, /* policyFlags = */0)
504 val altDownEvent =
505 createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_DOWN)
506 service.interceptKeyBeforeDispatching(null, altDownEvent, /* policyFlags = */0)
507 val altUpEvent =
508 createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_UP)
509 service.interceptKeyBeforeDispatching(null, altUpEvent, /* policyFlags = */0)
510
511 verify(native).toggleCapsLock(anyInt())
512 }
513
overrideSendActionKeyEventsToFocusedWindownull514 fun overrideSendActionKeyEventsToFocusedWindow(
515 hasPermission: Boolean,
516 hasPrivateFlag: Boolean
517 ) {
518 ExtendedMockito.doReturn(
519 if (hasPermission) {
520 PermissionChecker.PERMISSION_GRANTED
521 } else {
522 PermissionChecker.PERMISSION_HARD_DENIED
523 }
524 ).`when` {
525 PermissionChecker.checkPermissionForDataDelivery(
526 any(),
527 eq(Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW),
528 anyInt(),
529 anyInt(),
530 any(),
531 any(),
532 any()
533 )
534 }
535
536 val info = KeyInterceptionInfo(
537 /* type = */0,
538 if (hasPrivateFlag) {
539 WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS
540 } else {
541 0
542 },
543 "title",
544 /* uid = */0
545 )
546 whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
547 }
548
createKeyEventnull549 private fun createKeyEvent(
550 keycode: Int,
551 modifierState: Int = 0,
552 action: Int = KeyEvent.ACTION_DOWN
553 ): KeyEvent {
554 return KeyEvent(
555 /* downTime = */0,
556 /* eventTime = */0,
557 action,
558 keycode,
559 /* repeat = */0,
560 modifierState,
561 KeyCharacterMap.VIRTUAL_KEYBOARD,
562 /* scancode = */0,
563 /* flags = */0,
564 InputDevice.SOURCE_KEYBOARD
565 )
566 }
567 }
568
whenevernull569 private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
570