/* * 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 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 mOutRequestPool = new LinkedList(); // pool of requests for the IN endpoint private final LinkedList mInRequestPool = new LinkedList(); // list of currently opened sockets private final SparseArray mSockets = new SparseArray(); 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); } } } } } }