Methods Summary |
---|
public static void | addClientChangeListener(com.android.ddmlib.AndroidDebugBridge$IClientChangeListener listener)Adds the listener to the collection of listeners who will be notified when a {@link Client}
property changed, by sending it one of the messages defined in the
{@link IClientChangeListener} interface.
synchronized (sLock) {
if (sClientListeners.contains(listener) == false) {
sClientListeners.add(listener);
}
}
|
public static void | addDebugBridgeChangeListener(com.android.ddmlib.AndroidDebugBridge$IDebugBridgeChangeListener listener)Adds the listener to the collection of listeners who will be notified when a new
{@link AndroidDebugBridge} is connected, by sending it one of the messages defined
in the {@link IDebugBridgeChangeListener} interface.
synchronized (sLock) {
if (sBridgeListeners.contains(listener) == false) {
sBridgeListeners.add(listener);
if (sThis != null) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.bridgeChanged(sThis);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
}
}
|
public static void | addDeviceChangeListener(com.android.ddmlib.AndroidDebugBridge$IDeviceChangeListener listener)Adds the listener to the collection of listeners who will be notified when a {@link Device}
is connected, disconnected, or when its properties or its {@link Client} list changed,
by sending it one of the messages defined in the {@link IDeviceChangeListener} interface.
synchronized (sLock) {
if (sDeviceListeners.contains(listener) == false) {
sDeviceListeners.add(listener);
}
}
|
private void | checkAdbVersion()Queries adb for its version number and checks it against {@link #MIN_VERSION_NUMBER} and
{@link #MAX_VERSION_NUMBER}
// default is bad check
mVersionCheck = false;
if (mAdbOsLocation == null) {
return;
}
try {
String[] command = new String[2];
command[0] = mAdbOsLocation;
command[1] = "version"; //$NON-NLS-1$
Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); //$NON-NLS-1$
Process process = Runtime.getRuntime().exec(command);
ArrayList<String> errorOutput = new ArrayList<String>();
ArrayList<String> stdOutput = new ArrayList<String>();
int status = grabProcessOutput(process, errorOutput, stdOutput,
true /* waitForReaders */);
if (status != 0) {
StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$
for (String error : errorOutput) {
builder.append('\n");
builder.append(error);
}
Log.logAndDisplay(LogLevel.ERROR, "adb", builder.toString());
}
// check both stdout and stderr
boolean versionFound = false;
for (String line : stdOutput) {
versionFound = scanVersionLine(line);
if (versionFound) {
break;
}
}
if (!versionFound) {
for (String line : errorOutput) {
versionFound = scanVersionLine(line);
if (versionFound) {
break;
}
}
}
if (!versionFound) {
// if we get here, we failed to parse the output.
Log.logAndDisplay(LogLevel.ERROR, ADB,
"Failed to parse the output of 'adb version'"); //$NON-NLS-1$
}
} catch (IOException e) {
Log.logAndDisplay(LogLevel.ERROR, ADB,
"Failed to get the adb version: " + e.getMessage()); //$NON-NLS-1$
} catch (InterruptedException e) {
} finally {
}
|
void | clientChanged(Client client, int changeMask)Notify the listener of a modified {@link Client}.
The notification of the listeners is done in a synchronized block. It is important to
expect the listeners to potentially access various methods of {@link Device} as well as
{@link #getDevices()} which use internal locks.
For this reason, any call to this method from a method of {@link DeviceMonitor},
{@link Device} which is also inside a synchronized block, should first synchronize on
the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IClientChangeListener[] listenersCopy = null;
synchronized (sLock) {
listenersCopy = sClientListeners.toArray(
new IClientChangeListener[sClientListeners.size()]);
}
// Notify the listeners
for (IClientChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.clientChanged(client, changeMask);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
|
public static com.android.ddmlib.AndroidDebugBridge | createBridge()Creates a {@link AndroidDebugBridge} that is not linked to any particular executable.
This bridge will expect adb to be running. It will not be able to start/stop/restart
adb.
If a bridge has already been started, it is directly returned with no changes (similar
to calling {@link #getBridge()}).
synchronized (sLock) {
if (sThis != null) {
return sThis;
}
try {
sThis = new AndroidDebugBridge();
sThis.start();
} catch (InvalidParameterException e) {
sThis = null;
}
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray(
new IDebugBridgeChangeListener[sBridgeListeners.size()]);
// notify the listeners of the change
for (IDebugBridgeChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.bridgeChanged(sThis);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
return sThis;
}
|
public static com.android.ddmlib.AndroidDebugBridge | createBridge(java.lang.String osLocation, boolean forceNewBridge)Creates a new debug bridge from the location of the command line tool.
Any existing server will be disconnected, unless the location is the same and
forceNewBridge is set to false.
synchronized (sLock) {
if (sThis != null) {
if (sThis.mAdbOsLocation != null && sThis.mAdbOsLocation.equals(osLocation) &&
forceNewBridge == false) {
return sThis;
} else {
// stop the current server
sThis.stop();
}
}
try {
sThis = new AndroidDebugBridge(osLocation);
sThis.start();
} catch (InvalidParameterException e) {
sThis = null;
}
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray(
new IDebugBridgeChangeListener[sBridgeListeners.size()]);
// notify the listeners of the change
for (IDebugBridgeChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.bridgeChanged(sThis);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
return sThis;
}
|
void | deviceChanged(Device device, int changeMask)Notify the listener of a modified {@link Device}.
The notification of the listeners is done in a synchronized block. It is important to
expect the listeners to potentially access various methods of {@link Device} as well as
{@link #getDevices()} which use internal locks.
For this reason, any call to this method from a method of {@link DeviceMonitor},
{@link Device} which is also inside a synchronized block, should first synchronize on
the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDeviceChangeListener[] listenersCopy = null;
synchronized (sLock) {
listenersCopy = sDeviceListeners.toArray(
new IDeviceChangeListener[sDeviceListeners.size()]);
}
// Notify the listeners
for (IDeviceChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.deviceChanged(device, changeMask);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
|
void | deviceConnected(Device device)Notify the listener of a new {@link Device}.
The notification of the listeners is done in a synchronized block. It is important to
expect the listeners to potentially access various methods of {@link Device} as well as
{@link #getDevices()} which use internal locks.
For this reason, any call to this method from a method of {@link DeviceMonitor},
{@link Device} which is also inside a synchronized block, should first synchronize on
the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDeviceChangeListener[] listenersCopy = null;
synchronized (sLock) {
listenersCopy = sDeviceListeners.toArray(
new IDeviceChangeListener[sDeviceListeners.size()]);
}
// Notify the listeners
for (IDeviceChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.deviceConnected(device);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
|
void | deviceDisconnected(Device device)Notify the listener of a disconnected {@link Device}.
The notification of the listeners is done in a synchronized block. It is important to
expect the listeners to potentially access various methods of {@link Device} as well as
{@link #getDevices()} which use internal locks.
For this reason, any call to this method from a method of {@link DeviceMonitor},
{@link Device} which is also inside a synchronized block, should first synchronize on
the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDeviceChangeListener[] listenersCopy = null;
synchronized (sLock) {
listenersCopy = sDeviceListeners.toArray(
new IDeviceChangeListener[sDeviceListeners.size()]);
}
// Notify the listeners
for (IDeviceChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.deviceDisconnected(device);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
|
public static void | disconnectBridge()Disconnects the current debug bridge, and destroy the object.
A new object will have to be created with {@link #createBridge(String, boolean)}.
synchronized (sLock) {
if (sThis != null) {
sThis.stop();
sThis = null;
// because the listeners could remove themselves from the list while processing
// their event callback, we make a copy of the list and iterate on it instead of
// the main list.
// This mostly happens when the application quits.
IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray(
new IDebugBridgeChangeListener[sBridgeListeners.size()]);
// notify the listeners.
for (IDebugBridgeChangeListener listener : listenersCopy) {
// we attempt to catch any exception so that a bad listener doesn't kill our
// thread
try {
listener.bridgeChanged(sThis);
} catch (Exception e) {
Log.e(DDMS, e);
}
}
}
}
|
public static com.android.ddmlib.AndroidDebugBridge | getBridge()Returns the current debug bridge. Can be null if none were created.
return sThis;
|
static boolean | getClientSupport()Returns whether the ddmlib is setup to support monitoring and interacting with
{@link Client}s running on the {@link Device}s.
return sClientSupport;
|
public int | getConnectionAttemptCount()Returns the number of times the {@link AndroidDebugBridge} object attempted to connect
to the adb daemon.
if (mDeviceMonitor != null) {
return mDeviceMonitor.getConnectionAttemptCount();
}
return -1;
|
DeviceMonitor | getDeviceMonitor()Returns the {@link DeviceMonitor} object.
return mDeviceMonitor;
|
public Device[] | getDevices()Returns the devices.
synchronized (sLock) {
if (mDeviceMonitor != null) {
return mDeviceMonitor.getDevices();
}
}
return new Device[0];
|
static java.lang.Object | getLock()Returns the singleton lock used by this class to protect any access to the listener.
This includes adding/removing listeners, but also notifying listeners of new bridges,
devices, and clients.
return sLock;
|
public int | getRestartAttemptCount()Returns the number of times the {@link AndroidDebugBridge} object attempted to restart
the adb daemon.
if (mDeviceMonitor != null) {
return mDeviceMonitor.getRestartAttemptCount();
}
return -1;
|
private int | grabProcessOutput(java.lang.Process process, java.util.ArrayList errorOutput, java.util.ArrayList stdOutput, boolean waitforReaders)Get the stderr/stdout outputs of a process and return when the process is done.
Both must be read or the process will block on windows.
assert errorOutput != null;
assert stdOutput != null;
// read the lines as they come. if null is returned, it's
// because the process finished
Thread t1 = new Thread("") { //$NON-NLS-1$
@Override
public void run() {
// create a buffer to read the stderr output
InputStreamReader is = new InputStreamReader(process.getErrorStream());
BufferedReader errReader = new BufferedReader(is);
try {
while (true) {
String line = errReader.readLine();
if (line != null) {
Log.e(ADB, line);
errorOutput.add(line);
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
};
Thread t2 = new Thread("") { //$NON-NLS-1$
@Override
public void run() {
InputStreamReader is = new InputStreamReader(process.getInputStream());
BufferedReader outReader = new BufferedReader(is);
try {
while (true) {
String line = outReader.readLine();
if (line != null) {
Log.d(ADB, line);
stdOutput.add(line);
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
};
t1.start();
t2.start();
// it looks like on windows process#waitFor() can return
// before the thread have filled the arrays, so we wait for both threads and the
// process itself.
if (waitforReaders) {
try {
t1.join();
} catch (InterruptedException e) {
}
try {
t2.join();
} catch (InterruptedException e) {
}
}
// get the return code from the process
return process.waitFor();
|
public boolean | hasInitialDeviceList()Returns whether the bridge has acquired the initial list from adb after being created.
Calling {@link #getDevices()} right after {@link #createBridge(String, boolean)} will
generally result in an empty list. This is due to the internal asynchronous communication
mechanism with adb that does not guarantee that the {@link Device} list has been
built before the call to {@link #getDevices()}.
The recommended way to get the list of {@link Device} objects is to create a
{@link IDeviceChangeListener} object.
if (mDeviceMonitor != null) {
return mDeviceMonitor.hasInitialDeviceList();
}
return false;
|
public static void | init(boolean clientSupport)Initializes the ddm library.
This must be called once before any call to
{@link #createBridge(String, boolean)}.
The library can be initialized in 2 ways:
- Mode 1: clientSupport ==
true . The library monitors the
devices and the applications running on them. It will connect to each application, as a
debugger of sort, to be able to interact with them through JDWP packets.
- Mode 2: clientSupport ==
false . The library only monitors
devices. The applications are left untouched, letting other tools built on
ddmlib to connect a debugger to them.
Only one tool can run in mode 1 at the same time.
Note that mode 1 does not prevent debugging of applications running on devices. Mode 1
lets debuggers connect to ddmlib which acts as a proxy between the debuggers and
the applications to debug. See {@link Client#getDebuggerListenPort()}.
The preferences of ddmlib should also be initialized with whatever default
values were changed from the default values.
When the application quits, {@link #terminate()} should be called.
sClientSupport = clientSupport;
MonitorThread monitorThread = MonitorThread.createInstance();
monitorThread.start();
HandleHello.register(monitorThread);
HandleAppName.register(monitorThread);
HandleTest.register(monitorThread);
HandleThread.register(monitorThread);
HandleHeap.register(monitorThread);
HandleWait.register(monitorThread);
|
public boolean | isConnected()Returns whether the {@link AndroidDebugBridge} object is still connected to the adb daemon.
MonitorThread monitorThread = MonitorThread.getInstance();
if (mDeviceMonitor != null && monitorThread != null) {
return mDeviceMonitor.isMonitoring() && monitorThread.getState() != State.TERMINATED;
}
return false;
|
public static void | removeClientChangeListener(com.android.ddmlib.AndroidDebugBridge$IClientChangeListener listener)Removes the listener from the collection of listeners who will be notified when a
{@link Client} property changed.
synchronized (sLock) {
sClientListeners.remove(listener);
}
|
public static void | removeDebugBridgeChangeListener(com.android.ddmlib.AndroidDebugBridge$IDebugBridgeChangeListener listener)Removes the listener from the collection of listeners who will be notified when a new
{@link AndroidDebugBridge} is started.
synchronized (sLock) {
sBridgeListeners.remove(listener);
}
|
public static void | removeDeviceChangeListener(com.android.ddmlib.AndroidDebugBridge$IDeviceChangeListener listener)Removes the listener from the collection of listeners who will be notified when a
{@link Device} is connected, disconnected, or when its properties or its {@link Client}
list changed.
synchronized (sLock) {
sDeviceListeners.remove(listener);
}
|
public boolean | restart()Restarts adb, but not the services around it.
if (mAdbOsLocation == null) {
Log.e(ADB,
"Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
return false;
}
if (mVersionCheck == false) {
Log.logAndDisplay(LogLevel.ERROR, ADB,
"Attempting to restart adb, but version check failed!"); //$NON-NLS-1$
return false;
}
synchronized (this) {
stopAdb();
boolean restart = startAdb();
if (restart && mDeviceMonitor == null) {
mDeviceMonitor = new DeviceMonitor(this);
mDeviceMonitor.start();
}
return restart;
}
|
private boolean | scanVersionLine(java.lang.String line)Scans a line resulting from 'adb version' for a potential version number.
If a version number is found, it checks the version number against what is expected
by this version of ddms.
Returns true when a version number has been found so that we can stop scanning,
whether the version number is in the acceptable range or not.
if (line != null) {
Matcher matcher = sAdbVersion.matcher(line);
if (matcher.matches()) {
int majorVersion = Integer.parseInt(matcher.group(1));
int minorVersion = Integer.parseInt(matcher.group(2));
int microVersion = Integer.parseInt(matcher.group(3));
// check only the micro version for now.
if (microVersion < ADB_VERSION_MICRO_MIN) {
String message = String.format(
"Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
+ "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
majorVersion, minorVersion, ADB_VERSION_MICRO_MIN,
microVersion);
Log.logAndDisplay(LogLevel.ERROR, ADB, message);
} else if (ADB_VERSION_MICRO_MAX != -1 &&
microVersion > ADB_VERSION_MICRO_MAX) {
String message = String.format(
"Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
+ "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
majorVersion, minorVersion, ADB_VERSION_MICRO_MAX,
microVersion);
Log.logAndDisplay(LogLevel.ERROR, ADB, message);
} else {
mVersionCheck = true;
}
return true;
}
}
return false;
|
public void | setSelectedClient(Client selectedClient)Sets the client to accept debugger connection on the custom "Selected debug port".
MonitorThread monitorThread = MonitorThread.getInstance();
if (monitorThread != null) {
monitorThread.setSelectedClient(selectedClient);
}
|
boolean | start()Starts the debug bridge.
if (mAdbOsLocation != null && (mVersionCheck == false || startAdb() == false)) {
return false;
}
mStarted = true;
// now that the bridge is connected, we start the underlying services.
mDeviceMonitor = new DeviceMonitor(this);
mDeviceMonitor.start();
return true;
|
synchronized boolean | startAdb()Starts the adb host side server.
if (mAdbOsLocation == null) {
Log.e(ADB,
"Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
return false;
}
Process proc;
int status = -1;
try {
String[] command = new String[2];
command[0] = mAdbOsLocation;
command[1] = "start-server"; //$NON-NLS-1$
Log.d(DDMS,
String.format("Launching '%1$s %2$s' to ensure ADB is running.", //$NON-NLS-1$
mAdbOsLocation, command[1]));
proc = Runtime.getRuntime().exec(command);
ArrayList<String> errorOutput = new ArrayList<String>();
ArrayList<String> stdOutput = new ArrayList<String>();
status = grabProcessOutput(proc, errorOutput, stdOutput,
false /* waitForReaders */);
} catch (IOException ioe) {
Log.d(DDMS, "Unable to run 'adb': " + ioe.getMessage()); //$NON-NLS-1$
// we'll return false;
} catch (InterruptedException ie) {
Log.d(DDMS, "Unable to run 'adb': " + ie.getMessage()); //$NON-NLS-1$
// we'll return false;
}
if (status != 0) {
Log.w(DDMS,
"'adb start-server' failed -- run manually if necessary"); //$NON-NLS-1$
return false;
}
Log.d(DDMS, "'adb start-server' succeeded"); //$NON-NLS-1$
return true;
|
boolean | stop()Kills the debug bridge.
// if we haven't started we return false;
if (mStarted == false) {
return false;
}
// kill the monitoring services
mDeviceMonitor.stop();
mDeviceMonitor = null;
if (stopAdb() == false) {
return false;
}
mStarted = false;
return true;
|
private synchronized boolean | stopAdb()Stops the adb host side server.
if (mAdbOsLocation == null) {
Log.e(ADB,
"Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
return false;
}
Process proc;
int status = -1;
try {
String[] command = new String[2];
command[0] = mAdbOsLocation;
command[1] = "kill-server"; //$NON-NLS-1$
proc = Runtime.getRuntime().exec(command);
status = proc.waitFor();
}
catch (IOException ioe) {
// we'll return false;
}
catch (InterruptedException ie) {
// we'll return false;
}
if (status != 0) {
Log.w(DDMS,
"'adb kill-server' failed -- run manually if necessary"); //$NON-NLS-1$
return false;
}
Log.d(DDMS, "'adb kill-server' succeeded"); //$NON-NLS-1$
return true;
|
public static void | terminate()Terminates the ddm library. This must be called upon application termination.
// kill the monitoring services
if (sThis != null && sThis.mDeviceMonitor != null) {
sThis.mDeviceMonitor.stop();
sThis.mDeviceMonitor = null;
}
MonitorThread monitorThread = MonitorThread.getInstance();
if (monitorThread != null) {
monitorThread.quit();
}
|