diff options
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><build></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())); + } } } } |