aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/AdbCommandRejectedException.java56
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java338
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Device.java111
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java166
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java2
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java6
-rwxr-xr-xddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java176
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/ShellCommandUnresponsiveException.java28
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java67
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/TimeoutException.java27
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/log/EventLogParser.java3
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java38
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java23
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java12
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/ActivityLaunchAction.java34
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchMessages.java1
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/runtime/RemoteAdtTestRunner.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties3
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/device/DeviceBridge.java34
20 files changed, 769 insertions, 361 deletions
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/AdbCommandRejectedException.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbCommandRejectedException.java
new file mode 100644
index 0000000..673acb5
--- /dev/null
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbCommandRejectedException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 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.ddmlib;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when adb refuses a command.
+ */
+public class AdbCommandRejectedException extends IOException {
+ private static final long serialVersionUID = 1L;
+ private final boolean mIsDeviceOffline;
+ private final boolean mErrorDuringDeviceSelection;
+
+ AdbCommandRejectedException(String message) {
+ super(message);
+ mIsDeviceOffline = "device offline".equals(message);
+ mErrorDuringDeviceSelection = false;
+ }
+
+ AdbCommandRejectedException(String message, boolean errorDuringDeviceSelection) {
+ super(message);
+ mErrorDuringDeviceSelection = errorDuringDeviceSelection;
+ mIsDeviceOffline = "device offline".equals(message);
+ }
+
+ /**
+ * Returns true if the error is due to the device being offline.
+ */
+ public boolean isDeviceOffline() {
+ return mIsDeviceOffline;
+ }
+
+ /**
+ * Returns whether adb refused to target a given device for the command.
+ * <p/>If false, adb refused the command itself, if true, it refused to target the given
+ * device.
+ */
+ public boolean wasErrorDuringDeviceSelection() {
+ return mErrorDuringDeviceSelection;
+ }
+}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
index 7b39076..5b5a41f 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
@@ -16,7 +16,6 @@
package com.android.ddmlib;
-import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.log.LogReceiver;
import java.io.IOException;
@@ -51,17 +50,12 @@ final class AdbHelper {
*/
static class AdbResponse {
public AdbResponse() {
- // ioSuccess = okay = timeout = false;
message = "";
}
- public boolean ioSuccess; // read all expected data, no timeoutes
-
public boolean okay; // first 4 bytes in response were "OKAY"?
- public boolean timeout; // TODO: implement
-
- public String message; // diagnostic string
+ public String message; // diagnostic string if #okay is false
}
/**
@@ -72,9 +66,13 @@ final class AdbHelper {
* @param device the device to connect to. Can be null in which case the connection will be
* to the first available device.
* @param devicePort the port we're opening
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws IOException in case of I/O error on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
*/
public static SocketChannel open(InetSocketAddress adbSockAddr,
- Device device, int devicePort) throws IOException {
+ Device device, int devicePort)
+ throws IOException, TimeoutException, AdbCommandRejectedException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
try {
@@ -88,17 +86,20 @@ final class AdbHelper {
byte[] req = createAdbForwardRequest(null, devicePort);
// Log.hexDump(req);
- if (write(adbChan, req) == false)
- throw new IOException("failed submitting request to ADB"); //$NON-NLS-1$
+ write(adbChan, req);
AdbResponse resp = readAdbResponse(adbChan, false);
- if (!resp.okay)
- throw new IOException("connection request rejected"); //$NON-NLS-1$
+ if (resp.okay == false) {
+ throw new AdbCommandRejectedException(resp.message);
+ }
adbChan.configureBlocking(true);
- } catch (IOException ioe) {
+ } catch (TimeoutException e) {
adbChan.close();
- throw ioe;
+ throw e;
+ } catch (IOException e) {
+ adbChan.close();
+ throw e;
}
return adbChan;
@@ -112,9 +113,13 @@ final class AdbHelper {
* @param device the device to connect to. Can be null in which case the connection will be
* to the first available device.
* @param pid the process pid to connect to.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
public static SocketChannel createPassThroughConnection(InetSocketAddress adbSockAddr,
- Device device, int pid) throws IOException {
+ Device device, int pid)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
try {
@@ -128,17 +133,20 @@ final class AdbHelper {
byte[] req = createJdwpForwardRequest(pid);
// Log.hexDump(req);
- if (write(adbChan, req) == false)
- throw new IOException("failed submitting request to ADB"); //$NON-NLS-1$
+ write(adbChan, req);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.okay)
- throw new IOException("connection request rejected: " + resp.message); //$NON-NLS-1$
+ if (resp.okay == false) {
+ throw new AdbCommandRejectedException(resp.message);
+ }
adbChan.configureBlocking(true);
- } catch (IOException ioe) {
+ } catch (TimeoutException e) {
adbChan.close();
- throw ioe;
+ throw e;
+ } catch (IOException e) {
+ adbChan.close();
+ throw e;
}
return adbChan;
@@ -196,17 +204,16 @@ final class AdbHelper {
* @param readDiagString If true, we're expecting an OKAY response to be
* followed by a diagnostic string. Otherwise, we only expect the
* diagnostic string to follow a FAIL.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws IOException in case of I/O error on the connection.
*/
static AdbResponse readAdbResponse(SocketChannel chan, boolean readDiagString)
- throws IOException {
+ throws TimeoutException, IOException {
AdbResponse resp = new AdbResponse();
byte[] reply = new byte[4];
- if (read(chan, reply) == false) {
- return resp;
- }
- resp.ioSuccess = true;
+ read(chan, reply);
if (isOkay(reply)) {
resp.okay = true;
@@ -216,38 +223,37 @@ final class AdbHelper {
}
// not a loop -- use "while" so we can use "break"
- while (readDiagString) {
- // length string is in next 4 bytes
- byte[] lenBuf = new byte[4];
- if (read(chan, lenBuf) == false) {
- Log.w("ddms", "Expected diagnostic string not found");
- break;
- }
+ try {
+ while (readDiagString) {
+ // length string is in next 4 bytes
+ byte[] lenBuf = new byte[4];
+ read(chan, lenBuf);
- String lenStr = replyToString(lenBuf);
+ String lenStr = replyToString(lenBuf);
- int len;
- try {
- len = Integer.parseInt(lenStr, 16);
- } catch (NumberFormatException nfe) {
- Log.w("ddms", "Expected digits, got '" + lenStr + "': "
- + lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " "
- + lenBuf[3]);
- Log.w("ddms", "reply was " + replyToString(reply));
- break;
- }
+ int len;
+ try {
+ len = Integer.parseInt(lenStr, 16);
+ } catch (NumberFormatException nfe) {
+ Log.w("ddms", "Expected digits, got '" + lenStr + "': "
+ + lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " "
+ + lenBuf[3]);
+ Log.w("ddms", "reply was " + replyToString(reply));
+ break;
+ }
- byte[] msg = new byte[len];
- if (read(chan, msg) == false) {
- Log.w("ddms", "Failed reading diagnostic string, len=" + len);
- break;
- }
+ byte[] msg = new byte[len];
+ read(chan, msg);
- resp.message = replyToString(msg);
- Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='"
- + resp.message + "'");
+ resp.message = replyToString(msg);
+ Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='"
+ + resp.message + "'");
- break;
+ break;
+ }
+ } catch (Exception e) {
+ // ignore those, since it's just reading the diagnose string, the response will
+ // contain okay==false anyway.
}
return resp;
@@ -255,9 +261,12 @@ final class AdbHelper {
/**
* Retrieve the frame buffer from the device.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device)
- throws IOException {
+ static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
RawImage imageParams = new RawImage();
byte[] request = formAdbRequest("framebuffer:"); //$NON-NLS-1$
@@ -275,25 +284,17 @@ final class AdbHelper {
// to a specific device
setDevice(adbChan, device);
- if (write(adbChan, request) == false)
- throw new IOException("failed asking for frame buffer");
+ write(adbChan, request);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
- + resp.message);
- adbChan.close();
- return null;
+ if (resp.okay == false) {
+ throw new AdbCommandRejectedException(resp.message);
}
// first the protocol version.
reply = new byte[4];
- if (read(adbChan, reply) == false) {
- Log.w("ddms", "got partial reply from ADB fb:");
- Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
- adbChan.close();
- return null;
- }
+ read(adbChan, reply);
+
ByteBuffer buf = ByteBuffer.wrap(reply);
buf.order(ByteOrder.LITTLE_ENDIAN);
@@ -304,12 +305,8 @@ final class AdbHelper {
// read the header
reply = new byte[headerSize * 4];
- if (read(adbChan, reply) == false) {
- Log.w("ddms", "got partial reply from ADB fb:");
- Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
- adbChan.close();
- return null;
- }
+ read(adbChan, reply);
+
buf = ByteBuffer.wrap(reply);
buf.order(ByteOrder.LITTLE_ENDIAN);
@@ -323,15 +320,10 @@ final class AdbHelper {
+ imageParams.size + ", width=" + imageParams.width
+ ", height=" + imageParams.height);
- if (write(adbChan, nudge) == false)
- throw new IOException("failed nudging");
+ write(adbChan, nudge);
reply = new byte[imageParams.size];
- if (read(adbChan, reply) == false) {
- Log.w("ddms", "got truncated reply from ADB fb data");
- adbChan.close();
- return null;
- }
+ read(adbChan, reply);
imageParams.data = reply;
} finally {
@@ -344,12 +336,30 @@ final class AdbHelper {
}
/**
- * Execute a command on the device and retrieve the output. The output is
- * handed to "rcvr" as it arrives.
+ * Executes a shell command on the device and retrieve the output. The output is
+ * handed to <var>rcvr</var> as it arrives.
+ *
+ * @param adbSockAddr the {@link InetSocketAddress} to adb.
+ * @param command the shell command to execute
+ * @param device the {@link IDevice} on which to execute the command.
+ * @param rcvr the {@link IShellOutputReceiver} that will receives the output of the shell
+ * command
+ * @param maxTimeToOutputResponse max time between command output. If more time passes
+ * between command output, the method will throw
+ * {@link ShellCommandUnresponsiveException}. A value of 0 means the method will
+ * wait forever for command output and never throw.
+ * @throws TimeoutException in case of timeout on the connection when sending the command.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException in case the shell command doesn't send any output
+ * for a period longer than <var>maxTimeToOutputResponse</var>.
+ * @throws IOException in case of I/O error on the connection.
+ *
+ * @see DdmPreferences#getTimeOut()
*/
- public static void executeRemoteCommand(InetSocketAddress adbSockAddr,
- String command, Device device, IShellOutputReceiver rcvr)
- throws IOException {
+ static void executeRemoteCommand(InetSocketAddress adbSockAddr,
+ String command, IDevice device, IShellOutputReceiver rcvr, int maxTimeToOutputResponse)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
Log.v("ddms", "execute: running " + command);
SocketChannel adbChan = null;
@@ -363,17 +373,17 @@ final class AdbHelper {
setDevice(adbChan, device);
byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
- if (write(adbChan, request) == false)
- throw new IOException("failed submitting shell command");
+ write(adbChan, request);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
+ if (resp.okay == false) {
Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);
- throw new IOException("sad result from adb: " + resp.message);
+ throw new AdbCommandRejectedException(resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
+ int timeToResponseCount = 0;
while (true) {
int count;
@@ -391,10 +401,20 @@ final class AdbHelper {
break;
} else if (count == 0) {
try {
- Thread.sleep(WAIT_TIME * 5);
+ int wait = WAIT_TIME * 5;
+ timeToResponseCount += wait;
+ if (maxTimeToOutputResponse > 0 &&
+ timeToResponseCount > maxTimeToOutputResponse) {
+ throw new ShellCommandUnresponsiveException();
+ }
+ Thread.sleep(wait);
} catch (InterruptedException ie) {
}
} else {
+ // reset timeout
+ timeToResponseCount = 0;
+
+ // send data to receiver if present
if (rcvr != null) {
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());
}
@@ -412,26 +432,32 @@ final class AdbHelper {
/**
* Runs the Event log service on the {@link Device}, and provides its output to the
* {@link LogReceiver}.
+ * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
* @param adbSockAddr the socket address to connect to adb
* @param device the Device on which to run the service
* @param rcvr the {@link LogReceiver} to receive the log output
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
public static void runEventLogService(InetSocketAddress adbSockAddr, Device device,
- LogReceiver rcvr) throws IOException {
+ LogReceiver rcvr) throws TimeoutException, AdbCommandRejectedException, IOException {
runLogService(adbSockAddr, device, "events", rcvr); //$NON-NLS-1$
}
/**
* Runs a log service on the {@link Device}, and provides its output to the {@link LogReceiver}.
+ * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
* @param adbSockAddr the socket address to connect to adb
* @param device the Device on which to run the service
* @param logName the name of the log file to output
* @param rcvr the {@link LogReceiver} to receive the log output
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
public static void runLogService(InetSocketAddress adbSockAddr, Device device, String logName,
- LogReceiver rcvr) throws IOException {
+ LogReceiver rcvr) throws TimeoutException, AdbCommandRejectedException, IOException {
SocketChannel adbChan = null;
try {
@@ -443,13 +469,11 @@ final class AdbHelper {
setDevice(adbChan, device);
byte[] request = formAdbRequest("log:" + logName);
- if (write(adbChan, request) == false) {
- throw new IOException("failed to submit the log command");
- }
+ write(adbChan, request);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected log command: " + resp.message);
+ if (resp.okay == false) {
+ throw new AdbCommandRejectedException(resp.message);
}
byte[] data = new byte[16384];
@@ -489,11 +513,12 @@ final class AdbHelper {
* @param device the device on which to do the port fowarding
* @param localPort the local port to forward
* @param remotePort the remote port.
- * @return <code>true</code> if success.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public static boolean createForward(InetSocketAddress adbSockAddr, Device device, int localPort,
- int remotePort) throws IOException {
+ public static void createForward(InetSocketAddress adbSockAddr, Device device, int localPort,
+ int remotePort) throws TimeoutException, AdbCommandRejectedException, IOException {
SocketChannel adbChan = null;
try {
@@ -504,21 +529,18 @@ final class AdbHelper {
"host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.getSerialNumber(), localPort, remotePort));
- if (write(adbChan, request) == false) {
- throw new IOException("failed to submit the forward command.");
- }
+ write(adbChan, request);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected command: " + resp.message);
+ if (resp.okay == false) {
+ Log.w("create-forward", "Error creating forward: " + resp.message);
+ throw new AdbCommandRejectedException(resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
-
- return true;
}
/**
@@ -527,11 +549,12 @@ final class AdbHelper {
* @param device the device on which to remove the port fowarding
* @param localPort the local port of the forward
* @param remotePort the remote port.
- * @return <code>true</code> if success.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public static boolean removeForward(InetSocketAddress adbSockAddr, Device device, int localPort,
- int remotePort) throws IOException {
+ public static void removeForward(InetSocketAddress adbSockAddr, Device device, int localPort,
+ int remotePort) throws TimeoutException, AdbCommandRejectedException, IOException {
SocketChannel adbChan = null;
try {
@@ -542,21 +565,18 @@ final class AdbHelper {
"host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.getSerialNumber(), localPort, remotePort));
- if (!write(adbChan, request)) {
- throw new IOException("failed to submit the remove forward command.");
- }
+ write(adbChan, request);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected command: " + resp.message);
+ if (resp.okay == false) {
+ Log.w("remove-forward", "Error creating forward: " + resp.message);
+ throw new AdbCommandRejectedException(resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
-
- return true;
}
/**
@@ -584,22 +604,16 @@ final class AdbHelper {
/**
* Reads from the socket until the array is filled, or no more data is coming (because
* the socket closed or the timeout expired).
+ * <p/>This uses the default time out value.
*
* @param chan the opened socket to read from. It must be in non-blocking
* mode for timeouts to work
* @param data the buffer to store the read data into.
- * @return "true" if all data was read.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws IOException in case of I/O error on the connection.
*/
- static boolean read(SocketChannel chan, byte[] data) {
- try {
- read(chan, data, -1, DdmPreferences.getTimeOut());
- } catch (IOException e) {
- Log.d("ddms", "readAll: IOException: " + e.getMessage());
- return false;
- }
-
- return true;
+ static void read(SocketChannel chan, byte[] data) throws TimeoutException, IOException {
+ read(chan, data, -1, DdmPreferences.getTimeOut());
}
/**
@@ -614,9 +628,9 @@ final class AdbHelper {
* @param data the buffer to store the read data into.
* @param length the length to read or -1 to fill the data buffer completely
* @param timeout The timeout value. A timeout of zero means "wait forever".
- * @throws IOException
*/
- static void read(SocketChannel chan, byte[] data, int length, int timeout) throws IOException {
+ static void read(SocketChannel chan, byte[] data, int length, int timeout)
+ throws TimeoutException, IOException {
ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length);
int numWaits = 0;
@@ -631,7 +645,7 @@ final class AdbHelper {
// TODO: need more accurate timeout?
if (timeout != 0 && numWaits * WAIT_TIME > timeout) {
Log.d("ddms", "read: timeout");
- throw new IOException("timeout");
+ throw new TimeoutException();
}
// non-blocking spin
try {
@@ -646,20 +660,15 @@ final class AdbHelper {
}
/**
- * Write until all data in "data" is written or the connection fails.
+ * Write until all data in "data" is written or the connection fails or times out.
+ * <p/>This uses the default time out value.
* @param chan the opened socket to write to.
* @param data the buffer to send.
- * @return "true" if all data was written.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws IOException in case of I/O error on the connection.
*/
- static boolean write(SocketChannel chan, byte[] data) {
- try {
- write(chan, data, -1, DdmPreferences.getTimeOut());
- } catch (IOException e) {
- Log.e("ddms", e);
- return false;
- }
-
- return true;
+ static void write(SocketChannel chan, byte[] data) throws TimeoutException, IOException {
+ write(chan, data, -1, DdmPreferences.getTimeOut());
}
/**
@@ -670,10 +679,11 @@ final class AdbHelper {
* @param data the buffer to send.
* @param length the length to write or -1 to send the whole buffer.
* @param timeout The timeout value. A timeout of zero means "wait forever".
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws IOException in case of I/O error on the connection.
*/
static void write(SocketChannel chan, byte[] data, int length, int timeout)
- throws IOException {
+ throws TimeoutException, IOException {
ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length);
int numWaits = 0;
@@ -688,7 +698,7 @@ final class AdbHelper {
// TODO: need more accurate timeout?
if (timeout != 0 && numWaits * WAIT_TIME > timeout) {
Log.d("ddms", "write: timeout");
- throw new IOException("timeout");
+ throw new TimeoutException();
}
// non-blocking spin
try {
@@ -707,35 +717,38 @@ final class AdbHelper {
*
* @param adbChan the socket connection to adb
* @param device The device to talk to.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- static void setDevice(SocketChannel adbChan, Device device)
- throws IOException {
+ static void setDevice(SocketChannel adbChan, IDevice device)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
if (device != null) {
String msg = "host:transport:" + device.getSerialNumber(); //$NON-NLS-1$
byte[] device_query = formAdbRequest(msg);
- if (write(adbChan, device_query) == false)
- throw new IOException("failed submitting device (" + device +
- ") request to ADB");
+ write(adbChan, device_query);
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.okay)
- throw new IOException("device (" + device +
- ") request rejected: " + resp.message);
+ if (resp.okay == false) {
+ throw new AdbCommandRejectedException(resp.message,
+ true/*errorDuringDeviceSelection*/);
+ }
}
-
}
/**
* Reboot the device.
*
* @param into what to reboot into (recovery, bootloader). Or null to just reboot.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
public static void reboot(String into, InetSocketAddress adbSockAddr,
- Device device) throws IOException {
+ Device device) throws TimeoutException, AdbCommandRejectedException, IOException {
byte[] request;
if (into == null) {
request = formAdbRequest("reboot:"); //$NON-NLS-1$
@@ -752,8 +765,7 @@ final class AdbHelper {
// to a specific device
setDevice(adbChan, device);
- if (write(adbChan, request) == false)
- throw new IOException("failed asking for reboot");
+ write(adbChan, request);
} finally {
if (adbChan != null) {
adbChan.close();
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
index a601c16..7eff98e 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
@@ -36,6 +36,8 @@ import java.util.regex.Pattern;
*/
final class Device implements IDevice {
+ private final static int INSTALL_TIMEOUT = 2*60*1000; //2min
+
/** Emulator Serial Number regexp. */
final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$
@@ -246,7 +248,8 @@ final class Device implements IDevice {
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getSyncService()
*/
- public SyncService getSyncService() throws IOException {
+ public SyncService getSyncService()
+ throws TimeoutException, AdbCommandRejectedException, IOException {
SyncService syncService = new SyncService(AndroidDebugBridge.getSocketAddress(), this);
if (syncService.openSync()) {
return syncService;
@@ -263,67 +266,44 @@ final class Device implements IDevice {
return new FileListingService(this);
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#getScreenshot()
- */
- public RawImage getScreenshot() throws IOException {
+ public RawImage getScreenshot()
+ throws TimeoutException, AdbCommandRejectedException, IOException {
return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this);
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#executeShellCommand(java.lang.String, com.android.ddmlib.IShellOutputReceiver)
- */
public void executeShellCommand(String command, IShellOutputReceiver receiver)
- throws IOException {
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
- receiver);
+ receiver, DdmPreferences.getTimeOut());
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#runEventLogService(com.android.ddmlib.log.LogReceiver)
- */
- public void runEventLogService(LogReceiver receiver) throws IOException {
+ public void executeShellCommand(String command, IShellOutputReceiver receiver,
+ int maxTimeToOutputResponse)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
+ AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
+ receiver, maxTimeToOutputResponse);
+ }
+
+ public void runEventLogService(LogReceiver receiver)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver);
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#runLogService(com.android.ddmlib.log.LogReceiver)
- */
- public void runLogService(String logname,
- LogReceiver receiver) throws IOException {
+ public void runLogService(String logname, LogReceiver receiver)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver);
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#createForward(int, int)
- */
- public boolean createForward(int localPort, int remotePort) {
- try {
- return AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this,
- localPort, remotePort);
- } catch (IOException e) {
- Log.e("adb-forward", e); //$NON-NLS-1$
- return false;
- }
+ public void createForward(int localPort, int remotePort)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
+ AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
}
- /*
- * (non-Javadoc)
- * @see com.android.ddmlib.IDevice#removeForward(int, int)
- */
- public boolean removeForward(int localPort, int remotePort) {
- try {
- return AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this,
- localPort, remotePort);
- } catch (IOException e) {
- Log.e("adb-remove-forward", e); //$NON-NLS-1$
- return false;
- }
+ public void removeForward(int localPort, int remotePort)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
+ AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
}
/*
@@ -427,22 +407,17 @@ final class Device implements IDevice {
mMountPoints.put(name, value);
}
- /**
- * {@inheritDoc}
- */
public String installPackage(String packageFilePath, boolean reinstall)
- throws IOException {
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
String remoteFilePath = syncPackageToDevice(packageFilePath);
String result = installRemotePackage(remoteFilePath, reinstall);
removeRemotePackage(remoteFilePath);
return result;
}
- /**
- * {@inheritDoc}
- */
public String syncPackageToDevice(String localFilePath)
- throws IOException {
+ throws IOException, AdbCommandRejectedException, TimeoutException {
try {
String packageFileName = getFileName(localFilePath);
String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$
@@ -466,6 +441,9 @@ final class Device implements IDevice {
throw new IOException("Unable to open sync connection!");
}
return remoteFilePath;
+ } catch (TimeoutException e) {
+ Log.e(LOG_TAG, "Unable to open sync connection! Timeout.");
+ throw e;
} catch (IOException e) {
Log.e(LOG_TAG, String.format("Unable to open sync connection! reason: %1$s",
e.getMessage()));
@@ -482,25 +460,25 @@ final class Device implements IDevice {
return new File(filePath).getName();
}
- /**
- * {@inheritDoc}
- */
public String installRemotePackage(String remoteFilePath, boolean reinstall)
- throws IOException {
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
InstallReceiver receiver = new InstallReceiver();
String cmd = String.format(reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"",
remoteFilePath);
- executeShellCommand(cmd, receiver);
+ executeShellCommand(cmd, receiver, INSTALL_TIMEOUT);
return receiver.getErrorMessage();
}
/**
* {@inheritDoc}
*/
- public void removeRemotePackage(String remoteFilePath) throws IOException {
+ public void removeRemotePackage(String remoteFilePath)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
// now we delete the app we sync'ed
try {
- executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver());
+ executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver(), INSTALL_TIMEOUT);
} catch (IOException e) {
Log.e(LOG_TAG, String.format("Failed to delete temporary package: %1$s",
e.getMessage()));
@@ -511,9 +489,11 @@ final class Device implements IDevice {
/**
* {@inheritDoc}
*/
- public String uninstallPackage(String packageName) throws IOException {
+ public String uninstallPackage(String packageName)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
InstallReceiver receiver = new InstallReceiver();
- executeShellCommand("pm uninstall " + packageName, receiver);
+ executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT);
return receiver.getErrorMessage();
}
@@ -521,7 +501,8 @@ final class Device implements IDevice {
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#reboot()
*/
- public void reboot(String into) throws IOException {
+ public void reboot(String into)
+ throws TimeoutException, AdbCommandRejectedException, IOException {
AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this);
}
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
index 175b657..21869af 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
@@ -196,23 +196,33 @@ final class DeviceMonitor {
}
} catch (AsynchronousCloseException ace) {
// this happens because of a call to Quit. We do nothing, and the loop will break.
+ } catch (TimeoutException ioe) {
+ handleExpectioninMonitorLoop(ioe);
} catch (IOException ioe) {
- if (mQuit == false) {
- Log.e("DeviceMonitor", "Adb connection Error:" + ioe.getMessage());
- mMonitoring = false;
- if (mMainAdbConnection != null) {
- try {
- mMainAdbConnection.close();
- } catch (IOException ioe2) {
- // we can safely ignore that one.
- }
- mMainAdbConnection = null;
- }
- }
+ handleExpectioninMonitorLoop(ioe);
}
} while (mQuit == false);
}
+ private void handleExpectioninMonitorLoop(Exception e) {
+ if (mQuit == false) {
+ if (e instanceof TimeoutException) {
+ Log.e("DeviceMonitor", "Adb connection Error: timeout");
+ } else {
+ Log.e("DeviceMonitor", "Adb connection Error:" + e.getMessage());
+ }
+ mMonitoring = false;
+ if (mMainAdbConnection != null) {
+ try {
+ mMainAdbConnection.close();
+ } catch (IOException ioe) {
+ // we can safely ignore that one.
+ }
+ mMainAdbConnection = null;
+ }
+ }
+ }
+
/**
* Sleeps for a little bit.
*/
@@ -245,30 +255,26 @@ final class DeviceMonitor {
* @return
* @throws IOException
*/
- private boolean sendDeviceListMonitoringRequest() throws IOException {
+ private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException {
byte[] request = AdbHelper.formAdbRequest("host:track-devices"); //$NON-NLS-1$
- if (AdbHelper.write(mMainAdbConnection, request) == false) {
- Log.e("DeviceMonitor", "Sending Tracking request failed!");
- mMainAdbConnection.close();
- throw new IOException("Sending Tracking request failed!");
- }
+ try {
+ AdbHelper.write(mMainAdbConnection, request);
- AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection,
- false /* readDiagString */);
+ AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection,
+ false /* readDiagString */);
- if (resp.ioSuccess == false) {
- Log.e("DeviceMonitor", "Failed to read the adb response!");
- mMainAdbConnection.close();
- throw new IOException("Failed to read the adb response!");
- }
+ if (resp.okay == false) {
+ // request was refused by adb!
+ Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+ }
- if (resp.okay == false) {
- // request was refused by adb!
- Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+ return resp.okay;
+ } catch (IOException e) {
+ Log.e("DeviceMonitor", "Sending Tracking request failed!");
+ mMainAdbConnection.close();
+ throw e;
}
-
- return resp.okay;
}
/**
@@ -310,6 +316,10 @@ final class DeviceMonitor {
// because we are going to call mServer.deviceDisconnected which will acquire this lock
// we lock it first, so that the AndroidDebugBridge lock is always locked first.
synchronized (AndroidDebugBridge.getLock()) {
+ // array to store the devices that must be queried for information.
+ // it's important to not do it inside the synchronized loop as this could block
+ // the whole workspace (this lock is acquired during build too).
+ ArrayList<Device> devicesToQuery = new ArrayList<Device>();
synchronized (mDevices) {
// For each device in the current list, we look for a matching the new list.
// * if we find it, we update the current object with whatever new information
@@ -349,7 +359,7 @@ final class DeviceMonitor {
}
if (device.getPropertyCount() == 0) {
- queryNewDeviceForInfo(device);
+ devicesToQuery.add(device);
}
}
}
@@ -387,10 +397,15 @@ final class DeviceMonitor {
// look for their build info.
if (newDevice.isOnline()) {
- queryNewDeviceForInfo(newDevice);
+ devicesToQuery.add(newDevice);
}
}
}
+
+ // query the new devices for info.
+ for (Device d : devicesToQuery) {
+ queryNewDeviceForInfo(d);
+ }
}
newList.clear();
}
@@ -431,13 +446,31 @@ final class DeviceMonitor {
device.setAvdName(console.getAvdName());
}
}
+ } catch (TimeoutException e) {
+ Log.w("DeviceMonitor", String.format("Connection timeout getting info for device %s",
+ device.getSerialNumber()));
+
+ } catch (AdbCommandRejectedException e) {
+ // This should never happen as we only do this once the device is online.
+ Log.w("DeviceMonitor", String.format(
+ "Adb rejected command to get device %1$s info: %2$s",
+ device.getSerialNumber(), e.getMessage()));
+
+ } catch (ShellCommandUnresponsiveException e) {
+ Log.w("DeviceMonitor", String.format(
+ "Adb shell command took too long returning info for device %s",
+ device.getSerialNumber()));
+
} catch (IOException e) {
- // if we can't get the build info, it doesn't matter too much
+ Log.w("DeviceMonitor", String.format(
+ "IO Error getting info for device %s",
+ device.getSerialNumber()));
}
}
private void queryNewDeviceForMountingPoint(final Device device, final String name)
- throws IOException {
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
device.executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$
public boolean isCancelled() {
return false;
@@ -486,6 +519,26 @@ final class DeviceMonitor {
return true;
}
+ } catch (TimeoutException e) {
+ try {
+ // attempt to close the socket if needed.
+ socketChannel.close();
+ } catch (IOException e1) {
+ // we can ignore that one. It may already have been closed.
+ }
+ Log.d("DeviceMonitor",
+ "Connection Failure when starting to monitor device '"
+ + device + "' : timeout");
+ } catch (AdbCommandRejectedException e) {
+ try {
+ // attempt to close the socket if needed.
+ socketChannel.close();
+ } catch (IOException e1) {
+ // we can ignore that one. It may already have been closed.
+ }
+ Log.d("DeviceMonitor",
+ "Adb refused to start monitoring device '"
+ + device + "' : " + e.getMessage());
} catch (IOException e) {
try {
// attempt to close the socket if needed.
@@ -608,32 +661,30 @@ final class DeviceMonitor {
}
private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device)
- throws IOException {
+ throws TimeoutException, AdbCommandRejectedException, IOException {
- AdbHelper.setDevice(socket, device);
+ try {
+ AdbHelper.setDevice(socket, device);
- byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
+ byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
- if (AdbHelper.write(socket, request) == false) {
- Log.e("DeviceMonitor", "Sending jdwp tracking request failed!");
- socket.close();
- throw new IOException();
- }
+ AdbHelper.write(socket, request);
- AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
+ AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
- if (resp.ioSuccess == false) {
- Log.e("DeviceMonitor", "Failed to read the adb response!");
- socket.close();
- throw new IOException();
- }
+ if (resp.okay == false) {
+ // request was refused by adb!
+ Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+ }
- if (resp.okay == false) {
- // request was refused by adb!
- Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+ return resp.okay;
+ } catch (TimeoutException e) {
+ Log.e("DeviceMonitor", "Sending jdwp tracking request timed out!");
+ throw e;
+ } catch (IOException e) {
+ Log.e("DeviceMonitor", "Sending jdwp tracking request failed!");
+ throw e;
}
-
- return resp.okay;
}
private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length)
@@ -739,6 +790,15 @@ final class DeviceMonitor {
} catch (UnknownHostException uhe) {
Log.d("DeviceMonitor", "Unknown Jdwp pid: " + pid);
return;
+ } catch (TimeoutException e) {
+ Log.w("DeviceMonitor",
+ "Failed to connect to client '" + pid + "': timeout");
+ return;
+ } catch (AdbCommandRejectedException e) {
+ Log.w("DeviceMonitor",
+ "Adb rejected connection to client '" + pid + "': " + e.getMessage());
+ return;
+
} catch (IOException ioe) {
Log.w("DeviceMonitor",
"Failed to connect to client '" + pid + "': " + ioe.getMessage());
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
index 1a126b4..43d0c0c 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
@@ -545,7 +545,7 @@ public final class EmulatorConsole {
AdbHelper.write(mSocketChannel, bCommand, bCommand.length, DdmPreferences.getTimeOut());
result = true;
- } catch (IOException e) {
+ } catch (Exception e) {
return false;
} finally {
if (result == false) {
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java b/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
index f85d020..bfeeea2 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
@@ -16,7 +16,6 @@
package com.android.ddmlib;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -700,7 +699,7 @@ public final class FileListingService {
return false;
}
});
- } catch (IOException e) {
+ } catch (Exception e) {
// adb failed somehow, we do nothing.
}
}
@@ -756,7 +755,8 @@ public final class FileListingService {
// finish the process of the receiver to handle links
receiver.finishLinks();
- } catch (IOException e) {
+ } catch (Exception e) {
+ // catch all and do nothing.
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index bd6f436..01814ac 100755
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -21,7 +21,6 @@ import com.android.ddmlib.log.LogReceiver;
import java.io.IOException;
import java.util.Map;
-
/**
* A Device. It can be a physical device or an emulator.
*/
@@ -65,6 +64,7 @@ public interface IDevice {
/**
* Returns a {@link DeviceState} from the string returned by <code>adb devices</code>.
+ *
* @param state the device state.
* @return a {@link DeviceState} object or <code>null</code> if the state is unknown.
*/
@@ -88,6 +88,7 @@ public interface IDevice {
* <p/>This is only valid if {@link #isEmulator()} returns true.
* <p/>If the emulator is not running any AVD (for instance it's running from an Android source
* tree build), this method will return "<code>&lt;build&gt;</code>".
+ *
* @return the name of the AVD or <code>null</code> if there isn't any.
*/
public String getAvdName();
@@ -109,6 +110,7 @@ public interface IDevice {
/**
* Returns a property value.
+ *
* @param name the name of the value to return.
* @return the value or <code>null</code> if the property does not exist.
*/
@@ -116,6 +118,7 @@ public interface IDevice {
/**
* Returns a mount point.
+ *
* @param name the name of the mount point to return
*
* @see #MNT_EXTERNAL_STORAGE
@@ -126,6 +129,7 @@ public interface IDevice {
/**
* Returns if the device is ready.
+ *
* @return <code>true</code> if {@link #getState()} returns {@link DeviceState#ONLINE}.
*/
public boolean isOnline();
@@ -137,12 +141,14 @@ public interface IDevice {
/**
* Returns if the device is offline.
+ *
* @return <code>true</code> if {@link #getState()} returns {@link DeviceState#OFFLINE}.
*/
public boolean isOffline();
/**
* Returns if the device is in bootloader mode.
+ *
* @return <code>true</code> if {@link #getState()} returns {@link DeviceState#BOOTLOADER}.
*/
public boolean isBootLoader();
@@ -159,6 +165,7 @@ public interface IDevice {
/**
* Returns a {@link Client} by its application name.
+ *
* @param applicationName the name of the application
* @return the <code>Client</code> object or <code>null</code> if no match was found.
*/
@@ -166,11 +173,16 @@ public interface IDevice {
/**
* Returns a {@link SyncService} object to push / pull files to and from the device.
+ *
* @return <code>null</code> if the SyncService couldn't be created. This can happen if adb
- * refuse to open the connection because the {@link IDevice} is invalid (or got disconnected).
+ * refuse to open the connection because the {@link IDevice} is invalid
+ * (or got disconnected).
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
* @throws IOException if the connection with adb failed.
*/
- public SyncService getSyncService() throws IOException;
+ public SyncService getSyncService()
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Returns a {@link FileListingService} for this device.
@@ -179,51 +191,122 @@ public interface IDevice {
/**
* Takes a screen shot of the device and returns it as a {@link RawImage}.
- * @return the screenshot as a <code>RawImage</code> or <code>null</code> if
- * something went wrong.
- * @throws IOException
+ *
+ * @return the screenshot as a <code>RawImage</code> or <code>null</code> if something
+ * went wrong.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public RawImage getScreenshot() throws IOException;
+ public RawImage getScreenshot() throws TimeoutException, AdbCommandRejectedException,
+ IOException;
/**
- * Executes a shell command on the device, and sends the result to a receiver.
- * @param command The command to execute
- * @param receiver The receiver object getting the result from the command.
- * @throws IOException
+ * Executes a shell command on the device, and sends the result to a <var>receiver</var>
+ * <p/>This is similar to calling
+ * <code>executeShellCommand(command, receiver, DdmPreferences.getTimeOut())</code>.
+ *
+ * @param command the shell command to execute
+ * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell
+ * command
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException in case the shell command doesn't send output
+ * for a given time.
+ * @throws IOException in case of I/O error on the connection.
+ *
+ * @see #executeShellCommand(String, IShellOutputReceiver, int)
+ * @see DdmPreferences#getTimeOut()
+ */
+ public void executeShellCommand(String command, IShellOutputReceiver receiver)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
+
+ /**
+ * Executes a shell command on the device, and sends the result to a <var>receiver</var>.
+ * <p/><var>maxTimeToOutputResponse</var> is used as a maximum waiting time when expecting the
+ * command output from the device.<br>
+ * At any time, if the shell command does not output anything for a period longer than
+ * <var>maxTimeToOutputResponse</var>, then the method will throw
+ * {@link ShellCommandUnresponsiveException}.
+ * <p/>For commands like log output, a <var>maxTimeToOutputResponse</var> value of 0, meaning
+ * that the method will never throw and will block until the receiver's
+ * {@link IShellOutputReceiver#isCancelled()} returns <code>true</code>, should be
+ * used.
+ *
+ * @param command the shell command to execute
+ * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell
+ * command
+ * @param maxTimeToOutputResponse the maximum amount of time during which the command is allowed
+ * to not output any response. A value of 0 means the method will wait forever
+ * (until the <var>receiver</var> cancels the execution) for command output and
+ * never throw.
+ * @throws TimeoutException in case of timeout on the connection when sending the command.
+ * @throws AdbCommandRejectedException if adb rejects the command.
+ * @throws ShellCommandUnresponsiveException in case the shell command doesn't send any output
+ * for a period longer than <var>maxTimeToOutputResponse</var>.
+ * @throws IOException in case of I/O error on the connection.
+ *
+ * @see DdmPreferences#getTimeOut()
*/
- public void executeShellCommand(String command,
- IShellOutputReceiver receiver) throws IOException;
+ public void executeShellCommand(String command, IShellOutputReceiver receiver,
+ int maxTimeToOutputResponse)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
* Runs the event log service and outputs the event log to the {@link LogReceiver}.
+ * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
* @param receiver the receiver to receive the event log entries.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection. This can only be thrown if the
+ * timeout happens during setup. Once logs start being received, no timeout will occur as it's
+ * not possible to detect a difference between no log and timeout.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public void runEventLogService(LogReceiver receiver) throws IOException;
+ public void runEventLogService(LogReceiver receiver)
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Runs the log service for the given log and outputs the log to the {@link LogReceiver}.
+ * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
+ *
* @param logname the logname of the log to read from.
* @param receiver the receiver to receive the event log entries.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection. This can only be thrown if the
+ * timeout happens during setup. Once logs start being received, no timeout will
+ * occur as it's not possible to detect a difference between no log and timeout.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public void runLogService(String logname, LogReceiver receiver) throws IOException;
+ public void runLogService(String logname, LogReceiver receiver)
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Creates a port forwarding between a local and a remote port.
+ *
* @param localPort the local port to forward
* @param remotePort the remote port.
* @return <code>true</code> if success.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public boolean createForward(int localPort, int remotePort);
+ public void createForward(int localPort, int remotePort)
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Removes a port forwarding between a local and a remote port.
+ *
* @param localPort the local port to forward
* @param remotePort the remote port.
* @return <code>true</code> if success.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
- public boolean removeForward(int localPort, int remotePort);
+ public void removeForward(int localPort, int remotePort)
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Returns the name of the client by pid or <code>null</code> if pid is unknown
@@ -235,51 +318,84 @@ public interface IDevice {
* Installs an Android application on device.
* This is a helper method that combines the syncPackageToDevice, installRemotePackage,
* and removePackage steps
+ *
* @param packageFilePath the absolute file system path to file on local host to install
* @param reinstall set to <code>true</code> if re-install of app should be performed
* @return a {@link String} with an error code, or <code>null</code> if success.
- * @throws IOException
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when
+ * performing the action.
+ * @throws IOException in case of I/O error on the connection.
*/
- public String installPackage(String packageFilePath, boolean reinstall) throws IOException;
+ public String installPackage(String packageFilePath, boolean reinstall)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
* Pushes a file to device
+ *
* @param localFilePath the absolute path to file on local host
* @return {@link String} destination path on device for file
- * @throws IOException if fatal error occurred when pushing file
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException in case of I/O error on the connection.
*/
public String syncPackageToDevice(String localFilePath)
- throws IOException;
+ throws TimeoutException, AdbCommandRejectedException, IOException;
/**
* Installs the application package that was pushed to a temporary location on the device.
+ *
* @param remoteFilePath absolute file path to package file on device
* @param reinstall set to <code>true</code> if re-install of app should be performed
- * @throws InstallException if installation failed
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when
+ * performing the action.
+ * @throws IOException if installation failed
*/
public String installRemotePackage(String remoteFilePath, boolean reinstall)
- throws IOException;
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
- * Remove a file from device
+ * Removes a file from device.
+ *
* @param remoteFilePath path on device of file to remove
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when
+ * performing the action.
* @throws IOException if file removal failed
*/
- public void removeRemotePackage(String remoteFilePath) throws IOException;
+ public void removeRemotePackage(String remoteFilePath)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
- * Uninstall an package from the device.
+ * Uninstalls an package from the device.
+ *
* @param packageName the Android application package name to uninstall
* @return a {@link String} with an error code, or <code>null</code> if success.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when
+ * performing the action.
* @throws IOException
*/
- public String uninstallPackage(String packageName) throws IOException;
+ public String uninstallPackage(String packageName)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
* Reboot the device.
*
* @param into the bootloader name to reboot into, or null to just reboot the device.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
* @throws IOException
*/
- public void reboot(String into) throws IOException;
+ public void reboot(String into)
+ throws TimeoutException, AdbCommandRejectedException, IOException;
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/ShellCommandUnresponsiveException.java b/ddms/libs/ddmlib/src/com/android/ddmlib/ShellCommandUnresponsiveException.java
new file mode 100644
index 0000000..2dc5d3a
--- /dev/null
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/ShellCommandUnresponsiveException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 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.ddmlib;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when a shell command executed on a device takes too long to send its output.
+ * <p/>The command may not actually be unresponsive, it just has spent too much time not outputting
+ * any thing to the console.
+ */
+public class ShellCommandUnresponsiveException extends IOException {
+ private static final long serialVersionUID = 1L;
+}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
index 9f6b561..d2b8af3 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
@@ -108,6 +108,8 @@ public final class SyncService {
public static final int RESULT_REMOTE_IS_FILE = 13;
/** Result code for receiving too much data from the remove device at once */
public static final int RESULT_BUFFER_OVERRUN = 14;
+ /** Result code for network connection timeout */
+ public static final int RESULT_CONNECTION_TIMEOUT = 15;
/**
* A file transfer result.
@@ -211,9 +213,11 @@ public final class SyncService {
* Opens the sync connection. This must be called before any calls to push[File] / pull[File].
* @return true if the connection opened, false if adb refuse the connection. This can happen
* if the {@link Device} is invalid.
+ * @throws TimeoutException in case of timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
* @throws IOException If the connection to adb failed.
*/
- boolean openSync() throws IOException {
+ boolean openSync() throws TimeoutException, AdbCommandRejectedException, IOException {
try {
mChannel = SocketChannel.open(mAddress);
mChannel.configureBlocking(false);
@@ -226,14 +230,23 @@ public final class SyncService {
AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- Log.w("ddms",
- "Got timeout or unhappy response from ADB sync req: "
- + resp.message);
+ if (resp.okay == false) {
+ Log.w("ddms", "Got unhappy response from ADB sync req: " + resp.message);
mChannel.close();
mChannel = null;
return false;
}
+ } catch (TimeoutException e) {
+ if (mChannel != null) {
+ try {
+ mChannel.close();
+ } catch (IOException e2) {
+ // we want to throw the original exception, so we ignore this one.
+ }
+ mChannel = null;
+ }
+
+ throw e;
} catch (IOException e) {
if (mChannel != null) {
try {
@@ -309,6 +322,8 @@ public final class SyncService {
return "Remote path is a file.";
case RESULT_BUFFER_OVERRUN:
return "Receiving too much data.";
+ case RESULT_CONNECTION_TIMEOUT:
+ return "timeout";
}
throw new RuntimeException();
@@ -586,6 +601,8 @@ public final class SyncService {
}
} catch (UnsupportedEncodingException e) {
return new SyncResult(RESULT_REMOTE_PATH_ENCODING, e);
+ } catch (TimeoutException e) {
+ return new SyncResult(RESULT_CONNECTION_TIMEOUT, e);
} catch (IOException e) {
return new SyncResult(RESULT_CONNECTION_ERROR, e);
}
@@ -633,6 +650,8 @@ public final class SyncService {
// get the header for the next packet.
AdbHelper.read(mChannel, pullResult, -1, timeOut);
+ } catch (TimeoutException e) {
+ return new SyncResult(RESULT_CONNECTION_TIMEOUT, e);
} catch (IOException e) {
return new SyncResult(RESULT_CONNECTION_ERROR, e);
}
@@ -739,6 +758,8 @@ public final class SyncService {
// file and network IO exceptions.
try {
AdbHelper.write(mChannel, msg, -1, timeOut);
+ } catch (TimeoutException e) {
+ return new SyncResult(RESULT_CONNECTION_TIMEOUT, e);
} catch (IOException e) {
return new SyncResult(RESULT_CONNECTION_ERROR, e);
}
@@ -777,6 +798,8 @@ public final class SyncService {
// now write it
try {
AdbHelper.write(mChannel, mBuffer, readCount+8, timeOut);
+ } catch (TimeoutException e) {
+ return new SyncResult(RESULT_CONNECTION_TIMEOUT, e);
} catch (IOException e) {
return new SyncResult(RESULT_CONNECTION_ERROR, e);
}
@@ -819,6 +842,8 @@ public final class SyncService {
return new SyncResult(RESULT_UNKNOWN_ERROR);
}
+ } catch (TimeoutException e) {
+ return new SyncResult(RESULT_CONNECTION_TIMEOUT, e);
} catch (IOException e) {
return new SyncResult(RESULT_CONNECTION_ERROR, e);
}
@@ -831,29 +856,27 @@ public final class SyncService {
* @param path the remote file
* @return and Integer containing the mode if all went well or null
* otherwise
+ * @throws IOException
+ * @throws TimeoutException
*/
- private Integer readMode(String path) {
- try {
- // create the stat request message.
- byte[] msg = createFileReq(ID_STAT, path);
-
- AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut());
+ private Integer readMode(String path) throws TimeoutException, IOException {
+ // create the stat request message.
+ byte[] msg = createFileReq(ID_STAT, path);
- // read the result, in a byte array containing 4 ints
- // (id, mode, size, time)
- byte[] statResult = new byte[16];
- AdbHelper.read(mChannel, statResult, -1 /* full length */, DdmPreferences.getTimeOut());
+ AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut());
- // check we have the proper data back
- if (checkResult(statResult, ID_STAT) == false) {
- return null;
- }
+ // read the result, in a byte array containing 4 ints
+ // (id, mode, size, time)
+ byte[] statResult = new byte[16];
+ AdbHelper.read(mChannel, statResult, -1 /* full length */, DdmPreferences.getTimeOut());
- // we return the mode (2nd int in the array)
- return ArrayHelper.swap32bitFromArray(statResult, 4);
- } catch (IOException e) {
+ // check we have the proper data back
+ if (checkResult(statResult, ID_STAT) == false) {
return null;
}
+
+ // we return the mode (2nd int in the array)
+ return ArrayHelper.swap32bitFromArray(statResult, 4);
}
/**
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/TimeoutException.java b/ddms/libs/ddmlib/src/com/android/ddmlib/TimeoutException.java
new file mode 100644
index 0000000..25be2f9
--- /dev/null
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/TimeoutException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 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.ddmlib;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when a connection to Adb failed with a timeout.
+ *
+ */
+public class TimeoutException extends IOException {
+ private static final long serialVersionUID = 1L;
+}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/log/EventLogParser.java b/ddms/libs/ddmlib/src/com/android/ddmlib/log/EventLogParser.java
index 0b2ce69..31f265f 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/log/EventLogParser.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/log/EventLogParser.java
@@ -97,7 +97,8 @@ public final class EventLogParser {
return false;
}
});
- } catch (IOException e) {
+ } catch (Exception e) {
+ // catch all possible exceptions and return false.
return false;
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java
index cd40527..7cb6557 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java
@@ -16,6 +16,11 @@
package com.android.ddmlib.testrunner;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
+
import java.io.IOException;
import java.util.Collection;
@@ -168,22 +173,51 @@ public interface IRemoteAndroidTestRunner {
public void setCoverage(boolean coverage);
/**
+ * Sets the maximum time allowed between output of the shell command running the tests on
+ * the devices.
+ * <p/>
+ * This allows setting a timeout in case the tests can become stuck and never finish. This is
+ * different from the normal timeout on the connection.
+ * <p/>
+ * By default no timeout will be specified.
+ *
+ * @see {@link IDevice#executeShellCommand(String, com.android.ddmlib.IShellOutputReceiver, int)}
+ */
+ public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse);
+
+ /**
* Execute this test run.
* <p/>
* Convenience method for {@link #run(Collection)}.
*
* @param listeners listens for test results
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output any test result for
+ * a period longer than the max time to output.
* @throws IOException if connection to device was lost.
+ *
+ * @see #setMaxtimeToOutputResponse(int)
*/
- public void run(ITestRunListener... listeners) throws IOException;
+ public void run(ITestRunListener... listeners)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
* Execute this test run.
*
* @param listeners collection of listeners for test results
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output any test result for
+ * a period longer than the max time to output.
* @throws IOException if connection to device was lost.
+ *
+ * @see #setMaxtimeToOutputResponse(int)
*/
- public void run(Collection<ITestRunListener> listeners) throws IOException;
+ public void run(Collection<ITestRunListener> listeners)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException;
/**
* Requests cancellation of this test run.
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index c0ae309..681c214 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -19,6 +19,9 @@ package com.android.ddmlib.testrunner;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
import java.io.IOException;
import java.util.Arrays;
@@ -35,6 +38,9 @@ public class RemoteAndroidTestRunner implements IRemoteAndroidTestRunner {
private final String mPackageName;
private final String mRunnerName;
private IDevice mRemoteDevice;
+ // default to no timeout
+ private int mMaxTimeToOutputResponse = 0;
+
/** map of name-value instrumentation argument pairs */
private Map<String, String> mArgMap;
private InstrumentationResultParser mParser;
@@ -201,21 +207,32 @@ public class RemoteAndroidTestRunner implements IRemoteAndroidTestRunner {
/**
* {@inheritDoc}
*/
- public void run(ITestRunListener... listeners) throws IOException {
+ public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse) {
+ mMaxTimeToOutputResponse = maxTimeToOutputResponse;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void run(ITestRunListener... listeners)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
run(Arrays.asList(listeners));
}
/**
* {@inheritDoc}
*/
- public void run(Collection<ITestRunListener> listeners) throws IOException {
+ public void run(Collection<ITestRunListener> listeners)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
final String runCaseCommandStr = String.format("am instrument -w -r %s %s",
getArgsCommand(), getRunnerPath());
Log.i(LOG_TAG, String.format("Running %s on %s", runCaseCommandStr,
mRemoteDevice.getSerialNumber()));
mParser = new InstrumentationResultParser(listeners);
- mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser);
+ mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse);
}
/**
diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index d365248..85b57aa 100644
--- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -130,13 +130,21 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
}
/**
+ * Stores the provided command for later retrieval from getLastShellCommand.
+ */
+ public void executeShellCommand(String command,
+ IShellOutputReceiver receiver, int timeout) throws IOException {
+ mLastShellCommand = command;
+ }
+
+ /**
* Get the last command provided to executeShellCommand.
*/
public String getLastShellCommand() {
return mLastShellCommand;
}
- public boolean createForward(int localPort, int remotePort) {
+ public void createForward(int localPort, int remotePort) {
throw new UnsupportedOperationException();
}
@@ -208,7 +216,7 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
- public boolean removeForward(int localPort, int remotePort) {
+ public void removeForward(int localPort, int remotePort) {
throw new UnsupportedOperationException();
}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
index 297d731..710d7eb 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
@@ -513,7 +513,7 @@ public class LogPanel extends SelectionDependentPanel {
try {
mCurrentLoggedDevice = device;
- device.executeShellCommand("logcat -v long", mCurrentLogCat); //$NON-NLS-1$
+ device.executeShellCommand("logcat -v long", mCurrentLogCat, 0 /*timeout*/); //$NON-NLS-1$
} catch (Exception e) {
Log.e("Logcat", e);
} finally {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/ActivityLaunchAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/ActivityLaunchAction.java
index d1502d3..0a6257f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/ActivityLaunchAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/ActivityLaunchAction.java
@@ -16,7 +16,10 @@
package com.android.ide.eclipse.adt.internal.launch;
+import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.IDevice;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
import com.android.ide.eclipse.adt.AdtPlugin;
import java.io.IOException;
@@ -49,8 +52,16 @@ public class ActivityLaunchAction implements IAndroidLaunchAction {
* @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice)
*/
public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) {
+ String command = "am start" //$NON-NLS-1$
+ + (info.isDebugMode() ? " -D" //$NON-NLS-1$
+ : "") //$NON-NLS-1$
+ + " -n " //$NON-NLS-1$
+ + info.getPackageName() + "/" //$NON-NLS-1$
+ + mActivity.replaceAll("\\$", "\\\\\\$") //$NON-NLS-1$ //$NON-NLS-2$
+ + " -a android.intent.action.MAIN" //$NON-NLS-1$
+ + " -c android.intent.category.LAUNCHER";
try {
- String msg = String.format("Starting activity %1$s on device ", mActivity,
+ String msg = String.format("Starting activity %1$s on device %2$s", mActivity,
device);
AdtPlugin.printToConsole(info.getProject(), msg);
@@ -60,15 +71,7 @@ public class ActivityLaunchAction implements IAndroidLaunchAction {
info.incrementAttemptCount();
// now we actually launch the app.
- device.executeShellCommand("am start" //$NON-NLS-1$
- + (info.isDebugMode() ? " -D" //$NON-NLS-1$
- : "") //$NON-NLS-1$
- + " -n " //$NON-NLS-1$
- + info.getPackageName() + "/" //$NON-NLS-1$
- + mActivity.replaceAll("\\$", "\\\\\\$") //$NON-NLS-1$ //$NON-NLS-2$
- + " -a android.intent.action.MAIN" //$NON-NLS-1$
- + " -c android.intent.category.LAUNCHER", //$NON-NLS-1$
- new AMReceiver(info, device, mLaunchController));
+ device.executeShellCommand(command, new AMReceiver(info, device, mLaunchController));
// if the app is not a debug app, we need to do some clean up, as
// the process is done!
@@ -77,6 +80,17 @@ public class ActivityLaunchAction implements IAndroidLaunchAction {
// provide any control over the app
return false;
}
+ } catch (TimeoutException e) {
+ AdtPlugin.printErrorToConsole(info.getProject(), "Launch error: timeout");
+ return false;
+ } catch (AdbCommandRejectedException e) {
+ AdtPlugin.printErrorToConsole(info.getProject(), String.format(
+ "Launch error: adb rejected command: %1$s", e.getMessage()));
+ return false;
+ } catch (ShellCommandUnresponsiveException e) {
+ // we didn't get the output but that's ok, just log it
+ AdtPlugin.log(e, "No command output when running: '%1$s' on device %2$s", command,
+ device);
} catch (IOException e) {
// something went wrong trying to launch the app.
// lets stop the Launch
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchMessages.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchMessages.java
index d09ddff..b2772f7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchMessages.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchMessages.java
@@ -44,6 +44,7 @@ public class LaunchMessages extends NLS {
public static String RemoteAdtTestRunner_RunFailedMsg_s;
public static String RemoteAdtTestRunner_RunIOException_s;
+ public static String RemoteAdtTestRunner_RunTimeoutException;
public static String RemoteAdtTestRunner_RunStoppedMsg;
static {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/runtime/RemoteAdtTestRunner.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/runtime/RemoteAdtTestRunner.java
index 461fd9d..3f40230 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/runtime/RemoteAdtTestRunner.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/runtime/RemoteAdtTestRunner.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.launch.junit.runtime;
+import com.android.ddmlib.TimeoutException;
import com.android.ddmlib.testrunner.ITestRunListener;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.TestIdentifier;
@@ -125,6 +126,8 @@ public class RemoteAdtTestRunner extends RemoteTestRunner {
}
AdtPlugin.printToConsole(mLaunchInfo.getProject(), "Running tests...");
runner.run(new TestRunListener());
+ } catch (TimeoutException e) {
+ reportError(LaunchMessages.RemoteAdtTestRunner_RunTimeoutException);
} catch (IOException e) {
reportError(String.format(LaunchMessages.RemoteAdtTestRunner_RunIOException_s,
e.getMessage()));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
index 67c9116..1ac7fb2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
@@ -35,5 +35,6 @@ InstrValidator_NoTestLibMsg_s=The application does not declare uses-library %1$s
InstrValidator_WrongRunnerTypeMsg_s=The instrumentation runner must be of type %1$s
RemoteAdtTestRunner_RunCompleteMsg=Test run complete
RemoteAdtTestRunner_RunFailedMsg_s=Test run failed: %1$s
-RemoteAdtTestRunner_RunIOException_s=Test run failed. Lost connection with device: %s
+RemoteAdtTestRunner_RunTimeoutException=Connection with device timed out.
+RemoteAdtTestRunner_RunIOException_s=Lost connection with device: %s
RemoteAdtTestRunner_RunStoppedMsg=Test run stopped
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/device/DeviceBridge.java b/hierarchyviewer/src/com/android/hierarchyviewer/device/DeviceBridge.java
index 0f60be6..209577d 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/device/DeviceBridge.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/device/DeviceBridge.java
@@ -16,10 +16,12 @@
package com.android.hierarchyviewer.device;
+import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.TimeoutException;
import java.io.IOException;
import java.io.File;
@@ -120,8 +122,20 @@ public class DeviceBridge {
synchronized (devicePortMap) {
if (device.getState() == IDevice.DeviceState.ONLINE) {
int localPort = nextLocalPort++;
- device.createForward(localPort, Configuration.DEFAULT_SERVER_PORT);
- devicePortMap.put(device, localPort);
+ try {
+ device.createForward(localPort, Configuration.DEFAULT_SERVER_PORT);
+ devicePortMap.put(device, localPort);
+ } catch (TimeoutException e) {
+ Log.e("hierarchy", "Timeout setting up port forwarding for " + device);
+ } catch (AdbCommandRejectedException e) {
+ Log.e("hierarchy", String.format(
+ "Adb rejected forward command for device %1$s: %2$s",
+ device, e.getMessage()));
+ } catch (IOException e) {
+ Log.e("hierarchy", String.format(
+ "Failed to create forward for device %1$s: %2$s",
+ device, e.getMessage()));
+ }
}
}
}
@@ -130,8 +144,20 @@ public class DeviceBridge {
synchronized (devicePortMap) {
final Integer localPort = devicePortMap.get(device);
if (localPort != null) {
- device.removeForward(localPort, Configuration.DEFAULT_SERVER_PORT);
- devicePortMap.remove(device);
+ try {
+ device.removeForward(localPort, Configuration.DEFAULT_SERVER_PORT);
+ devicePortMap.remove(device);
+ } catch (TimeoutException e) {
+ Log.e("hierarchy", "Timeout removing port forwarding for " + device);
+ } catch (AdbCommandRejectedException e) {
+ Log.e("hierarchy", String.format(
+ "Adb rejected remove-forward command for device %1$s: %2$s",
+ device, e.getMessage()));
+ } catch (IOException e) {
+ Log.e("hierarchy", String.format(
+ "Failed to remove forward for device %1$s: %2$s",
+ device, e.getMessage()));
+ }
}
}
}