| /* |
| * 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<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); |
| } |
| } |
| } |
| } |
| } |
| } |