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 "JNIHelp.h"
22 #include "android_runtime/AndroidRuntime.h"
23 #include "android_runtime/Log.h"
24
25 #include <usbhost/usbhost.h>
26
27 #include <stdio.h>
28 #include <asm/byteorder.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <sys/ioctl.h>
33
34 namespace android
35 {
36
37 static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
38
39 static struct parcel_file_descriptor_offsets_t
40 {
41 jclass mClass;
42 jmethodID mConstructor;
43 } gParcelFileDescriptorOffsets;
44
45 static jmethodID method_beginUsbDeviceAdded;
46 static jmethodID method_addUsbConfiguration;
47 static jmethodID method_addUsbInterface;
48 static jmethodID method_addUsbEndpoint;
49 static jmethodID method_endUsbDeviceAdded;
50 static jmethodID method_usbDeviceRemoved;
51
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)52 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
53 if (env->ExceptionCheck()) {
54 ALOGE("An exception was thrown by callback '%s'.", methodName);
55 LOGE_EX(env);
56 env->ExceptionClear();
57 }
58 }
59
usb_device_added(const char * devname,void * client_data)60 static int usb_device_added(const char *devname, void* client_data) {
61 struct usb_descriptor_header* desc;
62 struct usb_descriptor_iter iter;
63
64 struct usb_device *device = usb_device_open(devname);
65 if (!device) {
66 ALOGE("usb_device_open failed\n");
67 return 0;
68 }
69
70 JNIEnv* env = AndroidRuntime::getJNIEnv();
71 jobject thiz = (jobject)client_data;
72 const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
73
74 char *manufacturer = usb_device_get_manufacturer_name(device,
75 USB_CONTROL_TRANSFER_TIMEOUT_MS);
76 char *product = usb_device_get_product_name(device,
77 USB_CONTROL_TRANSFER_TIMEOUT_MS);
78 int version = usb_device_get_version(device);
79 char *serial = usb_device_get_serial(device,
80 USB_CONTROL_TRANSFER_TIMEOUT_MS);
81
82 jstring deviceName = env->NewStringUTF(devname);
83 jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
84 jstring productName = AndroidRuntime::NewStringLatin1(env, product);
85 jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
86
87 jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
88 deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
89 deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
90 manufacturerName, productName, version, serialNumber);
91
92 env->DeleteLocalRef(serialNumber);
93 env->DeleteLocalRef(productName);
94 env->DeleteLocalRef(manufacturerName);
95 env->DeleteLocalRef(deviceName);
96 free(manufacturer);
97 free(product);
98 free(serial);
99
100 if (!result) goto fail;
101
102 usb_descriptor_iter_init(device, &iter);
103
104 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
105 if (desc->bDescriptorType == USB_DT_CONFIG) {
106 struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
107 char *name = usb_device_get_string(device, config->iConfiguration,
108 USB_CONTROL_TRANSFER_TIMEOUT_MS);
109 jstring configName = AndroidRuntime::NewStringLatin1(env, name);
110
111 env->CallVoidMethod(thiz, method_addUsbConfiguration,
112 config->bConfigurationValue, configName, config->bmAttributes,
113 config->bMaxPower);
114
115 env->DeleteLocalRef(configName);
116 free(name);
117 } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
118 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
119 char *name = usb_device_get_string(device, interface->iInterface,
120 USB_CONTROL_TRANSFER_TIMEOUT_MS);
121 jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
122
123 env->CallVoidMethod(thiz, method_addUsbInterface,
124 interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
125 interface->bInterfaceClass, interface->bInterfaceSubClass,
126 interface->bInterfaceProtocol);
127
128 env->DeleteLocalRef(interfaceName);
129 free(name);
130 } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
131 struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
132
133 env->CallVoidMethod(thiz, method_addUsbEndpoint,
134 endpoint->bEndpointAddress, endpoint->bmAttributes,
135 __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
136 }
137 }
138
139 env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
140
141 fail:
142 usb_device_close(device);
143 checkAndClearExceptionFromCallback(env, __FUNCTION__);
144
145 return 0;
146 }
147
usb_device_removed(const char * devname,void * client_data)148 static int usb_device_removed(const char *devname, void* client_data) {
149 JNIEnv* env = AndroidRuntime::getJNIEnv();
150 jobject thiz = (jobject)client_data;
151
152 jstring deviceName = env->NewStringUTF(devname);
153 env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
154 env->DeleteLocalRef(deviceName);
155 checkAndClearExceptionFromCallback(env, __FUNCTION__);
156 return 0;
157 }
158
android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *,jobject thiz)159 static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
160 {
161 struct usb_host_context* context = usb_host_init();
162 if (!context) {
163 ALOGE("usb_host_init failed");
164 return;
165 }
166 // this will never return so it is safe to pass thiz directly
167 usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
168 }
169
android_server_UsbHostManager_openDevice(JNIEnv * env,jobject,jstring deviceName)170 static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
171 jstring deviceName)
172 {
173 const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
174 struct usb_device* device = usb_device_open(deviceNameStr);
175 env->ReleaseStringUTFChars(deviceName, deviceNameStr);
176
177 if (!device)
178 return NULL;
179
180 int fd = usb_device_get_fd(device);
181 if (fd < 0) {
182 usb_device_close(device);
183 return NULL;
184 }
185 int newFD = dup(fd);
186 usb_device_close(device);
187
188 jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
189 if (fileDescriptor == NULL) {
190 return NULL;
191 }
192 return env->NewObject(gParcelFileDescriptorOffsets.mClass,
193 gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
194 }
195
196 static const JNINativeMethod method_table[] = {
197 { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
198 { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
199 (void*)android_server_UsbHostManager_openDevice },
200 };
201
register_android_server_UsbHostManager(JNIEnv * env)202 int register_android_server_UsbHostManager(JNIEnv *env)
203 {
204 jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager");
205 if (clazz == NULL) {
206 ALOGE("Can't find com/android/server/usb/UsbHostManager");
207 return -1;
208 }
209 method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
210 "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z");
211 if (method_beginUsbDeviceAdded == NULL) {
212 ALOGE("Can't find beginUsbDeviceAdded");
213 return -1;
214 }
215 method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
216 "(ILjava/lang/String;II)V");
217 if (method_addUsbConfiguration == NULL) {
218 ALOGE("Can't find addUsbConfiguration");
219 return -1;
220 }
221 method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
222 "(ILjava/lang/String;IIII)V");
223 if (method_addUsbInterface == NULL) {
224 ALOGE("Can't find addUsbInterface");
225 return -1;
226 }
227 method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
228 if (method_addUsbEndpoint == NULL) {
229 ALOGE("Can't find addUsbEndpoint");
230 return -1;
231 }
232 method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
233 if (method_endUsbDeviceAdded == NULL) {
234 ALOGE("Can't find endUsbDeviceAdded");
235 return -1;
236 }
237 method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
238 "(Ljava/lang/String;)V");
239 if (method_usbDeviceRemoved == NULL) {
240 ALOGE("Can't find usbDeviceRemoved");
241 return -1;
242 }
243
244 clazz = env->FindClass("android/os/ParcelFileDescriptor");
245 LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
246 gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
247 gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
248 "(Ljava/io/FileDescriptor;)V");
249 LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
250 "Unable to find constructor for android.os.ParcelFileDescriptor");
251
252 return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager",
253 method_table, NELEM(method_table));
254 }
255
256 };
257