1 /* <lambda>null2 * 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 android.input.cts 18 19 import android.Manifest 20 import android.cts.input.EventVerifier 21 import android.hardware.input.InputManager 22 import android.provider.Settings 23 import android.view.KeyEvent 24 import androidx.test.ext.junit.rules.ActivityScenarioRule 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.MediumTest 27 import androidx.test.platform.app.InstrumentationRegistry 28 import com.android.compatibility.common.util.PollingCheck 29 import com.android.compatibility.common.util.SystemUtil 30 import com.android.compatibility.common.util.ThrowingSupplier 31 import com.android.cts.input.UinputKeyboard 32 import com.android.cts.input.inputeventmatchers.withKeyAction 33 import com.android.cts.input.inputeventmatchers.withKeyCode 34 import com.android.cts.input.inputeventmatchers.withModifierState 35 import org.hamcrest.Matchers.allOf 36 import org.junit.After 37 import org.junit.Before 38 import org.junit.Rule 39 import org.junit.Test 40 import org.junit.runner.RunWith 41 42 /** 43 * Create virtual keyboard devices and inject a 'hardware' key event after remapping keys. Ensure 44 * that the event keys are correctly remapped. 45 */ 46 @MediumTest 47 @RunWith(AndroidJUnit4::class) 48 class ModifierKeyRemappingTest { 49 50 companion object { 51 // Linux keycode defined in the "linux/input-event-codes.h" header. 52 val KEY_LEFTALT = 56 53 } 54 55 private val instrumentation = InstrumentationRegistry.getInstrumentation() 56 57 @get:Rule 58 val rule = ActivityScenarioRule<CaptureEventActivity>(CaptureEventActivity::class.java) 59 60 private lateinit var activity: CaptureEventActivity 61 private lateinit var verifier: EventVerifier 62 private lateinit var inputManager: InputManager 63 private lateinit var existingRemappings: Map<Int, Int> 64 65 @Before 66 fun setUp() { 67 rule.getScenario().onActivity { 68 inputManager = it.getSystemService(InputManager::class.java) 69 activity = it 70 verifier = EventVerifier(activity::getInputEvent) 71 } 72 inputManager.resetLockedModifierState() 73 PollingCheck.waitFor { activity.hasWindowFocus() } 74 75 // Save existing remappings 76 existingRemappings = getModifierKeyRemapping() 77 clearAllModifierKeyRemappings() 78 } 79 80 @After 81 fun tearDown() { 82 if (this::existingRemappings.isInitialized) { 83 clearAllModifierKeyRemappings() 84 existingRemappings.forEach { entry -> 85 remapModifierKey(entry.key, entry.value) 86 } 87 } 88 if (this::inputManager.isInitialized) { 89 inputManager.resetLockedModifierState() 90 } 91 } 92 93 @Test 94 fun testHardwareKeyEventsWithRemapping_afterKeyboardAdded() { 95 ModifierRemappingFlag(true).use { 96 UinputKeyboard(instrumentation).use { keyboardDevice -> 97 val inputDevice = inputManager.getInputDevice(keyboardDevice.deviceId) 98 99 // Add remapping after device is already added 100 remapModifierKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_SHIFT_LEFT) 101 PollingCheck.waitFor { 102 KeyEvent.KEYCODE_SHIFT_LEFT == 103 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 104 } 105 106 injectKeyDown(keyboardDevice, KEY_LEFTALT) 107 verifier.assertReceivedKey( 108 allOf( 109 withKeyCode(KeyEvent.KEYCODE_SHIFT_LEFT), 110 withKeyAction(KeyEvent.ACTION_DOWN), 111 withModifierState(KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON) 112 ) 113 ) 114 115 injectKeyUp(keyboardDevice, KEY_LEFTALT) 116 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_SHIFT_LEFT)) 117 118 clearAllModifierKeyRemappings() 119 PollingCheck.waitFor { 120 KeyEvent.KEYCODE_ALT_LEFT == 121 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 122 } 123 124 injectKeyDown(keyboardDevice, KEY_LEFTALT) 125 verifier.assertReceivedKey( 126 allOf( 127 withKeyCode(KeyEvent.KEYCODE_ALT_LEFT), 128 withKeyAction(KeyEvent.ACTION_DOWN), 129 withModifierState(KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON) 130 ) 131 ) 132 133 injectKeyUp(keyboardDevice, KEY_LEFTALT) 134 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_ALT_LEFT)) 135 136 activity.assertNoEvents() 137 } 138 } 139 } 140 141 @Test 142 fun testHardwareKeyEventsWithRemapping_beforeKeyboardAdded() { 143 ModifierRemappingFlag(true).use { 144 // Add remapping before device is added 145 remapModifierKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_SHIFT_LEFT) 146 PollingCheck.waitFor { getModifierKeyRemapping().size == 1 } 147 148 UinputKeyboard(instrumentation).use { keyboardDevice -> 149 val inputDevice = inputManager.getInputDevice(keyboardDevice.deviceId) 150 PollingCheck.waitFor { 151 KeyEvent.KEYCODE_SHIFT_LEFT == 152 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 153 } 154 155 injectKeyDown(keyboardDevice, KEY_LEFTALT) 156 verifier.assertReceivedKey( 157 allOf( 158 withKeyCode(KeyEvent.KEYCODE_SHIFT_LEFT), 159 withKeyAction(KeyEvent.ACTION_DOWN), 160 withModifierState(KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON) 161 ) 162 ) 163 164 injectKeyUp(keyboardDevice, KEY_LEFTALT) 165 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_SHIFT_LEFT)) 166 167 clearAllModifierKeyRemappings() 168 PollingCheck.waitFor { 169 KeyEvent.KEYCODE_ALT_LEFT == 170 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 171 } 172 173 injectKeyDown(keyboardDevice, KEY_LEFTALT) 174 verifier.assertReceivedKey( 175 allOf( 176 withKeyCode(KeyEvent.KEYCODE_ALT_LEFT), 177 withKeyAction(KeyEvent.ACTION_DOWN), 178 withModifierState(KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON) 179 ) 180 ) 181 182 injectKeyUp(keyboardDevice, KEY_LEFTALT) 183 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_ALT_LEFT)) 184 185 activity.assertNoEvents() 186 } 187 } 188 } 189 190 @Test 191 fun testAltToCapsLockRemapping_forKeyboardWithNoCapsLockKey() { 192 ModifierRemappingFlag(true).use { 193 UinputKeyboard(instrumentation, listOf("KEY_Q", "KEY_LEFTALT")).use { keyboardDevice -> 194 val inputDevice = inputManager.getInputDevice(keyboardDevice.deviceId) 195 remapModifierKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_CAPS_LOCK) 196 PollingCheck.waitFor { 197 KeyEvent.KEYCODE_CAPS_LOCK == 198 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 199 } 200 201 injectKeyDown(keyboardDevice, KEY_LEFTALT) 202 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_CAPS_LOCK)) 203 204 injectKeyUp(keyboardDevice, KEY_LEFTALT) 205 verifier.assertReceivedKey( 206 allOf( 207 withKeyCode(KeyEvent.KEYCODE_CAPS_LOCK), 208 withModifierState(KeyEvent.META_CAPS_LOCK_ON), 209 ) 210 ) 211 212 // Send second pair of key presses to reset caps lock state 213 injectKeyDown(keyboardDevice, KEY_LEFTALT) 214 verifier.assertReceivedKey( 215 allOf( 216 withKeyCode(KeyEvent.KEYCODE_CAPS_LOCK), 217 withModifierState(KeyEvent.META_CAPS_LOCK_ON), 218 ) 219 ) 220 221 injectKeyUp(keyboardDevice, KEY_LEFTALT) 222 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_CAPS_LOCK)) 223 224 clearAllModifierKeyRemappings() 225 PollingCheck.waitFor { 226 KeyEvent.KEYCODE_ALT_LEFT == 227 inputDevice?.getKeyCodeForKeyLocation(KeyEvent.KEYCODE_ALT_LEFT) 228 } 229 230 injectKeyDown(keyboardDevice, KEY_LEFTALT) 231 verifier.assertReceivedKey( 232 allOf( 233 withKeyCode(KeyEvent.KEYCODE_ALT_LEFT), 234 withKeyAction(KeyEvent.ACTION_DOWN), 235 withModifierState(KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON) 236 ) 237 ) 238 239 injectKeyUp(keyboardDevice, KEY_LEFTALT) 240 verifier.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_ALT_LEFT)) 241 242 activity.assertNoEvents() 243 } 244 } 245 } 246 247 /** 248 * Remaps a modifier key to another modifier key 249 * 250 * @param fromKey modifier key getting remapped 251 * @param toKey modifier key that it is getting remapped to 252 */ 253 private fun remapModifierKey(fromKey: Int, toKey: Int) { 254 SystemUtil.runWithShellPermissionIdentity( 255 { inputManager.remapModifierKey(fromKey, toKey) }, 256 Manifest.permission.REMAP_MODIFIER_KEYS 257 ) 258 } 259 260 /** 261 * Clears remapping for a modifier key 262 */ 263 private fun clearAllModifierKeyRemappings() { 264 SystemUtil.runWithShellPermissionIdentity( 265 { inputManager.clearAllModifierKeyRemappings() }, 266 Manifest.permission.REMAP_MODIFIER_KEYS 267 ) 268 PollingCheck.waitFor { getModifierKeyRemapping().isEmpty() } 269 } 270 271 private fun getModifierKeyRemapping(): Map<Int, Int> { 272 return SystemUtil.runWithShellPermissionIdentity( 273 ThrowingSupplier<Map<Int, Int>> { inputManager.modifierKeyRemapping }, 274 Manifest.permission.REMAP_MODIFIER_KEYS 275 ) 276 } 277 278 private inner class ModifierRemappingFlag constructor(enabled: Boolean) : AutoCloseable { 279 init { 280 Settings.Global.putString( 281 activity.contentResolver, 282 "settings_new_keyboard_modifier_key", 283 enabled.toString() 284 ) 285 } 286 287 override fun close() { 288 Settings.Global.putString( 289 activity.contentResolver, 290 "settings_new_keyboard_modifier_key", 291 "" 292 ) 293 } 294 } 295 } 296