am 3385faf4: Add AdbTest sample program
* commit '3385faf4e60e1ac4f4cf387702ef3be211374ef2':
Add AdbTest sample program
diff --git a/samples/USB/AdbTest/Android.mk b/samples/USB/AdbTest/Android.mk
new file mode 100644
index 0000000..bfb9fa8
--- /dev/null
+++ b/samples/USB/AdbTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AdbTest
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/samples/USB/AdbTest/AndroidManifest.xml b/samples/USB/AdbTest/AndroidManifest.xml
new file mode 100644
index 0000000..9690ccf
--- /dev/null
+++ b/samples/USB/AdbTest/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.adb">
+
+ <uses-feature android:name="android.hardware.usb.host" />
+ <uses-sdk android:minSdkVersion="12" />
+
+ <application>
+ <activity android:name="AdbTestActivity" android:label="ADB Test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <intent-filter>
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+ </intent-filter>
+
+ <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/device_filter" />
+ </activity>
+ </application>
+
+</manifest>
diff --git a/samples/USB/AdbTest/README.txt b/samples/USB/AdbTest/README.txt
new file mode 100644
index 0000000..3fd9cbb
--- /dev/null
+++ b/samples/USB/AdbTest/README.txt
@@ -0,0 +1,11 @@
+AdbTest is a sample program that implements a subset of the adb USB protocol.
+Currently it only implements the "adb logcat" command and displays the log
+output in a text view and only allows connecting to one device at a time.
+However the support classes are structured in a way that would allow
+connecting to multiple devices and running multiple adb commands simultaneously.
+
+This program serves as an example of the following USB host features:
+
+- Matching devices based on interface class, subclass and protocol (see device_filter.xml)
+
+- Asynchronous IO on bulk endpoints
\ No newline at end of file
diff --git a/samples/USB/AdbTest/_index.html b/samples/USB/AdbTest/_index.html
new file mode 100644
index 0000000..424ee5b
--- /dev/null
+++ b/samples/USB/AdbTest/_index.html
@@ -0,0 +1,13 @@
+<p>AdbTest is a sample program that implements a subset of the <code>adb</code> USB protocol.
+Currently it only implements the <code>adb logcat</code> command and displays the log
+output in a text view and only allows connecting to one device at a time.
+However, the support classes are structured in a way that would allow
+connecting to multiple devices and running multiple <code>adb</code> commands simultaneously.</p>
+
+<p>This program serves as an example of the following USB host features:</p>
+
+<ul>
+<li>Matching devices based on interface class, subclass and protocol (see <code>device_filter.xml</code>)</li>
+
+<li>Asynchronous IO on bulk endpoints</li>
+</ul>
\ No newline at end of file
diff --git a/samples/USB/AdbTest/default.properties b/samples/USB/AdbTest/default.properties
new file mode 100644
index 0000000..3ac2523
--- /dev/null
+++ b/samples/USB/AdbTest/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-12
diff --git a/samples/USB/AdbTest/res/layout/adb.xml b/samples/USB/AdbTest/res/layout/adb.xml
new file mode 100644
index 0000000..10994d5
--- /dev/null
+++ b/samples/USB/AdbTest/res/layout/adb.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <TextView android:id="@+id/log"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="25dp"
+ android:textSize="18sp"
+ android:textColor="#ffffffff"
+ />
+
+</LinearLayout>
+
+
diff --git a/samples/USB/AdbTest/res/xml/device_filter.xml b/samples/USB/AdbTest/res/xml/device_filter.xml
new file mode 100644
index 0000000..bc6fa04
--- /dev/null
+++ b/samples/USB/AdbTest/res/xml/device_filter.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <usb-device class="255" subclass="66" protocol="1" />
+</resources>
diff --git a/samples/USB/AdbTest/src/com/android/adb/AdbDevice.java b/samples/USB/AdbTest/src/com/android/adb/AdbDevice.java
new file mode 100644
index 0000000..f793ce4
--- /dev/null
+++ b/samples/USB/AdbTest/src/com/android/adb/AdbDevice.java
@@ -0,0 +1,253 @@
+/*
+ * 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);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/samples/USB/AdbTest/src/com/android/adb/AdbMessage.java b/samples/USB/AdbTest/src/com/android/adb/AdbMessage.java
new file mode 100644
index 0000000..1dbb136
--- /dev/null
+++ b/samples/USB/AdbTest/src/com/android/adb/AdbMessage.java
@@ -0,0 +1,170 @@
+/*
+ * 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.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's message ID
+ public int getCommand() {
+ return mMessageBuffer.getInt(0);
+ }
+
+ // returns command's first argument
+ public int getArg0() {
+ return mMessageBuffer.getInt(4);
+ }
+
+ // returns command's second argument
+ public int getArg1() {
+ return mMessageBuffer.getInt(8);
+ }
+
+ // returns command's data buffer
+ public ByteBuffer getData() {
+ return mDataBuffer;
+ }
+
+ // returns command's data length
+ public int getDataLength() {
+ return mMessageBuffer.getInt(12);
+ }
+
+ // returns command's 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;
+ }
+}
\ No newline at end of file
diff --git a/samples/USB/AdbTest/src/com/android/adb/AdbSocket.java b/samples/USB/AdbTest/src/com/android/adb/AdbSocket.java
new file mode 100644
index 0000000..deb5980
--- /dev/null
+++ b/samples/USB/AdbTest/src/com/android/adb/AdbSocket.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+/* This class represents an adb socket. adb supports multiple independent
+ * socket connections to a single device. Typically a socket is created
+ * for each adb command that is executed.
+ */
+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);
+ }
+}
diff --git a/samples/USB/AdbTest/src/com/android/adb/AdbTestActivity.java b/samples/USB/AdbTest/src/com/android/adb/AdbTestActivity.java
new file mode 100644
index 0000000..bd11977
--- /dev/null
+++ b/samples/USB/AdbTest/src/com/android/adb/AdbTestActivity.java
@@ -0,0 +1,211 @@
+/*
+ * 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.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Rect;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.widget.TextView;
+
+/* Main activity for the adb test program */
+public class AdbTestActivity extends Activity {
+
+ private static final String TAG = "AdbTestActivity";
+
+ private TextView mLog;
+ private UsbManager mManager;
+ private UsbDevice mDevice;
+ private UsbDeviceConnection mDeviceConnection;
+ private UsbInterface mInterface;
+ private AdbDevice mAdbDevice;
+
+ private static final int MESSAGE_LOG = 1;
+ private static final int MESSAGE_DEVICE_ONLINE = 2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.adb);
+ mLog = (TextView)findViewById(R.id.log);
+
+ mManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ // check for existing devices
+ for (UsbDevice device : mManager.getDeviceList().values()) {
+ UsbInterface intf = findAdbInterface(device);
+ if (setAdbInterface(device, intf)) {
+ break;
+ }
+ }
+
+ // listen for new devices
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ registerReceiver(mUsbReceiver, filter);
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mUsbReceiver);
+ setAdbInterface(null, null);
+ super.onDestroy();
+ }
+
+ public void log(String s) {
+ Message m = Message.obtain(mHandler, MESSAGE_LOG);
+ m.obj = s;
+ mHandler.sendMessage(m);
+ }
+
+ private void appendLog(String text) {
+ Rect r = new Rect();
+ mLog.getDrawingRect(r);
+ int maxLines = r.height() / mLog.getLineHeight() - 1;
+ text = mLog.getText() + "\n" + text;
+
+ // see how many lines we have
+ int index = text.lastIndexOf('\n');
+ int count = 0;
+ while (index > 0 && count <= maxLines) {
+ count++;
+ index = text.lastIndexOf('\n', index - 1);
+ }
+
+ // truncate to maxLines
+ if (index > 0) {
+ text = text.substring(index + 1);
+ }
+ mLog.setText(text);
+ }
+
+ public void deviceOnline(AdbDevice device) {
+ Message m = Message.obtain(mHandler, MESSAGE_DEVICE_ONLINE);
+ m.obj = device;
+ mHandler.sendMessage(m);
+ }
+
+ private void handleDeviceOnline(AdbDevice device) {
+ log("device online: " + device.getSerial());
+ device.openSocket("shell:exec logcat");
+ }
+
+ // Sets the current USB device and interface
+ private boolean setAdbInterface(UsbDevice device, UsbInterface intf) {
+ if (mDeviceConnection != null) {
+ if (mInterface != null) {
+ mDeviceConnection.releaseInterface(mInterface);
+ mInterface = null;
+ }
+ mDeviceConnection.close();
+ mDevice = null;
+ mDeviceConnection = null;
+ }
+
+ if (device != null && intf != null) {
+ UsbDeviceConnection connection = mManager.openDevice(device);
+ if (connection != null) {
+ log("open succeeded");
+ if (connection.claimInterface(intf, false)) {
+ log("claim interface succeeded");
+ mDevice = device;
+ mDeviceConnection = connection;
+ mInterface = intf;
+ mAdbDevice = new AdbDevice(this, mDeviceConnection, intf);
+ log("call start");
+ mAdbDevice.start();
+ return true;
+ } else {
+ log("claim interface failed");
+ connection.close();
+ }
+ } else {
+ log("open failed");
+ }
+ }
+
+ if (mDeviceConnection == null && mAdbDevice != null) {
+ mAdbDevice.stop();
+ mAdbDevice = null;
+ }
+ return false;
+ }
+
+ // searches for an adb interface on the given USB device
+ static private UsbInterface findAdbInterface(UsbDevice device) {
+ Log.d(TAG, "findAdbInterface " + device);
+ int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ UsbInterface intf = device.getInterface(i);
+ if (intf.getInterfaceClass() == 255 && intf.getInterfaceSubclass() == 66 &&
+ intf.getInterfaceProtocol() == 1) {
+ return intf;
+ }
+ }
+ return null;
+ }
+
+ BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ UsbInterface intf = findAdbInterface(device);
+ if (intf != null) {
+ log("Found adb interface " + intf);
+ setAdbInterface(device, intf);
+ }
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ String deviceName = device.getDeviceName();
+ if (mDevice != null && mDevice.equals(deviceName)) {
+ log("adb interface removed");
+ setAdbInterface(null, null);
+ }
+ }
+ }
+ };
+
+ Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_LOG:
+ appendLog((String)msg.obj);
+ break;
+ case MESSAGE_DEVICE_ONLINE:
+ handleDeviceOnline((AdbDevice)msg.obj);
+ break;
+ }
+ }
+ };
+}
+
+