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