1 /*
2 * Copyright (C) 2020 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 #define LOG_TAG "UinputCommandDevice"
18
19 #include <linux/uinput.h>
20
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include <algorithm>
26 #include <array>
27 #include <cstdio>
28 #include <cstring>
29 #include <iterator>
30 #include <memory>
31 #include <vector>
32
33 #include <android/looper.h>
34 #include <android_os_Parcel.h>
35 #include <jni.h>
36 #include <log/log.h>
37 #include <nativehelper/JNIHelp.h>
38 #include <nativehelper/ScopedLocalRef.h>
39 #include <nativehelper/ScopedPrimitiveArray.h>
40 #include <nativehelper/ScopedUtfChars.h>
41
42 #include <android-base/stringprintf.h>
43
44 #include "com_android_commands_uinput_Device.h"
45
46 namespace android {
47 namespace uinput {
48
49 using src::com::android::commands::uinput::InputAbsInfo;
50
51 static constexpr const char* UINPUT_PATH = "/dev/uinput";
52
53 static struct {
54 jmethodID onDeviceConfigure;
55 jmethodID onDeviceVibrating;
56 jmethodID onDeviceError;
57 } gDeviceCallbackClassInfo;
58
checkAndClearException(JNIEnv * env,const char * methodName)59 static void checkAndClearException(JNIEnv* env, const char* methodName) {
60 if (env->ExceptionCheck()) {
61 ALOGE("An exception was thrown by callback '%s'.", methodName);
62 env->ExceptionClear();
63 }
64 }
65
DeviceCallback(JNIEnv * env,jobject callback)66 DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback)
67 : mCallbackObject(env->NewGlobalRef(callback)) {
68 env->GetJavaVM(&mJavaVM);
69 }
70
~DeviceCallback()71 DeviceCallback::~DeviceCallback() {
72 JNIEnv* env = getJNIEnv();
73 env->DeleteGlobalRef(mCallbackObject);
74 }
75
onDeviceError()76 void DeviceCallback::onDeviceError() {
77 JNIEnv* env = getJNIEnv();
78 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
79 checkAndClearException(env, "onDeviceError");
80 }
81
onDeviceConfigure(int handle)82 void DeviceCallback::onDeviceConfigure(int handle) {
83 JNIEnv* env = getJNIEnv();
84 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceConfigure, handle);
85 checkAndClearException(env, "onDeviceConfigure");
86 }
87
onDeviceVibrating(int value)88 void DeviceCallback::onDeviceVibrating(int value) {
89 JNIEnv* env = getJNIEnv();
90 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceVibrating, value);
91 checkAndClearException(env, "onDeviceVibrating");
92 }
93
getJNIEnv()94 JNIEnv* DeviceCallback::getJNIEnv() {
95 JNIEnv* env;
96 mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
97 return env;
98 }
99
open(int32_t id,const char * name,int32_t vid,int32_t pid,uint16_t bus,uint32_t ffEffectsMax,const char * port,std::unique_ptr<DeviceCallback> callback)100 std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
101 int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
102 const char* port,
103 std::unique_ptr<DeviceCallback> callback) {
104 android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
105 if (!fd.ok()) {
106 ALOGE("Failed to open uinput: %s", strerror(errno));
107 return nullptr;
108 }
109
110 int32_t version;
111 ::ioctl(fd, UI_GET_VERSION, &version);
112 if (version < 5) {
113 ALOGE("Kernel version %d older than 5 is not supported", version);
114 return nullptr;
115 }
116
117 struct uinput_setup setupDescriptor;
118 memset(&setupDescriptor, 0, sizeof(setupDescriptor));
119 strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
120 setupDescriptor.id.version = 1;
121 setupDescriptor.id.bustype = bus;
122 setupDescriptor.id.vendor = vid;
123 setupDescriptor.id.product = pid;
124 setupDescriptor.ff_effects_max = ffEffectsMax;
125
126 // Request device configuration.
127 callback->onDeviceConfigure(fd.get());
128
129 // register the input device
130 if (::ioctl(fd, UI_DEV_SETUP, &setupDescriptor)) {
131 ALOGE("UI_DEV_SETUP ioctl failed on fd %d: %s.", fd.get(), strerror(errno));
132 return nullptr;
133 }
134
135 // set the physical port.
136 ::ioctl(fd, UI_SET_PHYS, port);
137
138 if (::ioctl(fd, UI_DEV_CREATE) != 0) {
139 ALOGE("Unable to create uinput device: %s.", strerror(errno));
140 return nullptr;
141 }
142
143 // using 'new' to access non-public constructor
144 return std::unique_ptr<UinputDevice>(new UinputDevice(id, std::move(fd), std::move(callback)));
145 }
146
UinputDevice(int32_t id,android::base::unique_fd fd,std::unique_ptr<DeviceCallback> callback)147 UinputDevice::UinputDevice(int32_t id, android::base::unique_fd fd,
148 std::unique_ptr<DeviceCallback> callback)
149 : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
150 ALooper* aLooper = ALooper_forThread();
151 if (aLooper == nullptr) {
152 ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
153 aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
154 }
155 ALooper_addFd(
156 aLooper, mFd, 0, ALOOPER_EVENT_INPUT,
157 [](int, int events, void* data) {
158 UinputDevice* d = reinterpret_cast<UinputDevice*>(data);
159 return d->handleEvents(events);
160 },
161 reinterpret_cast<void*>(this));
162 ALOGI("uinput device %d created: version = %d, fd = %d", mId, UINPUT_VERSION, mFd.get());
163 }
164
~UinputDevice()165 UinputDevice::~UinputDevice() {
166 ::ioctl(mFd, UI_DEV_DESTROY);
167 }
168
injectEvent(uint16_t type,uint16_t code,int32_t value)169 void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
170 struct input_event event = {};
171 event.type = type;
172 event.code = code;
173 event.value = value;
174 timespec ts;
175 clock_gettime(CLOCK_MONOTONIC, &ts);
176 TIMESPEC_TO_TIMEVAL(&event.time, &ts);
177
178 if (::write(mFd, &event, sizeof(input_event)) < 0) {
179 ALOGE("Could not write event %" PRIu16 " %" PRIu16 " with value %" PRId32 " : %s", type,
180 code, value, strerror(errno));
181 }
182 }
183
handleEvents(int events)184 int UinputDevice::handleEvents(int events) {
185 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
186 ALOGE("uinput node was closed or an error occurred. events=0x%x", events);
187 mDeviceCallback->onDeviceError();
188 return 0;
189 }
190 struct input_event ev;
191 ssize_t ret = ::read(mFd, &ev, sizeof(ev));
192 if (ret < 0) {
193 ALOGE("Failed to read from uinput node: %s", strerror(errno));
194 mDeviceCallback->onDeviceError();
195 return 0;
196 }
197
198 switch (ev.type) {
199 case EV_UINPUT: {
200 if (ev.code == UI_FF_UPLOAD) {
201 struct uinput_ff_upload ff_upload;
202 ff_upload.request_id = ev.value;
203 ::ioctl(mFd, UI_BEGIN_FF_UPLOAD, &ff_upload);
204 ff_upload.retval = 0;
205 ::ioctl(mFd, UI_END_FF_UPLOAD, &ff_upload);
206 } else if (ev.code == UI_FF_ERASE) {
207 struct uinput_ff_erase ff_erase;
208 ff_erase.request_id = ev.value;
209 ::ioctl(mFd, UI_BEGIN_FF_ERASE, &ff_erase);
210 ff_erase.retval = 0;
211 ::ioctl(mFd, UI_END_FF_ERASE, &ff_erase);
212 }
213 break;
214 }
215 case EV_FF: {
216 ALOGI("EV_FF effect = %d value = %d", ev.code, ev.value);
217 mDeviceCallback->onDeviceVibrating(ev.value);
218 break;
219 }
220 default: {
221 ALOGI("Unhandled event type: %" PRIu32, ev.type);
222 break;
223 }
224 }
225
226 return 1;
227 }
228
229 } // namespace uinput
230
toVector(JNIEnv * env,jintArray javaArray)231 std::vector<int32_t> toVector(JNIEnv* env, jintArray javaArray) {
232 std::vector<int32_t> data;
233 if (javaArray == nullptr) {
234 return data;
235 }
236
237 ScopedIntArrayRO scopedArray(env, javaArray);
238 size_t size = scopedArray.size();
239 data.reserve(size);
240 for (size_t i = 0; i < size; i++) {
241 data.push_back(static_cast<int32_t>(scopedArray[i]));
242 }
243 return data;
244 }
245
openUinputDevice(JNIEnv * env,jclass,jstring rawName,jint id,jint vid,jint pid,jint bus,jint ffEffectsMax,jstring rawPort,jobject callback)246 static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
247 jint pid, jint bus, jint ffEffectsMax, jstring rawPort,
248 jobject callback) {
249 ScopedUtfChars name(env, rawName);
250 if (name.c_str() == nullptr) {
251 return 0;
252 }
253
254 ScopedUtfChars port(env, rawPort);
255 std::unique_ptr<uinput::DeviceCallback> cb =
256 std::make_unique<uinput::DeviceCallback>(env, callback);
257
258 std::unique_ptr<uinput::UinputDevice> d =
259 uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, port.c_str(),
260 std::move(cb));
261 return reinterpret_cast<jlong>(d.release());
262 }
263
closeUinputDevice(JNIEnv *,jclass,jlong ptr)264 static void closeUinputDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
265 uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
266 if (d != nullptr) {
267 delete d;
268 }
269 }
270
injectEvent(JNIEnv *,jclass,jlong ptr,jint type,jint code,jint value)271 static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
272 jint value) {
273 uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
274 if (d != nullptr) {
275 d->injectEvent(static_cast<uint16_t>(type), static_cast<uint16_t>(code),
276 static_cast<int32_t>(value));
277 } else {
278 ALOGE("Could not inject event, Device* is null!");
279 }
280 }
281
configure(JNIEnv * env,jclass,jint handle,jint code,jintArray rawConfigs)282 static void configure(JNIEnv* env, jclass /* clazz */, jint handle, jint code,
283 jintArray rawConfigs) {
284 std::vector<int32_t> configs = toVector(env, rawConfigs);
285 // Configure uinput device, with user specified code and value.
286 for (auto& config : configs) {
287 ::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config);
288 }
289 }
290
setAbsInfo(JNIEnv * env,jclass,jint handle,jint axisCode,jobject infoObj)291 static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCode,
292 jobject infoObj) {
293 Parcel* parcel = parcelForJavaObject(env, infoObj);
294 uinput::InputAbsInfo info;
295
296 info.readFromParcel(parcel);
297
298 struct uinput_abs_setup absSetup;
299 absSetup.code = axisCode;
300 absSetup.absinfo.maximum = info.maximum;
301 absSetup.absinfo.minimum = info.minimum;
302 absSetup.absinfo.value = info.value;
303 absSetup.absinfo.fuzz = info.fuzz;
304 absSetup.absinfo.flat = info.flat;
305 absSetup.absinfo.resolution = info.resolution;
306
307 ::ioctl(static_cast<int>(handle), UI_ABS_SETUP, &absSetup);
308 }
309
310 static JNINativeMethod sMethods[] = {
311 {"nativeOpenUinputDevice",
312 "(Ljava/lang/String;IIIIILjava/lang/String;"
313 "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
314 reinterpret_cast<void*>(openUinputDevice)},
315 {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
316 {"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
317 {"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
318 {"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
319 };
320
register_com_android_commands_uinput_Device(JNIEnv * env)321 int register_com_android_commands_uinput_Device(JNIEnv* env) {
322 jclass clazz = env->FindClass("com/android/commands/uinput/Device$DeviceCallback");
323 if (clazz == nullptr) {
324 ALOGE("Unable to find class 'DeviceCallback'");
325 return JNI_ERR;
326 }
327
328 uinput::gDeviceCallbackClassInfo.onDeviceConfigure =
329 env->GetMethodID(clazz, "onDeviceConfigure", "(I)V");
330 uinput::gDeviceCallbackClassInfo.onDeviceVibrating =
331 env->GetMethodID(clazz, "onDeviceVibrating", "(I)V");
332 uinput::gDeviceCallbackClassInfo.onDeviceError =
333 env->GetMethodID(clazz, "onDeviceError", "()V");
334 if (uinput::gDeviceCallbackClassInfo.onDeviceConfigure == nullptr ||
335 uinput::gDeviceCallbackClassInfo.onDeviceError == nullptr ||
336 uinput::gDeviceCallbackClassInfo.onDeviceVibrating == nullptr) {
337 ALOGE("Unable to obtain onDeviceConfigure or onDeviceError or onDeviceVibrating methods");
338 return JNI_ERR;
339 }
340 return jniRegisterNativeMethods(env, "com/android/commands/uinput/Device", sMethods,
341 NELEM(sMethods));
342 }
343
344 } // namespace android
345
JNI_OnLoad(JavaVM * jvm,void *)346 jint JNI_OnLoad(JavaVM* jvm, void*) {
347 JNIEnv* env = nullptr;
348 if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
349 return JNI_ERR;
350 }
351
352 if (android::register_com_android_commands_uinput_Device(env) < 0) {
353 return JNI_ERR;
354 }
355
356 return JNI_VERSION_1_6;
357 }
358