• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,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                                                  std::unique_ptr<DeviceCallback> callback) {
103     android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
104     if (!fd.ok()) {
105         ALOGE("Failed to open uinput: %s", strerror(errno));
106         return nullptr;
107     }
108 
109     int32_t version;
110     ::ioctl(fd, UI_GET_VERSION, &version);
111     if (version < 5) {
112         ALOGE("Kernel version %d older than 5 is not supported", version);
113         return nullptr;
114     }
115 
116     struct uinput_setup setupDescriptor;
117     memset(&setupDescriptor, 0, sizeof(setupDescriptor));
118     strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
119     setupDescriptor.id.version = 1;
120     setupDescriptor.id.bustype = bus;
121     setupDescriptor.id.vendor = vid;
122     setupDescriptor.id.product = pid;
123     setupDescriptor.ff_effects_max = ffEffectsMax;
124 
125     // Request device configuration.
126     callback->onDeviceConfigure(fd.get());
127 
128     // register the input device
129     if (::ioctl(fd, UI_DEV_SETUP, &setupDescriptor)) {
130         ALOGE("UI_DEV_SETUP ioctl failed on fd %d: %s.", fd.get(), strerror(errno));
131         return nullptr;
132     }
133 
134     if (::ioctl(fd, UI_DEV_CREATE) != 0) {
135         ALOGE("Unable to create uinput device: %s.", strerror(errno));
136         return nullptr;
137     }
138 
139     // using 'new' to access non-public constructor
140     return std::unique_ptr<UinputDevice>(new UinputDevice(id, std::move(fd), std::move(callback)));
141 }
142 
UinputDevice(int32_t id,android::base::unique_fd fd,std::unique_ptr<DeviceCallback> callback)143 UinputDevice::UinputDevice(int32_t id, android::base::unique_fd fd,
144                            std::unique_ptr<DeviceCallback> callback)
145       : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
146     ALooper* aLooper = ALooper_forThread();
147     if (aLooper == nullptr) {
148         ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
149         aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
150     }
151     ALooper_addFd(
152             aLooper, mFd, 0, ALOOPER_EVENT_INPUT,
153             [](int, int events, void* data) {
154                 UinputDevice* d = reinterpret_cast<UinputDevice*>(data);
155                 return d->handleEvents(events);
156             },
157             reinterpret_cast<void*>(this));
158     ALOGI("uinput device %d created: version = %d, fd = %d", mId, UINPUT_VERSION, mFd.get());
159 }
160 
~UinputDevice()161 UinputDevice::~UinputDevice() {
162     ::ioctl(mFd, UI_DEV_DESTROY);
163 }
164 
injectEvent(uint16_t type,uint16_t code,int32_t value)165 void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
166     struct input_event event = {};
167     event.type = type;
168     event.code = code;
169     event.value = value;
170     timespec ts;
171     clock_gettime(CLOCK_MONOTONIC, &ts);
172     TIMESPEC_TO_TIMEVAL(&event.time, &ts);
173 
174     if (::write(mFd, &event, sizeof(input_event)) < 0) {
175         ALOGE("Could not write event %" PRIu16 " %" PRIu16 " with value %" PRId32 " : %s", type,
176               code, value, strerror(errno));
177     }
178 }
179 
handleEvents(int events)180 int UinputDevice::handleEvents(int events) {
181     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
182         ALOGE("uinput node was closed or an error occurred. events=0x%x", events);
183         mDeviceCallback->onDeviceError();
184         return 0;
185     }
186     struct input_event ev;
187     ssize_t ret = ::read(mFd, &ev, sizeof(ev));
188     if (ret < 0) {
189         ALOGE("Failed to read from uinput node: %s", strerror(errno));
190         mDeviceCallback->onDeviceError();
191         return 0;
192     }
193 
194     switch (ev.type) {
195         case EV_UINPUT: {
196             if (ev.code == UI_FF_UPLOAD) {
197                 struct uinput_ff_upload ff_upload;
198                 ff_upload.request_id = ev.value;
199                 ::ioctl(mFd, UI_BEGIN_FF_UPLOAD, &ff_upload);
200                 ff_upload.retval = 0;
201                 ::ioctl(mFd, UI_END_FF_UPLOAD, &ff_upload);
202             } else if (ev.code == UI_FF_ERASE) {
203                 struct uinput_ff_erase ff_erase;
204                 ff_erase.request_id = ev.value;
205                 ::ioctl(mFd, UI_BEGIN_FF_ERASE, &ff_erase);
206                 ff_erase.retval = 0;
207                 ::ioctl(mFd, UI_END_FF_ERASE, &ff_erase);
208             }
209             break;
210         }
211         case EV_FF: {
212             ALOGI("EV_FF effect = %d value = %d", ev.code, ev.value);
213             mDeviceCallback->onDeviceVibrating(ev.value);
214             break;
215         }
216         default: {
217             ALOGI("Unhandled event type: %" PRIu32, ev.type);
218             break;
219         }
220     }
221 
222     return 1;
223 }
224 
225 } // namespace uinput
226 
toVector(JNIEnv * env,jintArray javaArray)227 std::vector<int32_t> toVector(JNIEnv* env, jintArray javaArray) {
228     std::vector<int32_t> data;
229     if (javaArray == nullptr) {
230         return data;
231     }
232 
233     ScopedIntArrayRO scopedArray(env, javaArray);
234     size_t size = scopedArray.size();
235     data.reserve(size);
236     for (size_t i = 0; i < size; i++) {
237         data.push_back(static_cast<int32_t>(scopedArray[i]));
238     }
239     return data;
240 }
241 
openUinputDevice(JNIEnv * env,jclass,jstring rawName,jint id,jint vid,jint pid,jint bus,jint ffEffectsMax,jobject callback)242 static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
243                               jint pid, jint bus, jint ffEffectsMax, jobject callback) {
244     ScopedUtfChars name(env, rawName);
245     if (name.c_str() == nullptr) {
246         return 0;
247     }
248 
249     std::unique_ptr<uinput::DeviceCallback> cb =
250             std::make_unique<uinput::DeviceCallback>(env, callback);
251 
252     std::unique_ptr<uinput::UinputDevice> d =
253             uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax,
254                                        std::move(cb));
255     return reinterpret_cast<jlong>(d.release());
256 }
257 
closeUinputDevice(JNIEnv *,jclass,jlong ptr)258 static void closeUinputDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
259     uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
260     if (d != nullptr) {
261         delete d;
262     }
263 }
264 
injectEvent(JNIEnv *,jclass,jlong ptr,jint type,jint code,jint value)265 static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
266                         jint value) {
267     uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
268     if (d != nullptr) {
269         d->injectEvent(static_cast<uint16_t>(type), static_cast<uint16_t>(code),
270                        static_cast<int32_t>(value));
271     } else {
272         ALOGE("Could not inject event, Device* is null!");
273     }
274 }
275 
configure(JNIEnv * env,jclass,jint handle,jint code,jintArray rawConfigs)276 static void configure(JNIEnv* env, jclass /* clazz */, jint handle, jint code,
277                       jintArray rawConfigs) {
278     std::vector<int32_t> configs = toVector(env, rawConfigs);
279     // Configure uinput device, with user specified code and value.
280     for (auto& config : configs) {
281         ::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config);
282     }
283 }
284 
setAbsInfo(JNIEnv * env,jclass,jint handle,jint axisCode,jobject infoObj)285 static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCode,
286                        jobject infoObj) {
287     Parcel* parcel = parcelForJavaObject(env, infoObj);
288     uinput::InputAbsInfo info;
289 
290     info.readFromParcel(parcel);
291 
292     struct uinput_abs_setup absSetup;
293     absSetup.code = axisCode;
294     absSetup.absinfo.maximum = info.maximum;
295     absSetup.absinfo.minimum = info.minimum;
296     absSetup.absinfo.value = info.value;
297     absSetup.absinfo.fuzz = info.fuzz;
298     absSetup.absinfo.flat = info.flat;
299     absSetup.absinfo.resolution = info.resolution;
300 
301     ::ioctl(static_cast<int>(handle), UI_ABS_SETUP, &absSetup);
302 }
303 
304 static JNINativeMethod sMethods[] = {
305         {"nativeOpenUinputDevice",
306          "(Ljava/lang/String;IIIII"
307          "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
308          reinterpret_cast<void*>(openUinputDevice)},
309         {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
310         {"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
311         {"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
312         {"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
313 };
314 
register_com_android_commands_uinput_Device(JNIEnv * env)315 int register_com_android_commands_uinput_Device(JNIEnv* env) {
316     jclass clazz = env->FindClass("com/android/commands/uinput/Device$DeviceCallback");
317     if (clazz == nullptr) {
318         ALOGE("Unable to find class 'DeviceCallback'");
319         return JNI_ERR;
320     }
321 
322     uinput::gDeviceCallbackClassInfo.onDeviceConfigure =
323             env->GetMethodID(clazz, "onDeviceConfigure", "(I)V");
324     uinput::gDeviceCallbackClassInfo.onDeviceVibrating =
325             env->GetMethodID(clazz, "onDeviceVibrating", "(I)V");
326     uinput::gDeviceCallbackClassInfo.onDeviceError =
327             env->GetMethodID(clazz, "onDeviceError", "()V");
328     if (uinput::gDeviceCallbackClassInfo.onDeviceConfigure == nullptr ||
329         uinput::gDeviceCallbackClassInfo.onDeviceError == nullptr ||
330         uinput::gDeviceCallbackClassInfo.onDeviceVibrating == nullptr) {
331         ALOGE("Unable to obtain onDeviceConfigure or onDeviceError or onDeviceVibrating methods");
332         return JNI_ERR;
333     }
334     return jniRegisterNativeMethods(env, "com/android/commands/uinput/Device", sMethods,
335                                     NELEM(sMethods));
336 }
337 
338 } // namespace android
339 
JNI_OnLoad(JavaVM * jvm,void *)340 jint JNI_OnLoad(JavaVM* jvm, void*) {
341     JNIEnv* env = nullptr;
342     if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
343         return JNI_ERR;
344     }
345 
346     if (android::register_com_android_commands_uinput_Device(env) < 0) {
347         return JNI_ERR;
348     }
349 
350     return JNI_VERSION_1_6;
351 }
352