This program serves as an example of the following USB host functions:
Appropriate devices based on interface class, subclass, and protocol (see device_filter.xml)
Volume Asynchronous I / O
All code Copyright:
Adbdevice
package com.android.adb; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbRequest; import android.util.SparseArray; import java.util.LinkedList; /* This class represents a USB device that supports the adb protocol. */ public class AdbDevice { private final AdbTestActivity mActivity; private final UsbDeviceConnection mDeviceConnection; private final UsbEndpoint mEndpointOut; private final UsbEndpoint mEndpointIn; private String mSerial; // pool of requests for the OUT endpoint private final LinkedList<UsbRequest> mOutRequestPool = new LinkedList<UsbRequest>(); // pool of requests for the IN endpoint private final LinkedList<UsbRequest> mInRequestPool = new LinkedList<UsbRequest>(); // list of currently opened sockets private final SparseArray<AdbSocket> mSockets = new SparseArray<AdbSocket>(); private int mNextSocketId = 1; private final WaiterThread mWaiterThread = new WaiterThread(); public AdbDevice(AdbTestActivity activity, UsbDeviceConnection connection, UsbInterface intf) { mActivity = activity; mDeviceConnection = connection; mSerial = connection.getSerial(); UsbEndpoint epOut = null; UsbEndpoint epIn = null; // look for our bulk endpoints for (int i = 0; i < intf.getEndpointCount(); i++) { UsbEndpoint ep = intf.getEndpoint(i); if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (ep.getDirection() == UsbConstants.USB_DIR_OUT) { epOut = ep; } else { epIn = ep; } } } if (epOut == null || epIn == null) { throw new IllegalArgumentException("not all endpoints found"); } mEndpointOut = epOut; mEndpointIn = epIn; } // return device serial number public String getSerial() { return mSerial; } // get an OUT request from our pool public UsbRequest getOutRequest() { synchronized(mOutRequestPool) { if (mOutRequestPool.isEmpty()) { UsbRequest request = new UsbRequest(); request.initialize(mDeviceConnection, mEndpointOut); return request; } else { return mOutRequestPool.removeFirst(); } } } // return an OUT request to the pool public void releaseOutRequest(UsbRequest request) { synchronized (mOutRequestPool) { mOutRequestPool.add(request); } } // get an IN request from the pool public UsbRequest getInRequest() { synchronized(mInRequestPool) { if (mInRequestPool.isEmpty()) { UsbRequest request = new UsbRequest(); request.initialize(mDeviceConnection, mEndpointIn); return request; } else { return mInRequestPool.removeFirst(); } } } public void start() { mWaiterThread.start(); connect(); } public AdbSocket openSocket(String destination) { AdbSocket socket; synchronized (mSockets) { int id = mNextSocketId++; socket = new AdbSocket(this, id); mSockets.put(id, socket); } if (socket.open(destination)) { return socket; } else { return null; } } private AdbSocket getSocket(int id) { synchronized (mSockets) { return mSockets.get(id); } } public void socketClosed(AdbSocket socket) { synchronized (mSockets) { mSockets.remove(socket.getId()); } } // send a connect command private void connect() { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_CNXN, AdbMessage.A_VERSION, AdbMessage.MAX_PAYLOAD, "host::\0"); message.write(this); } // handle connect response private void handleConnect(AdbMessage message) { if (message.getDataString().startsWith("device:")) { log("connected"); mActivity.deviceOnline(this); } } public void stop() { synchronized (mWaiterThread) { mWaiterThread.mStop = true; } } // dispatch a message from the device void dispatchMessage(AdbMessage message) { int command = message.getCommand(); switch (command) { case AdbMessage.A_SYNC: log("got A_SYNC"); break; case AdbMessage.A_CNXN: handleConnect(message); break; case AdbMessage.A_OPEN: case AdbMessage.A_OKAY: case AdbMessage.A_CLSE: case AdbMessage.A_WRTE: AdbSocket socket = getSocket(message.getArg1()); if (socket == null) { log("ERROR socket not found"); } else { socket.handleMessage(message); } break; } } void log(String s) { mActivity.log(s); } private class WaiterThread extends Thread { public boolean mStop; public void run() { // start out with a command read AdbMessage currentCommand = new AdbMessage(); AdbMessage currentData = null; // FIXME error checking currentCommand.readCommand(getInRequest()); while (true) { synchronized (this) { if (mStop) { return; } } UsbRequest request = mDeviceConnection.requestWait(); if (request == null) { break; } AdbMessage message = (AdbMessage)request.getClientData(); request.setClientData(null); AdbMessage messageToDispatch = null; if (message == currentCommand) { int dataLength = message.getDataLength(); // read data if length > 0 if (dataLength > 0) { message.readData(getInRequest(), dataLength); currentData = message; } else { messageToDispatch = message; } currentCommand = null; } else if (message == currentData) { messageToDispatch = message; currentData = null; } if (messageToDispatch != null) { // queue another read first currentCommand = new AdbMessage(); currentCommand.readCommand(getInRequest()); // then dispatch the current message dispatchMessage(messageToDispatch); } // put request back into the appropriate pool if (request.getEndpoint() == mEndpointOut) { releaseOutRequest(request); } else { synchronized (mInRequestPool) { mInRequestPool.add(request); } } } } } }
Adbmessage
package com.android.adb; import android.hardware.usb.UsbRequest; import java.nio.ByteBuffer; import java.nio.ByteOrder; /* This class encapsulates and adb command packet */ public class AdbMessage { // command names public static final int A_SYNC = 0x434e5953; public static final int A_CNXN = 0x4e584e43; public static final int A_OPEN = 0x4e45504f; public static final int A_OKAY = 0x59414b4f; public static final int A_CLSE = 0x45534c43; public static final int A_WRTE = 0x45545257; // ADB protocol version public static final int A_VERSION = 0x01000000; public static final int MAX_PAYLOAD = 4096; private final ByteBuffer mMessageBuffer; private final ByteBuffer mDataBuffer; public AdbMessage() { mMessageBuffer = ByteBuffer.allocate(24); mDataBuffer = ByteBuffer.allocate(MAX_PAYLOAD); mMessageBuffer.order(ByteOrder.LITTLE_ENDIAN); mDataBuffer.order(ByteOrder.LITTLE_ENDIAN); } // sets the fields in the command header public void set(int command, int arg0, int arg1, byte[] data) { mMessageBuffer.putInt(0, command); mMessageBuffer.putInt(4, arg0); mMessageBuffer.putInt(8, arg1); mMessageBuffer.putInt(12, (data == null ? 0 : data.length)); mMessageBuffer.putInt(16, (data == null ? 0 : checksum(data))); mMessageBuffer.putInt(20, command ^ 0xFFFFFFFF); if (data != null) { mDataBuffer.put(data, 0, data.length); } } public void set(int command, int arg0, int arg1) { set(command, arg0, arg1, (byte[])null); } public void set(int command, int arg0, int arg1, String data) { // add trailing zero data += "\0"; set(command, arg0, arg1, data.getBytes()); } // returns the command message ID public int getCommand() { return mMessageBuffer.getInt(0); } // returns command first argument public int getArg0() { return mMessageBuffer.getInt(4); } // returns command second argument public int getArg1() { return mMessageBuffer.getInt(8); } // returns command data buffer public ByteBuffer getData() { return mDataBuffer; } // returns command data length public int getDataLength() { return mMessageBuffer.getInt(12); } // returns command data as a string public String getDataString() { int length = getDataLength(); if (length == 0) return null; // trim trailing zero return new String(mDataBuffer.array(), 0, length - 1); } public boolean write(AdbDevice device) { synchronized (device) { UsbRequest request = device.getOutRequest(); request.setClientData(this); if (request.queue(mMessageBuffer, 24)) { int length = getDataLength(); if (length > 0) { request = device.getOutRequest(); request.setClientData(this); if (request.queue(mDataBuffer, length)) { return true; } else { device.releaseOutRequest(request); return false; } } return true; } else { device.releaseOutRequest(request); return false; } } } public boolean readCommand(UsbRequest request) { request.setClientData(this); return request.queue(mMessageBuffer, 24); } public boolean readData(UsbRequest request, int length) { request.setClientData(this); return request.queue(mDataBuffer, length); } private static String extractString(ByteBuffer buffer, int offset, int length) { byte[] bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = buffer.get(offset++); } return new String(bytes); } @Override public String toString() { String commandName = extractString(mMessageBuffer, 0, 4); int dataLength = getDataLength(); String result = "Adb Message: " + commandName + " arg0: " + getArg0() + " arg1: " + getArg1() + " dataLength: " + dataLength; if (dataLength > 0) { result += (" data: \"" + getDataString() + "\""); } return result; } private static int checksum(byte[] data) { int result = 0; for (int i = 0; i < data.length; i++) { int x = data[i]; // dang, no unsigned ints in java if (x < 0) x += 256; result += x; } return result; } }
Adb socket
package com.android.adb; public class AdbSocket { private final AdbDevice mDevice; private final int mId; private int mPeerId; public AdbSocket(AdbDevice device, int id) { mDevice = device; mId = id; } public int getId() { return mId; } public boolean open(String destination) { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_OPEN, mId, 0, destination); if (! message.write(mDevice)) { return false; } synchronized (this) { try { wait(); } catch (InterruptedException e) { return false; } } return true; } public void handleMessage(AdbMessage message) { switch (message.getCommand()) { case AdbMessage.A_OKAY: mPeerId = message.getArg0(); synchronized (this) { notify(); } break; case AdbMessage.A_WRTE: mDevice.log(message.getDataString()); sendReady(); break; } } private void sendReady() { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_OKAY, mId, mPeerId); message.write(mDevice); } }