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.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; 32 33 import com.android.server.accessibility.AccessibilityManagerService.Service; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Map; 38 39 /** 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. 44 * 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. 48 */ 49 public class KeyEventDispatcher { 50 // Debugging 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; 57 58 private final Pool<PendingKeyEvent> mPendingEventPool = new Pools.SimplePool<>(MAX_POOL_SIZE); 59 private final Object mLock; 60 61 /* 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 66 * respond. 67 */ 68 private final Map<Service, ArrayList<PendingKeyEvent>> mPendingEventsMap = new ArrayMap<>(); 69 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; 75 76 /** 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 84 * by a service 85 */ 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()); 92 } else { 93 mSentEventsVerifier = null; 94 } 95 mHandlerToSendKeyEventsToInputFilter = handlerToSendKeyEventsToInputFilter; 96 mMessageTypeForSendKeyEvent = messageTypeForSendKeyEvent; 97 mKeyEventTimeoutHandler = 98 new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback()); 99 mLock = lock; 100 mPowerManager = powerManager; 101 } 102 103 /** 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 106 * by a lock. 107 * 108 * @param event The new key event 109 * @param policyFlags Flags for the event 110 * @param boundServices A list of currently bound AccessibilityServices 111 * 112 * @return {@code true} if the event was passed to at least one AccessibilityService, 113 * {@code false} otherwise. 114 */ 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)) { 125 continue; 126 } 127 int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities() 128 & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS; 129 if (filterKeyEventBit == 0) { 130 continue; 131 } 132 133 try { 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) { 137 continue; 138 } 139 140 if (pendingKeyEvent == null) { 141 pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags); 142 } 143 ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service); 144 if (pendingEventList == null) { 145 pendingEventList = new ArrayList<>(); 146 mPendingEventsMap.put(service, pendingEventList); 147 } 148 pendingEventList.add(pendingKeyEvent); 149 pendingKeyEvent.referenceCount++; 150 } 151 152 if (pendingKeyEvent == null) { 153 localClone.recycle(); 154 return false; 155 } 156 157 Message message = mKeyEventTimeoutHandler.obtainMessage( 158 MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent); 159 mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS); 160 return true; 161 } 162 163 /** 164 * Set the result from onKeyEvent from one service. 165 * 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} 169 */ 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(); 178 try { 179 mPowerManager.userActivity(pendingEvent.event.getEventTime(), 180 PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); 181 } finally { 182 Binder.restoreCallingIdentity(identity); 183 } 184 } 185 removeReferenceToPendingEventLocked(pendingEvent); 186 } 187 } 188 } 189 190 /** 191 * Flush all pending key events for a service, treating all of them as unhandled 192 * 193 * @param service The service for which to flush events 194 */ 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); 202 } 203 mPendingEventsMap.remove(service); 204 } 205 } 206 } 207 208 private PendingKeyEvent obtainPendingEventLocked(KeyEvent event, int policyFlags) { 209 PendingKeyEvent pendingEvent = mPendingEventPool.acquire(); 210 if (pendingEvent == null) { 211 pendingEvent = new PendingKeyEvent(); 212 } 213 pendingEvent.event = event; 214 pendingEvent.policyFlags = policyFlags; 215 pendingEvent.referenceCount = 0; 216 pendingEvent.handled = false; 217 return pendingEvent; 218 } 219 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) { 226 /* 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 231 * be ideal. 232 */ 233 listOfEvents.remove(pendingKeyEvent); 234 return pendingKeyEvent; 235 } 236 } 237 return null; 238 } 239 240 /** 241 * @param pendingEvent The event whose reference count should be decreased 242 * @return {@code true} if the event was release, {@code false} if not. 243 */ 244 private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) { 245 if (--pendingEvent.referenceCount > 0) { 246 return false; 247 } 248 mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent); 249 if (!pendingEvent.handled) { 250 /* Pass event to input filter */ 251 if (DEBUG) { 252 Slog.i(LOG_TAG, "Injecting event: " + pendingEvent.event); 253 } 254 if (mSentEventsVerifier != null) { 255 mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0); 256 } 257 int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER; 258 mHandlerToSendKeyEventsToInputFilter 259 .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event) 260 .sendToTarget(); 261 } else { 262 pendingEvent.event.recycle(); 263 } 264 mPendingEventPool.release(pendingEvent); 265 return true; 266 } 267 268 private static final class PendingKeyEvent { 269 /* Event and policyFlag provided in notifyKeyEventLocked */ 270 KeyEvent event; 271 int policyFlags; 272 /* 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. 276 */ 277 int referenceCount; 278 /* Whether or not at least one service had handled this event */ 279 boolean handled; 280 } 281 282 private class Callback implements Handler.Callback { 283 @Override 284 public boolean handleMessage(Message message) { 285 if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) { 286 throw new IllegalArgumentException("Unknown message: " + message.what); 287 } 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)) { 293 break; 294 } 295 } 296 } 297 } 298 return true; 299 } 300 } 301 } 302