Add support for USB devices with multiple configurations and alternate interfaces
Added UsbConfiguration class, as well as accessors to UsbDevice to get configuration list Added methods to UsbDeviceConnection to select configurations and alternate interfaces. Also added accessors for USB descriptor name strings and fixed some memory leaks in the JNI code. Bug: 12425052 Change-Id: Idb990f4d5c054a8cb997eb3f440f6da9f83bce05
This commit is contained in:
@@ -11826,6 +11826,20 @@ package android.hardware.usb {
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
public class UsbConfiguration implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public int getAttributes();
|
||||
method public int getId();
|
||||
method public android.hardware.usb.UsbInterface getInterface(int);
|
||||
method public int getInterfaceCount();
|
||||
method public int getMaxPower();
|
||||
method public java.lang.String getName();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int ATTR_REMOTE_WAKEUP_MASK = 32; // 0x20
|
||||
field public static final int ATTR_SELF_POWERED_MASK = 64; // 0x40
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
public final class UsbConstants {
|
||||
ctor public UsbConstants();
|
||||
field public static final int USB_CLASS_APP_SPEC = 254; // 0xfe
|
||||
@@ -11865,6 +11879,8 @@ package android.hardware.usb {
|
||||
|
||||
public class UsbDevice implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public android.hardware.usb.UsbConfiguration getConfiguration(int);
|
||||
method public int getConfigurationCount();
|
||||
method public int getDeviceClass();
|
||||
method public int getDeviceId();
|
||||
method public static int getDeviceId(java.lang.String);
|
||||
@@ -11895,6 +11911,8 @@ package android.hardware.usb {
|
||||
method public java.lang.String getSerial();
|
||||
method public boolean releaseInterface(android.hardware.usb.UsbInterface);
|
||||
method public android.hardware.usb.UsbRequest requestWait();
|
||||
method public boolean setConfiguration(android.hardware.usb.UsbConfiguration);
|
||||
method public boolean setInterface(android.hardware.usb.UsbInterface);
|
||||
}
|
||||
|
||||
public class UsbEndpoint implements android.os.Parcelable {
|
||||
@@ -11912,12 +11930,14 @@ package android.hardware.usb {
|
||||
|
||||
public class UsbInterface implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public int getAlternateSetting();
|
||||
method public android.hardware.usb.UsbEndpoint getEndpoint(int);
|
||||
method public int getEndpointCount();
|
||||
method public int getId();
|
||||
method public int getInterfaceClass();
|
||||
method public int getInterfaceProtocol();
|
||||
method public int getInterfaceSubclass();
|
||||
method public java.lang.String getName();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
178
core/java/android/hardware/usb/UsbConfiguration.java
Normal file
178
core/java/android/hardware/usb/UsbConfiguration.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A class representing a configuration on a {@link UsbDevice}.
|
||||
* A USB configuration can have one or more interfaces, each one providing a different
|
||||
* piece of functionality, separate from the other interfaces.
|
||||
* An interface will have one or more {@link UsbEndpoint}s, which are the
|
||||
* channels by which the host transfers data with the device.
|
||||
*
|
||||
* <div class="special reference">
|
||||
* <h3>Developer Guides</h3>
|
||||
* <p>For more information about communicating with USB hardware, read the
|
||||
* <a href="{@docRoot}guide/topics/usb/index.html">USB</a> developer guide.</p>
|
||||
* </div>
|
||||
*/
|
||||
public class UsbConfiguration implements Parcelable {
|
||||
|
||||
private final int mId;
|
||||
private final String mName;
|
||||
private final int mAttributes;
|
||||
private final int mMaxPower;
|
||||
private Parcelable[] mInterfaces;
|
||||
|
||||
/**
|
||||
* Mask for "self-powered" bit in the configuration's attributes.
|
||||
* @see #getAttributes
|
||||
*/
|
||||
public static final int ATTR_SELF_POWERED_MASK = 1 << 6;
|
||||
|
||||
/**
|
||||
* Mask for "remote wakeup" bit in the configuration's attributes.
|
||||
* @see #getAttributes
|
||||
*/
|
||||
public static final int ATTR_REMOTE_WAKEUP_MASK = 1 << 5;
|
||||
|
||||
/**
|
||||
* UsbConfiguration should only be instantiated by UsbService implementation
|
||||
* @hide
|
||||
*/
|
||||
public UsbConfiguration(int id, String name, int attributes, int maxPower) {
|
||||
mId = id;
|
||||
mName = name;
|
||||
mAttributes = attributes;
|
||||
mMaxPower = maxPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration's ID field.
|
||||
* This is an integer that uniquely identifies the configuration on the device.
|
||||
*
|
||||
* @return the configuration's ID
|
||||
*/
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration's name.
|
||||
*
|
||||
* @return the configuration's name
|
||||
*/
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration's attributes field.
|
||||
* This field contains a bit field with the following flags:
|
||||
*
|
||||
* Bit 7: always set to 1
|
||||
* Bit 6: self-powered
|
||||
* Bit 5: remote wakeup enabled
|
||||
* Bit 0-4: reserved
|
||||
* @see #ATTR_SELF_POWERED_MASK
|
||||
* @see #ATTR_REMOTE_WAKEUP_MASK
|
||||
* @return the configuration's attributes
|
||||
*/
|
||||
public int getAttributes() {
|
||||
return mAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration's max power consumption, in milliamps.
|
||||
*
|
||||
* @return the configuration's max power
|
||||
*/
|
||||
public int getMaxPower() {
|
||||
return mMaxPower * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of {@link UsbInterface}s this configuration contains.
|
||||
*
|
||||
* @return the number of endpoints
|
||||
*/
|
||||
public int getInterfaceCount() {
|
||||
return mInterfaces.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link UsbInterface} at the given index.
|
||||
*
|
||||
* @return the interface
|
||||
*/
|
||||
public UsbInterface getInterface(int index) {
|
||||
return (UsbInterface)mInterfaces[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used by UsbService implementation
|
||||
* @hide
|
||||
*/
|
||||
public void setInterfaces(Parcelable[] interfaces) {
|
||||
mInterfaces = interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("UsbConfiguration[mId=" + mId +
|
||||
",mName=" + mName + ",mAttributes=" + mAttributes +
|
||||
",mMaxPower=" + mMaxPower + ",mInterfaces=[");
|
||||
for (int i = 0; i < mInterfaces.length; i++) {
|
||||
builder.append("\n");
|
||||
builder.append(mInterfaces[i].toString());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UsbConfiguration> CREATOR =
|
||||
new Parcelable.Creator<UsbConfiguration>() {
|
||||
public UsbConfiguration createFromParcel(Parcel in) {
|
||||
int id = in.readInt();
|
||||
String name = in.readString();
|
||||
int attributes = in.readInt();
|
||||
int maxPower = in.readInt();
|
||||
Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader());
|
||||
UsbConfiguration configuration = new UsbConfiguration(id, name, attributes, maxPower);
|
||||
configuration.setInterfaces(interfaces);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public UsbConfiguration[] newArray(int size) {
|
||||
return new UsbConfiguration[size];
|
||||
}
|
||||
};
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(mId);
|
||||
parcel.writeString(mName);
|
||||
parcel.writeInt(mAttributes);
|
||||
parcel.writeInt(mMaxPower);
|
||||
parcel.writeParcelableArray(mInterfaces, 0);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,10 @@ public class UsbDevice implements Parcelable {
|
||||
private final int mClass;
|
||||
private final int mSubclass;
|
||||
private final int mProtocol;
|
||||
private final Parcelable[] mInterfaces;
|
||||
private Parcelable[] mConfigurations;
|
||||
|
||||
// list of all interfaces on the device
|
||||
private UsbInterface[] mInterfaces;
|
||||
|
||||
/**
|
||||
* UsbDevice should only be instantiated by UsbService implementation
|
||||
@@ -58,8 +61,7 @@ public class UsbDevice implements Parcelable {
|
||||
*/
|
||||
public UsbDevice(String name, int vendorId, int productId,
|
||||
int Class, int subClass, int protocol,
|
||||
String manufacturerName, String productName, String serialNumber,
|
||||
Parcelable[] interfaces) {
|
||||
String manufacturerName, String productName, String serialNumber) {
|
||||
mName = name;
|
||||
mVendorId = vendorId;
|
||||
mProductId = productId;
|
||||
@@ -69,7 +71,6 @@ public class UsbDevice implements Parcelable {
|
||||
mManufacturerName = manufacturerName;
|
||||
mProductName = productName;
|
||||
mSerialNumber = serialNumber;
|
||||
mInterfaces = interfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,22 +169,75 @@ public class UsbDevice implements Parcelable {
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of {@link UsbConfiguration}s this device contains.
|
||||
*
|
||||
* @return the number of configurations
|
||||
*/
|
||||
public int getConfigurationCount() {
|
||||
return mConfigurations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link UsbConfiguration} at the given index.
|
||||
*
|
||||
* @return the configuration
|
||||
*/
|
||||
public UsbConfiguration getConfiguration(int index) {
|
||||
return (UsbConfiguration)mConfigurations[index];
|
||||
}
|
||||
|
||||
private UsbInterface[] getInterfaceList() {
|
||||
if (mInterfaces == null) {
|
||||
int configurationCount = mConfigurations.length;
|
||||
int interfaceCount = 0;
|
||||
for (int i = 0; i < configurationCount; i++) {
|
||||
UsbConfiguration configuration = (UsbConfiguration)mConfigurations[i];
|
||||
interfaceCount += configuration.getInterfaceCount();
|
||||
}
|
||||
|
||||
mInterfaces = new UsbInterface[interfaceCount];
|
||||
int offset = 0;
|
||||
for (int i = 0; i < configurationCount; i++) {
|
||||
UsbConfiguration configuration = (UsbConfiguration)mConfigurations[i];
|
||||
interfaceCount = configuration.getInterfaceCount();
|
||||
for (int j = 0; j < interfaceCount; j++) {
|
||||
mInterfaces[offset++] = configuration.getInterface(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of {@link UsbInterface}s this device contains.
|
||||
* For devices with multiple configurations, you will probably want to use
|
||||
* {@link UsbConfiguration#getInterfaceCount} instead.
|
||||
*
|
||||
* @return the number of interfaces
|
||||
*/
|
||||
public int getInterfaceCount() {
|
||||
return mInterfaces.length;
|
||||
return getInterfaceList().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link UsbInterface} at the given index.
|
||||
* For devices with multiple configurations, you will probably want to use
|
||||
* {@link UsbConfiguration#getInterface} instead.
|
||||
*
|
||||
* @return the interface
|
||||
*/
|
||||
public UsbInterface getInterface(int index) {
|
||||
return (UsbInterface)mInterfaces[index];
|
||||
return getInterfaceList()[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used by UsbService implementation
|
||||
* @hide
|
||||
*/
|
||||
public void setConfigurations(Parcelable[] configuration) {
|
||||
mConfigurations = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,11 +258,17 @@ public class UsbDevice implements Parcelable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId +
|
||||
",mProductId=" + mProductId + ",mClass=" + mClass +
|
||||
",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
|
||||
StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName +
|
||||
",mVendorId=" + mVendorId + ",mProductId=" + mProductId +
|
||||
",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
|
||||
",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
|
||||
",mSerialNumber=" + mSerialNumber + ",mInterfaces=" + mInterfaces + "]";
|
||||
",mSerialNumber=" + mSerialNumber + ",mConfigurations=[");
|
||||
for (int i = 0; i < mConfigurations.length; i++) {
|
||||
builder.append("\n");
|
||||
builder.append(mConfigurations[i].toString());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UsbDevice> CREATOR =
|
||||
@@ -223,9 +283,11 @@ public class UsbDevice implements Parcelable {
|
||||
String manufacturerName = in.readString();
|
||||
String productName = in.readString();
|
||||
String serialNumber = in.readString();
|
||||
Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader());
|
||||
return new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
|
||||
manufacturerName, productName, serialNumber, interfaces);
|
||||
Parcelable[] configurations = in.readParcelableArray(UsbInterface.class.getClassLoader());
|
||||
UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
|
||||
manufacturerName, productName, serialNumber);
|
||||
device.setConfigurations(configurations);
|
||||
return device;
|
||||
}
|
||||
|
||||
public UsbDevice[] newArray(int size) {
|
||||
@@ -247,7 +309,7 @@ public class UsbDevice implements Parcelable {
|
||||
parcel.writeString(mManufacturerName);
|
||||
parcel.writeString(mProductName);
|
||||
parcel.writeString(mSerialNumber);
|
||||
parcel.writeParcelableArray(mInterfaces, 0);
|
||||
parcel.writeParcelableArray(mConfigurations, 0);
|
||||
}
|
||||
|
||||
public static int getDeviceId(String name) {
|
||||
|
||||
@@ -100,6 +100,25 @@ public class UsbDeviceConnection {
|
||||
return native_release_interface(intf.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link android.hardware.usb.UsbInterface}.
|
||||
* Used to select between two interfaces with the same ID but different alternate setting.
|
||||
*
|
||||
* @return true if the interface was successfully released
|
||||
*/
|
||||
public boolean setInterface(UsbInterface intf) {
|
||||
return native_set_interface(intf.getId(), intf.getAlternateSetting());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the device's current {@link android.hardware.usb.UsbConfiguration}.
|
||||
*
|
||||
* @return true if the configuration was successfully set
|
||||
*/
|
||||
public boolean setConfiguration(UsbConfiguration configuration) {
|
||||
return native_set_configuration(configuration.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a control transaction on endpoint zero for this device.
|
||||
* The direction of the transfer is determined by the request type.
|
||||
@@ -236,6 +255,8 @@ public class UsbDeviceConnection {
|
||||
private native byte[] native_get_desc();
|
||||
private native boolean native_claim_interface(int interfaceID, boolean force);
|
||||
private native boolean native_release_interface(int interfaceID);
|
||||
private native boolean native_set_interface(int interfaceID, int alternateSetting);
|
||||
private native boolean native_set_configuration(int configurationID);
|
||||
private native int native_control_request(int requestType, int request, int value,
|
||||
int index, byte[] buffer, int offset, int length, int timeout);
|
||||
private native int native_bulk_request(int endpoint, byte[] buffer,
|
||||
|
||||
@@ -35,27 +35,31 @@ import android.os.Parcelable;
|
||||
public class UsbInterface implements Parcelable {
|
||||
|
||||
private final int mId;
|
||||
private final int mAlternateSetting;
|
||||
private final String mName;
|
||||
private final int mClass;
|
||||
private final int mSubclass;
|
||||
private final int mProtocol;
|
||||
private final Parcelable[] mEndpoints;
|
||||
private Parcelable[] mEndpoints;
|
||||
|
||||
/**
|
||||
* UsbInterface should only be instantiated by UsbService implementation
|
||||
* @hide
|
||||
*/
|
||||
public UsbInterface(int id, int Class, int subClass, int protocol,
|
||||
Parcelable[] endpoints) {
|
||||
public UsbInterface(int id, int alternateSetting, String name,
|
||||
int Class, int subClass, int protocol) {
|
||||
mId = id;
|
||||
mAlternateSetting = alternateSetting;
|
||||
mName = name;
|
||||
mClass = Class;
|
||||
mSubclass = subClass;
|
||||
mProtocol = protocol;
|
||||
mEndpoints = endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interface's ID field.
|
||||
* This is an integer that uniquely identifies the interface on the device.
|
||||
* Returns the interface's bInterfaceNumber field.
|
||||
* This is an integer that along with the alternate setting uniquely identifies
|
||||
* the interface on the device.
|
||||
*
|
||||
* @return the interface's ID
|
||||
*/
|
||||
@@ -63,6 +67,28 @@ public class UsbInterface implements Parcelable {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interface's bAlternateSetting field.
|
||||
* This is an integer that along with the ID uniquely identifies
|
||||
* the interface on the device.
|
||||
* {@link UsbDeviceConnection#setInterface} can be used to switch between
|
||||
* two interfaces with the same ID but different alternate setting.
|
||||
*
|
||||
* @return the interface's alternate setting
|
||||
*/
|
||||
public int getAlternateSetting() {
|
||||
return mAlternateSetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interface's name.
|
||||
*
|
||||
* @return the interface's name
|
||||
*/
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interface's class field.
|
||||
* Some useful constants for USB classes can be found in {@link UsbConstants}
|
||||
@@ -109,22 +135,42 @@ public class UsbInterface implements Parcelable {
|
||||
return (UsbEndpoint)mEndpoints[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used by UsbService implementation
|
||||
* @hide
|
||||
*/
|
||||
public void setEndpoints(Parcelable[] endpoints) {
|
||||
mEndpoints = endpoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UsbInterface[mId=" + mId + ",mClass=" + mClass +
|
||||
StringBuilder builder = new StringBuilder("UsbInterface[mId=" + mId +
|
||||
",mAlternateSetting=" + mAlternateSetting +
|
||||
",mName=" + mName + ",mClass=" + mClass +
|
||||
",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
|
||||
",mEndpoints=" + mEndpoints + "]";
|
||||
",mEndpoints=[");
|
||||
for (int i = 0; i < mEndpoints.length; i++) {
|
||||
builder.append("\n");
|
||||
builder.append(mEndpoints[i].toString());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UsbInterface> CREATOR =
|
||||
new Parcelable.Creator<UsbInterface>() {
|
||||
public UsbInterface createFromParcel(Parcel in) {
|
||||
int id = in.readInt();
|
||||
int alternateSetting = in.readInt();
|
||||
String name = in.readString();
|
||||
int Class = in.readInt();
|
||||
int subClass = in.readInt();
|
||||
int protocol = in.readInt();
|
||||
Parcelable[] endpoints = in.readParcelableArray(UsbEndpoint.class.getClassLoader());
|
||||
return new UsbInterface(id, Class, subClass, protocol, endpoints);
|
||||
UsbInterface intf = new UsbInterface(id, alternateSetting, name, Class, subClass, protocol);
|
||||
intf.setEndpoints(endpoints);
|
||||
return intf;
|
||||
}
|
||||
|
||||
public UsbInterface[] newArray(int size) {
|
||||
@@ -138,6 +184,8 @@ public class UsbInterface implements Parcelable {
|
||||
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(mId);
|
||||
parcel.writeInt(mAlternateSetting);
|
||||
parcel.writeString(mName);
|
||||
parcel.writeInt(mClass);
|
||||
parcel.writeInt(mSubclass);
|
||||
parcel.writeInt(mProtocol);
|
||||
|
||||
@@ -244,9 +244,11 @@ public class ZygoteInit {
|
||||
}
|
||||
|
||||
static void preload() {
|
||||
Log.d(TAG, "begin preload");
|
||||
preloadClasses();
|
||||
preloadResources();
|
||||
preloadOpenGL();
|
||||
Log.d(TAG, "end preload");
|
||||
}
|
||||
|
||||
private static void preloadOpenGL() {
|
||||
|
||||
@@ -123,20 +123,45 @@ android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz,
|
||||
return (ret == 0) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static jint
|
||||
static jboolean
|
||||
android_hardware_UsbDeviceConnection_release_interface(JNIEnv *env, jobject thiz, jint interfaceID)
|
||||
{
|
||||
struct usb_device* device = get_device_from_object(env, thiz);
|
||||
if (!device) {
|
||||
ALOGE("device is closed in native_release_interface");
|
||||
return -1;
|
||||
return JNI_FALSE;
|
||||
}
|
||||
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;
|
||||
return (ret == 0) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean
|
||||
android_hardware_UsbDeviceConnection_set_interface(JNIEnv *env, jobject thiz, jint interfaceID,
|
||||
jint alternateSetting)
|
||||
{
|
||||
struct usb_device* device = get_device_from_object(env, thiz);
|
||||
if (!device) {
|
||||
ALOGE("device is closed in native_set_interface");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
int ret = usb_device_set_interface(device, interfaceID, alternateSetting);
|
||||
return (ret == 0) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean
|
||||
android_hardware_UsbDeviceConnection_set_configuration(JNIEnv *env, jobject thiz, jint configurationID)
|
||||
{
|
||||
struct usb_device* device = get_device_from_object(env, thiz);
|
||||
if (!device) {
|
||||
ALOGE("device is closed in native_set_configuration");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
int ret = usb_device_set_configuration(device, configurationID);
|
||||
return (ret == 0) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static jint
|
||||
@@ -229,6 +254,8 @@ static JNINativeMethod method_table[] = {
|
||||
{"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
|
||||
{"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
|
||||
{"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
|
||||
{"native_set_interface","(II)Z", (void *)android_hardware_UsbDeviceConnection_set_interface},
|
||||
{"native_set_configuration","(I)Z", (void *)android_hardware_UsbDeviceConnection_set_configuration},
|
||||
{"native_control_request", "(IIII[BIII)I",
|
||||
(void *)android_hardware_UsbDeviceConnection_control_request},
|
||||
{"native_bulk_request", "(I[BIII)I",
|
||||
|
||||
@@ -41,7 +41,11 @@ static struct parcel_file_descriptor_offsets_t
|
||||
jmethodID mConstructor;
|
||||
} gParcelFileDescriptorOffsets;
|
||||
|
||||
static jmethodID method_usbDeviceAdded;
|
||||
static jmethodID method_beginUsbDeviceAdded;
|
||||
static jmethodID method_addUsbConfiguration;
|
||||
static jmethodID method_addUsbInterface;
|
||||
static jmethodID method_addUsbEndpoint;
|
||||
static jmethodID method_endUsbDeviceAdded;
|
||||
static jmethodID method_usbDeviceRemoved;
|
||||
|
||||
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
|
||||
@@ -68,64 +72,69 @@ static int usb_device_added(const char *devname, void* client_data) {
|
||||
Vector<int> endpointValues;
|
||||
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
|
||||
|
||||
uint16_t vendorId = usb_device_get_vendor_id(device);
|
||||
uint16_t productId = usb_device_get_product_id(device);
|
||||
uint8_t deviceClass = deviceDesc->bDeviceClass;
|
||||
uint8_t deviceSubClass = deviceDesc->bDeviceSubClass;
|
||||
uint8_t protocol = deviceDesc->bDeviceProtocol;
|
||||
char *manufacturer = usb_device_get_manufacturer_name(device);
|
||||
char *product = usb_device_get_product_name(device);
|
||||
char *serial = usb_device_get_serial(device);
|
||||
|
||||
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;
|
||||
|
||||
// push class, subclass, protocol and number of endpoints into interfaceValues vector
|
||||
interfaceValues.add(interface->bInterfaceNumber);
|
||||
interfaceValues.add(interface->bInterfaceClass);
|
||||
interfaceValues.add(interface->bInterfaceSubClass);
|
||||
interfaceValues.add(interface->bInterfaceProtocol);
|
||||
interfaceValues.add(interface->bNumEndpoints);
|
||||
} else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
|
||||
struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
|
||||
|
||||
// push address, attributes, max packet size and interval into endpointValues vector
|
||||
endpointValues.add(endpoint->bEndpointAddress);
|
||||
endpointValues.add(endpoint->bmAttributes);
|
||||
endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize));
|
||||
endpointValues.add(endpoint->bInterval);
|
||||
}
|
||||
}
|
||||
|
||||
usb_device_close(device);
|
||||
|
||||
// handle generic device notification
|
||||
int length = interfaceValues.size();
|
||||
jintArray interfaceArray = env->NewIntArray(length);
|
||||
env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array());
|
||||
|
||||
length = endpointValues.size();
|
||||
jintArray endpointArray = env->NewIntArray(length);
|
||||
env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array());
|
||||
|
||||
jstring deviceName = env->NewStringUTF(devname);
|
||||
jstring manufacturerName = env->NewStringUTF(manufacturer);
|
||||
jstring productName = env->NewStringUTF(product);
|
||||
jstring serialNumber = env->NewStringUTF(serial);
|
||||
env->CallVoidMethod(thiz, method_usbDeviceAdded,
|
||||
deviceName, vendorId, productId, deviceClass,
|
||||
deviceSubClass, protocol, manufacturerName,
|
||||
productName, serialNumber, interfaceArray, endpointArray);
|
||||
|
||||
env->DeleteLocalRef(interfaceArray);
|
||||
env->DeleteLocalRef(endpointArray);
|
||||
jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
|
||||
deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
|
||||
deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
|
||||
manufacturerName, productName, serialNumber);
|
||||
|
||||
env->DeleteLocalRef(serialNumber);
|
||||
env->DeleteLocalRef(productName);
|
||||
env->DeleteLocalRef(manufacturerName);
|
||||
env->DeleteLocalRef(deviceName);
|
||||
free(manufacturer);
|
||||
free(product);
|
||||
free(serial);
|
||||
|
||||
if (!result) goto fail;
|
||||
|
||||
usb_descriptor_iter_init(device, &iter);
|
||||
|
||||
while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
|
||||
if (desc->bDescriptorType == USB_DT_CONFIG) {
|
||||
struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
|
||||
char *name = usb_device_get_string(device, config->iConfiguration);
|
||||
jstring configName = env->NewStringUTF(name);
|
||||
|
||||
env->CallVoidMethod(thiz, method_addUsbConfiguration,
|
||||
config->bConfigurationValue, configName, config->bmAttributes,
|
||||
config->bMaxPower);
|
||||
|
||||
env->DeleteLocalRef(configName);
|
||||
free(name);
|
||||
} else if (desc->bDescriptorType == USB_DT_INTERFACE) {
|
||||
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
|
||||
char *name = usb_device_get_string(device, interface->iInterface);
|
||||
jstring interfaceName = env->NewStringUTF(name);
|
||||
|
||||
env->CallVoidMethod(thiz, method_addUsbInterface,
|
||||
interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
|
||||
interface->bInterfaceClass, interface->bInterfaceSubClass,
|
||||
interface->bInterfaceProtocol);
|
||||
|
||||
env->DeleteLocalRef(interfaceName);
|
||||
free(name);
|
||||
} else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
|
||||
struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
|
||||
|
||||
env->CallVoidMethod(thiz, method_addUsbEndpoint,
|
||||
endpoint->bEndpointAddress, endpoint->bmAttributes,
|
||||
__le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
|
||||
|
||||
fail:
|
||||
usb_device_close(device);
|
||||
checkAndClearExceptionFromCallback(env, __FUNCTION__);
|
||||
|
||||
return 0;
|
||||
@@ -191,12 +200,36 @@ int register_android_server_UsbHostManager(JNIEnv *env)
|
||||
ALOGE("Can't find com/android/server/usb/UsbHostManager");
|
||||
return -1;
|
||||
}
|
||||
method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I)V");
|
||||
if (method_usbDeviceAdded == NULL) {
|
||||
ALOGE("Can't find usbDeviceAdded");
|
||||
method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
|
||||
"(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
if (method_beginUsbDeviceAdded == NULL) {
|
||||
ALOGE("Can't find beginUsbDeviceAdded");
|
||||
return -1;
|
||||
}
|
||||
method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V");
|
||||
method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
|
||||
"(ILjava/lang/String;II)V");
|
||||
if (method_addUsbConfiguration == NULL) {
|
||||
ALOGE("Can't find addUsbConfiguration");
|
||||
return -1;
|
||||
}
|
||||
method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
|
||||
"(ILjava/lang/String;IIII)V");
|
||||
if (method_addUsbInterface == NULL) {
|
||||
ALOGE("Can't find addUsbInterface");
|
||||
return -1;
|
||||
}
|
||||
method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
|
||||
if (method_addUsbEndpoint == NULL) {
|
||||
ALOGE("Can't find addUsbEndpoint");
|
||||
return -1;
|
||||
}
|
||||
method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
|
||||
if (method_endUsbDeviceAdded == NULL) {
|
||||
ALOGE("Can't find endUsbDeviceAdded");
|
||||
return -1;
|
||||
}
|
||||
method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
|
||||
"(Ljava/lang/String;)V");
|
||||
if (method_usbDeviceRemoved == NULL) {
|
||||
ALOGE("Can't find usbDeviceRemoved");
|
||||
return -1;
|
||||
@@ -205,7 +238,8 @@ int register_android_server_UsbHostManager(JNIEnv *env)
|
||||
clazz = env->FindClass("android/os/ParcelFileDescriptor");
|
||||
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
|
||||
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
|
||||
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
|
||||
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
|
||||
"(Ljava/io/FileDescriptor;)V");
|
||||
LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
|
||||
"Unable to find constructor for android.os.ParcelFileDescriptor");
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.usb;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.usb.UsbConfiguration;
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
@@ -30,6 +31,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@@ -48,6 +50,13 @@ public class UsbHostManager {
|
||||
private final Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private UsbDevice mNewDevice;
|
||||
private UsbConfiguration mNewConfiguration;
|
||||
private UsbInterface mNewInterface;
|
||||
private ArrayList<UsbConfiguration> mNewConfigurations;
|
||||
private ArrayList<UsbInterface> mNewInterfaces;
|
||||
private ArrayList<UsbEndpoint> mNewEndpoints;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private UsbSettingsManager mCurrentSettings;
|
||||
|
||||
@@ -93,69 +102,103 @@ public class UsbHostManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Called from JNI in monitorUsbHostBus() to report new USB devices */
|
||||
private void usbDeviceAdded(String deviceName, int vendorID, int productID,
|
||||
/* Called from JNI in monitorUsbHostBus() to report new USB devices
|
||||
Returns true if successful, in which case the JNI code will continue adding configurations,
|
||||
interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
|
||||
have been processed
|
||||
*/
|
||||
private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
|
||||
int deviceClass, int deviceSubclass, int deviceProtocol,
|
||||
String manufacturerName, String productName, String serialNumber,
|
||||
/* array of quintuples containing id, class, subclass, protocol
|
||||
and number of endpoints for each interface */
|
||||
int[] interfaceValues,
|
||||
/* array of quadruples containing address, attributes, max packet size
|
||||
and interval for each endpoint */
|
||||
int[] endpointValues) {
|
||||
String manufacturerName, String productName, String serialNumber) {
|
||||
|
||||
if (isBlackListed(deviceName) ||
|
||||
isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mDevices.get(deviceName) != null) {
|
||||
Slog.w(TAG, "device already on mDevices list: " + deviceName);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int numInterfaces = interfaceValues.length / 5;
|
||||
Parcelable[] interfaces = new UsbInterface[numInterfaces];
|
||||
try {
|
||||
// repackage interfaceValues as an array of UsbInterface
|
||||
int intf, endp, ival = 0, eval = 0;
|
||||
for (intf = 0; intf < numInterfaces; intf++) {
|
||||
int interfaceId = interfaceValues[ival++];
|
||||
int interfaceClass = interfaceValues[ival++];
|
||||
int interfaceSubclass = interfaceValues[ival++];
|
||||
int interfaceProtocol = interfaceValues[ival++];
|
||||
int numEndpoints = interfaceValues[ival++];
|
||||
|
||||
Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
|
||||
for (endp = 0; endp < numEndpoints; endp++) {
|
||||
int address = endpointValues[eval++];
|
||||
int attributes = endpointValues[eval++];
|
||||
int maxPacketSize = endpointValues[eval++];
|
||||
int interval = endpointValues[eval++];
|
||||
endpoints[endp] = new UsbEndpoint(address, attributes,
|
||||
maxPacketSize, interval);
|
||||
}
|
||||
|
||||
// don't allow if any interfaces are blacklisted
|
||||
if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
|
||||
return;
|
||||
}
|
||||
interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
|
||||
interfaceSubclass, interfaceProtocol, endpoints);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// beware of index out of bound exceptions, which might happen if
|
||||
// a device does not set bNumEndpoints correctly
|
||||
Slog.e(TAG, "error parsing USB descriptors", e);
|
||||
return;
|
||||
if (mNewDevice != null) {
|
||||
Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
|
||||
return false;
|
||||
}
|
||||
|
||||
UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
|
||||
mNewDevice = new UsbDevice(deviceName, vendorID, productID,
|
||||
deviceClass, deviceSubclass, deviceProtocol,
|
||||
manufacturerName, productName, serialNumber, interfaces);
|
||||
mDevices.put(deviceName, device);
|
||||
getCurrentSettings().deviceAttached(device);
|
||||
manufacturerName, productName, serialNumber);
|
||||
|
||||
mNewConfigurations = new ArrayList<UsbConfiguration>();
|
||||
mNewInterfaces = new ArrayList<UsbInterface>();
|
||||
mNewEndpoints = new ArrayList<UsbEndpoint>();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
|
||||
currently being added. Returns true if successful, false in case of error.
|
||||
*/
|
||||
private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
|
||||
if (mNewConfiguration != null) {
|
||||
mNewConfiguration.setInterfaces(
|
||||
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
|
||||
mNewInterfaces.clear();
|
||||
}
|
||||
|
||||
mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
|
||||
mNewConfigurations.add(mNewConfiguration);
|
||||
}
|
||||
|
||||
/* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
|
||||
currently being added. Returns true if successful, false in case of error.
|
||||
*/
|
||||
private void addUsbInterface(int id, String name, int altSetting,
|
||||
int Class, int subClass, int protocol) {
|
||||
if (mNewInterface != null) {
|
||||
mNewInterface.setEndpoints(
|
||||
mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
|
||||
mNewEndpoints.clear();
|
||||
}
|
||||
|
||||
mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
|
||||
mNewInterfaces.add(mNewInterface);
|
||||
}
|
||||
|
||||
/* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
|
||||
currently being added. Returns true if successful, false in case of error.
|
||||
*/
|
||||
private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
|
||||
mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
|
||||
}
|
||||
|
||||
/* Called from JNI in monitorUsbHostBus() to finish adding a new device */
|
||||
private void endUsbDeviceAdded() {
|
||||
if (mNewInterface != null) {
|
||||
mNewInterface.setEndpoints(
|
||||
mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
|
||||
}
|
||||
if (mNewConfiguration != null) {
|
||||
mNewConfiguration.setInterfaces(
|
||||
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mNewDevice != null) {
|
||||
mNewDevice.setConfigurations(
|
||||
mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));
|
||||
mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
|
||||
Slog.d(TAG, "Added device " + mNewDevice);
|
||||
getCurrentSettings().deviceAttached(mNewDevice);
|
||||
} else {
|
||||
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
|
||||
}
|
||||
mNewDevice = null;
|
||||
mNewConfigurations = null;
|
||||
mNewInterfaces = null;
|
||||
mNewEndpoints = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user