1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adb; 18 19 import android.hardware.usb.UsbConstants; 20 import android.hardware.usb.UsbDeviceConnection; 21 import android.hardware.usb.UsbEndpoint; 22 import android.hardware.usb.UsbInterface; 23 import android.hardware.usb.UsbRequest; 24 import android.util.SparseArray; 25 26 import java.util.LinkedList; 27 28 /* This class represents a USB device that supports the adb protocol. */ 29 public class AdbDevice { 30 31 private final AdbTestActivity mActivity; 32 private final UsbDeviceConnection mDeviceConnection; 33 private final UsbEndpoint mEndpointOut; 34 private final UsbEndpoint mEndpointIn; 35 36 private String mSerial; 37 38 // pool of requests for the OUT endpoint 39 private final LinkedList<UsbRequest> mOutRequestPool = new LinkedList<UsbRequest>(); 40 // pool of requests for the IN endpoint 41 private final LinkedList<UsbRequest> mInRequestPool = new LinkedList<UsbRequest>(); 42 // list of currently opened sockets 43 private final SparseArray<AdbSocket> mSockets = new SparseArray<AdbSocket>(); 44 private int mNextSocketId = 1; 45 46 private final WaiterThread mWaiterThread = new WaiterThread(); 47 48 public AdbDevice(AdbTestActivity activity, UsbDeviceConnection connection, 49 UsbInterface intf) { 50 mActivity = activity; 51 mDeviceConnection = connection; 52 mSerial = connection.getSerial(); 53 54 UsbEndpoint epOut = null; 55 UsbEndpoint epIn = null; 56 // look for our bulk endpoints 57 for (int i = 0; i < intf.getEndpointCount(); i++) { 58 UsbEndpoint ep = intf.getEndpoint(i); 59 if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { 60 if (ep.getDirection() == UsbConstants.USB_DIR_OUT) { 61 epOut = ep; 62 } else { 63 epIn = ep; 64 } 65 } 66 } 67 if (epOut == null || epIn == null) { 68 throw new IllegalArgumentException("not all endpoints found"); 69 } 70 mEndpointOut = epOut; 71 mEndpointIn = epIn; 72 } 73 74 // return device serial number 75 public String getSerial() { 76 return mSerial; 77 } 78 79 // get an OUT request from our pool 80 public UsbRequest getOutRequest() { 81 synchronized(mOutRequestPool) { 82 if (mOutRequestPool.isEmpty()) { 83 UsbRequest request = new UsbRequest(); 84 request.initialize(mDeviceConnection, mEndpointOut); 85 return request; 86 } else { 87 return mOutRequestPool.removeFirst(); 88 } 89 } 90 } 91 92 // return an OUT request to the pool 93 public void releaseOutRequest(UsbRequest request) { 94 synchronized (mOutRequestPool) { 95 mOutRequestPool.add(request); 96 } 97 } 98 99 // get an IN request from the pool 100 public UsbRequest getInRequest() { 101 synchronized(mInRequestPool) { 102 if (mInRequestPool.isEmpty()) { 103 UsbRequest request = new UsbRequest(); 104 request.initialize(mDeviceConnection, mEndpointIn); 105 return request; 106 } else { 107 return mInRequestPool.removeFirst(); 108 } 109 } 110 } 111 112 public void start() { 113 mWaiterThread.start(); 114 connect(); 115 } 116 117 public AdbSocket openSocket(String destination) { 118 AdbSocket socket; 119 synchronized (mSockets) { 120 int id = mNextSocketId++; 121 socket = new AdbSocket(this, id); 122 mSockets.put(id, socket); 123 } 124 if (socket.open(destination)) { 125 return socket; 126 } else { 127 return null; 128 } 129 } 130 131 private AdbSocket getSocket(int id) { 132 synchronized (mSockets) { 133 return mSockets.get(id); 134 } 135 } 136 137 public void socketClosed(AdbSocket socket) { 138 synchronized (mSockets) { 139 mSockets.remove(socket.getId()); 140 } 141 } 142 143 // send a connect command 144 private void connect() { 145 AdbMessage message = new AdbMessage(); 146 message.set(AdbMessage.A_CNXN, AdbMessage.A_VERSION, AdbMessage.MAX_PAYLOAD, "host::\0"); 147 message.write(this); 148 } 149 150 // handle connect response 151 private void handleConnect(AdbMessage message) { 152 if (message.getDataString().startsWith("device:")) { 153 log("connected"); 154 mActivity.deviceOnline(this); 155 } 156 } 157 158 public void stop() { 159 synchronized (mWaiterThread) { 160 mWaiterThread.mStop = true; 161 } 162 } 163 164 // dispatch a message from the device 165 void dispatchMessage(AdbMessage message) { 166 int command = message.getCommand(); 167 switch (command) { 168 case AdbMessage.A_SYNC: 169 log("got A_SYNC"); 170 break; 171 case AdbMessage.A_CNXN: 172 handleConnect(message); 173 break; 174 case AdbMessage.A_OPEN: 175 case AdbMessage.A_OKAY: 176 case AdbMessage.A_CLSE: 177 case AdbMessage.A_WRTE: 178 AdbSocket socket = getSocket(message.getArg1()); 179 if (socket == null) { 180 log("ERROR socket not found"); 181 } else { 182 socket.handleMessage(message); 183 } 184 break; 185 } 186 } 187 188 void log(String s) { 189 mActivity.log(s); 190 } 191 192 193 private class WaiterThread extends Thread { 194 public boolean mStop; 195 196 public void run() { 197 // start out with a command read 198 AdbMessage currentCommand = new AdbMessage(); 199 AdbMessage currentData = null; 200 // FIXME error checking 201 currentCommand.readCommand(getInRequest()); 202 203 while (true) { 204 synchronized (this) { 205 if (mStop) { 206 return; 207 } 208 } 209 UsbRequest request = mDeviceConnection.requestWait(); 210 if (request == null) { 211 break; 212 } 213 214 AdbMessage message = (AdbMessage)request.getClientData(); 215 request.setClientData(null); 216 AdbMessage messageToDispatch = null; 217 218 if (message == currentCommand) { 219 int dataLength = message.getDataLength(); 220 // read data if length > 0 221 if (dataLength > 0) { 222 message.readData(getInRequest(), dataLength); 223 currentData = message; 224 } else { 225 messageToDispatch = message; 226 } 227 currentCommand = null; 228 } else if (message == currentData) { 229 messageToDispatch = message; 230 currentData = null; 231 } 232 233 if (messageToDispatch != null) { 234 // queue another read first 235 currentCommand = new AdbMessage(); 236 currentCommand.readCommand(getInRequest()); 237 238 // then dispatch the current message 239 dispatchMessage(messageToDispatch); 240 } 241 242 // put request back into the appropriate pool 243 if (request.getEndpoint() == mEndpointOut) { 244 releaseOutRequest(request); 245 } else { 246 synchronized (mInRequestPool) { 247 mInRequestPool.add(request); 248 } 249 } 250 } 251 } 252 } 253 } 254