1 /* 2 * Copyright (C) 2021 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.policy; 18 19 import static android.view.KeyEvent.ACTION_DOWN; 20 import static android.view.KeyEvent.ACTION_UP; 21 import static android.view.KeyEvent.KEYCODE_BACK; 22 import static android.view.KeyEvent.KEYCODE_POWER; 23 24 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertTrue; 30 31 import android.app.Instrumentation; 32 import android.content.Context; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Looper; 36 import android.os.Process; 37 import android.os.SystemClock; 38 import android.view.KeyEvent; 39 40 import org.junit.Before; 41 import org.junit.Test; 42 43 import java.util.concurrent.BlockingQueue; 44 import java.util.concurrent.CountDownLatch; 45 import java.util.concurrent.LinkedBlockingQueue; 46 import java.util.concurrent.TimeUnit; 47 48 /** 49 * Test class for {@link SingleKeyGestureDetector}. 50 * 51 * Build/Install/Run: 52 * atest WmTests:SingleKeyGestureTests 53 */ 54 public class SingleKeyGestureTests { 55 private SingleKeyGestureDetector mDetector; 56 57 private int mMaxMultiPressCount = 3; 58 private int mExpectedMultiPressCount = 2; 59 60 private CountDownLatch mShortPressed = new CountDownLatch(1); 61 private CountDownLatch mLongPressed = new CountDownLatch(1); 62 private CountDownLatch mVeryLongPressed = new CountDownLatch(1); 63 private CountDownLatch mMultiPressed = new CountDownLatch(1); 64 private BlockingQueue<KeyUpData> mKeyUpQueue = new LinkedBlockingQueue<>(); 65 66 private final Instrumentation mInstrumentation = getInstrumentation(); 67 private final Context mContext = mInstrumentation.getTargetContext(); 68 private long mWaitTimeout; 69 private long mLongPressTime; 70 private long mVeryLongPressTime; 71 72 // Allow press from non interactive mode. 73 private boolean mAllowNonInteractiveForPress = true; 74 private boolean mAllowNonInteractiveForLongPress = true; 75 76 private boolean mLongPressOnPowerBehavior = true; 77 private boolean mVeryLongPressOnPowerBehavior = true; 78 private boolean mLongPressOnBackBehavior = false; 79 80 @Before setUp()81 public void setUp() { 82 mInstrumentation.runOnMainSync( 83 () -> { 84 mDetector = SingleKeyGestureDetector.get(mContext, Looper.myLooper()); 85 initSingleKeyGestureRules(); 86 }); 87 88 mWaitTimeout = SingleKeyGestureDetector.MULTI_PRESS_TIMEOUT + 50; 89 mLongPressTime = SingleKeyGestureDetector.sDefaultLongPressTimeout + 50; 90 mVeryLongPressTime = SingleKeyGestureDetector.sDefaultVeryLongPressTimeout + 50; 91 } 92 initSingleKeyGestureRules()93 private void initSingleKeyGestureRules() { 94 mDetector.addRule( 95 new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) { 96 @Override 97 boolean supportLongPress() { 98 return mLongPressOnPowerBehavior; 99 } 100 101 @Override 102 boolean supportVeryLongPress() { 103 return mVeryLongPressOnPowerBehavior; 104 } 105 106 @Override 107 int getMaxMultiPressCount() { 108 return mMaxMultiPressCount; 109 } 110 111 @Override 112 public void onPress(long downTime, int displayId) { 113 if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) { 114 return; 115 } 116 mShortPressed.countDown(); 117 } 118 119 @Override 120 void onLongPress(long downTime) { 121 if (mDetector.beganFromNonInteractive() 122 && !mAllowNonInteractiveForLongPress) { 123 return; 124 } 125 mLongPressed.countDown(); 126 } 127 128 @Override 129 void onVeryLongPress(long downTime) { 130 mVeryLongPressed.countDown(); 131 } 132 133 @Override 134 void onMultiPress(long downTime, int count, int displayId) { 135 if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) { 136 return; 137 } 138 mMultiPressed.countDown(); 139 assertTrue(mMaxMultiPressCount >= count); 140 assertEquals(mExpectedMultiPressCount, count); 141 } 142 143 @Override 144 void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId, 145 int metaState) { 146 mKeyUpQueue.add(new KeyUpData(KEYCODE_POWER, multiPressCount)); 147 } 148 }); 149 150 mDetector.addRule( 151 new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_BACK) { 152 @Override 153 boolean supportLongPress() { 154 return mLongPressOnBackBehavior; 155 } 156 157 @Override 158 int getMaxMultiPressCount() { 159 return mMaxMultiPressCount; 160 } 161 162 @Override 163 public void onPress(long downTime, int displayId) { 164 if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) { 165 return; 166 } 167 mShortPressed.countDown(); 168 } 169 170 @Override 171 void onMultiPress(long downTime, int count, int displayId) { 172 if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) { 173 return; 174 } 175 mMultiPressed.countDown(); 176 assertTrue(mMaxMultiPressCount >= count); 177 assertEquals(mExpectedMultiPressCount, count); 178 } 179 180 @Override 181 void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId, 182 int metaState) { 183 mKeyUpQueue.add(new KeyUpData(KEYCODE_BACK, multiPressCount)); 184 } 185 186 @Override 187 void onLongPress(long downTime) { 188 mLongPressed.countDown(); 189 } 190 }); 191 } 192 193 private static class KeyUpData { 194 public final int keyCode; 195 public final int pressCount; 196 KeyUpData(int keyCode, int pressCount)197 KeyUpData(int keyCode, int pressCount) { 198 this.keyCode = keyCode; 199 this.pressCount = pressCount; 200 } 201 } 202 pressKey(int keyCode, long pressTime)203 private void pressKey(int keyCode, long pressTime) { 204 pressKey(keyCode, pressTime, true /* interactive */); 205 } 206 pressKey(int keyCode, long pressTime, boolean interactive)207 private void pressKey(int keyCode, long pressTime, boolean interactive) { 208 pressKey(keyCode, pressTime, interactive, false /* defaultDisplayOn */); 209 } 210 pressKey( int keyCode, long pressTime, boolean interactive, boolean defaultDisplayOn)211 private void pressKey( 212 int keyCode, long pressTime, boolean interactive, boolean defaultDisplayOn) { 213 long eventTime = SystemClock.uptimeMillis(); 214 final KeyEvent keyDown = 215 new KeyEvent( 216 eventTime, 217 eventTime, 218 ACTION_DOWN, 219 keyCode, 220 0 /* repeat */, 221 0 /* metaState */); 222 mDetector.interceptKey(keyDown, interactive, defaultDisplayOn); 223 224 // keep press down. 225 try { 226 Thread.sleep(pressTime); 227 } catch (InterruptedException e) { 228 e.printStackTrace(); 229 } 230 231 eventTime += pressTime; 232 final KeyEvent keyUp = 233 new KeyEvent( 234 eventTime, 235 eventTime, 236 ACTION_UP, 237 keyCode, 238 0 /* repeat */, 239 0 /* metaState */); 240 241 mDetector.interceptKey(keyUp, interactive, defaultDisplayOn); 242 } 243 244 @Test testShortPress()245 public void testShortPress() throws InterruptedException { 246 pressKey(KEYCODE_POWER, 0 /* pressTime */); 247 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 248 } 249 250 @Test testLongPress()251 public void testLongPress() throws InterruptedException { 252 pressKey(KEYCODE_POWER, mLongPressTime); 253 assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 254 } 255 256 @Test testVeryLongPress()257 public void testVeryLongPress() throws InterruptedException { 258 pressKey(KEYCODE_POWER, mVeryLongPressTime); 259 assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 260 } 261 262 @Test testMultiPress()263 public void testMultiPress() throws InterruptedException { 264 // Double presses. 265 mExpectedMultiPressCount = 2; 266 pressKey(KEYCODE_POWER, 0 /* pressTime */); 267 pressKey(KEYCODE_POWER, 0 /* pressTime */); 268 assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 269 270 // Triple presses. 271 mExpectedMultiPressCount = 3; 272 mMultiPressed = new CountDownLatch(1); 273 pressKey(KEYCODE_POWER, 0 /* pressTime */); 274 pressKey(KEYCODE_POWER, 0 /* pressTime */); 275 pressKey(KEYCODE_POWER, 0 /* pressTime */); 276 assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 277 } 278 279 @Test testOnKeyUp()280 public void testOnKeyUp() throws InterruptedException { 281 pressKey(KEYCODE_POWER, 0 /* pressTime */); 282 283 verifyKeyUpData(KEYCODE_POWER, 1 /* expectedMultiPressCount */); 284 } 285 verifyKeyUpData(int expectedKeyCode, int expectedMultiPressCount)286 private void verifyKeyUpData(int expectedKeyCode, int expectedMultiPressCount) 287 throws InterruptedException { 288 KeyUpData keyUpData = mKeyUpQueue.poll(mWaitTimeout, TimeUnit.MILLISECONDS); 289 assertNotNull(keyUpData); 290 assertEquals(expectedKeyCode, keyUpData.keyCode); 291 assertEquals(expectedMultiPressCount, keyUpData.pressCount); 292 } 293 294 @Test testNonInteractive()295 public void testNonInteractive() throws InterruptedException { 296 // Disallow short press behavior from non interactive. 297 mAllowNonInteractiveForPress = false; 298 pressKey(KEYCODE_POWER, 0 /* pressTime */, false /* interactive */); 299 assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 300 301 // Allow long press behavior from non interactive. 302 pressKey(KEYCODE_POWER, mLongPressTime, false /* interactive */); 303 assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 304 } 305 306 @Test testShortPress_Pressure()307 public void testShortPress_Pressure() throws InterruptedException { 308 final HandlerThread handlerThread = 309 new HandlerThread("testInputReader", Process.THREAD_PRIORITY_DISPLAY); 310 handlerThread.start(); 311 Handler newHandler = new Handler(handlerThread.getLooper()); 312 mMaxMultiPressCount = 1; // Will trigger short press when event up. 313 try { 314 // To make sure we won't get any crash while panic pressing keys. 315 for (int i = 0; i < 100; i++) { 316 mShortPressed = new CountDownLatch(2); 317 newHandler.runWithScissors( 318 () -> { 319 pressKey(KEYCODE_POWER, 0 /* pressTime */); 320 pressKey(KEYCODE_BACK, 0 /* pressTime */); 321 }, 322 mWaitTimeout); 323 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 324 } 325 } finally { 326 handlerThread.quitSafely(); 327 } 328 } 329 330 @Test testMultiPress_Pressure()331 public void testMultiPress_Pressure() throws InterruptedException { 332 final HandlerThread handlerThread = 333 new HandlerThread("testInputReader", Process.THREAD_PRIORITY_DISPLAY); 334 handlerThread.start(); 335 Handler newHandler = new Handler(handlerThread.getLooper()); 336 try { 337 // To make sure we won't get any unexpected multi-press count. 338 for (int i = 0; i < 5; i++) { 339 mMultiPressed = new CountDownLatch(1); 340 mShortPressed = new CountDownLatch(1); 341 newHandler.runWithScissors( 342 () -> { 343 pressKey(KEYCODE_POWER, 0 /* pressTime */); 344 pressKey(KEYCODE_POWER, 0 /* pressTime */); 345 }, 346 mWaitTimeout); 347 assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 348 349 newHandler.runWithScissors( 350 () -> pressKey(KEYCODE_POWER, 0 /* pressTime */), mWaitTimeout); 351 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 352 } 353 } finally { 354 handlerThread.quitSafely(); 355 } 356 } 357 358 @Test testOnKeyUp_Pressure()359 public void testOnKeyUp_Pressure() throws InterruptedException { 360 final HandlerThread handlerThread = 361 new HandlerThread("testInputReader", Process.THREAD_PRIORITY_DISPLAY); 362 handlerThread.start(); 363 Handler newHandler = new Handler(handlerThread.getLooper()); 364 try { 365 // To make sure we won't get any unexpected multi-press count. 366 for (int i = 0; i < 5; i++) { 367 newHandler.runWithScissors( 368 () -> { 369 pressKey(KEYCODE_POWER, 0 /* pressTime */); 370 pressKey(KEYCODE_POWER, 0 /* pressTime */); 371 }, 372 mWaitTimeout); 373 newHandler.runWithScissors( 374 () -> pressKey(KEYCODE_BACK, 0 /* pressTime */), mWaitTimeout); 375 376 verifyKeyUpData(KEYCODE_POWER, 1 /* expectedMultiPressCount */); 377 verifyKeyUpData(KEYCODE_POWER, 2 /* expectedMultiPressCount */); 378 verifyKeyUpData(KEYCODE_BACK, 1 /* expectedMultiPressCount */); 379 } 380 } finally { 381 handlerThread.quitSafely(); 382 } 383 } 384 385 @Test testUpdateRule()386 public void testUpdateRule() throws InterruptedException { 387 // Power key rule doesn't allow the long press gesture. 388 mLongPressOnPowerBehavior = false; 389 pressKey(KEYCODE_POWER, mLongPressTime); 390 assertFalse(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 391 392 // Back key rule allows the long press gesture. 393 mLongPressOnBackBehavior = true; 394 pressKey(KEYCODE_BACK, mLongPressTime); 395 assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 396 } 397 398 @Test testAddRemove()399 public void testAddRemove() throws InterruptedException { 400 final SingleKeyGestureDetector.SingleKeyRule rule = 401 new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) { 402 @Override 403 void onPress(long downTime, int displayId) { 404 mShortPressed.countDown(); 405 } 406 }; 407 408 mDetector.removeRule(rule); 409 pressKey(KEYCODE_POWER, 0 /* pressTime */); 410 assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 411 412 mDetector.addRule(rule); 413 pressKey(KEYCODE_POWER, 0 /* pressTime */); 414 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 415 } 416 417 // Verify short press should not be triggered if no very long press behavior defined but the 418 // press time exceeded the very long press timeout. 419 @Test testTimeoutExceedVeryLongPress()420 public void testTimeoutExceedVeryLongPress() throws InterruptedException { 421 mVeryLongPressOnPowerBehavior = false; 422 423 pressKey(KEYCODE_POWER, mVeryLongPressTime + 50); 424 assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); 425 assertEquals(mVeryLongPressed.getCount(), 1); 426 assertEquals(mShortPressed.getCount(), 1); 427 } 428 } 429