From 3385faf4e60e1ac4f4cf387702ef3be211374ef2 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 2 May 2011 14:33:08 -0400 Subject: [PATCH] Add AdbTest sample program This sample implements the "adb logcat" command using USB host APIs. Change-Id: Ifbf122e99358bd3a1cd2a7967c364ffd59f003b3 Signed-off-by: Mike Lockwood --- samples/USB/AdbTest/Android.mk | 12 + samples/USB/AdbTest/AndroidManifest.xml | 24 ++ samples/USB/AdbTest/README.txt | 11 + samples/USB/AdbTest/_index.html | 13 + samples/USB/AdbTest/default.properties | 11 + samples/USB/AdbTest/res/layout/adb.xml | 33 +++ samples/USB/AdbTest/res/xml/device_filter.xml | 18 ++ .../src/com/android/adb/AdbDevice.java | 253 ++++++++++++++++++ .../src/com/android/adb/AdbMessage.java | 170 ++++++++++++ .../src/com/android/adb/AdbSocket.java | 75 ++++++ .../src/com/android/adb/AdbTestActivity.java | 211 +++++++++++++++ 11 files changed, 831 insertions(+) create mode 100644 samples/USB/AdbTest/Android.mk create mode 100644 samples/USB/AdbTest/AndroidManifest.xml create mode 100644 samples/USB/AdbTest/README.txt create mode 100644 samples/USB/AdbTest/_index.html create mode 100644 samples/USB/AdbTest/default.properties create mode 100644 samples/USB/AdbTest/res/layout/adb.xml create mode 100644 samples/USB/AdbTest/res/xml/device_filter.xml create mode 100644 samples/USB/AdbTest/src/com/android/adb/AdbDevice.java create mode 100644 samples/USB/AdbTest/src/com/android/adb/AdbMessage.java create mode 100644 samples/USB/AdbTest/src/com/android/adb/AdbSocket.java create mode 100644 samples/USB/AdbTest/src/com/android/adb/AdbTestActivity.java diff --git a/samples/USB/AdbTest/Android.mk b/samples/USB/AdbTest/Android.mk new file mode 100644 index 000000000..bfb9fa83e --- /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 000000000..9690ccf24 --- /dev/null +++ b/samples/USB/AdbTest/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/USB/AdbTest/README.txt b/samples/USB/AdbTest/README.txt new file mode 100644 index 000000000..3fd9cbb73 --- /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 000000000..424ee5b7a --- /dev/null +++ b/samples/USB/AdbTest/_index.html @@ -0,0 +1,13 @@ +

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:

+ + \ 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 000000000..3ac252342 --- /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 000000000..10994d5f1 --- /dev/null +++ b/samples/USB/AdbTest/res/layout/adb.xml @@ -0,0 +1,33 @@ + + + + + + + + + + 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 000000000..bc6fa0461 --- /dev/null +++ b/samples/USB/AdbTest/res/xml/device_filter.xml @@ -0,0 +1,18 @@ + + + + + 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 000000000..f793ce4f4 --- /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 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); + } + } + } + } + } +} 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 000000000..1dbb13689 --- /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 000000000..deb5980be --- /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 000000000..bd11977bc --- /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; + } + } + }; +} + +