Home | History | Annotate | Download | only in accessibility
      1 /*
      2  ** Copyright 2015, 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.os.Binder;
     20 import android.os.Handler;
     21 import android.os.Message;
     22 import android.os.PowerManager;
     23 import android.util.ArrayMap;
     24 import android.util.Pools;
     25 import android.util.Pools.Pool;
     26 import android.util.Slog;
     27 import android.view.InputEventConsistencyVerifier;
     28 import android.view.KeyEvent;
     29 
     30 import com.android.server.policy.WindowManagerPolicy;
     31 
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 import java.util.Map;
     35 
     36 /**
     37  * Dispatcher to send KeyEvents to all accessibility services that are able to process them.
     38  * Events that are handled by one or more services are consumed. Events that are not processed
     39  * by any service (or time out before a service reports them as handled) are passed along to
     40  * the rest of the system.
     41  *
     42  * The class assumes that services report their return values in order, which is valid because
     43  * they process each call to {@code AccessibilityService.onKeyEvent} on a single thread, and so
     44  * don't see the N+1th event until they have processed the Nth event.
     45  */
     46 public class KeyEventDispatcher implements Handler.Callback {
     47     // Debugging
     48     private static final String LOG_TAG = "KeyEventDispatcher";
     49     private static final boolean DEBUG = false;
     50     /* KeyEvents must be processed in this time interval */
     51     private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
     52     public static final int MSG_ON_KEY_EVENT_TIMEOUT = 1;
     53     private static final int MAX_POOL_SIZE = 10;
     54 
     55     private final Pool<PendingKeyEvent> mPendingEventPool = new Pools.SimplePool<>(MAX_POOL_SIZE);
     56     private final Object mLock;
     57 
     58     /*
     59      * Track events sent to each filter. If a KeyEvent is to be sent to at least one service,
     60      * a corresponding PendingKeyEvent is created for it. This PendingKeyEvent is placed in
     61      * the list for each service its KeyEvent is sent to. It is removed from the list when
     62      * the service calls setOnKeyEventResult, or when we time out waiting for the service to
     63      * respond.
     64      */
     65     private final Map<KeyEventFilter, ArrayList<PendingKeyEvent>> mPendingEventsMap =
     66             new ArrayMap<>();
     67 
     68     private final InputEventConsistencyVerifier mSentEventsVerifier;
     69     private final Handler mHandlerToSendKeyEventsToInputFilter;
     70     private final int mMessageTypeForSendKeyEvent;
     71     private final PowerManager mPowerManager;
     72     private Handler mKeyEventTimeoutHandler;
     73 
     74     /**
     75      * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s
     76      * that have not been handled by any accessibility service.
     77      * @param messageTypeForSendKeyEvent The field to populate {@code message.what} for the
     78      * message that carries a {@code KeyEvent} to be sent to the input filter
     79      * @param lock The lock used for all synchronization in this package. This lock must be held
     80      * when calling {@code notifyKeyEventLocked}
     81      * @param powerManager The power manager to alert to user activity if a KeyEvent is processed
     82      * by a service
     83      */
     84     public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
     85             int messageTypeForSendKeyEvent, Object lock, PowerManager powerManager) {
     86         if (InputEventConsistencyVerifier.isInstrumentationEnabled()) {
     87             mSentEventsVerifier = new InputEventConsistencyVerifier(
     88                     this, 0, KeyEventDispatcher.class.getSimpleName());
     89         } else {
     90             mSentEventsVerifier = null;
     91         }
     92         mHandlerToSendKeyEventsToInputFilter = handlerToSendKeyEventsToInputFilter;
     93         mMessageTypeForSendKeyEvent = messageTypeForSendKeyEvent;
     94         mKeyEventTimeoutHandler =
     95                 new Handler(handlerToSendKeyEventsToInputFilter.getLooper(), this);
     96         mLock = lock;
     97         mPowerManager = powerManager;
     98     }
     99 
    100     /**
    101      * See above for most params
    102      * @param timeoutHandler Specify a handler to use for handling timeouts. This internal state is
    103      * exposed for testing.
    104      */
    105     public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
    106             int messageTypeForSendKeyEvent, Object lock, PowerManager powerManager,
    107             Handler timeoutHandler) {
    108         this(handlerToSendKeyEventsToInputFilter, messageTypeForSendKeyEvent, lock, powerManager);
    109         mKeyEventTimeoutHandler = timeoutHandler;
    110     }
    111 
    112     /**
    113      * Notify that a new KeyEvent is available to accessibility services. Must be called with the
    114      * lock used to construct this object held. The keyEventFilters list must also be protected
    115      * by the lock.
    116      *
    117      * @param event The new key event
    118      * @param policyFlags Flags for the event
    119      * @param keyEventFilters A list of keyEventFilters that should be considered for processing
    120      * this event
    121      *
    122      * @return {@code true} if the event was passed to at least one AccessibilityService,
    123      * {@code false} otherwise.
    124      */
    125     // TODO: The locking policy for keyEventFilters needs some thought.
    126     public boolean notifyKeyEventLocked(
    127             KeyEvent event, int policyFlags, List<? extends KeyEventFilter> keyEventFilters) {
    128         PendingKeyEvent pendingKeyEvent = null;
    129         KeyEvent localClone = KeyEvent.obtain(event);
    130         for (int i = 0; i < keyEventFilters.size(); i++) {
    131             KeyEventFilter keyEventFilter = keyEventFilters.get(i);
    132             if (keyEventFilter.onKeyEvent(localClone, localClone.getSequenceNumber())) {
    133                 if (pendingKeyEvent == null) {
    134                     pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
    135                 }
    136                 ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(keyEventFilter);
    137                 if (pendingEventList == null) {
    138                     pendingEventList = new ArrayList<>();
    139                     mPendingEventsMap.put(keyEventFilter, pendingEventList);
    140                 }
    141                 pendingEventList.add(pendingKeyEvent);
    142                 pendingKeyEvent.referenceCount++;
    143             }
    144         }
    145 
    146         if (pendingKeyEvent == null) {
    147             localClone.recycle();
    148             return false;
    149         }
    150 
    151         Message message = mKeyEventTimeoutHandler.obtainMessage(
    152                 MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
    153         mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
    154         return true;
    155     }
    156 
    157     /**
    158      * Set the result from onKeyEvent from one service.
    159      *
    160      * @param keyEventFilter The filter setting the result
    161      * @param handled {@code true} if the service handled the {@code KeyEvent}
    162      * @param sequence The sequence number of the {@code KeyEvent}
    163      */
    164     public void setOnKeyEventResult(KeyEventFilter keyEventFilter, boolean handled, int sequence) {
    165         synchronized (mLock) {
    166             PendingKeyEvent pendingEvent =
    167                     removeEventFromListLocked(mPendingEventsMap.get(keyEventFilter), sequence);
    168             if (pendingEvent != null) {
    169                 if (handled && !pendingEvent.handled) {
    170                     pendingEvent.handled = handled;
    171                     final long identity = Binder.clearCallingIdentity();
    172                     try {
    173                         mPowerManager.userActivity(pendingEvent.event.getEventTime(),
    174                                 PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
    175                     } finally {
    176                         Binder.restoreCallingIdentity(identity);
    177                     }
    178                 }
    179                 removeReferenceToPendingEventLocked(pendingEvent);
    180             }
    181         }
    182     }
    183 
    184     /**
    185      * Flush all pending key events for a service, treating all of them as unhandled
    186      *
    187      * @param keyEventFilter The filter for which to flush events
    188      */
    189     public void flush(KeyEventFilter keyEventFilter) {
    190         synchronized (mLock) {
    191             List<PendingKeyEvent> pendingEvents = mPendingEventsMap.get(keyEventFilter);
    192             if (pendingEvents != null) {
    193                 for (int i = 0; i < pendingEvents.size(); i++) {
    194                     PendingKeyEvent pendingEvent = pendingEvents.get(i);
    195                     removeReferenceToPendingEventLocked(pendingEvent);
    196                 }
    197                 mPendingEventsMap.remove(keyEventFilter);
    198             }
    199         }
    200     }
    201 
    202     @Override
    203     public boolean handleMessage(Message message) {
    204         if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) {
    205             Slog.w(LOG_TAG, "Unknown message: " + message.what);
    206             return false;
    207         }
    208         PendingKeyEvent pendingKeyEvent = (PendingKeyEvent) message.obj;
    209         synchronized (mLock) {
    210             for (ArrayList<PendingKeyEvent> listForService : mPendingEventsMap.values()) {
    211                 if (listForService.remove(pendingKeyEvent)) {
    212                     if(removeReferenceToPendingEventLocked(pendingKeyEvent)) {
    213                         break;
    214                     }
    215                 }
    216             }
    217         }
    218         return true;
    219     }
    220 
    221     private PendingKeyEvent obtainPendingEventLocked(KeyEvent event, int policyFlags) {
    222         PendingKeyEvent pendingEvent = mPendingEventPool.acquire();
    223         if (pendingEvent == null) {
    224             pendingEvent = new PendingKeyEvent();
    225         }
    226         pendingEvent.event = event;
    227         pendingEvent.policyFlags = policyFlags;
    228         pendingEvent.referenceCount = 0;
    229         pendingEvent.handled = false;
    230         return pendingEvent;
    231     }
    232 
    233     private static PendingKeyEvent removeEventFromListLocked(
    234             List<PendingKeyEvent> listOfEvents, int sequence) {
    235         /* In normal operation, the event should be first */
    236         for (int i = 0; i < listOfEvents.size(); i++) {
    237             PendingKeyEvent pendingKeyEvent = listOfEvents.get(i);
    238             if (pendingKeyEvent.event.getSequenceNumber() == sequence) {
    239                     /*
    240                      * Removing the first element of the ArrayList can be slow if there are a lot
    241                      * of events backed up, but for a handful of events it's better than incurring
    242                      * the fixed overhead of LinkedList. An ArrayList optimized for removing the
    243                      * first element (by treating the underlying array as a circular buffer) would
    244                      * be ideal.
    245                      */
    246                 listOfEvents.remove(pendingKeyEvent);
    247                 return pendingKeyEvent;
    248             }
    249         }
    250         return null;
    251     }
    252 
    253     /**
    254      * @param pendingEvent The event whose reference count should be decreased
    255      * @return {@code true} if the event was release, {@code false} if not.
    256      */
    257     private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) {
    258         if (--pendingEvent.referenceCount > 0) {
    259             return false;
    260         }
    261         mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
    262         if (!pendingEvent.handled) {
    263                 /* Pass event to input filter */
    264             if (DEBUG) {
    265                 Slog.i(LOG_TAG, "Injecting event: " + pendingEvent.event);
    266             }
    267             if (mSentEventsVerifier != null) {
    268                 mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0);
    269             }
    270             int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER;
    271             mHandlerToSendKeyEventsToInputFilter
    272                     .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event)
    273                     .sendToTarget();
    274         } else {
    275             pendingEvent.event.recycle();
    276         }
    277         mPendingEventPool.release(pendingEvent);
    278         return true;
    279     }
    280 
    281     private static final class PendingKeyEvent {
    282         /* Event and policyFlag provided in notifyKeyEventLocked */
    283         KeyEvent event;
    284         int policyFlags;
    285         /*
    286          * The referenceCount optimizes the process of determining the number of services
    287          * still holding a KeyEvent. It must be equal to the number of times the PendingEvent
    288          * appears in mPendingEventsMap, or PendingEvents will leak.
    289          */
    290         int referenceCount;
    291         /* Whether or not at least one service had handled this event */
    292         boolean handled;
    293     }
    294 
    295     public interface KeyEventFilter {
    296         /**
    297          * Filter a key event if possible
    298          *
    299          * @param event The event to filter
    300          * @param sequenceNumber The sequence number of the event
    301          *
    302          * @return {@code true} if the filter is active and will call back with status.
    303          * {@code false} if the filter is not active and will ignore the event
    304          */
    305         boolean onKeyEvent(KeyEvent event, int sequenceNumber);
    306     }
    307 }
    308