• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.companion.virtual;
18 
19 import static android.text.TextUtils.formatSimple;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.StringDef;
24 import android.content.AttributionSource;
25 import android.graphics.PointF;
26 import android.hardware.display.DisplayManagerInternal;
27 import android.hardware.input.InputDeviceIdentifier;
28 import android.hardware.input.InputManager;
29 import android.hardware.input.InputManagerGlobal;
30 import android.hardware.input.VirtualKeyEvent;
31 import android.hardware.input.VirtualMouseButtonEvent;
32 import android.hardware.input.VirtualMouseRelativeEvent;
33 import android.hardware.input.VirtualMouseScrollEvent;
34 import android.hardware.input.VirtualRotaryEncoderScrollEvent;
35 import android.hardware.input.VirtualStylusButtonEvent;
36 import android.hardware.input.VirtualStylusMotionEvent;
37 import android.hardware.input.VirtualTouchEvent;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.IInputConstants;
41 import android.os.RemoteException;
42 import android.util.ArrayMap;
43 import android.util.Log;
44 import android.util.Slog;
45 import android.view.InputDevice;
46 import android.view.WindowManager;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.modules.expresslog.Counter;
51 import com.android.server.LocalServices;
52 import com.android.server.input.InputManagerInternal;
53 
54 import java.io.PrintWriter;
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 import java.util.Iterator;
58 import java.util.Map;
59 import java.util.Objects;
60 import java.util.concurrent.CountDownLatch;
61 import java.util.concurrent.TimeUnit;
62 import java.util.concurrent.atomic.AtomicLong;
63 import java.util.function.Supplier;
64 
65 /** Controls virtual input devices, including device lifecycle and event dispatch. */
66 class InputController {
67 
68     private static final String TAG = "VirtualInputController";
69 
70     private static final AtomicLong sNextPhysId = new AtomicLong(1);
71 
72     static final String NAVIGATION_TOUCHPAD_DEVICE_TYPE = "touchNavigation";
73 
74     static final String PHYS_TYPE_DPAD = "Dpad";
75     static final String PHYS_TYPE_KEYBOARD = "Keyboard";
76     static final String PHYS_TYPE_MOUSE = "Mouse";
77     static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
78     static final String PHYS_TYPE_NAVIGATION_TOUCHPAD = "NavigationTouchpad";
79     static final String PHYS_TYPE_STYLUS = "Stylus";
80     static final String PHYS_TYPE_ROTARY_ENCODER = "RotaryEncoder";
81     @StringDef(prefix = { "PHYS_TYPE_" }, value = {
82             PHYS_TYPE_DPAD,
83             PHYS_TYPE_KEYBOARD,
84             PHYS_TYPE_MOUSE,
85             PHYS_TYPE_TOUCHSCREEN,
86             PHYS_TYPE_NAVIGATION_TOUCHPAD,
87             PHYS_TYPE_STYLUS,
88             PHYS_TYPE_ROTARY_ENCODER,
89     })
90     @Retention(RetentionPolicy.SOURCE)
91     @interface PhysType {
92     }
93 
94     final Object mLock = new Object();
95 
96     /* Token -> file descriptor associations. */
97     @GuardedBy("mLock")
98     private final ArrayMap<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors =
99             new ArrayMap<>();
100 
101     private final Handler mHandler;
102     private final NativeWrapper mNativeWrapper;
103     private final DisplayManagerInternal mDisplayManagerInternal;
104     private final InputManagerInternal mInputManagerInternal;
105     private final WindowManager mWindowManager;
106     private final AttributionSource mAttributionSource;
107     private final DeviceCreationThreadVerifier mThreadVerifier;
108 
InputController(@onNull Handler handler, @NonNull WindowManager windowManager, AttributionSource attributionSource)109     InputController(@NonNull Handler handler,
110             @NonNull WindowManager windowManager, AttributionSource attributionSource) {
111         this(new NativeWrapper(), handler, windowManager, attributionSource,
112                 // Verify that virtual devices are not created on the handler thread.
113                 () -> !handler.getLooper().isCurrentThread());
114     }
115 
116     @VisibleForTesting
InputController(@onNull NativeWrapper nativeWrapper, @NonNull Handler handler, @NonNull WindowManager windowManager, AttributionSource attributionSource, @NonNull DeviceCreationThreadVerifier threadVerifier)117     InputController(@NonNull NativeWrapper nativeWrapper,
118             @NonNull Handler handler, @NonNull WindowManager windowManager,
119             AttributionSource attributionSource,
120             @NonNull DeviceCreationThreadVerifier threadVerifier) {
121         mHandler = handler;
122         mNativeWrapper = nativeWrapper;
123         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
124         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
125         mWindowManager = windowManager;
126         mAttributionSource = attributionSource;
127         mThreadVerifier = threadVerifier;
128     }
129 
close()130     void close() {
131         synchronized (mLock) {
132             final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator =
133                     mInputDeviceDescriptors.entrySet().iterator();
134             if (iterator.hasNext()) {
135                 final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next();
136                 final IBinder token = entry.getKey();
137                 final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue();
138                 iterator.remove();
139                 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
140             }
141         }
142     }
143 
createDpad(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)144     void createDpad(@NonNull String deviceName, int vendorId, int productId,
145             @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
146         final String phys = createPhys(PHYS_TYPE_DPAD);
147         createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId,
148                     productId, deviceToken, displayId, phys,
149                     () -> mNativeWrapper.openUinputDpad(deviceName, vendorId, productId, phys));
150     }
151 
createKeyboard(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag, @NonNull String layoutType)152     void createKeyboard(@NonNull String deviceName, int vendorId, int productId,
153             @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag,
154             @NonNull String layoutType) throws DeviceCreationException {
155         final String phys = createPhys(PHYS_TYPE_KEYBOARD);
156         mInputManagerInternal.addKeyboardLayoutAssociation(phys, languageTag,
157                 layoutType);
158         try {
159             createDeviceInternal(InputDeviceDescriptor.TYPE_KEYBOARD, deviceName, vendorId,
160                     productId, deviceToken, displayId, phys,
161                     () -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys));
162         } catch (DeviceCreationException e) {
163             mInputManagerInternal.removeKeyboardLayoutAssociation(phys);
164             throw e;
165         }
166     }
167 
createMouse(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)168     void createMouse(@NonNull String deviceName, int vendorId, int productId,
169             @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
170         final String phys = createPhys(PHYS_TYPE_MOUSE);
171         createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId,
172                 deviceToken, displayId, phys,
173                 () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys));
174     }
175 
createTouchscreen(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, int height, int width)176     void createTouchscreen(@NonNull String deviceName, int vendorId, int productId,
177             @NonNull IBinder deviceToken, int displayId, int height, int width)
178             throws DeviceCreationException {
179         final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
180         createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId,
181                 productId, deviceToken, displayId, phys,
182                 () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
183                         height, width));
184     }
185 
createNavigationTouchpad(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, int height, int width)186     void createNavigationTouchpad(@NonNull String deviceName, int vendorId, int productId,
187             @NonNull IBinder deviceToken, int displayId, int height, int width)
188             throws DeviceCreationException {
189         final String phys = createPhys(PHYS_TYPE_NAVIGATION_TOUCHPAD);
190         mInputManagerInternal.setTypeAssociation(phys, NAVIGATION_TOUCHPAD_DEVICE_TYPE);
191         try {
192             createDeviceInternal(InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD, deviceName,
193                     vendorId, productId, deviceToken, displayId, phys,
194                     () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
195                             phys, height, width));
196         } catch (DeviceCreationException e) {
197             mInputManagerInternal.unsetTypeAssociation(phys);
198             throw e;
199         }
200     }
201 
createStylus(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, int height, int width)202     void createStylus(@NonNull String deviceName, int vendorId, int productId,
203             @NonNull IBinder deviceToken, int displayId, int height, int width)
204             throws DeviceCreationException {
205         final String phys = createPhys(PHYS_TYPE_STYLUS);
206         createDeviceInternal(InputDeviceDescriptor.TYPE_STYLUS, deviceName, vendorId,
207                 productId, deviceToken, displayId, phys,
208                 () -> mNativeWrapper.openUinputStylus(deviceName, vendorId, productId, phys,
209                         height, width));
210     }
211 
createRotaryEncoder(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)212     void createRotaryEncoder(@NonNull String deviceName, int vendorId, int productId,
213             @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
214         final String phys = createPhys(PHYS_TYPE_ROTARY_ENCODER);
215         createDeviceInternal(InputDeviceDescriptor.TYPE_ROTARY_ENCODER, deviceName, vendorId,
216                 productId, deviceToken, displayId, phys,
217                 () -> mNativeWrapper.openUinputRotaryEncoder(deviceName, vendorId, productId,
218                         phys));
219     }
220 
unregisterInputDevice(@onNull IBinder token)221     void unregisterInputDevice(@NonNull IBinder token) {
222         synchronized (mLock) {
223             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
224                     token);
225             if (inputDeviceDescriptor == null) {
226                 Slog.w(TAG, "Could not unregister input device for given token.");
227             } else {
228                 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
229             }
230         }
231     }
232 
233     @GuardedBy("mLock")
closeInputDeviceDescriptorLocked(IBinder token, InputDeviceDescriptor inputDeviceDescriptor)234     private void closeInputDeviceDescriptorLocked(IBinder token,
235             InputDeviceDescriptor inputDeviceDescriptor) {
236         token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
237         mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer());
238         String phys = inputDeviceDescriptor.getPhys();
239         InputManagerGlobal.getInstance().removeUniqueIdAssociationByPort(phys);
240         // Type associations are added in the case of navigation touchpads. Those should be removed
241         // once the input device gets closed.
242         if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD) {
243             mInputManagerInternal.unsetTypeAssociation(phys);
244         }
245 
246         if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_KEYBOARD) {
247             mInputManagerInternal.removeKeyboardLayoutAssociation(phys);
248         }
249     }
250 
251     /**
252      * @return the device id for a given token (identifiying a device)
253      */
getInputDeviceId(IBinder token)254     int getInputDeviceId(IBinder token) {
255         synchronized (mLock) {
256             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(token);
257             if (inputDeviceDescriptor == null) {
258                 throw new IllegalArgumentException("Could not get device id for given token");
259             }
260             return inputDeviceDescriptor.getInputDeviceId();
261         }
262     }
263 
setShowPointerIcon(boolean visible, int displayId)264     void setShowPointerIcon(boolean visible, int displayId) {
265         mInputManagerInternal.setPointerIconVisible(visible, displayId);
266     }
267 
setMouseScalingEnabled(boolean enabled, int displayId)268     void setMouseScalingEnabled(boolean enabled, int displayId) {
269         mInputManagerInternal.setMouseScalingEnabled(enabled, displayId);
270     }
271 
setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId)272     void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
273         mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible);
274     }
275 
setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy)276     void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
277         mWindowManager.setDisplayImePolicy(displayId, policy);
278     }
279 
280     /**
281      * Validates a device name by checking whether a device with the same name already exists.
282      * @param deviceName The name of the device to be validated
283      * @throws DeviceCreationException if {@code deviceName} is not valid.
284      */
validateDeviceName(String deviceName)285     private void validateDeviceName(String deviceName) throws DeviceCreationException {
286         synchronized (mLock) {
287             for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) {
288                 if (mInputDeviceDescriptors.valueAt(i).mName.equals(deviceName)) {
289                     throw new DeviceCreationException(
290                             "Input device name already in use: " + deviceName);
291                 }
292             }
293         }
294     }
295 
createPhys(@hysType String type)296     private static String createPhys(@PhysType String type) {
297         return formatSimple("virtual%s:%d", type, sNextPhysId.getAndIncrement());
298     }
299 
setUniqueIdAssociation(int displayId, String phys)300     private void setUniqueIdAssociation(int displayId, String phys) {
301         final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId;
302         InputManagerGlobal.getInstance().addUniqueIdAssociationByPort(phys, displayUniqueId);
303     }
304 
sendDpadKeyEvent(@onNull IBinder token, @NonNull VirtualKeyEvent event)305     boolean sendDpadKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
306         synchronized (mLock) {
307             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
308                     token);
309             if (inputDeviceDescriptor == null) {
310                 return false;
311             }
312             return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
313                     event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
314         }
315     }
316 
sendKeyEvent(@onNull IBinder token, @NonNull VirtualKeyEvent event)317     boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
318         synchronized (mLock) {
319             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
320                     token);
321             if (inputDeviceDescriptor == null) {
322                 return false;
323             }
324             return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
325                     event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
326         }
327     }
328 
sendButtonEvent(@onNull IBinder token, @NonNull VirtualMouseButtonEvent event)329     boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) {
330         synchronized (mLock) {
331             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
332                     token);
333             if (inputDeviceDescriptor == null) {
334                 return false;
335             }
336             return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
337                     event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
338         }
339     }
340 
sendTouchEvent(@onNull IBinder token, @NonNull VirtualTouchEvent event)341     boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) {
342         synchronized (mLock) {
343             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
344                     token);
345             if (inputDeviceDescriptor == null) {
346                 return false;
347             }
348             return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
349                     event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
350                     event.getY(), event.getPressure(), event.getMajorAxisSize(),
351                     event.getEventTimeNanos());
352         }
353     }
354 
sendRelativeEvent(@onNull IBinder token, @NonNull VirtualMouseRelativeEvent event)355     boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) {
356         synchronized (mLock) {
357             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
358                     token);
359             if (inputDeviceDescriptor == null) {
360                 return false;
361             }
362             return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
363                     event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos());
364         }
365     }
366 
sendScrollEvent(@onNull IBinder token, @NonNull VirtualMouseScrollEvent event)367     boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) {
368         synchronized (mLock) {
369             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
370                     token);
371             if (inputDeviceDescriptor == null) {
372                 return false;
373             }
374             return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
375                     event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos());
376         }
377     }
378 
getCursorPosition(@onNull IBinder token)379     public PointF getCursorPosition(@NonNull IBinder token) {
380         synchronized (mLock) {
381             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
382                     token);
383             if (inputDeviceDescriptor == null) {
384                 throw new IllegalArgumentException(
385                         "Could not get cursor position for input device for given token");
386             }
387             return LocalServices.getService(InputManagerInternal.class).getCursorPosition(
388                     inputDeviceDescriptor.getDisplayId());
389         }
390     }
391 
sendStylusMotionEvent(@onNull IBinder token, @NonNull VirtualStylusMotionEvent event)392     boolean sendStylusMotionEvent(@NonNull IBinder token, @NonNull VirtualStylusMotionEvent event) {
393         synchronized (mLock) {
394             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
395                     token);
396             if (inputDeviceDescriptor == null) {
397                 return false;
398             }
399             return mNativeWrapper.writeStylusMotionEvent(inputDeviceDescriptor.getNativePointer(),
400                     event.getToolType(), event.getAction(), event.getX(), event.getY(),
401                     event.getPressure(), event.getTiltX(), event.getTiltY(),
402                     event.getEventTimeNanos());
403         }
404     }
405 
sendStylusButtonEvent(@onNull IBinder token, @NonNull VirtualStylusButtonEvent event)406     boolean sendStylusButtonEvent(@NonNull IBinder token, @NonNull VirtualStylusButtonEvent event) {
407         synchronized (mLock) {
408             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
409                     token);
410             if (inputDeviceDescriptor == null) {
411                 return false;
412             }
413             return mNativeWrapper.writeStylusButtonEvent(inputDeviceDescriptor.getNativePointer(),
414                     event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
415         }
416     }
417 
sendRotaryEncoderScrollEvent(@onNull IBinder token, @NonNull VirtualRotaryEncoderScrollEvent event)418     boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
419             @NonNull VirtualRotaryEncoderScrollEvent event) {
420         synchronized (mLock) {
421             final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
422                     token);
423             if (inputDeviceDescriptor == null) {
424                 return false;
425             }
426             return mNativeWrapper.writeRotaryEncoderScrollEvent(
427                     inputDeviceDescriptor.getNativePointer(), event.getScrollAmount(),
428                     event.getEventTimeNanos());
429         }
430     }
431 
dump(@onNull PrintWriter fout)432     public void dump(@NonNull PrintWriter fout) {
433         fout.println("    InputController: ");
434         synchronized (mLock) {
435             fout.println("      Active descriptors: ");
436             for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) {
437                 InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.valueAt(i);
438                 fout.println("        ptr: " + inputDeviceDescriptor.getNativePointer());
439                 fout.println("          displayId: " + inputDeviceDescriptor.getDisplayId());
440                 fout.println("          creationOrder: "
441                         + inputDeviceDescriptor.getCreationOrderNumber());
442                 fout.println("          type: " + inputDeviceDescriptor.getType());
443                 fout.println("          phys: " + inputDeviceDescriptor.getPhys());
444                 fout.println(
445                         "          inputDeviceId: " + inputDeviceDescriptor.getInputDeviceId());
446             }
447         }
448     }
449 
450     @VisibleForTesting
addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys, String deviceName, int inputDeviceId)451     void addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys,
452             String deviceName, int inputDeviceId) {
453         synchronized (mLock) {
454             mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(ptr, () -> {
455             }, type, displayId, phys, deviceName, inputDeviceId));
456         }
457     }
458 
459     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getInputDeviceDescriptors()460     Map<IBinder, InputDeviceDescriptor> getInputDeviceDescriptors() {
461         final Map<IBinder, InputDeviceDescriptor> inputDeviceDescriptors = new ArrayMap<>();
462         synchronized (mLock) {
463             inputDeviceDescriptors.putAll(mInputDeviceDescriptors);
464         }
465         return inputDeviceDescriptors;
466     }
467 
nativeOpenUinputDpad(String deviceName, int vendorId, int productId, String phys)468     private static native long nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
469             String phys);
nativeOpenUinputKeyboard(String deviceName, int vendorId, int productId, String phys)470     private static native long nativeOpenUinputKeyboard(String deviceName, int vendorId,
471             int productId, String phys);
nativeOpenUinputMouse(String deviceName, int vendorId, int productId, String phys)472     private static native long nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
473             String phys);
nativeOpenUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)474     private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
475             int productId, String phys, int height, int width);
nativeOpenUinputStylus(String deviceName, int vendorId, int productId, String phys, int height, int width)476     private static native long nativeOpenUinputStylus(String deviceName, int vendorId,
477             int productId, String phys, int height, int width);
nativeOpenUinputRotaryEncoder(String deviceName, int vendorId, int productId, String phys)478     private static native long nativeOpenUinputRotaryEncoder(String deviceName, int vendorId,
479             int productId, String phys);
480 
nativeCloseUinput(long ptr)481     private static native void nativeCloseUinput(long ptr);
nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)482     private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action,
483             long eventTimeNanos);
nativeWriteKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)484     private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action,
485             long eventTimeNanos);
nativeWriteButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)486     private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action,
487             long eventTimeNanos);
nativeWriteTouchEvent(long ptr, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize, long eventTimeNanos)488     private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
489             int action, float locationX, float locationY, float pressure, float majorAxisSize,
490             long eventTimeNanos);
nativeWriteRelativeEvent(long ptr, float relativeX, float relativeY, long eventTimeNanos)491     private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
492             float relativeY, long eventTimeNanos);
nativeWriteScrollEvent(long ptr, float xAxisMovement, float yAxisMovement, long eventTimeNanos)493     private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
494             float yAxisMovement, long eventTimeNanos);
nativeWriteStylusMotionEvent(long ptr, int toolType, int action, int locationX, int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos)495     private static native boolean nativeWriteStylusMotionEvent(long ptr, int toolType, int action,
496             int locationX, int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos);
nativeWriteStylusButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)497     private static native boolean nativeWriteStylusButtonEvent(long ptr, int buttonCode, int action,
498             long eventTimeNanos);
nativeWriteRotaryEncoderScrollEvent(long ptr, float scrollAmount, long eventTimeNanos)499     private static native boolean nativeWriteRotaryEncoderScrollEvent(long ptr, float scrollAmount,
500             long eventTimeNanos);
501 
502     /** Wrapper around the static native methods for tests. */
503     @VisibleForTesting
504     protected static class NativeWrapper {
openUinputDpad(String deviceName, int vendorId, int productId, String phys)505         public long openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
506             return nativeOpenUinputDpad(deviceName, vendorId, productId, phys);
507         }
508 
openUinputKeyboard(String deviceName, int vendorId, int productId, String phys)509         public long openUinputKeyboard(String deviceName, int vendorId, int productId,
510                 String phys) {
511             return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
512         }
513 
openUinputMouse(String deviceName, int vendorId, int productId, String phys)514         public long openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
515             return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
516         }
517 
openUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)518         public long openUinputTouchscreen(String deviceName, int vendorId,
519                 int productId, String phys, int height, int width) {
520             return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
521                     width);
522         }
523 
openUinputStylus(String deviceName, int vendorId, int productId, String phys, int height, int width)524         public long openUinputStylus(String deviceName, int vendorId, int productId, String phys,
525                 int height, int width) {
526             return nativeOpenUinputStylus(deviceName, vendorId, productId, phys, height, width);
527         }
528 
openUinputRotaryEncoder(String deviceName, int vendorId, int productId, String phys)529         public long openUinputRotaryEncoder(String deviceName, int vendorId, int productId,
530                 String phys) {
531             return nativeOpenUinputRotaryEncoder(deviceName, vendorId, productId, phys);
532         }
533 
closeUinput(long ptr)534         public void closeUinput(long ptr) {
535             nativeCloseUinput(ptr);
536         }
537 
writeDpadKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)538         public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action,
539                 long eventTimeNanos) {
540             return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
541         }
542 
writeKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)543         public boolean writeKeyEvent(long ptr, int androidKeyCode, int action,
544                 long eventTimeNanos) {
545             return nativeWriteKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
546         }
547 
writeButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)548         public boolean writeButtonEvent(long ptr, int buttonCode, int action,
549                 long eventTimeNanos) {
550             return nativeWriteButtonEvent(ptr, buttonCode, action, eventTimeNanos);
551         }
552 
writeTouchEvent(long ptr, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize, long eventTimeNanos)553         public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
554                 float locationX, float locationY, float pressure, float majorAxisSize,
555                 long eventTimeNanos) {
556             return nativeWriteTouchEvent(ptr, pointerId, toolType,
557                     action, locationX, locationY,
558                     pressure, majorAxisSize, eventTimeNanos);
559         }
560 
writeRelativeEvent(long ptr, float relativeX, float relativeY, long eventTimeNanos)561         public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY,
562                 long eventTimeNanos) {
563             return nativeWriteRelativeEvent(ptr, relativeX, relativeY, eventTimeNanos);
564         }
565 
writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement, long eventTimeNanos)566         public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement,
567                 long eventTimeNanos) {
568             return nativeWriteScrollEvent(ptr, xAxisMovement, yAxisMovement, eventTimeNanos);
569         }
570 
writeStylusMotionEvent(long ptr, int toolType, int action, int locationX, int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos)571         public boolean writeStylusMotionEvent(long ptr, int toolType, int action, int locationX,
572                 int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos) {
573             return nativeWriteStylusMotionEvent(ptr, toolType, action, locationX, locationY,
574                     pressure, tiltX, tiltY, eventTimeNanos);
575         }
576 
writeStylusButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)577         public boolean writeStylusButtonEvent(long ptr, int buttonCode, int action,
578                 long eventTimeNanos) {
579             return nativeWriteStylusButtonEvent(ptr, buttonCode, action, eventTimeNanos);
580         }
581 
writeRotaryEncoderScrollEvent(long ptr, float scrollAmount, long eventTimeNanos)582         public boolean writeRotaryEncoderScrollEvent(long ptr, float scrollAmount,
583                 long eventTimeNanos) {
584             return nativeWriteRotaryEncoderScrollEvent(ptr, scrollAmount, eventTimeNanos);
585         }
586     }
587 
588     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
589     static final class InputDeviceDescriptor {
590 
591         static final int TYPE_KEYBOARD = 1;
592         static final int TYPE_MOUSE = 2;
593         static final int TYPE_TOUCHSCREEN = 3;
594         static final int TYPE_DPAD = 4;
595         static final int TYPE_NAVIGATION_TOUCHPAD = 5;
596         static final int TYPE_STYLUS = 6;
597         static final int TYPE_ROTARY_ENCODER = 7;
598         @IntDef(prefix = { "TYPE_" }, value = {
599                 TYPE_KEYBOARD,
600                 TYPE_MOUSE,
601                 TYPE_TOUCHSCREEN,
602                 TYPE_DPAD,
603                 TYPE_NAVIGATION_TOUCHPAD,
604                 TYPE_STYLUS,
605                 TYPE_ROTARY_ENCODER,
606         })
607         @Retention(RetentionPolicy.SOURCE)
608         @interface Type {
609         }
610 
611         private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);
612 
613         // Pointer to the native input device object.
614         private final long mPtr;
615         private final IBinder.DeathRecipient mDeathRecipient;
616         private final @Type int mType;
617         private final int mDisplayId;
618         private final String mPhys;
619         // The name given to this device by the client. Enforced to be unique within
620         // InputController.
621         private final String mName;
622         // The input device id that was associated to the device by the InputReader on device
623         // creation.
624         private final int mInputDeviceId;
625         // Monotonically increasing number; devices with lower numbers were created earlier.
626         private final long mCreationOrderNumber;
627 
InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type, int displayId, String phys, String name, int inputDeviceId)628         InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type,
629                 int displayId, String phys, String name, int inputDeviceId) {
630             mPtr = ptr;
631             mDeathRecipient = deathRecipient;
632             mType = type;
633             mDisplayId = displayId;
634             mPhys = phys;
635             mName = name;
636             mInputDeviceId = inputDeviceId;
637             mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
638         }
639 
getNativePointer()640         public long getNativePointer() {
641             return mPtr;
642         }
643 
getType()644         public int getType() {
645             return mType;
646         }
647 
isMouse()648         public boolean isMouse() {
649             return mType == TYPE_MOUSE;
650         }
651 
getDeathRecipient()652         public IBinder.DeathRecipient getDeathRecipient() {
653             return mDeathRecipient;
654         }
655 
getDisplayId()656         public int getDisplayId() {
657             return mDisplayId;
658         }
659 
getCreationOrderNumber()660         public long getCreationOrderNumber() {
661             return mCreationOrderNumber;
662         }
663 
getPhys()664         public String getPhys() {
665             return mPhys;
666         }
667 
getInputDeviceId()668         public int getInputDeviceId() {
669             return mInputDeviceId;
670         }
671     }
672 
673     private final class BinderDeathRecipient implements IBinder.DeathRecipient {
674 
675         private final IBinder mDeviceToken;
676 
BinderDeathRecipient(IBinder deviceToken)677         BinderDeathRecipient(IBinder deviceToken) {
678             mDeviceToken = deviceToken;
679         }
680 
681         @Override
binderDied()682         public void binderDied() {
683             // All callers are expected to call {@link VirtualDevice#unregisterInputDevice} before
684             // quitting, which removes this death recipient. If this is invoked, the remote end
685             // died, or they disposed of the object without properly unregistering.
686             Slog.e(TAG, "Virtual input controller binder died");
687             unregisterInputDevice(mDeviceToken);
688         }
689     }
690 
691     /** A helper class used to wait for an input device to be registered. */
692     private class WaitForDevice implements AutoCloseable {
693         private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1);
694         private final String mDeviceName;
695         private final InputManager.InputDeviceListener mListener;
696 
697         private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID;
698 
WaitForDevice(String deviceName, int vendorId, int productId, int associatedDisplayId)699         WaitForDevice(String deviceName, int vendorId, int productId, int associatedDisplayId) {
700             mDeviceName = deviceName;
701             mListener = new InputManager.InputDeviceListener() {
702                 @Override
703                 public void onInputDeviceAdded(int deviceId) {
704                     onInputDeviceChanged(deviceId);
705                 }
706 
707                 @Override
708                 public void onInputDeviceRemoved(int deviceId) {
709                 }
710 
711                 @Override
712                 public void onInputDeviceChanged(int deviceId) {
713                     if (isMatchingDevice(deviceId)) {
714                         mInputDeviceId = deviceId;
715                         mDeviceAddedLatch.countDown();
716                     }
717                 }
718 
719                 private boolean isMatchingDevice(int deviceId) {
720                     final InputDevice device = InputManagerGlobal.getInstance().getInputDevice(
721                             deviceId);
722                     Objects.requireNonNull(device, "Newly added input device was null.");
723                     if (!device.getName().equals(deviceName)) {
724                         return false;
725                     }
726                     final InputDeviceIdentifier id = device.getIdentifier();
727                     if (id.getVendorId() != vendorId || id.getProductId() != productId) {
728                         return false;
729                     }
730                     return device.getAssociatedDisplayId() == associatedDisplayId;
731                 }
732             };
733             InputManagerGlobal.getInstance().registerInputDeviceListener(mListener, mHandler);
734         }
735 
736         /**
737          * Note: This must not be called from {@link #mHandler}'s thread.
738          * @throws DeviceCreationException if the device was not created successfully within the
739          * timeout.
740          * @return The id of the created input device.
741          */
waitForDeviceCreation()742         int waitForDeviceCreation() throws DeviceCreationException {
743             try {
744                 if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) {
745                     throw new DeviceCreationException(
746                             "Timed out waiting for virtual input device " + mDeviceName
747                                     + " to be created.");
748                 }
749             } catch (InterruptedException e) {
750                 throw new DeviceCreationException(
751                         "Interrupted while waiting for virtual input device " + mDeviceName
752                                 + " to be created.", e);
753             }
754             if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) {
755                 throw new IllegalStateException(
756                         "Virtual input device " + mDeviceName + " was created with an invalid "
757                                 + "id=" + mInputDeviceId);
758             }
759             return mInputDeviceId;
760         }
761 
762         @Override
close()763         public void close() {
764             InputManagerGlobal.getInstance().unregisterInputDeviceListener(mListener);
765         }
766     }
767 
768     /** An internal exception that is thrown to indicate an error when opening a virtual device. */
769     static class DeviceCreationException extends Exception {
DeviceCreationException()770         DeviceCreationException() {
771             super();
772         }
DeviceCreationException(String message)773         DeviceCreationException(String message) {
774             super(message);
775         }
DeviceCreationException(String message, Throwable cause)776         DeviceCreationException(String message, Throwable cause) {
777             super(message, cause);
778         }
DeviceCreationException(Throwable cause)779         DeviceCreationException(Throwable cause) {
780             super(cause);
781         }
782     }
783 
784     /**
785      * Creates a virtual input device synchronously, and waits for the notification that the device
786      * was added.
787      *
788      * Note: Input device creation is expected to happen on a binder thread, and the calling thread
789      * will be blocked until the input device creation is successful. This should not be called on
790      * the handler's thread.
791      *
792      * @throws DeviceCreationException Throws this exception if anything unexpected happens in the
793      *                                 process of creating the device. This method will take care
794      *                                 to restore the state of the system in the event of any
795      *                                 unexpected behavior.
796      */
createDeviceInternal(@nputDeviceDescriptor.Type int type, String deviceName, int vendorId, int productId, IBinder deviceToken, int displayId, String phys, Supplier<Long> deviceOpener)797     private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName,
798             int vendorId, int productId, IBinder deviceToken, int displayId, String phys,
799             Supplier<Long> deviceOpener)
800             throws DeviceCreationException {
801         if (!mThreadVerifier.isValidThread()) {
802             throw new IllegalStateException(
803                     "Virtual device creation should happen on an auxiliary thread (e.g. binder "
804                             + "thread) and not from the handler's thread.");
805         }
806         validateDeviceName(deviceName);
807 
808         final long ptr;
809         final BinderDeathRecipient binderDeathRecipient;
810 
811         final int inputDeviceId;
812 
813         setUniqueIdAssociation(displayId, phys);
814         try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId, displayId)) {
815             ptr = deviceOpener.get();
816             // See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
817             if (ptr == 0) {
818                 throw new DeviceCreationException(
819                         "A native error occurred when creating virtual input device: "
820                                 + deviceName);
821             }
822             // The pointer to the native input device is valid from here, so ensure that all
823             // failures close the device after this point.
824             try {
825                 inputDeviceId = waiter.waitForDeviceCreation();
826 
827                 binderDeathRecipient = new BinderDeathRecipient(deviceToken);
828                 try {
829                     deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
830                 } catch (RemoteException e) {
831                     throw new DeviceCreationException(
832                             "Client died before virtual device could be created.", e);
833                 }
834             } catch (DeviceCreationException e) {
835                 mNativeWrapper.closeUinput(ptr);
836                 throw e;
837             }
838         } catch (DeviceCreationException e) {
839             InputManagerGlobal.getInstance().removeUniqueIdAssociationByPort(phys);
840             throw e;
841         }
842 
843         synchronized (mLock) {
844             mInputDeviceDescriptors.put(deviceToken,
845                     new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
846                             deviceName, inputDeviceId));
847         }
848 
849         String metricId = getMetricIdForInputType(type);
850         if (metricId != null) {
851             Counter.logIncrementWithUid(metricId, mAttributionSource.getUid());
852         }
853     }
854 
getMetricIdForInputType(@nputDeviceDescriptor.Type int type)855     private static String getMetricIdForInputType(@InputDeviceDescriptor.Type int type) {
856         switch (type) {
857             case InputDeviceDescriptor.TYPE_KEYBOARD:
858                 return "virtual_devices.value_virtual_keyboard_created_count";
859             case InputDeviceDescriptor.TYPE_MOUSE:
860                 return "virtual_devices.value_virtual_mouse_created_count";
861             case InputDeviceDescriptor.TYPE_TOUCHSCREEN:
862                 return "virtual_devices.value_virtual_touchscreen_created_count";
863             case InputDeviceDescriptor.TYPE_DPAD:
864                 return "virtual_devices.value_virtual_dpad_created_count";
865             case InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD:
866                 return "virtual_devices.value_virtual_navigationtouchpad_created_count";
867             case InputDeviceDescriptor.TYPE_STYLUS:
868                 return "virtual_devices.value_virtual_stylus_created_count";
869             case InputDeviceDescriptor.TYPE_ROTARY_ENCODER:
870                 return "virtual_devices.value_virtual_rotary_created_count";
871             default:
872                 Log.e(TAG, "No metric known for input type: " + type);
873                 return null;
874         }
875     }
876 
877     @VisibleForTesting
878     interface DeviceCreationThreadVerifier {
879         /** Returns true if the calling thread is a valid thread for device creation. */
isValidThread()880         boolean isValidThread();
881     }
882 }
883