diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
commit | 10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch) | |
tree | 8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/hardware/usb | |
parent | 677516fb6b6f207d373984757d3d9450474b6b00 (diff) | |
download | android-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz |
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \
--bid 4335822 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4335822.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/hardware/usb')
-rw-r--r-- | android/hardware/usb/UsbAccessory.java | 220 | ||||
-rw-r--r-- | android/hardware/usb/UsbConfiguration.java | 185 | ||||
-rw-r--r-- | android/hardware/usb/UsbConstants.java | 183 | ||||
-rw-r--r-- | android/hardware/usb/UsbDevice.java | 344 | ||||
-rw-r--r-- | android/hardware/usb/UsbDeviceConnection.java | 377 | ||||
-rw-r--r-- | android/hardware/usb/UsbEndpoint.java | 161 | ||||
-rw-r--r-- | android/hardware/usb/UsbInterface.java | 198 | ||||
-rw-r--r-- | android/hardware/usb/UsbManager.java | 737 | ||||
-rw-r--r-- | android/hardware/usb/UsbPort.java | 305 | ||||
-rw-r--r-- | android/hardware/usb/UsbPortStatus.java | 144 | ||||
-rw-r--r-- | android/hardware/usb/UsbRequest.java | 359 |
11 files changed, 3213 insertions, 0 deletions
diff --git a/android/hardware/usb/UsbAccessory.java b/android/hardware/usb/UsbAccessory.java new file mode 100644 index 00000000..4aeb40c1 --- /dev/null +++ b/android/hardware/usb/UsbAccessory.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2011 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.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +/** + * A class representing a USB accessory, which is an external hardware component + * that communicates with an android application over USB. + * The accessory is the USB host and android the device side of the USB connection. + * + * <p>When the accessory connects, it reports its manufacturer and model names, + * the version of the accessory, and a user visible description of the accessory to the device. + * The manufacturer, model and version strings are used by the USB Manager to choose + * an appropriate application for the accessory. + * The accessory may optionally provide a unique serial number + * and a URL to for the accessory's website to the device as well. + * + * <p>An instance of this class is sent to the application via the + * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} Intent. + * The application can then call {@link UsbManager#openAccessory} to open a file descriptor + * for reading and writing data to and from the accessory. + * + * <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 UsbAccessory implements Parcelable { + + private static final String TAG = "UsbAccessory"; + + private final @NonNull String mManufacturer; + private final @NonNull String mModel; + private final @Nullable String mDescription; + private final @Nullable String mVersion; + private final @Nullable String mUri; + private final @Nullable String mSerial; + + /** @hide */ + public static final int MANUFACTURER_STRING = 0; + /** @hide */ + public static final int MODEL_STRING = 1; + /** @hide */ + public static final int DESCRIPTION_STRING = 2; + /** @hide */ + public static final int VERSION_STRING = 3; + /** @hide */ + public static final int URI_STRING = 4; + /** @hide */ + public static final int SERIAL_STRING = 5; + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(@NonNull String manufacturer, @NonNull String model, + @Nullable String description, @Nullable String version, @Nullable String uri, + @Nullable String serial) { + mManufacturer = Preconditions.checkNotNull(manufacturer); + mModel = Preconditions.checkNotNull(model); + mDescription = description; + mVersion = version; + mUri = uri; + mSerial = serial; + } + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(String[] strings) { + this(strings[MANUFACTURER_STRING], strings[MODEL_STRING], strings[DESCRIPTION_STRING], + strings[VERSION_STRING], strings[URI_STRING], strings[SERIAL_STRING]); + } + + /** + * Returns the manufacturer name of the accessory. + * + * @return the accessory manufacturer + */ + public @NonNull String getManufacturer() { + return mManufacturer; + } + + /** + * Returns the model name of the accessory. + * + * @return the accessory model + */ + public @NonNull String getModel() { + return mModel; + } + + /** + * Returns a user visible description of the accessory. + * + * @return the accessory description, or {@code null} if not set + */ + public @Nullable String getDescription() { + return mDescription; + } + + /** + * Returns the version of the accessory. + * + * @return the accessory version, or {@code null} if not set + */ + public @Nullable String getVersion() { + return mVersion; + } + + /** + * Returns the URI for the accessory. + * This is an optional URI that might show information about the accessory + * or provide the option to download an application for the accessory + * + * @return the accessory URI, or {@code null} if not set + */ + public @Nullable String getUri() { + return mUri; + } + + /** + * Returns the unique serial number for the accessory. + * This is an optional serial number that can be used to differentiate + * between individual accessories of the same model and manufacturer + * + * @return the unique serial number, or {@code null} if not set + */ + public @Nullable String getSerial() { + return mSerial; + } + + private static boolean compare(String s1, String s2) { + if (s1 == null) return (s2 == null); + return s1.equals(s2); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UsbAccessory) { + UsbAccessory accessory = (UsbAccessory)obj; + return (compare(mManufacturer, accessory.getManufacturer()) && + compare(mModel, accessory.getModel()) && + compare(mDescription, accessory.getDescription()) && + compare(mVersion, accessory.getVersion()) && + compare(mUri, accessory.getUri()) && + compare(mSerial, accessory.getSerial())); + } + return false; + } + + @Override + public int hashCode() { + return mManufacturer.hashCode() ^ mModel.hashCode() ^ + (mDescription == null ? 0 : mDescription.hashCode()) ^ + (mVersion == null ? 0 : mVersion.hashCode()) ^ + (mUri == null ? 0 : mUri.hashCode()) ^ (mSerial == null ? 0 : mSerial.hashCode()); + } + + @Override + public String toString() { + return "UsbAccessory[mManufacturer=" + mManufacturer + + ", mModel=" + mModel + + ", mDescription=" + mDescription + + ", mVersion=" + mVersion + + ", mUri=" + mUri + + ", mSerial=" + mSerial + "]"; + } + + public static final Parcelable.Creator<UsbAccessory> CREATOR = + new Parcelable.Creator<UsbAccessory>() { + public UsbAccessory createFromParcel(Parcel in) { + String manufacturer = in.readString(); + String model = in.readString(); + String description = in.readString(); + String version = in.readString(); + String uri = in.readString(); + String serial = in.readString(); + return new UsbAccessory(manufacturer, model, description, version, uri, serial); + } + + public UsbAccessory[] newArray(int size) { + return new UsbAccessory[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mManufacturer); + parcel.writeString(mModel); + parcel.writeString(mDescription); + parcel.writeString(mVersion); + parcel.writeString(mUri); + parcel.writeString(mSerial); + } +} diff --git a/android/hardware/usb/UsbConfiguration.java b/android/hardware/usb/UsbConfiguration.java new file mode 100644 index 00000000..a1715708 --- /dev/null +++ b/android/hardware/usb/UsbConfiguration.java @@ -0,0 +1,185 @@ +/* + * 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.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +/** + * 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 @Nullable String mName; + private final int mAttributes; + private final int mMaxPower; + + /** All interfaces for this config, only null during creation */ + private @Nullable Parcelable[] mInterfaces; + + /** + * Mask for "self-powered" bit in the configuration's attributes. + */ + private static final int ATTR_SELF_POWERED = 1 << 6; + + /** + * Mask for "remote wakeup" bit in the configuration's attributes. + */ + private static final int ATTR_REMOTE_WAKEUP = 1 << 5; + + /** + * UsbConfiguration should only be instantiated by UsbService implementation + * @hide + */ + public UsbConfiguration(int id, @Nullable 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, or {@code null} if the property could not be read + */ + public @Nullable String getName() { + return mName; + } + + /** + * Returns the self-powered attribute value configuration's attributes field. + * This attribute indicates that the device has a power source other than the USB connection. + * + * @return the configuration's self-powered attribute + */ + public boolean isSelfPowered() { + return (mAttributes & ATTR_SELF_POWERED) != 0; + } + + /** + * Returns the remote-wakeup attribute value configuration's attributes field. + * This attributes that the device may signal the host to wake from suspend. + * + * @return the configuration's remote-wakeup attribute + */ + public boolean isRemoteWakeup() { + return (mAttributes & ATTR_REMOTE_WAKEUP) != 0; + } + + /** + * 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 @NonNull UsbInterface getInterface(int index) { + return (UsbInterface)mInterfaces[index]; + } + + /** + * Only used by UsbService implementation + * @hide + */ + public void setInterfaces(@NonNull Parcelable[] interfaces) { + mInterfaces = Preconditions.checkArrayElementsNotNull(interfaces, "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); + } +} diff --git a/android/hardware/usb/UsbConstants.java b/android/hardware/usb/UsbConstants.java new file mode 100644 index 00000000..0e8d47ca --- /dev/null +++ b/android/hardware/usb/UsbConstants.java @@ -0,0 +1,183 @@ +/* + * 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. + */ + +package android.hardware.usb; + +/** + * Contains constants for the USB protocol. + * These constants correspond to definitions in linux/usb/ch9.h in the linux kernel. + */ +public final class UsbConstants { + + /** + * Bitmask used for extracting the {@link UsbEndpoint} direction from its address field. + * @see UsbEndpoint#getAddress + * @see UsbEndpoint#getDirection + * @see #USB_DIR_OUT + * @see #USB_DIR_IN + * + */ + public static final int USB_ENDPOINT_DIR_MASK = 0x80; + /** + * Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device) + * @see UsbEndpoint#getDirection + */ + public static final int USB_DIR_OUT = 0; + /** + * Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host) + * @see UsbEndpoint#getDirection + */ + public static final int USB_DIR_IN = 0x80; + + /** + * Bitmask used for extracting the {@link UsbEndpoint} number its address field. + * @see UsbEndpoint#getAddress + * @see UsbEndpoint#getEndpointNumber + */ + public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f; + + /** + * Bitmask used for extracting the {@link UsbEndpoint} type from its address field. + * @see UsbEndpoint#getAddress + * @see UsbEndpoint#getType + * @see #USB_ENDPOINT_XFER_CONTROL + * @see #USB_ENDPOINT_XFER_ISOC + * @see #USB_ENDPOINT_XFER_BULK + * @see #USB_ENDPOINT_XFER_INT + */ + public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03; + /** + * Control endpoint type (endpoint zero) + * @see UsbEndpoint#getType + */ + public static final int USB_ENDPOINT_XFER_CONTROL = 0; + /** + * Isochronous endpoint type (currently not supported) + * @see UsbEndpoint#getType + */ + public static final int USB_ENDPOINT_XFER_ISOC = 1; + /** + * Bulk endpoint type + * @see UsbEndpoint#getType + */ + public static final int USB_ENDPOINT_XFER_BULK = 2; + /** + * Interrupt endpoint type + * @see UsbEndpoint#getType + */ + public static final int USB_ENDPOINT_XFER_INT = 3; + + + /** + * Bitmask used for encoding the request type for a control request on endpoint zero. + */ + public static final int USB_TYPE_MASK = (0x03 << 5); + /** + * Used to specify that an endpoint zero control request is a standard request. + */ + public static final int USB_TYPE_STANDARD = (0x00 << 5); + /** + * Used to specify that an endpoint zero control request is a class specific request. + */ + public static final int USB_TYPE_CLASS = (0x01 << 5); + /** + * Used to specify that an endpoint zero control request is a vendor specific request. + */ + public static final int USB_TYPE_VENDOR = (0x02 << 5); + /** + * Reserved endpoint zero control request type (currently unused). + */ + public static final int USB_TYPE_RESERVED = (0x03 << 5); + + + /** + * USB class indicating that the class is determined on a per-interface basis. + */ + public static final int USB_CLASS_PER_INTERFACE = 0; + /** + * USB class for audio devices. + */ + public static final int USB_CLASS_AUDIO = 1; + /** + * USB class for communication devices. + */ + public static final int USB_CLASS_COMM = 2; + /** + * USB class for human interface devices (for example, mice and keyboards). + */ + public static final int USB_CLASS_HID = 3; + /** + * USB class for physical devices. + */ + public static final int USB_CLASS_PHYSICA = 5; + /** + * USB class for still image devices (digital cameras). + */ + public static final int USB_CLASS_STILL_IMAGE = 6; + /** + * USB class for printers. + */ + public static final int USB_CLASS_PRINTER = 7; + /** + * USB class for mass storage devices. + */ + public static final int USB_CLASS_MASS_STORAGE = 8; + /** + * USB class for USB hubs. + */ + public static final int USB_CLASS_HUB = 9; + /** + * USB class for CDC devices (communications device class). + */ + public static final int USB_CLASS_CDC_DATA = 0x0a; + /** + * USB class for content smart card devices. + */ + public static final int USB_CLASS_CSCID = 0x0b; + /** + * USB class for content security devices. + */ + public static final int USB_CLASS_CONTENT_SEC = 0x0d; + /** + * USB class for video devices. + */ + public static final int USB_CLASS_VIDEO = 0x0e; + /** + * USB class for wireless controller devices. + */ + public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0; + /** + * USB class for wireless miscellaneous devices. + */ + public static final int USB_CLASS_MISC = 0xef; + /** + * Application specific USB class. + */ + public static final int USB_CLASS_APP_SPEC = 0xfe; + /** + * Vendor specific USB class. + */ + public static final int USB_CLASS_VENDOR_SPEC = 0xff; + + /** + * Boot subclass for HID devices. + */ + public static final int USB_INTERFACE_SUBCLASS_BOOT = 1; + /** + * Vendor specific USB subclass. + */ + public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff; +} diff --git a/android/hardware/usb/UsbDevice.java b/android/hardware/usb/UsbDevice.java new file mode 100644 index 00000000..1e983016 --- /dev/null +++ b/android/hardware/usb/UsbDevice.java @@ -0,0 +1,344 @@ +/* + * 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. + */ + +package android.hardware.usb; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +/** + * This class represents a USB device attached to the android device with the android device + * acting as the USB host. + * Each device contains one or more {@link UsbInterface}s, each of which contains a number of + * {@link UsbEndpoint}s (the channels via which data is transmitted over USB). + * + * <p> This class contains information (along with {@link UsbInterface} and {@link UsbEndpoint}) + * that describes the capabilities of the USB device. + * To communicate with the device, you open a {@link UsbDeviceConnection} for the device + * and use {@link UsbRequest} to send and receive data on an endpoint. + * {@link UsbDeviceConnection#controlTransfer} is used for control requests on endpoint zero. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about communicating with USB hardware, read the + * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB</a> developer guide.</p> + * </div> + */ +public class UsbDevice implements Parcelable { + + private static final String TAG = "UsbDevice"; + private static final boolean DEBUG = false; + + private final @NonNull String mName; + private final @Nullable String mManufacturerName; + private final @Nullable String mProductName; + private final @NonNull String mVersion; + private final @Nullable String mSerialNumber; + private final int mVendorId; + private final int mProductId; + private final int mClass; + private final int mSubclass; + private final int mProtocol; + + /** All configurations for this device, only null during creation */ + private @Nullable Parcelable[] mConfigurations; + + /** All interfaces on the device. Initialized on first call to getInterfaceList */ + private @Nullable UsbInterface[] mInterfaces; + + /** + * UsbDevice should only be instantiated by UsbService implementation + * @hide + */ + public UsbDevice(@NonNull String name, int vendorId, int productId, int Class, int subClass, + int protocol, @Nullable String manufacturerName, @Nullable String productName, + @NonNull String version, @Nullable String serialNumber) { + mName = Preconditions.checkNotNull(name); + mVendorId = vendorId; + mProductId = productId; + mClass = Class; + mSubclass = subClass; + mProtocol = protocol; + mManufacturerName = manufacturerName; + mProductName = productName; + mVersion = Preconditions.checkStringNotEmpty(version); + mSerialNumber = serialNumber; + } + + /** + * Returns the name of the device. + * In the standard implementation, this is the path of the device file + * for the device in the usbfs file system. + * + * @return the device name + */ + public @NonNull String getDeviceName() { + return mName; + } + + /** + * Returns the manufacturer name of the device. + * + * @return the manufacturer name, or {@code null} if the property could not be read + */ + public @Nullable String getManufacturerName() { + return mManufacturerName; + } + + /** + * Returns the product name of the device. + * + * @return the product name, or {@code null} if the property could not be read + */ + public @Nullable String getProductName() { + return mProductName; + } + + /** + * Returns the version number of the device. + * + * @return the device version + */ + public @NonNull String getVersion() { + return mVersion; + } + + /** + * Returns the serial number of the device. + * + * @return the serial number name, or {@code null} if the property could not be read + */ + public @Nullable String getSerialNumber() { + return mSerialNumber; + } + + /** + * Returns a unique integer ID for the device. + * This is a convenience for clients that want to use an integer to represent + * the device, rather than the device name. + * IDs are not persistent across USB disconnects. + * + * @return the device ID + */ + public int getDeviceId() { + return getDeviceId(mName); + } + + /** + * Returns a vendor ID for the device. + * + * @return the device vendor ID + */ + public int getVendorId() { + return mVendorId; + } + + /** + * Returns a product ID for the device. + * + * @return the device product ID + */ + public int getProductId() { + return mProductId; + } + + /** + * Returns the devices's class field. + * Some useful constants for USB device classes can be found in {@link UsbConstants}. + * + * @return the devices's class + */ + public int getDeviceClass() { + return mClass; + } + + /** + * Returns the device's subclass field. + * + * @return the device's subclass + */ + public int getDeviceSubclass() { + return mSubclass; + } + + /** + * Returns the device's protocol field. + * + * @return the device's protocol + */ + public int getDeviceProtocol() { + 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 @NonNull UsbConfiguration getConfiguration(int index) { + return (UsbConfiguration)mConfigurations[index]; + } + + private @Nullable 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 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 @NonNull UsbInterface getInterface(int index) { + return getInterfaceList()[index]; + } + + /** + * Only used by UsbService implementation + * @hide + */ + public void setConfigurations(@NonNull Parcelable[] configuration) { + mConfigurations = Preconditions.checkArrayElementsNotNull(configuration, "configuration"); + } + + @Override + public boolean equals(Object o) { + if (o instanceof UsbDevice) { + return ((UsbDevice)o).mName.equals(mName); + } else if (o instanceof String) { + return ((String)o).equals(mName); + } else { + return false; + } + } + + @Override + public int hashCode() { + return mName.hashCode(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName + + ",mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + + ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName + + ",mVersion=" + mVersion + ",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 = + new Parcelable.Creator<UsbDevice>() { + public UsbDevice createFromParcel(Parcel in) { + String name = in.readString(); + int vendorId = in.readInt(); + int productId = in.readInt(); + int clasz = in.readInt(); + int subClass = in.readInt(); + int protocol = in.readInt(); + String manufacturerName = in.readString(); + String productName = in.readString(); + String version = in.readString(); + String serialNumber = in.readString(); + Parcelable[] configurations = in.readParcelableArray(UsbInterface.class.getClassLoader()); + UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, + manufacturerName, productName, version, serialNumber); + device.setConfigurations(configurations); + return device; + } + + public UsbDevice[] newArray(int size) { + return new UsbDevice[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mName); + parcel.writeInt(mVendorId); + parcel.writeInt(mProductId); + parcel.writeInt(mClass); + parcel.writeInt(mSubclass); + parcel.writeInt(mProtocol); + parcel.writeString(mManufacturerName); + parcel.writeString(mProductName); + parcel.writeString(mVersion); + parcel.writeString(mSerialNumber); + parcel.writeParcelableArray(mConfigurations, 0); + } + + public static int getDeviceId(String name) { + return native_get_device_id(name); + } + + public static String getDeviceName(int id) { + return native_get_device_name(id); + } + + private static native int native_get_device_id(String name); + private static native String native_get_device_name(int id); +} diff --git a/android/hardware/usb/UsbDeviceConnection.java b/android/hardware/usb/UsbDeviceConnection.java new file mode 100644 index 00000000..5b15c0d2 --- /dev/null +++ b/android/hardware/usb/UsbDeviceConnection.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2011 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.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Build; +import android.os.ParcelFileDescriptor; + +import com.android.internal.util.Preconditions; + +import dalvik.system.CloseGuard; + +import java.io.FileDescriptor; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeoutException; + +/** + * This class is used for sending and receiving data and control messages to a USB device. + * Instances of this class are created by {@link UsbManager#openDevice}. + */ +public class UsbDeviceConnection { + + private static final String TAG = "UsbDeviceConnection"; + + private final UsbDevice mDevice; + + private Context mContext; + + // used by the JNI code + private long mNativeContext; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + /** + * UsbDevice should only be instantiated by UsbService implementation + * @hide + */ + public UsbDeviceConnection(UsbDevice device) { + mDevice = device; + } + + /* package */ boolean open(String name, ParcelFileDescriptor pfd, @NonNull Context context) { + mContext = context.getApplicationContext(); + boolean wasOpened = native_open(name, pfd.getFileDescriptor()); + + if (wasOpened) { + mCloseGuard.open("close"); + } + + return wasOpened; + } + + /** + * @return The application context the connection was created for. + * + * @hide + */ + public @Nullable Context getContext() { + return mContext; + } + + /** + * Releases all system resources related to the device. + * Once the object is closed it cannot be used again. + * The client must call {@link UsbManager#openDevice} again + * to retrieve a new instance to reestablish communication with the device. + */ + public void close() { + if (mNativeContext != 0) { + native_close(); + mCloseGuard.close(); + } + } + + /** + * Returns the native file descriptor for the device, or + * -1 if the device is not opened. + * This is intended for passing to native code to access the device. + * + * @return the native file descriptor + */ + public int getFileDescriptor() { + return native_get_fd(); + } + + /** + * Returns the raw USB descriptors for the device. + * This can be used to access descriptors not supported directly + * via the higher level APIs. + * + * @return raw USB descriptors + */ + public byte[] getRawDescriptors() { + return native_get_desc(); + } + + /** + * Claims exclusive access to a {@link android.hardware.usb.UsbInterface}. + * This must be done before sending or receiving data on any + * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface. + * + * @param intf the interface to claim + * @param force true to disconnect kernel driver if necessary + * @return true if the interface was successfully claimed + */ + public boolean claimInterface(UsbInterface intf, boolean force) { + return native_claim_interface(intf.getId(), force); + } + + /** + * Releases exclusive access to a {@link android.hardware.usb.UsbInterface}. + * + * @return true if the interface was successfully released + */ + public boolean releaseInterface(UsbInterface intf) { + 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 selected + */ + 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. + * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is + * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, + * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer + * is a read. + * <p> + * This method transfers data starting from index 0 in the buffer. + * To specify a different offset, use + * {@link #controlTransfer(int, int, int, int, byte[], int, int, int)}. + * </p> + * + * @param requestType request type for this transaction + * @param request request ID for this transaction + * @param value value field for this transaction + * @param index index field for this transaction + * @param buffer buffer for data portion of transaction, + * or null if no data needs to be sent or received + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int controlTransfer(int requestType, int request, int value, + int index, byte[] buffer, int length, int timeout) { + return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout); + } + + /** + * Performs a control transaction on endpoint zero for this device. + * The direction of the transfer is determined by the request type. + * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is + * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, + * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer + * is a read. + * + * @param requestType request type for this transaction + * @param request request ID for this transaction + * @param value value field for this transaction + * @param index index field for this transaction + * @param buffer buffer for data portion of transaction, + * or null if no data needs to be sent or received + * @param offset the index of the first byte in the buffer to send or receive + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int controlTransfer(int requestType, int request, int value, int index, + byte[] buffer, int offset, int length, int timeout) { + checkBounds(buffer, offset, length); + return native_control_request(requestType, request, value, index, + buffer, offset, length, timeout); + } + + /** + * Performs a bulk transaction on the given endpoint. + * The direction of the transfer is determined by the direction of the endpoint. + * <p> + * This method transfers data starting from index 0 in the buffer. + * To specify a different offset, use + * {@link #bulkTransfer(UsbEndpoint, byte[], int, int, int)}. + * </p> + * + * @param endpoint the endpoint for this transaction + * @param buffer buffer for data to send or receive; can be {@code null} to wait for next + * transaction without reading data + * @param length the length of the data to send or receive + * @param timeout in milliseconds, 0 is infinite + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int bulkTransfer(UsbEndpoint endpoint, + byte[] buffer, int length, int timeout) { + return bulkTransfer(endpoint, buffer, 0, length, timeout); + } + + /** + * Performs a bulk transaction on the given endpoint. + * The direction of the transfer is determined by the direction of the endpoint. + * + * @param endpoint the endpoint for this transaction + * @param buffer buffer for data to send or receive + * @param offset the index of the first byte in the buffer to send or receive + * @param length the length of the data to send or receive + * @param timeout in milliseconds, 0 is infinite + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int bulkTransfer(UsbEndpoint endpoint, + byte[] buffer, int offset, int length, int timeout) { + checkBounds(buffer, offset, length); + return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout); + } + + /** + * Reset USB port for the connected device. + * + * @return true if reset succeeds. + * + * @hide + */ + @SystemApi + @SuppressLint("Doclava125") + public boolean resetDevice() { + return native_reset_device(); + } + + /** + * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation + * <p>Note that this may return requests queued on multiple + * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use, + * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link + * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process + * the result of this function.</p> + * + * @return a completed USB request, or null if an error occurred + * + * @throws IllegalArgumentException Before API {@value Build.VERSION_CODES#O}: if the number of + * bytes read or written is more than the limit of the + * request's buffer. The number of bytes is determined by the + * {@code length} parameter of + * {@link UsbRequest#queue(ByteBuffer, int)} + * @throws BufferOverflowException In API {@value Build.VERSION_CODES#O} and after: if the + * number of bytes read or written is more than the limit of the + * request's buffer. The number of bytes is determined by the + * {@code length} parameter of + * {@link UsbRequest#queue(ByteBuffer, int)} + */ + public UsbRequest requestWait() { + UsbRequest request = null; + try { + // -1 is special value indicating infinite wait + request = native_request_wait(-1); + } catch (TimeoutException e) { + // Does not happen, infinite timeout + } + + if (request != null) { + request.dequeue( + mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O); + } + return request; + } + + /** + * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation + * <p>Note that this may return requests queued on multiple + * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use, + * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link + * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process + * the result of this function.</p> + * <p>Android processes {@link UsbRequest UsbRequests} asynchronously. Hence it is not + * guaranteed that {@link #requestWait(long) requestWait(0)} returns a request that has been + * queued right before even if the request could have been processed immediately.</p> + * + * @param timeout timeout in milliseconds. If 0 this method does not wait. + * + * @return a completed USB request, or {@code null} if an error occurred + * + * @throws BufferOverflowException if the number of bytes read or written is more than the + * limit of the request's buffer. The number of bytes is + * determined by the {@code length} parameter of + * {@link UsbRequest#queue(ByteBuffer, int)} + * @throws TimeoutException if no request was received in {@code timeout} milliseconds. + */ + public UsbRequest requestWait(long timeout) throws TimeoutException { + timeout = Preconditions.checkArgumentNonnegative(timeout, "timeout"); + + UsbRequest request = native_request_wait(timeout); + if (request != null) { + request.dequeue(true); + } + return request; + } + + /** + * Returns the serial number for the device. + * This will return null if the device has not been opened. + * + * @return the device serial number + */ + public String getSerial() { + return native_get_serial(); + } + + private static void checkBounds(byte[] buffer, int start, int length) { + final int bufferLength = (buffer != null ? buffer.length : 0); + if (length < 0 || start < 0 || start + length > bufferLength) { + throw new IllegalArgumentException("Buffer start or length out of bounds."); + } + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + } finally { + super.finalize(); + } + } + + private native boolean native_open(String deviceName, FileDescriptor pfd); + private native void native_close(); + private native int native_get_fd(); + 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, + int offset, int length, int timeout); + private native UsbRequest native_request_wait(long timeout) throws TimeoutException; + private native String native_get_serial(); + private native boolean native_reset_device(); +} diff --git a/android/hardware/usb/UsbEndpoint.java b/android/hardware/usb/UsbEndpoint.java new file mode 100644 index 00000000..c346700a --- /dev/null +++ b/android/hardware/usb/UsbEndpoint.java @@ -0,0 +1,161 @@ +/* + * 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. + */ + +package android.hardware.usb; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class representing an endpoint on a {@link UsbInterface}. + * Endpoints are the channels for sending and receiving data over USB. + * Typically bulk endpoints are used for sending non-trivial amounts of data. + * Interrupt endpoints are used for sending small amounts of data, typically events, + * separately from the main data streams. + * The endpoint zero is a special endpoint for control messages sent from the host + * to device. + * Isochronous endpoints are currently unsupported. + */ +public class UsbEndpoint implements Parcelable { + + private final int mAddress; + private final int mAttributes; + private final int mMaxPacketSize; + private final int mInterval; + + /** + * UsbEndpoint should only be instantiated by UsbService implementation + * @hide + */ + public UsbEndpoint(int address, int attributes, int maxPacketSize, int interval) { + mAddress = address; + mAttributes = attributes; + mMaxPacketSize = maxPacketSize; + mInterval = interval; + } + + /** + * Returns the endpoint's address field. + * The address is a bitfield containing both the endpoint number + * as well as the data direction of the endpoint. + * the endpoint number and direction can also be accessed via + * {@link #getEndpointNumber} and {@link #getDirection}. + * + * @return the endpoint's address + */ + public int getAddress() { + return mAddress; + } + + /** + * Extracts the endpoint's endpoint number from its address + * + * @return the endpoint's endpoint number + */ + public int getEndpointNumber() { + return mAddress & UsbConstants.USB_ENDPOINT_NUMBER_MASK; + } + + /** + * Returns the endpoint's direction. + * Returns {@link UsbConstants#USB_DIR_OUT} + * if the direction is host to device, and + * {@link UsbConstants#USB_DIR_IN} if the + * direction is device to host. + * @see UsbConstants#USB_DIR_IN + * @see UsbConstants#USB_DIR_OUT + * + * @return the endpoint's direction + */ + public int getDirection() { + return mAddress & UsbConstants.USB_ENDPOINT_DIR_MASK; + } + + /** + * Returns the endpoint's attributes field. + * + * @return the endpoint's attributes + */ + public int getAttributes() { + return mAttributes; + } + + /** + * Returns the endpoint's type. + * Possible results are: + * <ul> + * <li>{@link UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero) + * <li>{@link UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint) + * <li>{@link UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint) + * <li>{@link UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint) + * </ul> + * + * @return the endpoint's type + */ + public int getType() { + return mAttributes & UsbConstants.USB_ENDPOINT_XFERTYPE_MASK; + } + + /** + * Returns the endpoint's maximum packet size. + * + * @return the endpoint's maximum packet size + */ + public int getMaxPacketSize() { + return mMaxPacketSize; + } + + /** + * Returns the endpoint's interval field. + * + * @return the endpoint's interval + */ + public int getInterval() { + return mInterval; + } + + @Override + public String toString() { + return "UsbEndpoint[mAddress=" + mAddress + ",mAttributes=" + mAttributes + + ",mMaxPacketSize=" + mMaxPacketSize + ",mInterval=" + mInterval +"]"; + } + + public static final Parcelable.Creator<UsbEndpoint> CREATOR = + new Parcelable.Creator<UsbEndpoint>() { + public UsbEndpoint createFromParcel(Parcel in) { + int address = in.readInt(); + int attributes = in.readInt(); + int maxPacketSize = in.readInt(); + int interval = in.readInt(); + return new UsbEndpoint(address, attributes, maxPacketSize, interval); + } + + public UsbEndpoint[] newArray(int size) { + return new UsbEndpoint[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mAddress); + parcel.writeInt(mAttributes); + parcel.writeInt(mMaxPacketSize); + parcel.writeInt(mInterval); + } +} diff --git a/android/hardware/usb/UsbInterface.java b/android/hardware/usb/UsbInterface.java new file mode 100644 index 00000000..4b5278c7 --- /dev/null +++ b/android/hardware/usb/UsbInterface.java @@ -0,0 +1,198 @@ +/* + * 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. + */ + +package android.hardware.usb; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +/** + * A class representing an interface on a {@link UsbDevice}. + * USB devices 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 UsbInterface implements Parcelable { + + private final int mId; + private final int mAlternateSetting; + private @Nullable final String mName; + private final int mClass; + private final int mSubclass; + private final int mProtocol; + + /** All endpoints of this interface, only null during creation */ + private Parcelable[] mEndpoints; + + /** + * UsbInterface should only be instantiated by UsbService implementation + * @hide + */ + public UsbInterface(int id, int alternateSetting, @Nullable String name, + int Class, int subClass, int protocol) { + mId = id; + mAlternateSetting = alternateSetting; + mName = name; + mClass = Class; + mSubclass = subClass; + mProtocol = protocol; + } + + /** + * 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 + */ + public int getId() { + 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, or {@code null} if the property could not be read + */ + public @Nullable String getName() { + return mName; + } + + /** + * Returns the interface's class field. + * Some useful constants for USB classes can be found in {@link UsbConstants} + * + * @return the interface's class + */ + public int getInterfaceClass() { + return mClass; + } + + /** + * Returns the interface's subclass field. + * + * @return the interface's subclass + */ + public int getInterfaceSubclass() { + return mSubclass; + } + + /** + * Returns the interface's protocol field. + * + * @return the interface's protocol + */ + public int getInterfaceProtocol() { + return mProtocol; + } + + /** + * Returns the number of {@link android.hardware.usb.UsbEndpoint}s this interface contains. + * + * @return the number of endpoints + */ + public int getEndpointCount() { + return mEndpoints.length; + } + + /** + * Returns the {@link android.hardware.usb.UsbEndpoint} at the given index. + * + * @return the endpoint + */ + public UsbEndpoint getEndpoint(int index) { + return (UsbEndpoint)mEndpoints[index]; + } + + /** + * Only used by UsbService implementation + * @hide + */ + public void setEndpoints(Parcelable[] endpoints) { + mEndpoints = Preconditions.checkArrayElementsNotNull(endpoints, "endpoints"); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("UsbInterface[mId=" + mId + + ",mAlternateSetting=" + mAlternateSetting + + ",mName=" + mName + ",mClass=" + mClass + + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + + ",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()); + UsbInterface intf = new UsbInterface(id, alternateSetting, name, Class, subClass, protocol); + intf.setEndpoints(endpoints); + return intf; + } + + public UsbInterface[] newArray(int size) { + return new UsbInterface[size]; + } + }; + + public int describeContents() { + return 0; + } + + 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); + parcel.writeParcelableArray(mEndpoints, 0); + } +} diff --git a/android/hardware/usb/UsbManager.java b/android/hardware/usb/UsbManager.java new file mode 100644 index 00000000..595d8571 --- /dev/null +++ b/android/hardware/usb/UsbManager.java @@ -0,0 +1,737 @@ +/* + * 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. + */ + + +package android.hardware.usb; + +import android.Manifest; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import java.util.HashMap; + +/** + * This class allows you to access the state of USB and communicate with USB devices. + * Currently only host mode is supported in the public API. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about communicating with USB hardware, read the + * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB developer guide</a>.</p> + * </div> + */ +@SystemService(Context.USB_SERVICE) +public class UsbManager { + private static final String TAG = "UsbManager"; + + /** + * Broadcast Action: A sticky broadcast for USB state change events when in device mode. + * + * This is a sticky broadcast for clients that includes USB connected/disconnected state, + * <ul> + * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected. + * <li> {@link #USB_HOST_CONNECTED} boolean indicating whether USB is connected or + * disconnected as host. + * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured. + * currently zero if not configured, one for configured. + * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the + * adb function is enabled + * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the + * RNDIS ethernet function is enabled + * <li> {@link #USB_FUNCTION_MTP} boolean extra indicating whether the + * MTP function is enabled + * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the + * PTP function is enabled + * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the + * accessory function is enabled + * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the + * audio source function is enabled + * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the + * MIDI function is enabled + * </ul> + * If the sticky intent has not been found, that indicates USB is disconnected, + * USB is not configued, MTP function is enabled, and all the other functions are disabled. + * + * {@hide} + */ + public static final String ACTION_USB_STATE = + "android.hardware.usb.action.USB_STATE"; + + /** + * Broadcast Action: A broadcast for USB port changes. + * + * This intent is sent when a USB port is added, removed, or changes state. + * <ul> + * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort} + * for the port. + * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus} + * for the port, or null if the port has been removed + * </ul> + * + * @hide + */ + public static final String ACTION_USB_PORT_CHANGED = + "android.hardware.usb.action.USB_PORT_CHANGED"; + + /** + * Broadcast Action: A broadcast for USB device attached event. + * + * This intent is sent when a USB device is attached to the USB bus when in host mode. + * <ul> + * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice} + * for the attached device + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_DEVICE_ATTACHED = + "android.hardware.usb.action.USB_DEVICE_ATTACHED"; + + /** + * Broadcast Action: A broadcast for USB device detached event. + * + * This intent is sent when a USB device is detached from the USB bus when in host mode. + * <ul> + * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice} + * for the detached device + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_DEVICE_DETACHED = + "android.hardware.usb.action.USB_DEVICE_DETACHED"; + + /** + * Broadcast Action: A broadcast for USB accessory attached event. + * + * This intent is sent when a USB accessory is attached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory} + * for the attached accessory + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_ACCESSORY_ATTACHED = + "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"; + + /** + * Broadcast Action: A broadcast for USB accessory detached event. + * + * This intent is sent when a USB accessory is detached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory} + * for the attached accessory that was detached + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_ACCESSORY_DETACHED = + "android.hardware.usb.action.USB_ACCESSORY_DETACHED"; + + /** + * Boolean extra indicating whether USB is connected or disconnected. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + * + * {@hide} + */ + public static final String USB_CONNECTED = "connected"; + + /** + * Boolean extra indicating whether USB is connected or disconnected as host. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + * + * {@hide} + */ + public static final String USB_HOST_CONNECTED = "host_connected"; + + /** + * Boolean extra indicating whether USB is configured. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + * + * {@hide} + */ + public static final String USB_CONFIGURED = "configured"; + + /** + * Boolean extra indicating whether confidential user data, such as photos, should be + * made available on the USB connection. This variable will only be set when the user + * has explicitly asked for this data to be unlocked. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + * + * {@hide} + */ + public static final String USB_DATA_UNLOCKED = "unlocked"; + + /** + * Boolean extra indicating whether the intent represents a change in the usb + * configuration (as opposed to a state update). + * + * {@hide} + */ + public static final String USB_CONFIG_CHANGED = "config_changed"; + + /** + * A placeholder indicating that no USB function is being specified. + * Used to distinguish between selecting no function vs. the default function in + * {@link #setCurrentFunction(String)}. + * + * {@hide} + */ + public static final String USB_FUNCTION_NONE = "none"; + + /** + * Name of the adb USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_ADB = "adb"; + + /** + * Name of the RNDIS ethernet USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_RNDIS = "rndis"; + + /** + * Name of the MTP USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_MTP = "mtp"; + + /** + * Name of the PTP USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_PTP = "ptp"; + + /** + * Name of the audio source USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source"; + + /** + * Name of the MIDI USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_MIDI = "midi"; + + /** + * Name of the Accessory USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + public static final String USB_FUNCTION_ACCESSORY = "accessory"; + + /** + * Name of extra for {@link #ACTION_USB_PORT_CHANGED} + * containing the {@link UsbPort} object for the port. + * + * @hide + */ + public static final String EXTRA_PORT = "port"; + + /** + * Name of extra for {@link #ACTION_USB_PORT_CHANGED} + * containing the {@link UsbPortStatus} object for the port, or null if the port + * was removed. + * + * @hide + */ + public static final String EXTRA_PORT_STATUS = "portStatus"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and + * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts + * containing the {@link UsbDevice} object for the device. + */ + public static final String EXTRA_DEVICE = "device"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and + * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts + * containing the {@link UsbAccessory} object for the accessory. + */ + public static final String EXTRA_ACCESSORY = "accessory"; + + /** + * Name of extra added to the {@link android.app.PendingIntent} + * passed into {@link #requestPermission(UsbDevice, PendingIntent)} + * or {@link #requestPermission(UsbAccessory, PendingIntent)} + * containing a boolean value indicating whether the user granted permission or not. + */ + public static final String EXTRA_PERMISSION_GRANTED = "permission"; + + private final Context mContext; + private final IUsbManager mService; + + /** + * {@hide} + */ + public UsbManager(Context context, IUsbManager service) { + mContext = context; + mService = service; + } + + /** + * Returns a HashMap containing all USB devices currently attached. + * USB device name is the key for the returned HashMap. + * The result will be empty if no devices are attached, or if + * USB host mode is inactive or unsupported. + * + * @return HashMap containing all connected USB devices. + */ + public HashMap<String,UsbDevice> getDeviceList() { + HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); + if (mService == null) { + return result; + } + Bundle bundle = new Bundle(); + try { + mService.getDeviceList(bundle); + for (String name : bundle.keySet()) { + result.put(name, (UsbDevice)bundle.get(name)); + } + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Opens the device so it can be used to send and receive + * data using {@link android.hardware.usb.UsbRequest}. + * + * @param device the device to open + * @return a {@link UsbDeviceConnection}, or {@code null} if open failed + */ + public UsbDeviceConnection openDevice(UsbDevice device) { + try { + String deviceName = device.getDeviceName(); + ParcelFileDescriptor pfd = mService.openDevice(deviceName); + if (pfd != null) { + UsbDeviceConnection connection = new UsbDeviceConnection(device); + boolean result = connection.open(deviceName, pfd, mContext); + pfd.close(); + if (result) { + return connection; + } + } + } catch (Exception e) { + Log.e(TAG, "exception in UsbManager.openDevice", e); + } + return null; + } + + /** + * Returns a list of currently attached USB accessories. + * (in the current implementation there can be at most one) + * + * @return list of USB accessories, or null if none are attached. + */ + public UsbAccessory[] getAccessoryList() { + if (mService == null) { + return null; + } + try { + UsbAccessory accessory = mService.getCurrentAccessory(); + if (accessory == null) { + return null; + } else { + return new UsbAccessory[] { accessory }; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Opens a file descriptor for reading and writing data to the USB accessory. + * + * @param accessory the USB accessory to open + * @return file descriptor, or null if the accessor could not be opened. + */ + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + try { + return mService.openAccessory(accessory); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns true if the caller has permission to access the device. + * Permission might have been granted temporarily via + * {@link #requestPermission(UsbDevice, PendingIntent)} or + * by the user choosing the caller as the default application for the device. + * + * @param device to check permissions for + * @return true if caller has permission + */ + public boolean hasPermission(UsbDevice device) { + if (mService == null) { + return false; + } + try { + return mService.hasDevicePermission(device); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns true if the caller has permission to access the accessory. + * Permission might have been granted temporarily via + * {@link #requestPermission(UsbAccessory, PendingIntent)} or + * by the user choosing the caller as the default application for the accessory. + * + * @param accessory to check permissions for + * @return true if caller has permission + */ + public boolean hasPermission(UsbAccessory accessory) { + if (mService == null) { + return false; + } + try { + return mService.hasAccessoryPermission(accessory); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests temporary permission for the given package to access the device. + * This may result in a system dialog being displayed to the user + * if permission had not already been granted. + * Success or failure is returned via the {@link android.app.PendingIntent} pi. + * If successful, this grants the caller permission to access the device only + * until the device is disconnected. + * + * The following extras will be added to pi: + * <ul> + * <li> {@link #EXTRA_DEVICE} containing the device passed into this call + * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + * </ul> + * + * @param device to request permissions for + * @param pi PendingIntent for returning result + */ + public void requestPermission(UsbDevice device, PendingIntent pi) { + try { + mService.requestDevicePermission(device, mContext.getPackageName(), pi); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests temporary permission for the given package to access the accessory. + * This may result in a system dialog being displayed to the user + * if permission had not already been granted. + * Success or failure is returned via the {@link android.app.PendingIntent} pi. + * If successful, this grants the caller permission to access the accessory only + * until the device is disconnected. + * + * The following extras will be added to pi: + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call + * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + * </ul> + * + * @param accessory to request permissions for + * @param pi PendingIntent for returning result + */ + public void requestPermission(UsbAccessory accessory, PendingIntent pi) { + try { + mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Grants permission for USB device without showing system dialog. + * Only system components can call this function. + * @param device to request permissions for + * + * {@hide} + */ + public void grantPermission(UsbDevice device) { + grantPermission(device, Process.myUid()); + } + + /** + * Grants permission for USB device to given uid without showing system dialog. + * Only system components can call this function. + * @param device to request permissions for + * @uid uid to give permission + * + * {@hide} + */ + public void grantPermission(UsbDevice device, int uid) { + try { + mService.grantDevicePermission(device, uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Grants permission to specified package for USB device without showing system dialog. + * Only system components can call this function, as it requires the MANAGE_USB permission. + * @param device to request permissions for + * @param packageName of package to grant permissions + * + * {@hide} + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) + public void grantPermission(UsbDevice device, String packageName) { + try { + int uid = mContext.getPackageManager() + .getPackageUidAsUser(packageName, mContext.getUserId()); + grantPermission(device, uid); + } catch (NameNotFoundException e) { + Log.e(TAG, "Package " + packageName + " not found.", e); + } + } + + /** + * Returns true if the specified USB function is currently enabled when in device mode. + * <p> + * USB functions represent interfaces which are published to the host to access + * services offered by the device. + * </p> + * + * @param function name of the USB function + * @return true if the USB function is enabled + * + * {@hide} + */ + public boolean isFunctionEnabled(String function) { + if (mService == null) { + return false; + } + try { + return mService.isFunctionEnabled(function); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the current USB function when in device mode. + * <p> + * USB functions represent interfaces which are published to the host to access + * services offered by the device. + * </p><p> + * This method is intended to select among primary USB functions. The system may + * automatically activate additional functions such as {@link #USB_FUNCTION_ADB} + * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states. + * </p><p> + * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE}, + * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, + * or {@link #USB_FUNCTION_RNDIS}. + * </p><p> + * Also sets whether USB data (for example, MTP exposed pictures) should be made available + * on the USB connection when in device mode. Unlocking usb data should only be done with + * user involvement, since exposing pictures or other data could leak sensitive + * user information. + * </p><p> + * Note: This function is asynchronous and may fail silently without applying + * the requested changes. + * </p> + * + * @param function name of the USB function, or null to restore the default function + * @param usbDataUnlocked whether user data is accessible + * + * {@hide} + */ + public void setCurrentFunction(String function, boolean usbDataUnlocked) { + try { + mService.setCurrentFunction(function, usbDataUnlocked); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns a list of physical USB ports on the device. + * <p> + * This list is guaranteed to contain all dual-role USB Type C ports but it might + * be missing other ports depending on whether the kernel USB drivers have been + * updated to publish all of the device's ports through the new "dual_role_usb" + * device class (which supports all types of ports despite its name). + * </p> + * + * @return The list of USB ports, or null if none. + * + * @hide + */ + public UsbPort[] getPorts() { + if (mService == null) { + return null; + } + try { + return mService.getPorts(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the status of the specified USB port. + * + * @param port The port to query. + * @return The status of the specified USB port, or null if unknown. + * + * @hide + */ + public UsbPortStatus getPortStatus(UsbPort port) { + Preconditions.checkNotNull(port, "port must not be null"); + + try { + return mService.getPortStatus(port.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the desired role combination of the port. + * <p> + * The supported role combinations depend on what is connected to the port and may be + * determined by consulting + * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}. + * </p><p> + * Note: This function is asynchronous and may fail silently without applying + * the requested changes. If this function does cause a status change to occur then + * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent. + * </p> + * + * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE} + * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role. + * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST} + * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role. + * + * @hide + */ + public void setPortRoles(UsbPort port, int powerRole, int dataRole) { + Preconditions.checkNotNull(port, "port must not be null"); + UsbPort.checkRoles(powerRole, dataRole); + + Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName()); + try { + mService.setPortRoles(port.getId(), powerRole, dataRole); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the component that will handle USB device connection. + * <p> + * Setting component allows to specify external USB host manager to handle use cases, where + * selection dialog for an activity that will handle USB device is undesirable. + * Only system components can call this function, as it requires the MANAGE_USB permission. + * + * @param usbDeviceConnectionHandler The component to handle usb connections, + * {@code null} to unset. + * + * {@hide} + */ + public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { + try { + mService.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public static String addFunction(String functions, String function) { + if (USB_FUNCTION_NONE.equals(functions)) { + return function; + } + if (!containsFunction(functions, function)) { + if (functions.length() > 0) { + functions += ","; + } + functions += function; + } + return functions; + } + + /** @hide */ + public static String removeFunction(String functions, String function) { + String[] split = functions.split(","); + for (int i = 0; i < split.length; i++) { + if (function.equals(split[i])) { + split[i] = null; + } + } + if (split.length == 1 && split[0] == null) { + return USB_FUNCTION_NONE; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < split.length; i++) { + String s = split[i]; + if (s != null) { + if (builder.length() > 0) { + builder.append(","); + } + builder.append(s); + } + } + return builder.toString(); + } + + /** @hide */ + public static boolean containsFunction(String functions, String function) { + int index = functions.indexOf(function); + if (index < 0) return false; + if (index > 0 && functions.charAt(index - 1) != ',') return false; + int charAfter = index + function.length(); + if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; + return true; + } +} diff --git a/android/hardware/usb/UsbPort.java b/android/hardware/usb/UsbPort.java new file mode 100644 index 00000000..afdb2022 --- /dev/null +++ b/android/hardware/usb/UsbPort.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2015 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.hardware.usb.V1_0.Constants; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Represents a physical USB port and describes its characteristics. + * <p> + * This object is immutable. + * </p> + * + * @hide + */ +public final class UsbPort implements Parcelable { + private final String mId; + private final int mSupportedModes; + + public static final int MODE_NONE = Constants.PortMode.NONE; + /** + * Mode bit: This USB port can act as a downstream facing port (host). + * <p> + * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST} + * combination of roles (and possibly others as well). + * </p> + */ + public static final int MODE_DFP = Constants.PortMode.DFP; + + /** + * Mode bit: This USB port can act as an upstream facing port (device). + * <p> + * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE} + * combination of roles (and possibly others as well). + * </p> + */ + public static final int MODE_UFP = Constants.PortMode.UFP; + + /** + * Mode bit: This USB port can act either as an downstream facing port (host) or as + * an upstream facing port (device). + * <p> + * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST} + * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE} + * combination of roles (and possibly others as well). + * </p> + */ + public static final int MODE_DUAL = Constants.PortMode.DRP; + + /** + * Mode bit: This USB port can support USB Type-C Audio accessory. + */ + public static final int MODE_AUDIO_ACCESSORY = + android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY; + + /** + * Mode bit: This USB port can support USB Type-C debug accessory. + */ + public static final int MODE_DEBUG_ACCESSORY = + android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY; + + /** + * Power role: This USB port does not have a power role. + */ + public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE; + + /** + * Power role: This USB port can act as a source (provide power). + */ + public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE; + + /** + * Power role: This USB port can act as a sink (receive power). + */ + public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK; + + /** + * Power role: This USB port does not have a data role. + */ + public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE; + + /** + * Data role: This USB port can act as a host (access data services). + */ + public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST; + + /** + * Data role: This USB port can act as a device (offer data services). + */ + public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE; + + private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES; + /** + * Points to the first power role in the IUsb HAL. + */ + private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE; + + /** @hide */ + public UsbPort(String id, int supportedModes) { + mId = id; + mSupportedModes = supportedModes; + } + + /** + * Gets the unique id of the port. + * + * @return The unique id of the port; not intended for display. + */ + public String getId() { + return mId; + } + + /** + * Gets the supported modes of the port. + * <p> + * The actual mode of the port may vary depending on what is plugged into it. + * </p> + * + * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or + * {@link #MODE_DUAL}. + */ + public int getSupportedModes() { + return mSupportedModes; + } + + /** + * Combines one power and one data role together into a unique value with + * exactly one bit set. This can be used to efficiently determine whether + * a combination of roles is supported by testing whether that bit is present + * in a bit-field. + * + * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE} + * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role. + * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST} + * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role. + * @hide + */ + public static int combineRolesAsBit(int powerRole, int dataRole) { + checkRoles(powerRole, dataRole); + final int index = ((powerRole - POWER_ROLE_OFFSET) * NUM_DATA_ROLES) + dataRole; + return 1 << index; + } + + /** @hide */ + public static String modeToString(int mode) { + StringBuilder modeString = new StringBuilder(); + if (mode == MODE_NONE) { + return "none"; + } + + if ((mode & MODE_DUAL) == MODE_DUAL) { + modeString.append("dual, "); + } else { + if ((mode & MODE_DFP) == MODE_DFP) { + modeString.append("dfp, "); + } else if ((mode & MODE_UFP) == MODE_UFP) { + modeString.append("ufp, "); + } + } + if ((mode & MODE_AUDIO_ACCESSORY) == MODE_AUDIO_ACCESSORY) { + modeString.append("audio_acc, "); + } + if ((mode & MODE_DEBUG_ACCESSORY) == MODE_DEBUG_ACCESSORY) { + modeString.append("debug_acc, "); + } + + if (modeString.length() == 0) { + return Integer.toString(mode); + } + return modeString.substring(0, modeString.length() - 2); + } + + /** @hide */ + public static String powerRoleToString(int role) { + switch (role) { + case POWER_ROLE_NONE: + return "no-power"; + case POWER_ROLE_SOURCE: + return "source"; + case POWER_ROLE_SINK: + return "sink"; + default: + return Integer.toString(role); + } + } + + /** @hide */ + public static String dataRoleToString(int role) { + switch (role) { + case DATA_ROLE_NONE: + return "no-data"; + case DATA_ROLE_HOST: + return "host"; + case DATA_ROLE_DEVICE: + return "device"; + default: + return Integer.toString(role); + } + } + + /** @hide */ + public static String roleCombinationsToString(int combo) { + StringBuilder result = new StringBuilder(); + result.append("["); + + boolean first = true; + while (combo != 0) { + final int index = Integer.numberOfTrailingZeros(combo); + combo &= ~(1 << index); + final int powerRole = (index / NUM_DATA_ROLES + POWER_ROLE_OFFSET); + final int dataRole = index % NUM_DATA_ROLES; + if (first) { + first = false; + } else { + result.append(", "); + } + result.append(powerRoleToString(powerRole)); + result.append(':'); + result.append(dataRoleToString(dataRole)); + } + + result.append("]"); + return result.toString(); + } + + /** @hide */ + public static void checkMode(int powerRole) { + Preconditions.checkArgumentInRange(powerRole, Constants.PortMode.NONE, + Constants.PortMode.NUM_MODES - 1, "portMode"); + } + + /** @hide */ + public static void checkPowerRole(int dataRole) { + Preconditions.checkArgumentInRange(dataRole, Constants.PortPowerRole.NONE, + Constants.PortPowerRole.NUM_POWER_ROLES - 1, "powerRole"); + } + + /** @hide */ + public static void checkDataRole(int mode) { + Preconditions.checkArgumentInRange(mode, Constants.PortDataRole.NONE, + Constants.PortDataRole.NUM_DATA_ROLES - 1, "powerRole"); + } + + /** @hide */ + public static void checkRoles(int powerRole, int dataRole) { + Preconditions.checkArgumentInRange(powerRole, POWER_ROLE_NONE, POWER_ROLE_SINK, + "powerRole"); + Preconditions.checkArgumentInRange(dataRole, DATA_ROLE_NONE, DATA_ROLE_DEVICE, "dataRole"); + } + + /** @hide */ + public boolean isModeSupported(int mode) { + if ((mSupportedModes & mode) == mode) return true; + return false; + } + + + @Override + public String toString() { + return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mId); + dest.writeInt(mSupportedModes); + } + + public static final Parcelable.Creator<UsbPort> CREATOR = + new Parcelable.Creator<UsbPort>() { + @Override + public UsbPort createFromParcel(Parcel in) { + String id = in.readString(); + int supportedModes = in.readInt(); + return new UsbPort(id, supportedModes); + } + + @Override + public UsbPort[] newArray(int size) { + return new UsbPort[size]; + } + }; +} diff --git a/android/hardware/usb/UsbPortStatus.java b/android/hardware/usb/UsbPortStatus.java new file mode 100644 index 00000000..5c0e81ad --- /dev/null +++ b/android/hardware/usb/UsbPortStatus.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 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; + +/** + * Describes the status of a USB port. + * <p> + * This object is immutable. + * </p> + * + * @hide + */ +public final class UsbPortStatus implements Parcelable { + private final int mCurrentMode; + private final int mCurrentPowerRole; + private final int mCurrentDataRole; + private final int mSupportedRoleCombinations; + + /** @hide */ + public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, + int supportedRoleCombinations) { + mCurrentMode = currentMode; + mCurrentPowerRole = currentPowerRole; + mCurrentDataRole = currentDataRole; + mSupportedRoleCombinations = supportedRoleCombinations; + } + + /** + * Returns true if there is anything connected to the port. + * + * @return True if there is anything connected to the port. + */ + public boolean isConnected() { + return mCurrentMode != 0; + } + + /** + * Gets the current mode of the port. + * + * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP}, + * or 0 if nothing is connected. + */ + public int getCurrentMode() { + return mCurrentMode; + } + + /** + * Gets the current power role of the port. + * + * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE}, + * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected. + */ + public int getCurrentPowerRole() { + return mCurrentPowerRole; + } + + /** + * Gets the current data role of the port. + * + * @return The current data role: {@link UsbPort#DATA_ROLE_HOST}, + * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected. + */ + public int getCurrentDataRole() { + return mCurrentDataRole; + } + + /** + * Returns true if the specified power and data role combination is supported + * given what is currently connected to the port. + * + * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE} + * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role. + * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST} + * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role. + */ + public boolean isRoleCombinationSupported(int powerRole, int dataRole) { + return (mSupportedRoleCombinations & + UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0; + } + + /** @hide */ + public int getSupportedRoleCombinations() { + return mSupportedRoleCombinations; + } + + @Override + public String toString() { + return "UsbPortStatus{connected=" + isConnected() + + ", currentMode=" + UsbPort.modeToString(mCurrentMode) + + ", currentPowerRole=" + UsbPort.powerRoleToString(mCurrentPowerRole) + + ", currentDataRole=" + UsbPort.dataRoleToString(mCurrentDataRole) + + ", supportedRoleCombinations=" + + UsbPort.roleCombinationsToString(mSupportedRoleCombinations) + + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCurrentMode); + dest.writeInt(mCurrentPowerRole); + dest.writeInt(mCurrentDataRole); + dest.writeInt(mSupportedRoleCombinations); + } + + public static final Parcelable.Creator<UsbPortStatus> CREATOR = + new Parcelable.Creator<UsbPortStatus>() { + @Override + public UsbPortStatus createFromParcel(Parcel in) { + int currentMode = in.readInt(); + int currentPowerRole = in.readInt(); + int currentDataRole = in.readInt(); + int supportedRoleCombinations = in.readInt(); + return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, + supportedRoleCombinations); + } + + @Override + public UsbPortStatus[] newArray(int size) { + return new UsbPortStatus[size]; + } + }; +} diff --git a/android/hardware/usb/UsbRequest.java b/android/hardware/usb/UsbRequest.java new file mode 100644 index 00000000..2e8f8e12 --- /dev/null +++ b/android/hardware/usb/UsbRequest.java @@ -0,0 +1,359 @@ +/* + * 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. + */ + +package android.hardware.usb; + +import android.annotation.Nullable; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import dalvik.system.CloseGuard; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * A class representing USB request packet. + * This can be used for both reading and writing data to or from a + * {@link android.hardware.usb.UsbDeviceConnection}. + * UsbRequests can be used to transfer data on bulk and interrupt endpoints. + * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer} + * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}. + * Requests on interrupt endpoints are only send and received asynchronously. + * + * <p>Requests on endpoint zero are not supported by this class; + * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead. + */ +public class UsbRequest { + + private static final String TAG = "UsbRequest"; + + // From drivers/usb/core/devio.c + private static final int MAX_USBFS_BUFFER_SIZE = 16384; + + // used by the JNI code + private long mNativeContext; + + private UsbEndpoint mEndpoint; + + /** The buffer that is currently being read / written */ + private ByteBuffer mBuffer; + + /** The amount of data to read / write when using {@link #queue} */ + private int mLength; + + // for client use + private Object mClientData; + + // Prevent the connection from being finalized + private UsbDeviceConnection mConnection; + + /** + * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or + * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. + */ + private boolean mIsUsingNewQueue; + + /** Temporary buffer than might be used while buffer is enqueued */ + private ByteBuffer mTempBuffer; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + /** + * Lock for queue, enqueue and dequeue, so a queue operation can be finished by a dequeue + * operation on a different thread. + */ + private final Object mLock = new Object(); + + public UsbRequest() { + } + + /** + * Initializes the request so it can read or write data on the given endpoint. + * Whether the request allows reading or writing depends on the direction of the endpoint. + * + * @param endpoint the endpoint to be used for this request. + * @return true if the request was successfully opened. + */ + public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) { + mEndpoint = endpoint; + mConnection = Preconditions.checkNotNull(connection, "connection"); + + boolean wasInitialized = native_init(connection, endpoint.getAddress(), + endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval()); + + if (wasInitialized) { + mCloseGuard.open("close"); + } + + return wasInitialized; + } + + /** + * Releases all resources related to this request. + */ + public void close() { + if (mNativeContext != 0) { + mEndpoint = null; + mConnection = null; + native_close(); + mCloseGuard.close(); + } + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + close(); + } finally { + super.finalize(); + } + } + + /** + * Returns the endpoint for the request, or null if the request is not opened. + * + * @return the request's endpoint + */ + public UsbEndpoint getEndpoint() { + return mEndpoint; + } + + /** + * Returns the client data for the request. + * This can be used in conjunction with {@link #setClientData} + * to associate another object with this request, which can be useful for + * maintaining state between calls to {@link #queue} and + * {@link android.hardware.usb.UsbDeviceConnection#requestWait} + * + * @return the client data for the request + */ + public Object getClientData() { + return mClientData; + } + + /** + * Sets the client data for the request. + * This can be used in conjunction with {@link #getClientData} + * to associate another object with this request, which can be useful for + * maintaining state between calls to {@link #queue} and + * {@link android.hardware.usb.UsbDeviceConnection#requestWait} + * + * @param data the client data for the request + */ + public void setClientData(Object data) { + mClientData = data; + } + + /** + * Queues the request to send or receive data on its endpoint. + * <p>For OUT endpoints, the given buffer data will be sent on the endpoint. For IN endpoints, + * the endpoint will attempt to read the given number of bytes into the specified buffer. If the + * queueing operation is successful, return true. The result will be returned via + * {@link UsbDeviceConnection#requestWait}</p> + * + * @param buffer the buffer containing the bytes to write, or location to store the results of a + * read. Position and array offset will be ignored and assumed to be 0. Limit and + * capacity will be ignored. Once the request + * {@link UsbDeviceConnection#requestWait() is processed} the position will be set + * to the number of bytes read/written. + * @param length number of bytes to read or write. + * + * @return true if the queueing operation succeeded + * + * @deprecated Use {@link #queue(ByteBuffer)} instead. + */ + @Deprecated + public boolean queue(ByteBuffer buffer, int length) { + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); + boolean result; + + synchronized (mLock) { + // save our buffer for when the request has completed + mBuffer = buffer; + mLength = length; + + // Note: On a buffer slice we lost the capacity information about the underlying buffer, + // hence we cannot check if the access would be a data leak/memory corruption. + + if (buffer.isDirect()) { + result = native_queue_direct(buffer, length, out); + } else if (buffer.hasArray()) { + result = native_queue_array(buffer.array(), length, out); + } else { + throw new IllegalArgumentException("buffer is not direct and has no array"); + } + if (!result) { + mBuffer = null; + mLength = 0; + } + } + + return result; + } + + /** + * Queues the request to send or receive data on its endpoint. + * + * <p>For OUT endpoints, the remaining bytes of the buffer will be sent on the endpoint. For IN + * endpoints, the endpoint will attempt to fill the remaining bytes of the buffer. If the + * queueing operation is successful, return true. The result will be returned via + * {@link UsbDeviceConnection#requestWait}</p> + * + * @param buffer the buffer containing the bytes to send, or the buffer to fill. The state + * of the buffer is undefined until the request is returned by + * {@link UsbDeviceConnection#requestWait}. If the request failed the buffer + * will be unchanged; if the request succeeded the position of the buffer is + * incremented by the number of bytes sent/received. + * + * @return true if the queueing operation succeeded + */ + public boolean queue(@Nullable ByteBuffer buffer) { + // Request need to be initialized + Preconditions.checkState(mNativeContext != 0, "request is not initialized"); + + // Request can not be currently queued + Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); + + boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); + boolean wasQueued; + + synchronized (mLock) { + mBuffer = buffer; + + if (buffer == null) { + // Null buffers enqueue empty USB requests which is supported + mIsUsingNewQueue = true; + wasQueued = native_queue(null, 0, 0); + } else { + // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once + Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, + "number of remaining bytes"); + + // Can not receive into read-only buffers. + Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be " + + "read-only when receiving data"); + + if (!buffer.isDirect()) { + mTempBuffer = ByteBuffer.allocateDirect(mBuffer.remaining()); + + if (isSend) { + // Copy buffer into temporary buffer + mBuffer.mark(); + mTempBuffer.put(mBuffer); + mTempBuffer.flip(); + mBuffer.reset(); + } + + // Send/Receive into the temp buffer instead + buffer = mTempBuffer; + } + + mIsUsingNewQueue = true; + wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); + } + } + + if (!wasQueued) { + mIsUsingNewQueue = false; + mTempBuffer = null; + mBuffer = null; + } + + return wasQueued; + } + + /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) { + boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); + int bytesTransferred; + + synchronized (mLock) { + if (mIsUsingNewQueue) { + bytesTransferred = native_dequeue_direct(); + mIsUsingNewQueue = false; + + if (mBuffer == null) { + // Nothing to do + } else if (mTempBuffer == null) { + mBuffer.position(mBuffer.position() + bytesTransferred); + } else { + mTempBuffer.limit(bytesTransferred); + + // The user might have modified mBuffer which might make put/position fail. + // Changing the buffer while a request is in flight is not supported. Still, + // make sure to free mTempBuffer correctly. + try { + if (isSend) { + mBuffer.position(mBuffer.position() + bytesTransferred); + } else { + // Copy temp buffer back into original buffer + mBuffer.put(mTempBuffer); + } + } finally { + mTempBuffer = null; + } + } + } else { + if (mBuffer.isDirect()) { + bytesTransferred = native_dequeue_direct(); + } else { + bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend); + } + if (bytesTransferred >= 0) { + int bytesToStore = Math.min(bytesTransferred, mLength); + try { + mBuffer.position(bytesToStore); + } catch (IllegalArgumentException e) { + if (useBufferOverflowInsteadOfIllegalArg) { + Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read " + + bytesToStore + " bytes", e); + throw new BufferOverflowException(); + } else { + throw e; + } + } + } + } + + mBuffer = null; + mLength = 0; + } + } + + /** + * Cancels a pending queue operation. + * + * @return true if cancelling succeeded + */ + public boolean cancel() { + return native_cancel(); + } + + private native boolean native_init(UsbDeviceConnection connection, int ep_address, + int ep_attributes, int ep_max_packet_size, int ep_interval); + private native void native_close(); + private native boolean native_queue(ByteBuffer buffer, int offset, int length); + private native boolean native_queue_array(byte[] buffer, int length, boolean out); + private native int native_dequeue_array(byte[] buffer, int length, boolean out); + private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); + private native int native_dequeue_direct(); + private native boolean native_cancel(); +} |