summaryrefslogtreecommitdiffstats
path: root/services/jni/com_android_server_UsbObserver.cpp
blob: 7c478d56956f8986d5cf8362eea083bc4450bac4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "UsbObserver"
#include "utils/Log.h"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "utils/Vector.h"

#include <usbhost/usbhost.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif

#include <stdio.h>

namespace android
{

static jmethodID method_usbCameraAdded;
static jmethodID method_usbCameraRemoved;

Vector<int> mDeviceList;

static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
    if (env->ExceptionCheck()) {
        LOGE("An exception was thrown by callback '%s'.", methodName);
        LOGE_EX(env);
        env->ExceptionClear();
    }
}

static int usb_device_added(const char *devname, void* client_data) {
    // check to see if it is a camera
    struct usb_descriptor_header* desc;
    struct usb_descriptor_iter iter;

    struct usb_device *device = usb_device_open(devname);
    if (!device) {
        LOGE("usb_device_open failed\n");
        return 0;
    }

    usb_descriptor_iter_init(device, &iter);

    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
        if (desc->bDescriptorType == USB_DT_INTERFACE) {
            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;

            if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
                interface->bInterfaceSubClass == 1 && // Still Image Capture
                interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
            {
                LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
                        usb_device_get_product_name(device));

                // interface should be followed by three endpoints
                struct usb_endpoint_descriptor *ep;
                struct usb_endpoint_descriptor *ep_in_desc = NULL;
                struct usb_endpoint_descriptor *ep_out_desc = NULL;
                struct usb_endpoint_descriptor *ep_intr_desc = NULL;
                for (int i = 0; i < 3; i++) {
                    ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
                    if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
                        LOGE("endpoints not found\n");
                        goto done;
                    }
                    if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
                        if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
                            ep_in_desc = ep;
                        else
                            ep_out_desc = ep;
                    } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
                        ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
                        ep_intr_desc = ep;
                    }
                }
                if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
                    LOGE("endpoints not found\n");
                    goto done;
                }

                // if we got here, we found a camera
                JNIEnv* env = AndroidRuntime::getJNIEnv();
                jobject thiz = (jobject)client_data;

                int id = usb_device_get_unique_id_from_name(devname);
                mDeviceList.add(id);

                env->CallVoidMethod(thiz, method_usbCameraAdded, id);
                checkAndClearExceptionFromCallback(env, __FUNCTION__);
            }
        }
    }
done:
    usb_device_close(device);
    return 0;
}

static int usb_device_removed(const char *devname, void* client_data) {
    int id = usb_device_get_unique_id_from_name(devname);

    // see if it is a device we know about
    for (int i = 0; i < mDeviceList.size(); i++) {
        if (id  == mDeviceList[i]) {
            mDeviceList.removeAt(i);

            JNIEnv* env = AndroidRuntime::getJNIEnv();
            jobject thiz = (jobject)client_data;

            env->CallVoidMethod(thiz, method_usbCameraRemoved, id);
            checkAndClearExceptionFromCallback(env, __FUNCTION__);
            break;
        }
    }
    return 0;
}

static void android_server_UsbObserver_monitorUsbHostBus(JNIEnv *env, jobject thiz)
{
    struct usb_host_context* context = usb_host_init();
    if (!context) {
        LOGE("usb_host_init failed");
        return;
    }
    // this will never return so it is safe to pass thiz directly
    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}

static JNINativeMethod method_table[] = {
    { "monitorUsbHostBus", "()V", (void*)android_server_UsbObserver_monitorUsbHostBus }
};

int register_android_server_UsbObserver(JNIEnv *env)
{
    jclass clazz = env->FindClass("com/android/server/UsbObserver");
    if (clazz == NULL) {
        LOGE("Can't find com/android/server/UsbObserver");
        return -1;
    }
    method_usbCameraAdded = env->GetMethodID(clazz, "usbCameraAdded", "(I)V");
    if (method_usbCameraAdded == NULL) {
        LOGE("Can't find usbCameraAdded");
        return -1;
    }
    method_usbCameraRemoved = env->GetMethodID(clazz, "usbCameraRemoved", "(I)V");
    if (method_usbCameraRemoved == NULL) {
        LOGE("Can't find usbCameraRemoved");
        return -1;
    }

    return jniRegisterNativeMethods(env, "com/android/server/UsbObserver",
            method_table, NELEM(method_table));
}

};