1 /* 2 * Copyright (C) 2016 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.tv; 18 19 import android.os.IBinder; 20 21 import dalvik.system.CloseGuard; 22 23 import java.io.IOException; 24 25 /** 26 * Sends the input event to the linux driver. 27 */ 28 public final class UinputBridge { 29 private final CloseGuard mCloseGuard = CloseGuard.get(); 30 private long mPtr; 31 private IBinder mToken; 32 nativeOpen(String name, String uniqueId, int width, int height, int maxPointers)33 private static native long nativeOpen(String name, String uniqueId, int width, int height, 34 int maxPointers); nativeClose(long ptr)35 private static native void nativeClose(long ptr); nativeClear(long ptr)36 private static native void nativeClear(long ptr); nativeSendKey(long ptr, int keyCode, boolean down)37 private static native void nativeSendKey(long ptr, int keyCode, boolean down); nativeSendPointerDown(long ptr, int pointerId, int x, int y)38 private static native void nativeSendPointerDown(long ptr, int pointerId, int x, int y); nativeSendPointerUp(long ptr, int pointerId)39 private static native void nativeSendPointerUp(long ptr, int pointerId); nativeSendPointerSync(long ptr)40 private static native void nativeSendPointerSync(long ptr); 41 42 /** Opens a gamepad - will support gamepad key and axis sending */ nativeGamepadOpen(String name, String uniqueId)43 private static native long nativeGamepadOpen(String name, String uniqueId); 44 45 /** 46 * Marks the specified key up/down for a gamepad. 47 * 48 * @param keyCode - a code like BUTTON_MODE, BUTTON_A, BUTTON_B, ... 49 */ nativeSendGamepadKey(long ptr, int keyCode, boolean down)50 private static native void nativeSendGamepadKey(long ptr, int keyCode, boolean down); 51 52 /** 53 * Send an axis value. 54 * 55 * Available axes are: 56 * <li> Left joystick: AXIS_X, AXIS_Y 57 * <li> Right joystick: AXIS_Z, AXIS_RZ 58 * <li> Analog triggers: AXIS_LTRIGGER, AXIS_RTRIGGER 59 * <li> DPad: AXIS_HAT_X, AXIS_HAT_Y 60 * 61 * @param axis is a MotionEvent.AXIS_* value. 62 * @param value is a value between -1 and 1 (inclusive) 63 * 64 */ nativeSendGamepadAxisValue(long ptr, int axis, float value)65 private static native void nativeSendGamepadAxisValue(long ptr, int axis, float value); 66 UinputBridge(IBinder token, String name, int width, int height, int maxPointers)67 public UinputBridge(IBinder token, String name, int width, int height, int maxPointers) 68 throws IOException { 69 if (width < 1 || height < 1) { 70 throw new IllegalArgumentException("Touchpad must be at least 1x1."); 71 } 72 if (maxPointers < 1 || maxPointers > 32) { 73 throw new IllegalArgumentException("Touchpad must support between 1 and 32 pointers."); 74 } 75 if (token == null) { 76 throw new IllegalArgumentException("Token cannot be null"); 77 } 78 mPtr = nativeOpen(name, token.toString(), width, height, maxPointers); 79 if (mPtr == 0) { 80 throw new IOException("Could not open uinput device " + name); 81 } 82 mToken = token; 83 mCloseGuard.open("close"); 84 } 85 86 /** Constructor used by static factory methods */ UinputBridge(IBinder token, long ptr)87 private UinputBridge(IBinder token, long ptr) { 88 mPtr = ptr; 89 mToken = token; 90 mCloseGuard.open("close"); 91 } 92 93 /** Opens a UinputBridge that supports gamepad buttons and axes. */ openGamepad(IBinder token, String name)94 public static UinputBridge openGamepad(IBinder token, String name) 95 throws IOException { 96 if (token == null) { 97 throw new IllegalArgumentException("Token cannot be null"); 98 } 99 long ptr = nativeGamepadOpen(name, token.toString()); 100 if (ptr == 0) { 101 throw new IOException("Could not open uinput device " + name); 102 } 103 104 return new UinputBridge(token, ptr); 105 } 106 107 @Override finalize()108 protected void finalize() throws Throwable { 109 try { 110 mCloseGuard.warnIfOpen(); 111 close(mToken); 112 } finally { 113 mToken = null; 114 super.finalize(); 115 } 116 } 117 close(IBinder token)118 public void close(IBinder token) { 119 if (isTokenValid(token)) { 120 if (mPtr != 0) { 121 clear(token); 122 nativeClose(mPtr); 123 124 mPtr = 0; 125 mCloseGuard.close(); 126 } 127 } 128 } 129 getToken()130 public IBinder getToken() { 131 return mToken; 132 } 133 isTokenValid(IBinder token)134 protected boolean isTokenValid(IBinder token) { 135 return mToken.equals(token); 136 } 137 sendKeyDown(IBinder token, int keyCode)138 public void sendKeyDown(IBinder token, int keyCode) { 139 if (isTokenValid(token)) { 140 nativeSendKey(mPtr, keyCode, true /*down*/); 141 } 142 } 143 sendKeyUp(IBinder token, int keyCode)144 public void sendKeyUp(IBinder token, int keyCode) { 145 if (isTokenValid(token)) { 146 nativeSendKey(mPtr, keyCode, false /*down*/); 147 } 148 } 149 sendPointerDown(IBinder token, int pointerId, int x, int y)150 public void sendPointerDown(IBinder token, int pointerId, int x, int y) { 151 if (isTokenValid(token)) { 152 nativeSendPointerDown(mPtr, pointerId, x, y); 153 } 154 } 155 sendPointerUp(IBinder token, int pointerId)156 public void sendPointerUp(IBinder token, int pointerId) { 157 if (isTokenValid(token)) { 158 nativeSendPointerUp(mPtr, pointerId); 159 } 160 } 161 sendPointerSync(IBinder token)162 public void sendPointerSync(IBinder token) { 163 if (isTokenValid(token)) { 164 nativeSendPointerSync(mPtr); 165 } 166 } 167 168 /** Send a gamepad key 169 * @param keyIndex - the index of the w3-spec key 170 * @param down - is the key pressed ? 171 */ sendGamepadKey(IBinder token, int keyCode, boolean down)172 public void sendGamepadKey(IBinder token, int keyCode, boolean down) { 173 if (isTokenValid(token)) { 174 nativeSendGamepadKey(mPtr, keyCode, down); 175 } 176 } 177 178 /** 179 * Send a gamepad axis value. 180 * 181 * @param axis is the axis code (MotionEvent.AXIS_*) 182 * @param value is the value to set for that axis in [-1, 1] 183 */ sendGamepadAxisValue(IBinder token, int axis, float value)184 public void sendGamepadAxisValue(IBinder token, int axis, float value) { 185 if (isTokenValid(token)) { 186 nativeSendGamepadAxisValue(mPtr, axis, value); 187 } 188 } 189 clear(IBinder token)190 public void clear(IBinder token) { 191 if (isTokenValid(token)) { 192 nativeClear(mPtr); 193 } 194 } 195 196 } 197