Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2011 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.accessibility;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 import android.os.PowerManager;
     22 import android.util.Pools.SimplePool;
     23 import android.util.Slog;
     24 import android.util.SparseBooleanArray;
     25 import android.view.Choreographer;
     26 import android.view.InputDevice;
     27 import android.view.InputEvent;
     28 import android.view.InputFilter;
     29 import android.view.KeyEvent;
     30 import android.view.MotionEvent;
     31 import android.view.WindowManagerPolicy;
     32 import android.view.accessibility.AccessibilityEvent;
     33 
     34 import com.android.server.LocalServices;
     35 
     36 /**
     37  * This class is an input filter for implementing accessibility features such
     38  * as display magnification and explore by touch.
     39  *
     40  * NOTE: This class has to be created and poked only from the main thread.
     41  */
     42 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
     43 
     44     private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
     45 
     46     private static final boolean DEBUG = false;
     47 
     48     /**
     49      * Flag for enabling the screen magnification feature.
     50      *
     51      * @see #setUserAndEnabledFeatures(int, int)
     52      */
     53     static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
     54 
     55     /**
     56      * Flag for enabling the touch exploration feature.
     57      *
     58      * @see #setUserAndEnabledFeatures(int, int)
     59      */
     60     static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
     61 
     62     /**
     63      * Flag for enabling the filtering key events feature.
     64      *
     65      * @see #setUserAndEnabledFeatures(int, int)
     66      */
     67     static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004;
     68 
     69     /**
     70      * Flag for enabling "Automatically click on mouse stop" feature.
     71      *
     72      * @see #setUserAndEnabledFeatures(int, int)
     73      */
     74     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
     75 
     76     /**
     77      * Flag for enabling motion event injection.
     78      *
     79      * @see #setUserAndEnabledFeatures(int, int)
     80      */
     81     static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
     82 
     83     /**
     84      * Flag for enabling the feature to control the screen magnifier. If
     85      * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
     86      * as the screen magnifier feature performs a super set of the work
     87      * performed by this feature.
     88      *
     89      * @see #setUserAndEnabledFeatures(int, int)
     90      */
     91     static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
     92 
     93     /**
     94      * Flag for enabling the feature to trigger the screen magnifier
     95      * from another on-device interaction.
     96      */
     97     static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040;
     98 
     99     static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
    100             | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
    101             | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
    102 
    103     private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
    104         @Override
    105         public void run() {
    106             final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
    107             if (DEBUG) {
    108                 Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos);
    109             }
    110             processBatchedEvents(frameTimeNanos);
    111             if (DEBUG) {
    112                 Slog.i(TAG, "End batch processing.");
    113             }
    114             if (mEventQueue != null) {
    115                 scheduleProcessBatchedEvents();
    116             }
    117         }
    118     };
    119 
    120     private final Context mContext;
    121 
    122     private final PowerManager mPm;
    123 
    124     private final AccessibilityManagerService mAms;
    125 
    126     private final Choreographer mChoreographer;
    127 
    128     private boolean mInstalled;
    129 
    130     private int mUserId;
    131 
    132     private int mEnabledFeatures;
    133 
    134     private TouchExplorer mTouchExplorer;
    135 
    136     private MagnificationGestureHandler mMagnificationGestureHandler;
    137 
    138     private MotionEventInjector mMotionEventInjector;
    139 
    140     private AutoclickController mAutoclickController;
    141 
    142     private KeyboardInterceptor mKeyboardInterceptor;
    143 
    144     private EventStreamTransformation mEventHandler;
    145 
    146     private MotionEventHolder mEventQueue;
    147 
    148     private EventStreamState mMouseStreamState;
    149 
    150     private EventStreamState mTouchScreenStreamState;
    151 
    152     private EventStreamState mKeyboardStreamState;
    153 
    154     AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
    155         super(context.getMainLooper());
    156         mContext = context;
    157         mAms = service;
    158         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    159         mChoreographer = Choreographer.getInstance();
    160     }
    161 
    162     @Override
    163     public void onInstalled() {
    164         if (DEBUG) {
    165             Slog.d(TAG, "Accessibility input filter installed.");
    166         }
    167         mInstalled = true;
    168         disableFeatures();
    169         enableFeatures();
    170         super.onInstalled();
    171     }
    172 
    173     @Override
    174     public void onUninstalled() {
    175         if (DEBUG) {
    176             Slog.d(TAG, "Accessibility input filter uninstalled.");
    177         }
    178         mInstalled = false;
    179         disableFeatures();
    180         super.onUninstalled();
    181     }
    182 
    183     @Override
    184     public void onInputEvent(InputEvent event, int policyFlags) {
    185         if (DEBUG) {
    186             Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
    187                     + Integer.toHexString(policyFlags));
    188         }
    189 
    190         if (mEventHandler == null) {
    191             super.onInputEvent(event, policyFlags);
    192             return;
    193         }
    194 
    195         EventStreamState state = getEventStreamState(event);
    196         if (state == null) {
    197             super.onInputEvent(event, policyFlags);
    198             return;
    199         }
    200 
    201         int eventSource = event.getSource();
    202         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
    203             state.reset();
    204             mEventHandler.clearEvents(eventSource);
    205             super.onInputEvent(event, policyFlags);
    206             return;
    207         }
    208 
    209         if (state.updateDeviceId(event.getDeviceId())) {
    210             mEventHandler.clearEvents(eventSource);
    211         }
    212 
    213         if (!state.deviceIdValid()) {
    214             super.onInputEvent(event, policyFlags);
    215             return;
    216         }
    217 
    218         if (event instanceof MotionEvent) {
    219             if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
    220                 MotionEvent motionEvent = (MotionEvent) event;
    221                 processMotionEvent(state, motionEvent, policyFlags);
    222                 return;
    223             } else {
    224                 super.onInputEvent(event, policyFlags);
    225             }
    226         } else if (event instanceof KeyEvent) {
    227             KeyEvent keyEvent = (KeyEvent) event;
    228             processKeyEvent(state, keyEvent, policyFlags);
    229         }
    230     }
    231 
    232     /**
    233      * Gets current event stream state associated with an input event.
    234      * @return The event stream state that should be used for the event. Null if the event should
    235      *     not be handled by #AccessibilityInputFilter.
    236      */
    237     private EventStreamState getEventStreamState(InputEvent event) {
    238         if (event instanceof MotionEvent) {
    239           if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
    240               if (mTouchScreenStreamState == null) {
    241                   mTouchScreenStreamState = new TouchScreenEventStreamState();
    242               }
    243               return mTouchScreenStreamState;
    244           }
    245           if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
    246               if (mMouseStreamState == null) {
    247                   mMouseStreamState = new MouseEventStreamState();
    248               }
    249               return mMouseStreamState;
    250           }
    251         } else if (event instanceof KeyEvent) {
    252           if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
    253               if (mKeyboardStreamState == null) {
    254                   mKeyboardStreamState = new KeyboardEventStreamState();
    255               }
    256               return mKeyboardStreamState;
    257           }
    258         }
    259         return null;
    260     }
    261 
    262     private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
    263         if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
    264             super.onInputEvent(event, policyFlags);
    265             return;
    266         }
    267 
    268         if (!state.shouldProcessMotionEvent(event)) {
    269             return;
    270         }
    271 
    272         batchMotionEvent(event, policyFlags);
    273     }
    274 
    275     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
    276         if (!state.shouldProcessKeyEvent(event)) {
    277             super.onInputEvent(event, policyFlags);
    278             return;
    279         }
    280         mEventHandler.onKeyEvent(event, policyFlags);
    281     }
    282 
    283     private void scheduleProcessBatchedEvents() {
    284         mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
    285                 mProcessBatchedEventsRunnable, null);
    286     }
    287 
    288     private void batchMotionEvent(MotionEvent event, int policyFlags) {
    289         if (DEBUG) {
    290             Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags);
    291         }
    292         if (mEventQueue == null) {
    293             mEventQueue = MotionEventHolder.obtain(event, policyFlags);
    294             scheduleProcessBatchedEvents();
    295             return;
    296         }
    297         if (mEventQueue.event.addBatch(event)) {
    298             return;
    299         }
    300         MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags);
    301         holder.next = mEventQueue;
    302         mEventQueue.previous = holder;
    303         mEventQueue = holder;
    304     }
    305 
    306     private void processBatchedEvents(long frameNanos) {
    307         MotionEventHolder current = mEventQueue;
    308         if (current == null) {
    309             return;
    310         }
    311         while (current.next != null) {
    312             current = current.next;
    313         }
    314         while (true) {
    315             if (current == null) {
    316                 mEventQueue = null;
    317                 break;
    318             }
    319             if (current.event.getEventTimeNano() >= frameNanos) {
    320                 // Finished with this choreographer frame. Do the rest on the next one.
    321                 current.next = null;
    322                 break;
    323             }
    324             handleMotionEvent(current.event, current.policyFlags);
    325             MotionEventHolder prior = current;
    326             current = current.previous;
    327             prior.recycle();
    328         }
    329     }
    330 
    331     private void handleMotionEvent(MotionEvent event, int policyFlags) {
    332         if (DEBUG) {
    333             Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags);
    334         }
    335         // Since we do batch processing it is possible that by the time the
    336         // next batch is processed the event handle had been set to null.
    337         if (mEventHandler != null) {
    338             mPm.userActivity(event.getEventTime(), false);
    339             MotionEvent transformedEvent = MotionEvent.obtain(event);
    340             mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
    341             transformedEvent.recycle();
    342         }
    343     }
    344 
    345     @Override
    346     public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
    347             int policyFlags) {
    348         sendInputEvent(transformedEvent, policyFlags);
    349     }
    350 
    351     @Override
    352     public void onKeyEvent(KeyEvent event, int policyFlags) {
    353         sendInputEvent(event, policyFlags);
    354     }
    355 
    356     @Override
    357     public void onAccessibilityEvent(AccessibilityEvent event) {
    358         // TODO Implement this to inject the accessibility event
    359         //      into the accessibility manager service similarly
    360         //      to how this is done for input events.
    361     }
    362 
    363     @Override
    364     public void setNext(EventStreamTransformation sink) {
    365         /* do nothing */
    366     }
    367 
    368     @Override
    369     public void clearEvents(int inputSource) {
    370         /* do nothing */
    371     }
    372 
    373     void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
    374         if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
    375             return;
    376         }
    377         if (mInstalled) {
    378             disableFeatures();
    379         }
    380         mUserId = userId;
    381         mEnabledFeatures = enabledFeatures;
    382         if (mInstalled) {
    383             enableFeatures();
    384         }
    385     }
    386 
    387     void notifyAccessibilityEvent(AccessibilityEvent event) {
    388         if (mEventHandler != null) {
    389             mEventHandler.onAccessibilityEvent(event);
    390         }
    391     }
    392 
    393     void notifyAccessibilityButtonClicked() {
    394         if (mMagnificationGestureHandler != null) {
    395             mMagnificationGestureHandler.notifyShortcutTriggered();
    396         }
    397     }
    398 
    399     private void enableFeatures() {
    400         resetStreamState();
    401 
    402         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
    403             mAutoclickController = new AutoclickController(mContext, mUserId);
    404             addFirstEventHandler(mAutoclickController);
    405         }
    406 
    407         if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
    408             mTouchExplorer = new TouchExplorer(mContext, mAms);
    409             addFirstEventHandler(mTouchExplorer);
    410         }
    411 
    412         if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
    413                 || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
    414                 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
    415             final boolean detectControlGestures = (mEnabledFeatures
    416                     & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
    417             final boolean triggerable = (mEnabledFeatures
    418                     & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
    419             mMagnificationGestureHandler = new MagnificationGestureHandler(
    420                     mContext, mAms, detectControlGestures, triggerable);
    421             addFirstEventHandler(mMagnificationGestureHandler);
    422         }
    423 
    424         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
    425             mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
    426             addFirstEventHandler(mMotionEventInjector);
    427             mAms.setMotionEventInjector(mMotionEventInjector);
    428         }
    429 
    430         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
    431             mKeyboardInterceptor = new KeyboardInterceptor(mAms,
    432                     LocalServices.getService(WindowManagerPolicy.class));
    433             addFirstEventHandler(mKeyboardInterceptor);
    434         }
    435     }
    436 
    437     /**
    438      * Adds an event handler to the event handler chain. The handler is added at the beginning of
    439      * the chain.
    440      *
    441      * @param handler The handler to be added to the event handlers list.
    442      */
    443     private void addFirstEventHandler(EventStreamTransformation handler) {
    444         if (mEventHandler != null) {
    445            handler.setNext(mEventHandler);
    446         } else {
    447             handler.setNext(this);
    448         }
    449         mEventHandler = handler;
    450     }
    451 
    452     private void disableFeatures() {
    453         // Give the features a chance to process any batched events so we'll keep a consistent
    454         // event stream
    455         processBatchedEvents(Long.MAX_VALUE);
    456         if (mMotionEventInjector != null) {
    457             mAms.setMotionEventInjector(null);
    458             mMotionEventInjector.onDestroy();
    459             mMotionEventInjector = null;
    460         }
    461         if (mAutoclickController != null) {
    462             mAutoclickController.onDestroy();
    463             mAutoclickController = null;
    464         }
    465         if (mTouchExplorer != null) {
    466             mTouchExplorer.onDestroy();
    467             mTouchExplorer = null;
    468         }
    469         if (mMagnificationGestureHandler != null) {
    470             mMagnificationGestureHandler.onDestroy();
    471             mMagnificationGestureHandler = null;
    472         }
    473         if (mKeyboardInterceptor != null) {
    474             mKeyboardInterceptor.onDestroy();
    475             mKeyboardInterceptor = null;
    476         }
    477 
    478         mEventHandler = null;
    479         resetStreamState();
    480     }
    481 
    482     void resetStreamState() {
    483         if (mTouchScreenStreamState != null) {
    484             mTouchScreenStreamState.reset();
    485         }
    486         if (mMouseStreamState != null) {
    487             mMouseStreamState.reset();
    488         }
    489         if (mKeyboardStreamState != null) {
    490             mKeyboardStreamState.reset();
    491         }
    492     }
    493 
    494     @Override
    495     public void onDestroy() {
    496         /* ignore */
    497     }
    498 
    499     private static class MotionEventHolder {
    500         private static final int MAX_POOL_SIZE = 32;
    501         private static final SimplePool<MotionEventHolder> sPool =
    502                 new SimplePool<MotionEventHolder>(MAX_POOL_SIZE);
    503 
    504         public int policyFlags;
    505         public MotionEvent event;
    506         public MotionEventHolder next;
    507         public MotionEventHolder previous;
    508 
    509         public static MotionEventHolder obtain(MotionEvent event, int policyFlags) {
    510             MotionEventHolder holder = sPool.acquire();
    511             if (holder == null) {
    512                 holder = new MotionEventHolder();
    513             }
    514             holder.event = MotionEvent.obtain(event);
    515             holder.policyFlags = policyFlags;
    516             return holder;
    517         }
    518 
    519         public void recycle() {
    520             event.recycle();
    521             event = null;
    522             policyFlags = 0;
    523             next = null;
    524             previous = null;
    525             sPool.release(this);
    526         }
    527     }
    528 
    529     /**
    530      * Keeps state of event streams observed for an input device with a certain source.
    531      * Provides information about whether motion and key events should be processed by accessibility
    532      * #EventStreamTransformations. Base implementation describes behaviour for event sources that
    533      * whose events should not be handled by a11y event stream transformations.
    534      */
    535     private static class EventStreamState {
    536         private int mDeviceId;
    537 
    538         EventStreamState() {
    539             mDeviceId = -1;
    540         }
    541 
    542         /**
    543          * Updates the ID of the device associated with the state. If the ID changes, resets
    544          * internal state.
    545          *
    546          * @param deviceId Updated input device ID.
    547          * @return Whether the device ID has changed.
    548          */
    549         public boolean updateDeviceId(int deviceId) {
    550             if (mDeviceId == deviceId) {
    551                 return false;
    552             }
    553             // Reset clears internal state, so make sure it's called before |mDeviceId| is updated.
    554             reset();
    555             mDeviceId = deviceId;
    556             return true;
    557         }
    558 
    559         /**
    560          * @return Whether device ID is valid.
    561          */
    562         public boolean deviceIdValid() {
    563             return mDeviceId >= 0;
    564         }
    565 
    566         /**
    567          * Resets the event stream state.
    568          */
    569         public void reset() {
    570             mDeviceId = -1;
    571         }
    572 
    573         /**
    574          * @return Whether scroll events for device should be handled by event transformations.
    575          */
    576         public boolean shouldProcessScroll() {
    577             return false;
    578         }
    579 
    580         /**
    581          * @param event An observed motion event.
    582          * @return Whether the event should be handled by event transformations.
    583          */
    584         public boolean shouldProcessMotionEvent(MotionEvent event) {
    585             return false;
    586         }
    587 
    588         /**
    589          * @param event An observed key event.
    590          * @return Whether the event should be handled by event transformations.
    591          */
    592         public boolean shouldProcessKeyEvent(KeyEvent event) {
    593             return false;
    594         }
    595     }
    596 
    597     /**
    598      * Keeps state of stream of events from a mouse device.
    599      */
    600     private static class MouseEventStreamState extends EventStreamState {
    601         private boolean mMotionSequenceStarted;
    602 
    603         public MouseEventStreamState() {
    604             reset();
    605         }
    606 
    607         @Override
    608         final public void reset() {
    609             super.reset();
    610             mMotionSequenceStarted = false;
    611         }
    612 
    613         @Override
    614         final public boolean shouldProcessScroll() {
    615             return true;
    616         }
    617 
    618         @Override
    619         final public boolean shouldProcessMotionEvent(MotionEvent event) {
    620             if (mMotionSequenceStarted) {
    621                 return true;
    622             }
    623             // Wait for down or move event to start processing mouse events.
    624             int action = event.getActionMasked();
    625             mMotionSequenceStarted =
    626                     action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
    627             return mMotionSequenceStarted;
    628         }
    629     }
    630 
    631     /**
    632      * Keeps state of stream of events from a touch screen device.
    633      */
    634     private static class TouchScreenEventStreamState extends EventStreamState {
    635         private boolean mTouchSequenceStarted;
    636         private boolean mHoverSequenceStarted;
    637 
    638         public TouchScreenEventStreamState() {
    639             reset();
    640         }
    641 
    642         @Override
    643         final public void reset() {
    644             super.reset();
    645             mTouchSequenceStarted = false;
    646             mHoverSequenceStarted = false;
    647         }
    648 
    649         @Override
    650         final public boolean shouldProcessMotionEvent(MotionEvent event) {
    651             // Wait for a down touch event to start processing.
    652             if (event.isTouchEvent()) {
    653                 if (mTouchSequenceStarted) {
    654                     return true;
    655                 }
    656                 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
    657                 return mTouchSequenceStarted;
    658             }
    659 
    660             // Wait for an enter hover event to start processing.
    661             if (mHoverSequenceStarted) {
    662                 return true;
    663             }
    664             mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
    665             return mHoverSequenceStarted;
    666         }
    667     }
    668 
    669     /**
    670      * Keeps state of streams of events from all keyboard devices.
    671      */
    672     private static class KeyboardEventStreamState extends EventStreamState {
    673         private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray();
    674 
    675         public KeyboardEventStreamState() {
    676             reset();
    677         }
    678 
    679         @Override
    680         final public void reset() {
    681             super.reset();
    682             mEventSequenceStartedMap.clear();
    683         }
    684 
    685         /*
    686          * Key events from different devices may be interleaved. For example, the volume up and
    687          * down keys can come from different device IDs.
    688          */
    689         @Override
    690         public boolean updateDeviceId(int deviceId) {
    691             return false;
    692         }
    693 
    694         // We manage all device ids simultaneously; there is no concept of validity.
    695         @Override
    696         public boolean deviceIdValid() {
    697             return true;
    698         }
    699 
    700 
    701         @Override
    702         final public boolean shouldProcessKeyEvent(KeyEvent event) {
    703             // For each keyboard device, wait for a down event from a device to start processing
    704             int deviceId = event.getDeviceId();
    705             if (mEventSequenceStartedMap.get(deviceId, false)) {
    706                 return true;
    707             }
    708             boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN;
    709             mEventSequenceStartedMap.put(deviceId, shouldProcess);
    710             return shouldProcess;
    711         }
    712     }
    713 }
    714