diff options
author | Mike Lockwood <lockwood@android.com> | 2010-12-30 13:39:37 -0500 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2011-01-22 15:56:09 -0800 |
commit | e7d511e148bc901ef41ac44d7b3593e5d803f72f (patch) | |
tree | 4561bf7d69a83c285c874c6b9ec038f9411d062c /core/jni | |
parent | f5426634d8228c5bc3fe968caf09cc369e5a9272 (diff) | |
download | frameworks_base-e7d511e148bc901ef41ac44d7b3593e5d803f72f.zip frameworks_base-e7d511e148bc901ef41ac44d7b3593e5d803f72f.tar.gz frameworks_base-e7d511e148bc901ef41ac44d7b3593e5d803f72f.tar.bz2 |
New APIs for USB host support:
UsbManager:
- is now a service retrievable via Context.getSystemService(Context.USB_SERVICE).
- provides support for returning a list all connected USB devices
- broadcasts ACTION_USB_DEVICE_ATTACHED and USB_DEVICE_DETACHED when devices
are added and removed from the USB host bus
UsbDevice:
- represents an attached USB device.
UsbInterface:
- represents an interface on a USB device
- devices may have multiple interfaces if they provide multiple
sets of functionality (for example, android phones typically have interfaces
for both USB mass storage and adb)
UsbEndpoint:
- represents an endpoint on a USB interface
- endpoints are used for sending or receiving data
(only in one or the other direction)
UsbRequest:
- encapsulates a send or receive request to be sent over an endpoint
Change-Id: Ieef3e434c62760770ea839070cf5eba1a705967a
Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 3 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_hardware_UsbDevice.cpp | 239 | ||||
-rw-r--r-- | core/jni/android_hardware_UsbEndpoint.cpp | 124 | ||||
-rw-r--r-- | core/jni/android_hardware_UsbRequest.cpp | 217 |
5 files changed, 587 insertions, 0 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c635b39..d1e7e5c 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -124,6 +124,8 @@ LOCAL_SRC_FILES:= \ android_media_ToneGenerator.cpp \ android_hardware_Camera.cpp \ android_hardware_SensorManager.cpp \ + android_hardware_UsbDevice.cpp \ + android_hardware_UsbRequest.cpp \ android_debug_JNITest.cpp \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ @@ -202,6 +204,7 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg \ libnfc_ndef \ + libusbhost \ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 342b884..c1c6c91 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -77,6 +77,8 @@ extern int register_android_opengl_jni_GLES20(JNIEnv* env); extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_SensorManager(JNIEnv *env); +extern int register_android_hardware_UsbDevice(JNIEnv *env); +extern int register_android_hardware_UsbRequest(JNIEnv *env); extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); @@ -1275,6 +1277,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_SensorManager), + REG_JNI(register_android_hardware_UsbDevice), + REG_JNI(register_android_hardware_UsbRequest), REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioTrack), diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp new file mode 100644 index 0000000..22cf387 --- /dev/null +++ b/core/jni/android_hardware_UsbDevice.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 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 "UsbDeviceJNI" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +using namespace android; + +static jfieldID field_context; + +struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice) +{ + return (struct usb_device*)env->GetIntField(javaDevice, field_context); +} + +// in android_hardware_UsbEndpoint.cpp +extern struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint); + +static jboolean +android_hardware_UsbDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, + jobject fileDescriptor) +{ + int fd = getParcelFileDescriptorFD(env, fileDescriptor); + // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy + fd = dup(fd); + if (fd < 0) + return false; + + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + struct usb_device* device = usb_device_new(deviceNameStr, fd); + if (device) { + env->SetIntField(thiz, field_context, (int)device); + } else { + LOGE("usb_device_open failed for %s", deviceNameStr); + close(fd); + } + + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + return (device != NULL); +} + +static void +android_hardware_UsbDevice_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_device* device = get_device_from_object(env, thiz); + if (device) { + usb_device_close(device); + env->SetIntField(thiz, field_context, 0); + } +} + +static jint +android_hardware_UsbDevice_get_fd(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_get_fd"); + return -1; + } + return usb_device_get_fd(device); +} + +static jint +android_hardware_UsbDevice_claim_interface(JNIEnv *env, jobject thiz, int interfaceID, jboolean force) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_claim_interface"); + return -1; + } + + int ret = usb_device_claim_interface(device, interfaceID); + if (ret && force && errno == EBUSY) { + // disconnect kernel driver and try again + usb_device_connect_kernel_driver(device, interfaceID, false); + ret = usb_device_claim_interface(device, interfaceID); + } + return ret; +} + +static jint +android_hardware_UsbDevice_release_interface(JNIEnv *env, jobject thiz, int interfaceID) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_release_interface"); + return -1; + } + int ret = usb_device_release_interface(device, interfaceID); + if (ret == 0) { + // allow kernel to reconnect its driver + usb_device_connect_kernel_driver(device, interfaceID, true); + } + return ret; +} + +static jint +android_hardware_UsbDevice_control_request(JNIEnv *env, jobject thiz, + jint requestType, jint request, jint value, jint index, + jbyteArray buffer, jint length) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_control_request"); + return -1; + } + + jbyte* bufferBytes = NULL; + if (buffer) { + if (env->GetArrayLength(buffer) < length) { + env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL); + return -1; + } + bufferBytes = env->GetByteArrayElements(buffer, 0); + } + + jint result = usb_device_send_control(device, requestType, request, + value, index, length, bufferBytes); + + if (bufferBytes) + env->ReleaseByteArrayElements(buffer, bufferBytes, 0); + + return result; +} + +static jobject +android_hardware_UsbDevice_request_wait(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_request_wait"); + return NULL; + } + + struct usb_request* request = usb_request_wait(device); + if (request) + return (jobject)request->client_data; + else + return NULL; +} + +static jstring +android_hardware_UsbDevice_get_serial(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_request_wait"); + return NULL; + } + char* serial = usb_device_get_serial(device); + if (!serial) + return NULL; + jstring result = env->NewStringUTF(serial); + free(serial); + return result; +} + +static jint +android_hardware_UsbDevice_get_device_id(JNIEnv *env, jobject clazz, jstring name) +{ + const char *nameStr = env->GetStringUTFChars(name, NULL); + int id = usb_device_get_unique_id_from_name(nameStr); + env->ReleaseStringUTFChars(name, nameStr); + return id; +} + +static jstring +android_hardware_UsbDevice_get_device_name(JNIEnv *env, jobject clazz, jint id) +{ + char* name = usb_device_get_name_from_unique_id(id); + jstring result = env->NewStringUTF(name); + free(name); + return result; +} + +static JNINativeMethod method_table[] = { + {"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z", + (void *)android_hardware_UsbDevice_open}, + {"native_close", "()V", (void *)android_hardware_UsbDevice_close}, + {"native_get_fd", "()I", (void *)android_hardware_UsbDevice_get_fd}, + {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDevice_claim_interface}, + {"native_release_interface","(I)Z", (void *)android_hardware_UsbDevice_release_interface}, + {"native_control_request", "(IIII[BI)I", + (void *)android_hardware_UsbDevice_control_request}, + {"native_request_wait", "()Landroid/hardware/UsbRequest;", + (void *)android_hardware_UsbDevice_request_wait}, + { "native_get_serial", "()Ljava/lang/String;", + (void*)android_hardware_UsbDevice_get_serial }, + + // static methods + { "native_get_device_id", "(Ljava/lang/String;)I", + (void*)android_hardware_UsbDevice_get_device_id }, + { "native_get_device_name", "(I)Ljava/lang/String;", + (void*)android_hardware_UsbDevice_get_device_name }, +}; + +int register_android_hardware_UsbDevice(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbDevice"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbDevice"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbDevice.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbDevice", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_hardware_UsbEndpoint.cpp b/core/jni/android_hardware_UsbEndpoint.cpp new file mode 100644 index 0000000..00c8235 --- /dev/null +++ b/core/jni/android_hardware_UsbEndpoint.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2010 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 "UsbEndpoint" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> + +using namespace android; + +static jfieldID field_context; +static jfieldID field_address; +static jfieldID field_attributes; +static jfieldID field_max_packet_size; +static jfieldID field_interval; + +struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint) +{ + return (struct usb_endpoint*)env->GetIntField(javaEndpoint, field_context); +} + +// in android_hardware_UsbDevice.cpp +extern struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice); + +static jboolean +android_hardware_UsbEndpoint_init(JNIEnv *env, jobject thiz, jobject javaDevice) +{ + LOGD("open\n"); + + struct usb_device* device = get_device_from_object(env, javaDevice); + if (!device) { + LOGE("device null in native_init"); + return false; + } + + // construct an endpoint descriptor from the Java object fields + struct usb_endpoint_descriptor desc; + desc.bLength = USB_DT_ENDPOINT_SIZE; + desc.bDescriptorType = USB_DT_ENDPOINT; + desc.bEndpointAddress = env->GetIntField(thiz, field_address); + desc.bmAttributes = env->GetIntField(thiz, field_attributes); + desc.wMaxPacketSize = env->GetIntField(thiz, field_max_packet_size); + desc.bInterval = env->GetIntField(thiz, field_interval); + + struct usb_endpoint* endpoint = usb_endpoint_init(device, &desc); + if (endpoint) + env->SetIntField(thiz, field_context, (int)device); + return (endpoint != NULL); +} + +static void +android_hardware_UsbEndpoint_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_endpoint* endpoint = get_endpoint_from_object(env, thiz); + if (endpoint) { + usb_endpoint_close(endpoint); + env->SetIntField(thiz, field_context, 0); + } +} + +static JNINativeMethod method_table[] = { + {"native_init", "(Landroid/hardware/UsbDevice;)Z", + (void *)android_hardware_UsbEndpoint_init}, + {"native_close", "()V", (void *)android_hardware_UsbEndpoint_close}, +}; + +int register_android_hardware_UsbEndpoint(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbEndpoint"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbEndpoint"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbEndpoint.mNativeContext"); + return -1; + } + field_address = env->GetFieldID(clazz, "mAddress", "I"); + if (field_address == NULL) { + LOGE("Can't find UsbEndpoint.mAddress"); + return -1; + } + field_attributes = env->GetFieldID(clazz, "mAttributes", "I"); + if (field_attributes == NULL) { + LOGE("Can't find UsbEndpoint.mAttributes"); + return -1; + } + field_max_packet_size = env->GetFieldID(clazz, "mMaxPacketSize", "I"); + if (field_max_packet_size == NULL) { + LOGE("Can't find UsbEndpoint.mMaxPacketSize"); + return -1; + } + field_interval = env->GetFieldID(clazz, "mInterval", "I"); + if (field_interval == NULL) { + LOGE("Can't find UsbEndpoint.mInterval"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbEndpoint", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp new file mode 100644 index 0000000..710afae --- /dev/null +++ b/core/jni/android_hardware_UsbRequest.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 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 "UsbRequestJNI" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> + +using namespace android; + +static jfieldID field_context; + +struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request) +{ + return (struct usb_request*)env->GetIntField(java_request, field_context); +} + +// in android_hardware_UsbDevice.cpp +extern struct usb_device* get_device_from_object(JNIEnv* env, jobject java_device); + +static jboolean +android_hardware_UsbRequest_init(JNIEnv *env, jobject thiz, jobject java_device, + jint ep_address, jint ep_attributes, jint ep_max_packet_size, jint ep_interval) +{ + LOGD("init\n"); + + struct usb_device* device = get_device_from_object(env, java_device); + if (!device) { + LOGE("device null in native_init"); + return false; + } + + // construct an endpoint descriptor from the Java object fields + struct usb_endpoint_descriptor desc; + desc.bLength = USB_DT_ENDPOINT_SIZE; + desc.bDescriptorType = USB_DT_ENDPOINT; + desc.bEndpointAddress = ep_address; + desc.bmAttributes = ep_attributes; + desc.wMaxPacketSize = ep_max_packet_size; + desc.bInterval = ep_interval; + + struct usb_request* request = usb_request_new(device, &desc); + if (request) + env->SetIntField(thiz, field_context, (int)request); + return (request != NULL); +} + +static void +android_hardware_UsbRequest_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_request* request = get_request_from_object(env, thiz); + if (request) { + usb_request_free(request); + env->SetIntField(thiz, field_context, 0); + } +} + +static jboolean +android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz, + jbyteArray buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_queue"); + return false; + } + + if (buffer && length) { + request->buffer = malloc(length); + if (!request->buffer) + return false; + if (out) { + // copy data from Java buffer to native buffer + env->GetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer); + } + } else { + request->buffer = NULL; + } + request->buffer_length = length; + + if (usb_request_queue(request)) { + if (request->buffer) { + // free our buffer if usb_request_queue fails + free(request->buffer); + request->buffer = NULL; + } + return false; + } else { + // save a reference to ourselves so UsbDevice.waitRequest() can find us + request->client_data = (void *)env->NewGlobalRef(thiz); + return true; + } +} + +static void +android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz, + jbyteArray buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_dequeue"); + return; + } + + if (buffer && length && request->buffer && !out) { + // copy data from native buffer to Java buffer + env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer); + } + free(request->buffer); + env->DeleteGlobalRef((jobject)request->client_data); + +} + +static jboolean +android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz, + jobject buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_queue"); + return false; + } + + if (buffer && length) { + request->buffer = env->GetDirectBufferAddress(buffer); + if (!request->buffer) + return false; + } else { + request->buffer = NULL; + } + request->buffer_length = length; + + if (usb_request_queue(request)) { + request->buffer = NULL; + return false; + } else { + // save a reference to ourselves so UsbDevice.waitRequest() can find us + // we also need this to make sure our native buffer is not deallocated + // while IO is active + request->client_data = (void *)env->NewGlobalRef(thiz); + return true; + } +} + +static void +android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_dequeue"); + return; + } + // all we need to do is delete our global ref + env->DeleteGlobalRef((jobject)request->client_data); +} + +static jboolean +android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_cancel"); + return false; + } + return (usb_request_cancel(request) == 0); +} + +static JNINativeMethod method_table[] = { + {"native_init", "(Landroid/hardware/UsbDevice;IIII)Z", + (void *)android_hardware_UsbRequest_init}, + {"native_close", "()V", (void *)android_hardware_UsbRequest_close}, + {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array}, + {"native_dequeue_array", "([BIZ)V", (void *)android_hardware_UsbRequest_dequeue_array}, + {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z", + (void *)android_hardware_UsbRequest_queue_direct}, + {"native_dequeue_direct", "()V", (void *)android_hardware_UsbRequest_dequeue_direct}, + {"native_cancel", "()Z", (void *)android_hardware_UsbRequest_cancel}, +}; + +int register_android_hardware_UsbRequest(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbRequest"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbRequest"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbRequest.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbRequest", + method_table, NELEM(method_table)); +} + |