1 /*
2  * Copyright (C) 2010 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 "UsbHostManagerJNI"
18 #include "utils/Log.h"
19 
20 #include "jni.h"
21 #include <nativehelper/JNIPlatformHelp.h>
22 #include "android_runtime/AndroidRuntime.h"
23 #include "android_runtime/Log.h"
24 
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <sys/ioctl.h>
30 
31 #include <usbhost/usbhost.h>
32 
33 namespace android
34 {
35 
36 static struct parcel_file_descriptor_offsets_t
37 {
38     jclass mClass;
39     jmethodID mConstructor;
40 } gParcelFileDescriptorOffsets;
41 
42 static jmethodID method_usbDeviceAdded;
43 static jmethodID method_usbDeviceRemoved;
44 
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)45 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
46     if (env->ExceptionCheck()) {
47         ALOGE("An exception was thrown by callback '%s'.", methodName);
48         LOGE_EX(env);
49         env->ExceptionClear();
50     }
51 }
52 
usb_device_added(const char * devAddress,void * clientData)53 static int usb_device_added(const char *devAddress, void* clientData) {
54     struct usb_device *device = usb_device_open(devAddress);
55     if (!device) {
56         ALOGE("usb_device_open failed\n");
57         return 0;
58     }
59 
60     const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
61     int classID = deviceDesc->bDeviceClass;
62     int subClassID = deviceDesc->bDeviceSubClass;
63 
64     // get the raw descriptors
65     int numBytes = usb_device_get_descriptors_length(device);
66     if (numBytes > 0) {
67         JNIEnv* env = AndroidRuntime::getJNIEnv();
68         jobject thiz = (jobject)clientData;
69         jstring deviceAddress = env->NewStringUTF(devAddress);
70 
71         jbyteArray descriptorsArray = env->NewByteArray(numBytes);
72         const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
73         env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
74 
75         env->CallBooleanMethod(thiz, method_usbDeviceAdded,
76                 deviceAddress, classID, subClassID, descriptorsArray);
77 
78         env->DeleteLocalRef(descriptorsArray);
79         env->DeleteLocalRef(deviceAddress);
80 
81         checkAndClearExceptionFromCallback(env, __FUNCTION__);
82     } else {
83         // TODO return an error code here?
84         ALOGE("error reading descriptors\n");
85     }
86 
87     usb_device_close(device);
88 
89     return 0;
90 }
91 
usb_device_removed(const char * devAddress,void * clientData)92 static int usb_device_removed(const char *devAddress, void* clientData) {
93     JNIEnv* env = AndroidRuntime::getJNIEnv();
94     jobject thiz = (jobject)clientData;
95 
96     jstring deviceAddress = env->NewStringUTF(devAddress);
97     env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
98     env->DeleteLocalRef(deviceAddress);
99     checkAndClearExceptionFromCallback(env, __FUNCTION__);
100     return 0;
101 }
102 
android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *,jobject thiz)103 static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
104 {
105     struct usb_host_context* context = usb_host_init();
106     if (!context) {
107         ALOGE("usb_host_init failed");
108         return;
109     }
110     // this will never return so it is safe to pass thiz directly
111     usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
112 }
113 
android_server_UsbHostManager_openDevice(JNIEnv * env,jobject,jstring deviceAddress)114 static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
115                                                         jstring deviceAddress)
116 {
117     const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
118     struct usb_device* device = usb_device_open(deviceAddressStr);
119     env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
120 
121     if (!device)
122         return NULL;
123 
124     int fd = usb_device_get_fd(device);
125     if (fd < 0) {
126         usb_device_close(device);
127         return NULL;
128     }
129     int newFD = dup(fd);
130     usb_device_close(device);
131 
132     jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
133     if (fileDescriptor == NULL) {
134         close(newFD);
135         return NULL;
136     }
137     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
138         gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
139 }
140 
141 static const JNINativeMethod method_table[] = {
142     { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
143     { "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
144                                   (void*)android_server_UsbHostManager_openDevice },
145 };
146 
register_android_server_UsbHostManager(JNIEnv * env)147 int register_android_server_UsbHostManager(JNIEnv *env)
148 {
149     jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager");
150     if (clazz == NULL) {
151         ALOGE("Can't find com/android/server/usb/UsbHostManager");
152         return -1;
153     }
154     method_usbDeviceAdded =
155             env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
156     if (method_usbDeviceAdded == NULL) {
157         ALOGE("Can't find beginUsbDeviceAdded");
158         return -1;
159     }
160     method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
161             "(Ljava/lang/String;)V");
162     if (method_usbDeviceRemoved == NULL) {
163         ALOGE("Can't find usbDeviceRemoved");
164         return -1;
165     }
166 
167     clazz = env->FindClass("android/os/ParcelFileDescriptor");
168     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
169     gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
170     gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
171             "(Ljava/io/FileDescriptor;)V");
172     LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
173                  "Unable to find constructor for android.os.ParcelFileDescriptor");
174 
175     return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager",
176             method_table, NELEM(method_table));
177 }
178 
179 };
180