1/* 2 * Copyright (C) 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.accessibilityservice.IAccessibilityServiceClient; 20import android.os.Handler; 21import android.os.Looper; 22import android.os.Message; 23import android.os.RemoteException; 24import android.os.SystemClock; 25import android.util.Slog; 26import android.util.SparseArray; 27import android.view.InputDevice; 28import android.view.KeyEvent; 29import android.view.MotionEvent; 30import android.view.WindowManagerPolicy; 31import android.view.accessibility.AccessibilityEvent; 32import com.android.internal.os.SomeArgs; 33import com.android.server.accessibility.AccessibilityManagerService.Service; 34 35import java.util.List; 36 37/** 38 * Injects MotionEvents to permit {@code AccessibilityService}s to touch the screen on behalf of 39 * users. 40 * 41 * All methods except {@code injectEvents} must be called only from the main thread. 42 */ 43public class MotionEventInjector implements EventStreamTransformation { 44 private static final String LOG_TAG = "MotionEventInjector"; 45 private static final int MESSAGE_SEND_MOTION_EVENT = 1; 46 private static final int MESSAGE_INJECT_EVENTS = 2; 47 private static final int MAX_POINTERS = 11; // Non-binding maximum 48 49 private final Handler mHandler; 50 private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>(); 51 52 // These two arrays must be the same length 53 private MotionEvent.PointerProperties[] mPointerProperties = 54 new MotionEvent.PointerProperties[MAX_POINTERS]; 55 private MotionEvent.PointerCoords[] mPointerCoords = 56 new MotionEvent.PointerCoords[MAX_POINTERS]; 57 private EventStreamTransformation mNext; 58 private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture; 59 private int mSequenceForCurrentGesture; 60 private int mSourceOfInjectedGesture = InputDevice.SOURCE_UNKNOWN; 61 private boolean mIsDestroyed = false; 62 63 /** 64 * @param looper A looper on the main thread to use for dispatching new events 65 */ 66 public MotionEventInjector(Looper looper) { 67 mHandler = new Handler(looper, new Callback()); 68 } 69 70 /** 71 * Schedule a series of events for injection. These events must comprise a complete, valid 72 * sequence. All gestures currently in progress will be cancelled, and all {@code downTime} 73 * and {@code eventTime} fields will be offset by the current time. 74 * 75 * @param events The events to inject. Must all be from the same source. 76 * @param serviceInterface The interface to call back with a result when the gesture is 77 * either complete or cancelled. 78 */ 79 public void injectEvents(List<MotionEvent> events, 80 IAccessibilityServiceClient serviceInterface, int sequence) { 81 SomeArgs args = SomeArgs.obtain(); 82 args.arg1 = events; 83 args.arg2 = serviceInterface; 84 args.argi1 = sequence; 85 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args)); 86 } 87 88 @Override 89 public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 90 cancelAnyPendingInjectedEvents(); 91 sendMotionEventToNext(event, rawEvent, policyFlags); 92 } 93 94 @Override 95 public void onKeyEvent(KeyEvent event, int policyFlags) { 96 if (mNext != null) { 97 mNext.onKeyEvent(event, policyFlags); 98 } 99 } 100 101 @Override 102 public void onAccessibilityEvent(AccessibilityEvent event) { 103 if (mNext != null) { 104 mNext.onAccessibilityEvent(event); 105 } 106 } 107 108 @Override 109 public void setNext(EventStreamTransformation next) { 110 mNext = next; 111 } 112 113 @Override 114 public void clearEvents(int inputSource) { 115 /* 116 * Reset state for motion events passing through so we won't send a cancel event for 117 * them. 118 */ 119 if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) { 120 mOpenGesturesInProgress.put(inputSource, false); 121 } 122 } 123 124 @Override 125 public void onDestroy() { 126 cancelAnyPendingInjectedEvents(); 127 mIsDestroyed = true; 128 } 129 130 private void injectEventsMainThread(List<MotionEvent> events, 131 IAccessibilityServiceClient serviceInterface, int sequence) { 132 if (mIsDestroyed) { 133 try { 134 serviceInterface.onPerformGestureResult(sequence, false); 135 } catch (RemoteException re) { 136 Slog.e(LOG_TAG, "Error sending status with mIsDestroyed to " + serviceInterface, 137 re); 138 } 139 return; 140 } 141 cancelAnyPendingInjectedEvents(); 142 mSourceOfInjectedGesture = events.get(0).getSource(); 143 cancelAnyGestureInProgress(mSourceOfInjectedGesture); 144 mServiceInterfaceForCurrentGesture = serviceInterface; 145 mSequenceForCurrentGesture = sequence; 146 if (mNext == null) { 147 notifyService(false); 148 return; 149 } 150 151 long startTime = SystemClock.uptimeMillis(); 152 for (int i = 0; i < events.size(); i++) { 153 MotionEvent event = events.get(i); 154 int numPointers = event.getPointerCount(); 155 if (numPointers > mPointerCoords.length) { 156 mPointerCoords = new MotionEvent.PointerCoords[numPointers]; 157 mPointerProperties = new MotionEvent.PointerProperties[numPointers]; 158 } 159 for (int j = 0; j < numPointers; j++) { 160 if (mPointerCoords[j] == null) { 161 mPointerCoords[j] = new MotionEvent.PointerCoords(); 162 mPointerProperties[j] = new MotionEvent.PointerProperties(); 163 } 164 event.getPointerCoords(j, mPointerCoords[j]); 165 event.getPointerProperties(j, mPointerProperties[j]); 166 } 167 168 /* 169 * MotionEvent doesn't have a setEventTime() method (it carries around history data, 170 * which could become inconsistent), so we need to obtain a new one. 171 */ 172 MotionEvent offsetEvent = MotionEvent.obtain(startTime + event.getDownTime(), 173 startTime + event.getEventTime(), event.getAction(), numPointers, 174 mPointerProperties, mPointerCoords, event.getMetaState(), 175 event.getButtonState(), event.getXPrecision(), event.getYPrecision(), 176 event.getDeviceId(), event.getEdgeFlags(), event.getSource(), 177 event.getFlags()); 178 Message message = mHandler.obtainMessage(MESSAGE_SEND_MOTION_EVENT, offsetEvent); 179 mHandler.sendMessageDelayed(message, event.getEventTime()); 180 } 181 } 182 183 private void sendMotionEventToNext(MotionEvent event, MotionEvent rawEvent, 184 int policyFlags) { 185 if (mNext != null) { 186 mNext.onMotionEvent(event, rawEvent, policyFlags); 187 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 188 mOpenGesturesInProgress.put(event.getSource(), true); 189 } 190 if ((event.getActionMasked() == MotionEvent.ACTION_UP) 191 || (event.getActionMasked() == MotionEvent.ACTION_CANCEL)) { 192 mOpenGesturesInProgress.put(event.getSource(), false); 193 } 194 } 195 } 196 197 private void cancelAnyGestureInProgress(int source) { 198 if ((mNext != null) && mOpenGesturesInProgress.get(source, false)) { 199 long now = SystemClock.uptimeMillis(); 200 MotionEvent cancelEvent = 201 MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 202 sendMotionEventToNext(cancelEvent, cancelEvent, 203 WindowManagerPolicy.FLAG_PASS_TO_USER); 204 } 205 } 206 207 private void cancelAnyPendingInjectedEvents() { 208 if (mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) { 209 cancelAnyGestureInProgress(mSourceOfInjectedGesture); 210 mHandler.removeMessages(MESSAGE_SEND_MOTION_EVENT); 211 notifyService(false); 212 } 213 214 } 215 216 private void notifyService(boolean success) { 217 try { 218 mServiceInterfaceForCurrentGesture.onPerformGestureResult( 219 mSequenceForCurrentGesture, success); 220 } catch (RemoteException re) { 221 Slog.e(LOG_TAG, "Error sending motion event injection status to " 222 + mServiceInterfaceForCurrentGesture, re); 223 } 224 } 225 226 private class Callback implements Handler.Callback { 227 @Override 228 public boolean handleMessage(Message message) { 229 if (message.what == MESSAGE_INJECT_EVENTS) { 230 SomeArgs args = (SomeArgs) message.obj; 231 injectEventsMainThread((List<MotionEvent>) args.arg1, 232 (IAccessibilityServiceClient) args.arg2, args.argi1); 233 args.recycle(); 234 return true; 235 } 236 if (message.what != MESSAGE_SEND_MOTION_EVENT) { 237 throw new IllegalArgumentException("Unknown message: " + message.what); 238 } 239 MotionEvent motionEvent = (MotionEvent) message.obj; 240 sendMotionEventToNext(motionEvent, motionEvent, 241 WindowManagerPolicy.FLAG_PASS_TO_USER); 242 // If the message queue is now empty, then this gesture is complete 243 if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) { 244 notifyService(true); 245 } 246 return true; 247 } 248 } 249} 250