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