This sample implements the "adb logcat" command using USB host APIs. Change-Id: Ifbf122e99358bd3a1cd2a7967c364ffd59f003b3 Signed-off-by: Mike Lockwood <lockwood@android.com>
254 lines
8.0 KiB
Java
254 lines
8.0 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|