2 ** Copyright 2015, The Android Open Source Project
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
8 ** http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.accessibility;
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.os.Binder;
21 import android.os.Handler;
22 import android.os.Message;
23 import android.os.PowerManager;
24 import android.os.RemoteException;
25 import android.util.ArrayMap;
26 import android.util.Pools;
27 import android.util.Pools.Pool;
28 import android.util.Slog;
29 import android.view.InputEventConsistencyVerifier;
30 import android.view.KeyEvent;
31 import android.view.WindowManagerPolicy;
33 import com.android.server.accessibility.AccessibilityManagerService.Service;
35 import java.util.ArrayList;
36 import java.util.List;
40 * Dispatcher to send KeyEvents to all accessibility services that are able to process them.
41 * Events that are handled by one or more services are consumed. Events that are not processed
42 * by any service (or time out before a service reports them as handled) are passed along to
43 * the rest of the system.
45 * The class assumes that services report their return values in order, which is valid because
46 * they process each call to {@code AccessibilityService.onKeyEvent} on a single thread, and so
47 * don't see the N+1th event until they have processed the Nth event.
49 public class KeyEventDispatcher {
51 private static final String LOG_TAG = "KeyEventDispatcher";
52 private static final boolean DEBUG = false;
53 /* KeyEvents must be processed in this time interval */
54 private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
55 private static final int MSG_ON_KEY_EVENT_TIMEOUT = 1;
56 private static final int MAX_POOL_SIZE = 10;
58 private final Pool<PendingKeyEvent> mPendingEventPool = new Pools.SimplePool<>(MAX_POOL_SIZE);
59 private final Object mLock;
62 * Track events sent to each service. If a KeyEvent is to be sent to at least one service,
63 * a corresponding PendingKeyEvent is created for it. This PendingKeyEvent is placed in
64 * the list for each service its KeyEvent is sent to. It is removed from the list when
65 * the service calls setOnKeyEventResult, or when we time out waiting for the service to
68 private final Map<Service, ArrayList<PendingKeyEvent>> mPendingEventsMap = new ArrayMap<>();
70 private final InputEventConsistencyVerifier mSentEventsVerifier;
71 private final Handler mHandlerToSendKeyEventsToInputFilter;
72 private final int mMessageTypeForSendKeyEvent;
73 private final Handler mKeyEventTimeoutHandler;
74 private final PowerManager mPowerManager;
77 * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s
78 * that have not been handled by any accessibility service.
79 * @param messageTypeForSendKeyEvent The field to populate {@code message.what} for the
80 * message that carries a {@code KeyEvent} to be sent to the input filter
81 * @param lock The lock used for all synchronization in this package. This lock must be held
82 * when calling {@code notifyKeyEventLocked}
83 * @param powerManager The power manager to alert to user activity if a KeyEvent is processed
86 public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
87 int messageTypeForSendKeyEvent, Object lock,
88 PowerManager powerManager) {
89 if (InputEventConsistencyVerifier.isInstrumentationEnabled()) {
90 mSentEventsVerifier = new InputEventConsistencyVerifier(
91 this, 0, KeyEventDispatcher.class.getSimpleName());
93 mSentEventsVerifier = null;
95 mHandlerToSendKeyEventsToInputFilter = handlerToSendKeyEventsToInputFilter;
96 mMessageTypeForSendKeyEvent = messageTypeForSendKeyEvent;
97 mKeyEventTimeoutHandler =
98 new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback());
100 mPowerManager = powerManager;
104 * Notify that a new KeyEvent is available to accessibility services. Must be called with the
105 * lock used to construct this object held. The boundServices list must also be protected
108 * @param event The new key event
109 * @param policyFlags Flags for the event
110 * @param boundServices A list of currently bound AccessibilityServices
112 * @return {@code true} if the event was passed to at least one AccessibilityService,
113 * {@code false} otherwise.
115 // TODO: The locking policy for boundServices needs some thought.
116 public boolean notifyKeyEventLocked(
117 KeyEvent event, int policyFlags, List<Service> boundServices) {
118 PendingKeyEvent pendingKeyEvent = null;
119 KeyEvent localClone = KeyEvent.obtain(event);
120 for (int i = 0; i < boundServices.size(); i++) {
121 Service service = boundServices.get(i);
122 // Key events are handled only by services that declared
123 // this capability and requested to filter key events.
124 if (!service.mRequestFilterKeyEvents || (service.mServiceInterface == null)) {
127 int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
128 & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
129 if (filterKeyEventBit == 0) {
134 // The event will be cloned in the IPC call, so it doesn't need to be here.
135 service.mServiceInterface.onKeyEvent(localClone, localClone.getSequenceNumber());
136 } catch (RemoteException re) {
140 if (pendingKeyEvent == null) {
141 pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
143 ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service);
144 if (pendingEventList == null) {
145 pendingEventList = new ArrayList<>();
146 mPendingEventsMap.put(service, pendingEventList);
148 pendingEventList.add(pendingKeyEvent);
149 pendingKeyEvent.referenceCount++;
152 if (pendingKeyEvent == null) {
153 localClone.recycle();
157 Message message = mKeyEventTimeoutHandler.obtainMessage(
158 MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
159 mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
164 * Set the result from onKeyEvent from one service.
166 * @param service The service setting the result
167 * @param handled {@code true} if the service handled the {@code KeyEvent}
168 * @param sequence The sequence number of the {@code KeyEvent}
170 public void setOnKeyEventResult(Service service, boolean handled, int sequence) {
171 synchronized (mLock) {
172 PendingKeyEvent pendingEvent =
173 removeEventFromListLocked(mPendingEventsMap.get(service), sequence);
174 if (pendingEvent != null) {
175 if (handled && !pendingEvent.handled) {
176 pendingEvent.handled = handled;
177 final long identity = Binder.clearCallingIdentity();
179 mPowerManager.userActivity(pendingEvent.event.getEventTime(),
180 PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
182 Binder.restoreCallingIdentity(identity);
185 removeReferenceToPendingEventLocked(pendingEvent);
191 * Flush all pending key events for a service, treating all of them as unhandled
193 * @param service The service for which to flush events
195 public void flush(Service service) {
196 synchronized (mLock) {
197 List<PendingKeyEvent> pendingEvents = mPendingEventsMap.get(service);
198 if (pendingEvents != null) {
199 for (int i = 0; i < pendingEvents.size(); i++) {
200 PendingKeyEvent pendingEvent = pendingEvents.get(i);
201 removeReferenceToPendingEventLocked(pendingEvent);
203 mPendingEventsMap.remove(service);
208 private PendingKeyEvent obtainPendingEventLocked(KeyEvent event, int policyFlags) {
209 PendingKeyEvent pendingEvent = mPendingEventPool.acquire();
210 if (pendingEvent == null) {
211 pendingEvent = new PendingKeyEvent();
213 pendingEvent.event = event;
214 pendingEvent.policyFlags = policyFlags;
215 pendingEvent.referenceCount = 0;
216 pendingEvent.handled = false;
220 private static PendingKeyEvent removeEventFromListLocked(
221 List<PendingKeyEvent> listOfEvents, int sequence) {
222 /* In normal operation, the event should be first */
223 for (int i = 0; i < listOfEvents.size(); i++) {
224 PendingKeyEvent pendingKeyEvent = listOfEvents.get(i);
225 if (pendingKeyEvent.event.getSequenceNumber() == sequence) {
227 * Removing the first element of the ArrayList can be slow if there are a lot
228 * of events backed up, but for a handful of events it's better than incurring
229 * the fixed overhead of LinkedList. An ArrayList optimized for removing the
230 * first element (by treating the underlying array as a circular buffer) would
233 listOfEvents.remove(pendingKeyEvent);
234 return pendingKeyEvent;
241 * @param pendingEvent The event whose reference count should be decreased
242 * @return {@code true} if the event was release, {@code false} if not.
244 private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) {
245 if (--pendingEvent.referenceCount > 0) {
248 mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
249 if (!pendingEvent.handled) {
250 /* Pass event to input filter */
252 Slog.i(LOG_TAG, "Injecting event: " + pendingEvent.event);
254 if (mSentEventsVerifier != null) {
255 mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0);
257 int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER;
258 mHandlerToSendKeyEventsToInputFilter
259 .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event)
262 pendingEvent.event.recycle();
264 mPendingEventPool.release(pendingEvent);
268 private static final class PendingKeyEvent {
269 /* Event and policyFlag provided in notifyKeyEventLocked */
273 * The referenceCount optimizes the process of determining the number of services
274 * still holding a KeyEvent. It must be equal to the number of times the PendingEvent
275 * appears in mPendingEventsMap, or PendingEvents will leak.
278 /* Whether or not at least one service had handled this event */
282 private class Callback implements Handler.Callback {
284 public boolean handleMessage(Message message) {
285 if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) {
286 throw new IllegalArgumentException("Unknown message: " + message.what);
288 PendingKeyEvent pendingKeyEvent = (PendingKeyEvent) message.obj;
289 synchronized (mLock) {
290 for (ArrayList<PendingKeyEvent> listForService : mPendingEventsMap.values()) {
291 if (listForService.remove(pendingKeyEvent)) {
292 if(removeReferenceToPendingEventLocked(pendingKeyEvent)) {