diff options
author | Raphael Moll <ralf@android.com> | 2013-01-11 10:59:25 -0800 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2013-01-11 11:17:26 -0800 |
commit | 11e58451f45294022625179d01a4b35b65f9ff07 (patch) | |
tree | 8d6d42e029fa6e765d8263c6f5162b201c5d2e21 /ddms | |
parent | 5b30d8114124620147d411c4e74bac426b7d34d4 (diff) | |
download | sdk-11e58451f45294022625179d01a4b35b65f9ff07.zip sdk-11e58451f45294022625179d01a4b35b65f9ff07.tar.gz sdk-11e58451f45294022625179d01a4b35b65f9ff07.tar.bz2 |
SDK: switch ddmlib to prebuilts/devtools/ddmlib/ddmlib.jar
This also removes the ddmlib-tests.jar module.
Change-Id: Id44bcc4b91ee76e22a3a50c000cda1b1e7bb972e
Diffstat (limited to 'ddms')
71 files changed, 9 insertions, 20259 deletions
diff --git a/ddms/libs/ddmlib/Android.mk b/ddms/libs/ddmlib/Android.mk index c0191f9..c479e79 100644 --- a/ddms/libs/ddmlib/Android.mk +++ b/ddms/libs/ddmlib/Android.mk @@ -13,32 +13,20 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) - include $(CLEAR_VARS) -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) -LOCAL_JAVA_RESOURCE_DIRS := src/main/java - -LOCAL_JAVA_LIBRARIES := kxml2-2.3.0 +# The ddmlib source has moved to tools/base/ddmlib. +# The rule below uses the prebuilt ddmlib.jar if found. +# +# If you want to run the tests, cd to tools/base/ddmlib +# and run ./gradlew :ddmlib:test LOCAL_MODULE := ddmlib - -include $(BUILD_HOST_JAVA_LIBRARY) - -# Build tests - -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) - -LOCAL_MODULE := ddmlib-tests LOCAL_MODULE_TAGS := optional +LOCAL_JAVA_LIBRARIES := kxml2-2.3.0 -LOCAL_JAVA_LIBRARIES := ddmlib junit easymock +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -include $(BUILD_HOST_JAVA_LIBRARY) +include $(BUILD_HOST_PREBUILT) -# Build all sub-directories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AdbCommandRejectedException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AdbCommandRejectedException.java deleted file mode 100644 index ae7d014..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AdbCommandRejectedException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - -/** - * Exception thrown when adb refuses a command. - */ -public class AdbCommandRejectedException extends Exception { - 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/main/java/com/android/ddmlib/AdbHelper.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java deleted file mode 100644 index c1c9300..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.log.LogReceiver; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.SocketChannel; - -/** - * Helper class to handle requests and connections to adb. - * <p/>{@link DebugBridgeServer} is the public API to connection to adb, while {@link AdbHelper} - * does the low level stuff. - * <p/>This currently uses spin-wait non-blocking I/O. A Selector would be more efficient, - * but seems like overkill for what we're doing here. - */ -final class AdbHelper { - - // public static final long kOkay = 0x59414b4fL; - // public static final long kFail = 0x4c494146L; - - static final int WAIT_TIME = 5; // spin-wait sleep, in ms - - static final String DEFAULT_ENCODING = "ISO-8859-1"; //$NON-NLS-1$ - - /** do not instantiate */ - private AdbHelper() { - } - - /** - * Response from ADB. - */ - static class AdbResponse { - public AdbResponse() { - message = ""; - } - - public boolean okay; // first 4 bytes in response were "OKAY"? - - public String message; // diagnostic string if #okay is false - } - - /** - * Create and connect a new pass-through socket, from the host to a port on - * the device. - * - * @param adbSockAddr - * @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, TimeoutException, AdbCommandRejectedException { - - SocketChannel adbChan = SocketChannel.open(adbSockAddr); - try { - adbChan.socket().setTcpNoDelay(true); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to - // talk to a specific device - setDevice(adbChan, device); - - byte[] req = createAdbForwardRequest(null, devicePort); - // Log.hexDump(req); - - write(adbChan, req); - - AdbResponse resp = readAdbResponse(adbChan, false); - if (resp.okay == false) { - throw new AdbCommandRejectedException(resp.message); - } - - adbChan.configureBlocking(true); - } catch (TimeoutException e) { - adbChan.close(); - throw e; - } catch (IOException e) { - adbChan.close(); - throw e; - } - - return adbChan; - } - - /** - * Creates and connects a new pass-through socket, from the host to a port on - * the device. - * - * @param adbSockAddr - * @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 TimeoutException, AdbCommandRejectedException, IOException { - - SocketChannel adbChan = SocketChannel.open(adbSockAddr); - try { - adbChan.socket().setTcpNoDelay(true); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to - // talk to a specific device - setDevice(adbChan, device); - - byte[] req = createJdwpForwardRequest(pid); - // Log.hexDump(req); - - write(adbChan, req); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - throw new AdbCommandRejectedException(resp.message); - } - - adbChan.configureBlocking(true); - } catch (TimeoutException e) { - adbChan.close(); - throw e; - } catch (IOException e) { - adbChan.close(); - throw e; - } - - return adbChan; - } - - /** - * Creates a port forwarding request for adb. This returns an array - * containing "####tcp:{port}:{addStr}". - * @param addrStr the host. Can be null. - * @param port the port on the device. This does not need to be numeric. - */ - private static byte[] createAdbForwardRequest(String addrStr, int port) { - String reqStr; - - if (addrStr == null) - reqStr = "tcp:" + port; - else - reqStr = "tcp:" + port + ":" + addrStr; - return formAdbRequest(reqStr); - } - - /** - * Creates a port forwarding request to a jdwp process. This returns an array - * containing "####jwdp:{pid}". - * @param pid the jdwp process pid on the device. - */ - private static byte[] createJdwpForwardRequest(int pid) { - String reqStr = String.format("jdwp:%1$d", pid); //$NON-NLS-1$ - return formAdbRequest(reqStr); - } - - /** - * Create an ASCII string preceeded by four hex digits. The opening "####" - * is the length of the rest of the string, encoded as ASCII hex (case - * doesn't matter). "port" and "host" are what we want to forward to. If - * we're on the host side connecting into the device, "addrStr" should be - * null. - */ - static byte[] formAdbRequest(String req) { - String resultStr = String.format("%04X%s", req.length(), req); //$NON-NLS-1$ - byte[] result; - try { - result = resultStr.getBytes(DEFAULT_ENCODING); - } catch (UnsupportedEncodingException uee) { - uee.printStackTrace(); // not expected - return null; - } - assert result.length == req.length() + 4; - return result; - } - - /** - * Reads the response from ADB after a command. - * @param chan The socket channel that is connected to adb. - * @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 TimeoutException, IOException { - - AdbResponse resp = new AdbResponse(); - - byte[] reply = new byte[4]; - read(chan, reply); - - if (isOkay(reply)) { - resp.okay = true; - } else { - readDiagString = true; // look for a reason after the FAIL - resp.okay = false; - } - - // not a loop -- use "while" so we can use "break" - try { - while (readDiagString) { - // length string is in next 4 bytes - byte[] lenBuf = new byte[4]; - read(chan, 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; - } - - byte[] msg = new byte[len]; - read(chan, msg); - - resp.message = replyToString(msg); - Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='" - + resp.message + "'"); - - break; - } - } catch (Exception e) { - // ignore those, since it's just reading the diagnose string, the response will - // contain okay==false anyway. - } - - return resp; - } - - /** - * 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. - */ - static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device) - throws TimeoutException, AdbCommandRejectedException, IOException { - - RawImage imageParams = new RawImage(); - byte[] request = formAdbRequest("framebuffer:"); //$NON-NLS-1$ - byte[] nudge = { - 0 - }; - byte[] reply; - - SocketChannel adbChan = null; - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to talk - // to a specific device - setDevice(adbChan, device); - - write(adbChan, request); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - throw new AdbCommandRejectedException(resp.message); - } - - // first the protocol version. - reply = new byte[4]; - read(adbChan, reply); - - ByteBuffer buf = ByteBuffer.wrap(reply); - buf.order(ByteOrder.LITTLE_ENDIAN); - - int version = buf.getInt(); - - // get the header size (this is a count of int) - int headerSize = RawImage.getHeaderSize(version); - - // read the header - reply = new byte[headerSize * 4]; - read(adbChan, reply); - - buf = ByteBuffer.wrap(reply); - buf.order(ByteOrder.LITTLE_ENDIAN); - - // fill the RawImage with the header - if (imageParams.readHeader(version, buf) == false) { - Log.e("Screenshot", "Unsupported protocol: " + version); - return null; - } - - Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size=" - + imageParams.size + ", width=" + imageParams.width - + ", height=" + imageParams.height); - - write(adbChan, nudge); - - reply = new byte[imageParams.size]; - read(adbChan, reply); - - imageParams.data = reply; - } finally { - if (adbChan != null) { - adbChan.close(); - } - } - - return imageParams; - } - - /** - * 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() - */ - 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; - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to - // talk - // to a specific device - setDevice(adbChan, device); - - byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$ - write(adbChan, request); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message); - throw new AdbCommandRejectedException(resp.message); - } - - byte[] data = new byte[16384]; - ByteBuffer buf = ByteBuffer.wrap(data); - int timeToResponseCount = 0; - while (true) { - int count; - - if (rcvr != null && rcvr.isCancelled()) { - Log.v("ddms", "execute: cancelled"); - break; - } - - count = adbChan.read(buf); - if (count < 0) { - // we're at the end, we flush the output - rcvr.flush(); - Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: " - + count); - break; - } else if (count == 0) { - try { - 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()); - } - buf.rewind(); - } - } - } finally { - if (adbChan != null) { - adbChan.close(); - } - Log.v("ddms", "execute: returning"); - } - } - - /** - * 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 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 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 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 TimeoutException, AdbCommandRejectedException, IOException { - SocketChannel adbChan = null; - - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to talk - // to a specific device - setDevice(adbChan, device); - - byte[] request = formAdbRequest("log:" + logName); - write(adbChan, request); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - throw new AdbCommandRejectedException(resp.message); - } - - byte[] data = new byte[16384]; - ByteBuffer buf = ByteBuffer.wrap(data); - while (true) { - int count; - - if (rcvr != null && rcvr.isCancelled()) { - break; - } - - count = adbChan.read(buf); - if (count < 0) { - break; - } else if (count == 0) { - try { - Thread.sleep(WAIT_TIME * 5); - } catch (InterruptedException ie) { - } - } else { - if (rcvr != null) { - rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position()); - } - buf.rewind(); - } - } - } finally { - if (adbChan != null) { - adbChan.close(); - } - } - } - - /** - * Creates a port forwarding between a local and a remote port. - * @param adbSockAddr the socket address to connect to adb - * @param device the device on which to do the port fowarding - * @param localPortSpec specification of the local port to forward, should be of format - * tcp:<port number> - * @param remotePortSpec specification of the remote port to forward to, one of: - * tcp:<port> - * localabstract:<unix domain socket name> - * localreserved:<unix domain socket name> - * localfilesystem:<unix domain socket name> - * dev:<character device name> - * jdwp:<process pid> (remote only) - * @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 createForward(InetSocketAddress adbSockAddr, Device device, - String localPortSpec, String remotePortSpec) - throws TimeoutException, AdbCommandRejectedException, IOException { - - SocketChannel adbChan = null; - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - byte[] request = formAdbRequest(String.format( - "host-serial:%1$s:forward:%2$s;%3$s", //$NON-NLS-1$ - device.getSerialNumber(), localPortSpec, remotePortSpec)); - - write(adbChan, request); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - Log.w("create-forward", "Error creating forward: " + resp.message); - throw new AdbCommandRejectedException(resp.message); - } - } finally { - if (adbChan != null) { - adbChan.close(); - } - } - } - - /** - * Remove a port forwarding between a local and a remote port. - * @param adbSockAddr the socket address to connect to adb - * @param device the device on which to remove the port fowarding - * @param localPortSpec specification of the local port that was forwarded, should be of format - * tcp:<port number> - * @param remotePortSpec specification of the remote port forwarded to, one of: - * tcp:<port> - * localabstract:<unix domain socket name> - * localreserved:<unix domain socket name> - * localfilesystem:<unix domain socket name> - * dev:<character device name> - * jdwp:<process pid> (remote only) - * @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 removeForward(InetSocketAddress adbSockAddr, Device device, - String localPortSpec, String remotePortSpec) - throws TimeoutException, AdbCommandRejectedException, IOException { - - SocketChannel adbChan = null; - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - byte[] request = formAdbRequest(String.format( - "host-serial:%1$s:killforward:%2$s;%3$s", //$NON-NLS-1$ - device.getSerialNumber(), localPortSpec, remotePortSpec)); - - write(adbChan, request); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - if (resp.okay == false) { - Log.w("remove-forward", "Error creating forward: " + resp.message); - throw new AdbCommandRejectedException(resp.message); - } - } finally { - if (adbChan != null) { - adbChan.close(); - } - } - } - - /** - * Checks to see if the first four bytes in "reply" are OKAY. - */ - static boolean isOkay(byte[] reply) { - return reply[0] == (byte)'O' && reply[1] == (byte)'K' - && reply[2] == (byte)'A' && reply[3] == (byte)'Y'; - } - - /** - * Converts an ADB reply to a string. - */ - static String replyToString(byte[] reply) { - String result; - try { - result = new String(reply, DEFAULT_ENCODING); - } catch (UnsupportedEncodingException uee) { - uee.printStackTrace(); // not expected - result = ""; - } - return result; - } - - /** - * 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. - * @throws TimeoutException in case of timeout on the connection. - * @throws IOException in case of I/O error on the connection. - */ - static void read(SocketChannel chan, byte[] data) throws TimeoutException, IOException { - read(chan, data, -1, DdmPreferences.getTimeOut()); - } - - /** - * Reads from the socket until the array is filled, the optional length - * is reached, or no more data is coming (because the socket closed or the - * timeout expired). After "timeout" milliseconds since the - * previous successful read, this will return whether or not new data has - * been found. - * - * @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. - * @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". - */ - 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; - - while (buf.position() != buf.limit()) { - int count; - - count = chan.read(buf); - if (count < 0) { - Log.d("ddms", "read: channel EOF"); - throw new IOException("EOF"); - } else if (count == 0) { - // TODO: need more accurate timeout? - if (timeout != 0 && numWaits * WAIT_TIME > timeout) { - Log.d("ddms", "read: timeout"); - throw new TimeoutException(); - } - // non-blocking spin - try { - Thread.sleep(WAIT_TIME); - } catch (InterruptedException ie) { - } - numWaits++; - } else { - numWaits = 0; - } - } - } - - /** - * 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. - * @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) throws TimeoutException, IOException { - write(chan, data, -1, DdmPreferences.getTimeOut()); - } - - /** - * Write until all data in "data" is written, the optional length is reached, - * the timeout expires, or the connection fails. Returns "true" if all - * data was written. - * @param chan the opened socket to write to. - * @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 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 TimeoutException, IOException { - ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); - int numWaits = 0; - - while (buf.position() != buf.limit()) { - int count; - - count = chan.write(buf); - if (count < 0) { - Log.d("ddms", "write: channel EOF"); - throw new IOException("channel EOF"); - } else if (count == 0) { - // TODO: need more accurate timeout? - if (timeout != 0 && numWaits * WAIT_TIME > timeout) { - Log.d("ddms", "write: timeout"); - throw new TimeoutException(); - } - // non-blocking spin - try { - Thread.sleep(WAIT_TIME); - } catch (InterruptedException ie) { - } - numWaits++; - } else { - numWaits = 0; - } - } - } - - /** - * tells adb to talk to a specific device - * - * @param adbChan the socket connection to adb - * @param device The device to talk 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. - */ - 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); - - write(adbChan, device_query); - - AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); - 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 TimeoutException, AdbCommandRejectedException, IOException { - byte[] request; - if (into == null) { - request = formAdbRequest("reboot:"); //$NON-NLS-1$ - } else { - request = formAdbRequest("reboot:" + into); //$NON-NLS-1$ - } - - SocketChannel adbChan = null; - try { - adbChan = SocketChannel.open(adbSockAddr); - adbChan.configureBlocking(false); - - // if the device is not -1, then we first tell adb we're looking to talk - // to a specific device - setDevice(adbChan, device); - - write(adbChan, request); - } finally { - if (adbChan != null) { - adbChan.close(); - } - } - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AllocationInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AllocationInfo.java deleted file mode 100644 index 157b044..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AllocationInfo.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2008 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.util.Comparator; -import java.util.Locale; - -/** - * Holds an Allocation information. - */ -public class AllocationInfo implements IStackTraceInfo { - private final String mAllocatedClass; - private final int mAllocNumber; - private final int mAllocationSize; - private final short mThreadId; - private final StackTraceElement[] mStackTrace; - - public static enum SortMode { - NUMBER, SIZE, CLASS, THREAD, IN_CLASS, IN_METHOD; - } - - public final static class AllocationSorter implements Comparator<AllocationInfo> { - - private SortMode mSortMode = SortMode.SIZE; - private boolean mDescending = true; - - public AllocationSorter() { - } - - public void setSortMode(SortMode mode) { - if (mSortMode == mode) { - mDescending = !mDescending; - } else { - mSortMode = mode; - } - } - - public SortMode getSortMode() { - return mSortMode; - } - - public boolean isDescending() { - return mDescending; - } - - @Override - public int compare(AllocationInfo o1, AllocationInfo o2) { - int diff = 0; - switch (mSortMode) { - case NUMBER: - diff = o1.mAllocNumber - o2.mAllocNumber; - break; - case SIZE: - // pass, since diff is init with 0, we'll use SIZE compare below - // as a back up anyway. - break; - case CLASS: - diff = o1.mAllocatedClass.compareTo(o2.mAllocatedClass); - break; - case THREAD: - diff = o1.mThreadId - o2.mThreadId; - break; - case IN_CLASS: - String class1 = o1.getFirstTraceClassName(); - String class2 = o2.getFirstTraceClassName(); - diff = compareOptionalString(class1, class2); - break; - case IN_METHOD: - String method1 = o1.getFirstTraceMethodName(); - String method2 = o2.getFirstTraceMethodName(); - diff = compareOptionalString(method1, method2); - break; - } - - if (diff == 0) { - // same? compare on size - diff = o1.mAllocationSize - o2.mAllocationSize; - } - - if (mDescending) { - diff = -diff; - } - - return diff; - } - - /** compares two strings that could be null */ - private int compareOptionalString(String str1, String str2) { - if (str1 != null) { - if (str2 == null) { - return -1; - } else { - return str1.compareTo(str2); - } - } else { - if (str2 == null) { - return 0; - } else { - return 1; - } - } - } - } - - /* - * Simple constructor. - */ - AllocationInfo(int allocNumber, String allocatedClass, int allocationSize, - short threadId, StackTraceElement[] stackTrace) { - mAllocNumber = allocNumber; - mAllocatedClass = allocatedClass; - mAllocationSize = allocationSize; - mThreadId = threadId; - mStackTrace = stackTrace; - } - - /** - * Returns the allocation number. Allocations are numbered as they happen with the most - * recent one having the highest number - */ - public int getAllocNumber() { - return mAllocNumber; - } - - /** - * Returns the name of the allocated class. - */ - public String getAllocatedClass() { - return mAllocatedClass; - } - - /** - * Returns the size of the allocation. - */ - public int getSize() { - return mAllocationSize; - } - - /** - * Returns the id of the thread that performed the allocation. - */ - public short getThreadId() { - return mThreadId; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IStackTraceInfo#getStackTrace() - */ - @Override - public StackTraceElement[] getStackTrace() { - return mStackTrace; - } - - public int compareTo(AllocationInfo otherAlloc) { - return otherAlloc.mAllocationSize - mAllocationSize; - } - - public String getFirstTraceClassName() { - if (mStackTrace.length > 0) { - return mStackTrace[0].getClassName(); - } - - return null; - } - - public String getFirstTraceMethodName() { - if (mStackTrace.length > 0) { - return mStackTrace[0].getMethodName(); - } - - return null; - } - - /** - * Returns true if the given filter matches case insensitively (according to - * the given locale) this allocation info. - */ - public boolean filter(String filter, boolean fullTrace, Locale locale) { - if (mAllocatedClass.toLowerCase(locale).contains(filter)) { - return true; - } - - if (mStackTrace.length > 0) { - // check the top of the stack trace always - final int length = fullTrace ? mStackTrace.length : 1; - - for (int i = 0 ; i < length ; i++) { - if (mStackTrace[i].getClassName().toLowerCase(locale).contains(filter)) { - return true; - } - - if (mStackTrace[i].getMethodName().toLowerCase(locale).contains(filter)) { - return true; - } - } - } - - return false; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java deleted file mode 100644 index 5407d7f..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java +++ /dev/null @@ -1,1163 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.Log.LogLevel; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.Thread.State; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A connection to the host-side android debug bridge (adb) - * <p/>This is the central point to communicate with any devices, emulators, or the applications - * running on them. - * <p/><b>{@link #init(boolean)} must be called before anything is done.</b> - */ -public final class AndroidDebugBridge { - - /* - * Minimum and maximum version of adb supported. This correspond to - * ADB_SERVER_VERSION found in //device/tools/adb/adb.h - */ - - private final static int ADB_VERSION_MICRO_MIN = 20; - private final static int ADB_VERSION_MICRO_MAX = -1; - - private final static Pattern sAdbVersion = Pattern.compile( - "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"); //$NON-NLS-1$ - - private final static String ADB = "adb"; //$NON-NLS-1$ - private final static String DDMS = "ddms"; //$NON-NLS-1$ - private final static String SERVER_PORT_ENV_VAR = "ANDROID_ADB_SERVER_PORT"; //$NON-NLS-1$ - - // Where to find the ADB bridge. - final static String ADB_HOST = "127.0.0.1"; //$NON-NLS-1$ - final static int ADB_PORT = 5037; - - private static InetAddress sHostAddr; - private static InetSocketAddress sSocketAddr; - - private static AndroidDebugBridge sThis; - private static boolean sInitialized = false; - private static boolean sClientSupport; - - /** Full path to adb. */ - private String mAdbOsLocation = null; - - private boolean mVersionCheck; - - private boolean mStarted = false; - - private DeviceMonitor mDeviceMonitor; - - private final static ArrayList<IDebugBridgeChangeListener> sBridgeListeners = - new ArrayList<IDebugBridgeChangeListener>(); - private final static ArrayList<IDeviceChangeListener> sDeviceListeners = - new ArrayList<IDeviceChangeListener>(); - private final static ArrayList<IClientChangeListener> sClientListeners = - new ArrayList<IClientChangeListener>(); - - // lock object for synchronization - private static final Object sLock = sBridgeListeners; - - /** - * Classes which implement this interface provide a method that deals - * with {@link AndroidDebugBridge} changes. - */ - public interface IDebugBridgeChangeListener { - /** - * Sent when a new {@link AndroidDebugBridge} is connected. - * <p/> - * This is sent from a non UI thread. - * @param bridge the new {@link AndroidDebugBridge} object. - */ - public void bridgeChanged(AndroidDebugBridge bridge); - } - - /** - * Classes which implement this interface provide methods that deal - * with {@link IDevice} addition, deletion, and changes. - */ - public interface IDeviceChangeListener { - /** - * Sent when the a device is connected to the {@link AndroidDebugBridge}. - * <p/> - * This is sent from a non UI thread. - * @param device the new device. - */ - public void deviceConnected(IDevice device); - - /** - * Sent when the a device is connected to the {@link AndroidDebugBridge}. - * <p/> - * This is sent from a non UI thread. - * @param device the new device. - */ - public void deviceDisconnected(IDevice device); - - /** - * Sent when a device data changed, or when clients are started/terminated on the device. - * <p/> - * This is sent from a non UI thread. - * @param device the device that was updated. - * @param changeMask the mask describing what changed. It can contain any of the following - * values: {@link IDevice#CHANGE_BUILD_INFO}, {@link IDevice#CHANGE_STATE}, - * {@link IDevice#CHANGE_CLIENT_LIST} - */ - public void deviceChanged(IDevice device, int changeMask); - } - - /** - * Classes which implement this interface provide methods that deal - * with {@link Client} changes. - */ - public interface IClientChangeListener { - /** - * Sent when an existing client information changed. - * <p/> - * This is sent from a non UI thread. - * @param client the updated client. - * @param changeMask the bit mask describing the changed properties. It can contain - * any of the following values: {@link Client#CHANGE_INFO}, - * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, - * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, - * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} - */ - public void clientChanged(Client client, int changeMask); - } - - /** - * Initializes the <code>ddm</code> library. - * <p/>This must be called once <b>before</b> any call to - * {@link #createBridge(String, boolean)}. - * <p>The library can be initialized in 2 ways: - * <ul> - * <li>Mode 1: <var>clientSupport</var> == <code>true</code>.<br>The library monitors the - * devices and the applications running on them. It will connect to each application, as a - * debugger of sort, to be able to interact with them through JDWP packets.</li> - * <li>Mode 2: <var>clientSupport</var> == <code>false</code>.<br>The library only monitors - * devices. The applications are left untouched, letting other tools built on - * <code>ddmlib</code> to connect a debugger to them.</li> - * </ul> - * <p/><b>Only one tool can run in mode 1 at the same time.</b> - * <p/>Note that mode 1 does not prevent debugging of applications running on devices. Mode 1 - * lets debuggers connect to <code>ddmlib</code> which acts as a proxy between the debuggers and - * the applications to debug. See {@link Client#getDebuggerListenPort()}. - * <p/>The preferences of <code>ddmlib</code> should also be initialized with whatever default - * values were changed from the default values. - * <p/>When the application quits, {@link #terminate()} should be called. - * @param clientSupport Indicates whether the library should enable the monitoring and - * interaction with applications running on the devices. - * @see AndroidDebugBridge#createBridge(String, boolean) - * @see DdmPreferences - */ - public static synchronized void init(boolean clientSupport) { - if (sInitialized) { - throw new IllegalStateException("AndroidDebugBridge.init() has already been called."); - } - sInitialized = true; - sClientSupport = clientSupport; - - // Determine port and instantiate socket address. - initAdbSocketAddr(); - - MonitorThread monitorThread = MonitorThread.createInstance(); - monitorThread.start(); - - HandleHello.register(monitorThread); - HandleAppName.register(monitorThread); - HandleTest.register(monitorThread); - HandleThread.register(monitorThread); - HandleHeap.register(monitorThread); - HandleWait.register(monitorThread); - HandleProfiling.register(monitorThread); - HandleNativeHeap.register(monitorThread); - } - - /** - * Terminates the ddm library. This must be called upon application termination. - */ - public static synchronized void terminate() { - // kill the monitoring services - if (sThis != null && sThis.mDeviceMonitor != null) { - sThis.mDeviceMonitor.stop(); - sThis.mDeviceMonitor = null; - } - - MonitorThread monitorThread = MonitorThread.getInstance(); - if (monitorThread != null) { - monitorThread.quit(); - } - - sInitialized = false; - } - - /** - * Returns whether the ddmlib is setup to support monitoring and interacting with - * {@link Client}s running on the {@link IDevice}s. - */ - static boolean getClientSupport() { - return sClientSupport; - } - - /** - * Returns the socket address of the ADB server on the host. - */ - public static InetSocketAddress getSocketAddress() { - return sSocketAddr; - } - - /** - * Creates a {@link AndroidDebugBridge} that is not linked to any particular executable. - * <p/>This bridge will expect adb to be running. It will not be able to start/stop/restart - * adb. - * <p/>If a bridge has already been started, it is directly returned with no changes (similar - * to calling {@link #getBridge()}). - * @return a connected bridge. - */ - public static AndroidDebugBridge createBridge() { - synchronized (sLock) { - if (sThis != null) { - return sThis; - } - - try { - sThis = new AndroidDebugBridge(); - sThis.start(); - } catch (InvalidParameterException e) { - sThis = null; - } - - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( - new IDebugBridgeChangeListener[sBridgeListeners.size()]); - - // notify the listeners of the change - for (IDebugBridgeChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.bridgeChanged(sThis); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - - return sThis; - } - } - - - /** - * Creates a new debug bridge from the location of the command line tool. - * <p/> - * Any existing server will be disconnected, unless the location is the same and - * <code>forceNewBridge</code> is set to false. - * @param osLocation the location of the command line tool 'adb' - * @param forceNewBridge force creation of a new bridge even if one with the same location - * already exists. - * @return a connected bridge. - */ - public static AndroidDebugBridge createBridge(String osLocation, boolean forceNewBridge) { - synchronized (sLock) { - if (sThis != null) { - if (sThis.mAdbOsLocation != null && sThis.mAdbOsLocation.equals(osLocation) && - forceNewBridge == false) { - return sThis; - } else { - // stop the current server - sThis.stop(); - } - } - - try { - sThis = new AndroidDebugBridge(osLocation); - sThis.start(); - } catch (InvalidParameterException e) { - sThis = null; - } - - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( - new IDebugBridgeChangeListener[sBridgeListeners.size()]); - - // notify the listeners of the change - for (IDebugBridgeChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.bridgeChanged(sThis); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - - return sThis; - } - } - - /** - * Returns the current debug bridge. Can be <code>null</code> if none were created. - */ - public static AndroidDebugBridge getBridge() { - return sThis; - } - - /** - * Disconnects the current debug bridge, and destroy the object. - * <p/>This also stops the current adb host server. - * <p/> - * A new object will have to be created with {@link #createBridge(String, boolean)}. - */ - public static void disconnectBridge() { - synchronized (sLock) { - if (sThis != null) { - sThis.stop(); - sThis = null; - - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( - new IDebugBridgeChangeListener[sBridgeListeners.size()]); - - // notify the listeners. - for (IDebugBridgeChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.bridgeChanged(sThis); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - } - } - - /** - * Adds the listener to the collection of listeners who will be notified when a new - * {@link AndroidDebugBridge} is connected, by sending it one of the messages defined - * in the {@link IDebugBridgeChangeListener} interface. - * @param listener The listener which should be notified. - */ - public static void addDebugBridgeChangeListener(IDebugBridgeChangeListener listener) { - synchronized (sLock) { - if (sBridgeListeners.contains(listener) == false) { - sBridgeListeners.add(listener); - if (sThis != null) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.bridgeChanged(sThis); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - } - } - - /** - * Removes the listener from the collection of listeners who will be notified when a new - * {@link AndroidDebugBridge} is started. - * @param listener The listener which should no longer be notified. - */ - public static void removeDebugBridgeChangeListener(IDebugBridgeChangeListener listener) { - synchronized (sLock) { - sBridgeListeners.remove(listener); - } - } - - /** - * Adds the listener to the collection of listeners who will be notified when a {@link IDevice} - * is connected, disconnected, or when its properties or its {@link Client} list changed, - * by sending it one of the messages defined in the {@link IDeviceChangeListener} interface. - * @param listener The listener which should be notified. - */ - public static void addDeviceChangeListener(IDeviceChangeListener listener) { - synchronized (sLock) { - if (sDeviceListeners.contains(listener) == false) { - sDeviceListeners.add(listener); - } - } - } - - /** - * Removes the listener from the collection of listeners who will be notified when a - * {@link IDevice} is connected, disconnected, or when its properties or its {@link Client} - * list changed. - * @param listener The listener which should no longer be notified. - */ - public static void removeDeviceChangeListener(IDeviceChangeListener listener) { - synchronized (sLock) { - sDeviceListeners.remove(listener); - } - } - - /** - * Adds the listener to the collection of listeners who will be notified when a {@link Client} - * property changed, by sending it one of the messages defined in the - * {@link IClientChangeListener} interface. - * @param listener The listener which should be notified. - */ - public static void addClientChangeListener(IClientChangeListener listener) { - synchronized (sLock) { - if (sClientListeners.contains(listener) == false) { - sClientListeners.add(listener); - } - } - } - - /** - * Removes the listener from the collection of listeners who will be notified when a - * {@link Client} property changed. - * @param listener The listener which should no longer be notified. - */ - public static void removeClientChangeListener(IClientChangeListener listener) { - synchronized (sLock) { - sClientListeners.remove(listener); - } - } - - - /** - * Returns the devices. - * @see #hasInitialDeviceList() - */ - public IDevice[] getDevices() { - synchronized (sLock) { - if (mDeviceMonitor != null) { - return mDeviceMonitor.getDevices(); - } - } - - return new IDevice[0]; - } - - /** - * Returns whether the bridge has acquired the initial list from adb after being created. - * <p/>Calling {@link #getDevices()} right after {@link #createBridge(String, boolean)} will - * generally result in an empty list. This is due to the internal asynchronous communication - * mechanism with <code>adb</code> that does not guarantee that the {@link IDevice} list has been - * built before the call to {@link #getDevices()}. - * <p/>The recommended way to get the list of {@link IDevice} objects is to create a - * {@link IDeviceChangeListener} object. - */ - public boolean hasInitialDeviceList() { - if (mDeviceMonitor != null) { - return mDeviceMonitor.hasInitialDeviceList(); - } - - return false; - } - - /** - * Sets the client to accept debugger connection on the custom "Selected debug port". - * @param selectedClient the client. Can be null. - */ - public void setSelectedClient(Client selectedClient) { - MonitorThread monitorThread = MonitorThread.getInstance(); - if (monitorThread != null) { - monitorThread.setSelectedClient(selectedClient); - } - } - - /** - * Returns whether the {@link AndroidDebugBridge} object is still connected to the adb daemon. - */ - public boolean isConnected() { - MonitorThread monitorThread = MonitorThread.getInstance(); - if (mDeviceMonitor != null && monitorThread != null) { - return mDeviceMonitor.isMonitoring() && monitorThread.getState() != State.TERMINATED; - } - return false; - } - - /** - * Returns the number of times the {@link AndroidDebugBridge} object attempted to connect - * to the adb daemon. - */ - public int getConnectionAttemptCount() { - if (mDeviceMonitor != null) { - return mDeviceMonitor.getConnectionAttemptCount(); - } - return -1; - } - - /** - * Returns the number of times the {@link AndroidDebugBridge} object attempted to restart - * the adb daemon. - */ - public int getRestartAttemptCount() { - if (mDeviceMonitor != null) { - return mDeviceMonitor.getRestartAttemptCount(); - } - return -1; - } - - /** - * Creates a new bridge. - * @param osLocation the location of the command line tool - * @throws InvalidParameterException - */ - private AndroidDebugBridge(String osLocation) throws InvalidParameterException { - if (osLocation == null || osLocation.length() == 0) { - throw new InvalidParameterException(); - } - mAdbOsLocation = osLocation; - - checkAdbVersion(); - } - - /** - * Creates a new bridge not linked to any particular adb executable. - */ - private AndroidDebugBridge() { - } - - /** - * Queries adb for its version number and checks it against {@link #MIN_VERSION_NUMBER} and - * {@link #MAX_VERSION_NUMBER} - */ - private void checkAdbVersion() { - // default is bad check - mVersionCheck = false; - - if (mAdbOsLocation == null) { - return; - } - - String[] command = new String[2]; - command[0] = mAdbOsLocation; - command[1] = "version"; //$NON-NLS-1$ - Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); - Process process = null; - try { - process = Runtime.getRuntime().exec(command); - } catch (IOException e) { - boolean exists = new File(mAdbOsLocation).exists(); - String msg; - if (exists) { - msg = String.format( - "Unexpected exception '%1$s' while attempting to get adb version from '%2$s'", - e.getMessage(), mAdbOsLocation); - } else { - msg = "Unable to locate adb.\n" + - "Please use SDK Manager and check if Android SDK platform-tools are installed."; - } - Log.logAndDisplay(LogLevel.ERROR, ADB, msg); - return; - } - - ArrayList<String> errorOutput = new ArrayList<String>(); - ArrayList<String> stdOutput = new ArrayList<String>(); - int status; - try { - status = grabProcessOutput(process, errorOutput, stdOutput, - true /* waitForReaders */); - } catch (InterruptedException e) { - return; - } - - if (status != 0) { - StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$ - for (String error : errorOutput) { - builder.append('\n'); - builder.append(error); - } - Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString()); - } - - // check both stdout and stderr - boolean versionFound = false; - for (String line : stdOutput) { - versionFound = scanVersionLine(line); - if (versionFound) { - break; - } - } - if (!versionFound) { - for (String line : errorOutput) { - versionFound = scanVersionLine(line); - if (versionFound) { - break; - } - } - } - - if (!versionFound) { - // if we get here, we failed to parse the output. - StringBuilder builder = new StringBuilder( - "Failed to parse the output of 'adb version':\n"); //$NON-NLS-1$ - builder.append("Standard Output was:\n"); //$NON-NLS-1$ - for (String line : stdOutput) { - builder.append(line); - builder.append('\n'); - } - builder.append("\nError Output was:\n"); //$NON-NLS-1$ - for (String line : errorOutput) { - builder.append(line); - builder.append('\n'); - } - Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString()); - } - } - - /** - * Scans a line resulting from 'adb version' for a potential version number. - * <p/> - * If a version number is found, it checks the version number against what is expected - * by this version of ddms. - * <p/> - * Returns true when a version number has been found so that we can stop scanning, - * whether the version number is in the acceptable range or not. - * - * @param line The line to scan. - * @return True if a version number was found (whether it is acceptable or not). - */ - @SuppressWarnings("all") // With Eclipse 3.6, replace by @SuppressWarnings("unused") - private boolean scanVersionLine(String line) { - if (line != null) { - Matcher matcher = sAdbVersion.matcher(line); - if (matcher.matches()) { - int majorVersion = Integer.parseInt(matcher.group(1)); - int minorVersion = Integer.parseInt(matcher.group(2)); - int microVersion = Integer.parseInt(matcher.group(3)); - - // check only the micro version for now. - if (microVersion < ADB_VERSION_MICRO_MIN) { - String message = String.format( - "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ - + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ - majorVersion, minorVersion, ADB_VERSION_MICRO_MIN, - microVersion); - Log.logAndDisplay(LogLevel.ERROR, ADB, message); - } else if (ADB_VERSION_MICRO_MAX != -1 && - microVersion > ADB_VERSION_MICRO_MAX) { - String message = String.format( - "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ - + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ - majorVersion, minorVersion, ADB_VERSION_MICRO_MAX, - microVersion); - Log.logAndDisplay(LogLevel.ERROR, ADB, message); - } else { - mVersionCheck = true; - } - - return true; - } - } - return false; - } - - /** - * Starts the debug bridge. - * @return true if success. - */ - boolean start() { - if (mAdbOsLocation != null && (mVersionCheck == false || startAdb() == false)) { - return false; - } - - mStarted = true; - - // now that the bridge is connected, we start the underlying services. - mDeviceMonitor = new DeviceMonitor(this); - mDeviceMonitor.start(); - - return true; - } - - /** - * Kills the debug bridge, and the adb host server. - * @return true if success - */ - boolean stop() { - // if we haven't started we return false; - if (mStarted == false) { - return false; - } - - // kill the monitoring services - mDeviceMonitor.stop(); - mDeviceMonitor = null; - - if (stopAdb() == false) { - return false; - } - - mStarted = false; - return true; - } - - /** - * Restarts adb, but not the services around it. - * @return true if success. - */ - public boolean restart() { - if (mAdbOsLocation == null) { - Log.e(ADB, - "Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ - return false; - } - - if (mVersionCheck == false) { - Log.logAndDisplay(LogLevel.ERROR, ADB, - "Attempting to restart adb, but version check failed!"); //$NON-NLS-1$ - return false; - } - synchronized (this) { - stopAdb(); - - boolean restart = startAdb(); - - if (restart && mDeviceMonitor == null) { - mDeviceMonitor = new DeviceMonitor(this); - mDeviceMonitor.start(); - } - - return restart; - } - } - - /** - * Notify the listener of a new {@link IDevice}. - * <p/> - * The notification of the listeners is done in a synchronized block. It is important to - * expect the listeners to potentially access various methods of {@link IDevice} as well as - * {@link #getDevices()} which use internal locks. - * <p/> - * For this reason, any call to this method from a method of {@link DeviceMonitor}, - * {@link IDevice} which is also inside a synchronized block, should first synchronize on - * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. - * @param device the new <code>IDevice</code>. - * @see #getLock() - */ - void deviceConnected(IDevice device) { - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDeviceChangeListener[] listenersCopy = null; - synchronized (sLock) { - listenersCopy = sDeviceListeners.toArray( - new IDeviceChangeListener[sDeviceListeners.size()]); - } - - // Notify the listeners - for (IDeviceChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.deviceConnected(device); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - - /** - * Notify the listener of a disconnected {@link IDevice}. - * <p/> - * The notification of the listeners is done in a synchronized block. It is important to - * expect the listeners to potentially access various methods of {@link IDevice} as well as - * {@link #getDevices()} which use internal locks. - * <p/> - * For this reason, any call to this method from a method of {@link DeviceMonitor}, - * {@link IDevice} which is also inside a synchronized block, should first synchronize on - * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. - * @param device the disconnected <code>IDevice</code>. - * @see #getLock() - */ - void deviceDisconnected(IDevice device) { - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDeviceChangeListener[] listenersCopy = null; - synchronized (sLock) { - listenersCopy = sDeviceListeners.toArray( - new IDeviceChangeListener[sDeviceListeners.size()]); - } - - // Notify the listeners - for (IDeviceChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.deviceDisconnected(device); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - - /** - * Notify the listener of a modified {@link IDevice}. - * <p/> - * The notification of the listeners is done in a synchronized block. It is important to - * expect the listeners to potentially access various methods of {@link IDevice} as well as - * {@link #getDevices()} which use internal locks. - * <p/> - * For this reason, any call to this method from a method of {@link DeviceMonitor}, - * {@link IDevice} which is also inside a synchronized block, should first synchronize on - * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. - * @param device the modified <code>IDevice</code>. - * @see #getLock() - */ - void deviceChanged(IDevice device, int changeMask) { - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IDeviceChangeListener[] listenersCopy = null; - synchronized (sLock) { - listenersCopy = sDeviceListeners.toArray( - new IDeviceChangeListener[sDeviceListeners.size()]); - } - - // Notify the listeners - for (IDeviceChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.deviceChanged(device, changeMask); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - - /** - * Notify the listener of a modified {@link Client}. - * <p/> - * The notification of the listeners is done in a synchronized block. It is important to - * expect the listeners to potentially access various methods of {@link IDevice} as well as - * {@link #getDevices()} which use internal locks. - * <p/> - * For this reason, any call to this method from a method of {@link DeviceMonitor}, - * {@link IDevice} which is also inside a synchronized block, should first synchronize on - * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. - * @param device the modified <code>Client</code>. - * @param changeMask the mask indicating what changed in the <code>Client</code> - * @see #getLock() - */ - void clientChanged(Client client, int changeMask) { - // because the listeners could remove themselves from the list while processing - // their event callback, we make a copy of the list and iterate on it instead of - // the main list. - // This mostly happens when the application quits. - IClientChangeListener[] listenersCopy = null; - synchronized (sLock) { - listenersCopy = sClientListeners.toArray( - new IClientChangeListener[sClientListeners.size()]); - - } - - // Notify the listeners - for (IClientChangeListener listener : listenersCopy) { - // we attempt to catch any exception so that a bad listener doesn't kill our - // thread - try { - listener.clientChanged(client, changeMask); - } catch (Exception e) { - Log.e(DDMS, e); - } - } - } - - /** - * Returns the {@link DeviceMonitor} object. - */ - DeviceMonitor getDeviceMonitor() { - return mDeviceMonitor; - } - - /** - * Starts the adb host side server. - * @return true if success - */ - synchronized boolean startAdb() { - if (mAdbOsLocation == null) { - Log.e(ADB, - "Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ - return false; - } - - Process proc; - int status = -1; - - try { - String[] command = new String[2]; - command[0] = mAdbOsLocation; - command[1] = "start-server"; //$NON-NLS-1$ - Log.d(DDMS, - String.format("Launching '%1$s %2$s' to ensure ADB is running.", //$NON-NLS-1$ - mAdbOsLocation, command[1])); - ProcessBuilder processBuilder = new ProcessBuilder(command); - if (DdmPreferences.getUseAdbHost()) { - String adbHostValue = DdmPreferences.getAdbHostValue(); - if (adbHostValue != null && adbHostValue.length() > 0) { - //TODO : check that the String is a valid IP address - Map<String, String> env = processBuilder.environment(); - env.put("ADBHOST", adbHostValue); - } - } - proc = processBuilder.start(); - - ArrayList<String> errorOutput = new ArrayList<String>(); - ArrayList<String> stdOutput = new ArrayList<String>(); - status = grabProcessOutput(proc, errorOutput, stdOutput, - false /* waitForReaders */); - - } catch (IOException ioe) { - Log.d(DDMS, "Unable to run 'adb': " + ioe.getMessage()); //$NON-NLS-1$ - // we'll return false; - } catch (InterruptedException ie) { - Log.d(DDMS, "Unable to run 'adb': " + ie.getMessage()); //$NON-NLS-1$ - // we'll return false; - } - - if (status != 0) { - Log.w(DDMS, - "'adb start-server' failed -- run manually if necessary"); //$NON-NLS-1$ - return false; - } - - Log.d(DDMS, "'adb start-server' succeeded"); //$NON-NLS-1$ - - return true; - } - - /** - * Stops the adb host side server. - * @return true if success - */ - private synchronized boolean stopAdb() { - if (mAdbOsLocation == null) { - Log.e(ADB, - "Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ - return false; - } - - Process proc; - int status = -1; - - try { - String[] command = new String[2]; - command[0] = mAdbOsLocation; - command[1] = "kill-server"; //$NON-NLS-1$ - proc = Runtime.getRuntime().exec(command); - status = proc.waitFor(); - } - catch (IOException ioe) { - // we'll return false; - } - catch (InterruptedException ie) { - // we'll return false; - } - - if (status != 0) { - Log.w(DDMS, - "'adb kill-server' failed -- run manually if necessary"); //$NON-NLS-1$ - return false; - } - - Log.d(DDMS, "'adb kill-server' succeeded"); //$NON-NLS-1$ - return true; - } - - /** - * Get the stderr/stdout outputs of a process and return when the process is done. - * Both <b>must</b> be read or the process will block on windows. - * @param process The process to get the ouput from - * @param errorOutput The array to store the stderr output. cannot be null. - * @param stdOutput The array to store the stdout output. cannot be null. - * @param displayStdOut If true this will display stdout as well - * @param waitforReaders if true, this will wait for the reader threads. - * @return the process return code. - * @throws InterruptedException - */ - private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput, - final ArrayList<String> stdOutput, boolean waitforReaders) - throws InterruptedException { - assert errorOutput != null; - assert stdOutput != null; - // read the lines as they come. if null is returned, it's - // because the process finished - Thread t1 = new Thread("") { //$NON-NLS-1$ - @Override - public void run() { - // create a buffer to read the stderr output - InputStreamReader is = new InputStreamReader(process.getErrorStream()); - BufferedReader errReader = new BufferedReader(is); - - try { - while (true) { - String line = errReader.readLine(); - if (line != null) { - Log.e(ADB, line); - errorOutput.add(line); - } else { - break; - } - } - } catch (IOException e) { - // do nothing. - } - } - }; - - Thread t2 = new Thread("") { //$NON-NLS-1$ - @Override - public void run() { - InputStreamReader is = new InputStreamReader(process.getInputStream()); - BufferedReader outReader = new BufferedReader(is); - - try { - while (true) { - String line = outReader.readLine(); - if (line != null) { - Log.d(ADB, line); - stdOutput.add(line); - } else { - break; - } - } - } catch (IOException e) { - // do nothing. - } - } - }; - - t1.start(); - t2.start(); - - // it looks like on windows process#waitFor() can return - // before the thread have filled the arrays, so we wait for both threads and the - // process itself. - if (waitforReaders) { - try { - t1.join(); - } catch (InterruptedException e) { - } - try { - t2.join(); - } catch (InterruptedException e) { - } - } - - // get the return code from the process - return process.waitFor(); - } - - /** - * Returns the singleton lock used by this class to protect any access to the listener. - * <p/> - * This includes adding/removing listeners, but also notifying listeners of new bridges, - * devices, and clients. - */ - static Object getLock() { - return sLock; - } - - /** - * Instantiates sSocketAddr with the address of the host's adb process. - */ - private static void initAdbSocketAddr() { - try { - int adb_port = determineAndValidateAdbPort(); - sHostAddr = InetAddress.getByName(ADB_HOST); - sSocketAddr = new InetSocketAddress(sHostAddr, adb_port); - } catch (UnknownHostException e) { - // localhost should always be known. - } - } - - /** - * Determines port where ADB is expected by looking at an env variable. - * <p/> - * The value for the environment variable ANDROID_ADB_SERVER_PORT is validated, - * IllegalArgumentException is thrown on illegal values. - * <p/> - * @return The port number where the host's adb should be expected or started. - * @throws IllegalArgumentException if ANDROID_ADB_SERVER_PORT has a non-numeric value. - */ - private static int determineAndValidateAdbPort() { - String adb_env_var; - int result = ADB_PORT; - try { - adb_env_var = System.getenv(SERVER_PORT_ENV_VAR); - - if (adb_env_var != null) { - adb_env_var = adb_env_var.trim(); - } - - if (adb_env_var != null && adb_env_var.length() > 0) { - // C tools (adb, emulator) accept hex and octal port numbers, so need to accept - // them too. - result = Integer.decode(adb_env_var); - - if (result <= 0) { - String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$ - + ": must be >=0, got " //$NON-NLS-1$ - + System.getenv(SERVER_PORT_ENV_VAR); - throw new IllegalArgumentException(errMsg); - } - } - } catch (NumberFormatException nfEx) { - String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$ - + ": illegal value '" //$NON-NLS-1$ - + System.getenv(SERVER_PORT_ENV_VAR) + "'"; //$NON-NLS-1$ - throw new IllegalArgumentException(errMsg); - } catch (SecurityException secEx) { - // A security manager has been installed that doesn't allow access to env vars. - // So an environment variable might have been set, but we can't tell. - // Let's log a warning and continue with ADB's default port. - // The issue is that adb would be started (by the forked process having access - // to the env vars) on the desired port, but within this process, we can't figure out - // what that port is. However, a security manager not granting access to env vars - // but allowing to fork is a rare and interesting configuration, so the right - // thing seems to be to continue using the default port, as forking is likely to - // fail later on in the scenario of the security manager. - Log.w(DDMS, - "No access to env variables allowed by current security manager. " //$NON-NLS-1$ - + "If you've set ANDROID_ADB_SERVER_PORT: it's being ignored."); //$NON-NLS-1$ - } - return result; - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/BadPacketException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/BadPacketException.java deleted file mode 100644 index 129b312..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/BadPacketException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* //device/tools/ddms/libs/ddmlib/src/com/android/ddmlib/BadPacketException.java -** -** Copyright 2007, 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; - -/** - * Thrown if the contents of a packet are bad. - */ -@SuppressWarnings("serial") -class BadPacketException extends RuntimeException { - public BadPacketException() - { - super(); - } - - public BadPacketException(String msg) - { - super(msg); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CanceledException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CanceledException.java deleted file mode 100644 index 84eda03..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CanceledException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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; - -/** - * Abstract exception for exception that can be thrown when a user input cancels the action. - * <p/> - * {@link #wasCanceled()} returns whether the action was canceled because of user input. - * - */ -public abstract class CanceledException extends Exception { - private static final long serialVersionUID = 1L; - - CanceledException(String message) { - super(message); - } - - CanceledException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Returns true if the action was canceled by user input. - */ - public abstract boolean wasCanceled(); -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ChunkHandler.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ChunkHandler.java deleted file mode 100644 index 74fa318..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ChunkHandler.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.DebugPortManager.IDebugPortProvider; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Subclass this with a class that handles one or more chunk types. - */ -abstract class ChunkHandler { - - public static final int CHUNK_HEADER_LEN = 8; // 4-byte type, 4-byte len - public static final ByteOrder CHUNK_ORDER = ByteOrder.BIG_ENDIAN; - - public static final int CHUNK_FAIL = type("FAIL"); - - ChunkHandler() {} - - /** - * Client is ready. The monitor thread calls this method on all - * handlers when the client is determined to be DDM-aware (usually - * after receiving a HELO response.) - * - * The handler can use this opportunity to initialize client-side - * activity. Because there's a fair chance we'll want to send a - * message to the client, this method can throw an IOException. - */ - abstract void clientReady(Client client) throws IOException; - - /** - * Client has gone away. Can be used to clean up any resources - * associated with this client connection. - */ - abstract void clientDisconnected(Client client); - - /** - * Handle an incoming chunk. The data, of chunk type "type", begins - * at the start of "data" and continues to data.limit(). - * - * If "isReply" is set, then "msgId" will be the ID of the request - * we sent to the client. Otherwise, it's the ID generated by the - * client for this event. Note that it's possible to receive chunks - * in reply packets for which we are not registered. - * - * The handler may not modify the contents of "data". - */ - abstract void handleChunk(Client client, int type, - ByteBuffer data, boolean isReply, int msgId); - - /** - * Handle chunks not recognized by handlers. The handleChunk() method - * in sub-classes should call this if the chunk type isn't recognized. - */ - protected void handleUnknownChunk(Client client, int type, - ByteBuffer data, boolean isReply, int msgId) { - if (type == CHUNK_FAIL) { - int errorCode, msgLen; - String msg; - - errorCode = data.getInt(); - msgLen = data.getInt(); - msg = getString(data, msgLen); - Log.w("ddms", "WARNING: failure code=" + errorCode + " msg=" + msg); - } else { - Log.w("ddms", "WARNING: received unknown chunk " + name(type) - + ": len=" + data.limit() + ", reply=" + isReply - + ", msgId=0x" + Integer.toHexString(msgId)); - } - Log.w("ddms", " client " + client + ", handler " + this); - } - - - /** - * Utility function to copy a String out of a ByteBuffer. - * - * This is here because multiple chunk handlers can make use of it, - * and there's nowhere better to put it. - */ - static String getString(ByteBuffer buf, int len) { - char[] data = new char[len]; - for (int i = 0; i < len; i++) - data[i] = buf.getChar(); - return new String(data); - } - - /** - * Utility function to copy a String into a ByteBuffer. - */ - static void putString(ByteBuffer buf, String str) { - int len = str.length(); - for (int i = 0; i < len; i++) - buf.putChar(str.charAt(i)); - } - - /** - * Convert a 4-character string to a 32-bit type. - */ - static int type(String typeName) { - int val = 0; - - if (typeName.length() != 4) { - Log.e("ddms", "Type name must be 4 letter long"); - throw new RuntimeException("Type name must be 4 letter long"); - } - - for (int i = 0; i < 4; i++) { - val <<= 8; - val |= (byte) typeName.charAt(i); - } - - return val; - } - - /** - * Convert an integer type to a 4-character string. - */ - static String name(int type) { - char[] ascii = new char[4]; - - ascii[0] = (char) ((type >> 24) & 0xff); - ascii[1] = (char) ((type >> 16) & 0xff); - ascii[2] = (char) ((type >> 8) & 0xff); - ascii[3] = (char) (type & 0xff); - - return new String(ascii); - } - - /** - * Allocate a ByteBuffer with enough space to hold the JDWP packet - * header and one chunk header in addition to the demands of the - * chunk being created. - * - * "maxChunkLen" indicates the size of the chunk contents only. - */ - static ByteBuffer allocBuffer(int maxChunkLen) { - ByteBuffer buf = - ByteBuffer.allocate(JdwpPacket.JDWP_HEADER_LEN + 8 +maxChunkLen); - buf.order(CHUNK_ORDER); - return buf; - } - - /** - * Return the slice of the JDWP packet buffer that holds just the - * chunk data. - */ - static ByteBuffer getChunkDataBuf(ByteBuffer jdwpBuf) { - ByteBuffer slice; - - assert jdwpBuf.position() == 0; - - jdwpBuf.position(JdwpPacket.JDWP_HEADER_LEN + CHUNK_HEADER_LEN); - slice = jdwpBuf.slice(); - slice.order(CHUNK_ORDER); - jdwpBuf.position(0); - - return slice; - } - - /** - * Write the chunk header at the start of the chunk. - * - * Pass in the byte buffer returned by JdwpPacket.getPayload(). - */ - static void finishChunkPacket(JdwpPacket packet, int type, int chunkLen) { - ByteBuffer buf = packet.getPayload(); - - buf.putInt(0x00, type); - buf.putInt(0x04, chunkLen); - - packet.finishPacket(CHUNK_HEADER_LEN + chunkLen); - } - - /** - * Check that the client is opened with the proper debugger port for the - * specified application name, and if not, reopen it. - * @param client - * @param uiThread - * @param appName - * @return - */ - protected static Client checkDebuggerPortForAppName(Client client, String appName) { - IDebugPortProvider provider = DebugPortManager.getProvider(); - if (provider != null) { - Device device = client.getDeviceImpl(); - int newPort = provider.getPort(device, appName); - - if (newPort != IDebugPortProvider.NO_STATIC_PORT && - newPort != client.getDebuggerListenPort()) { - - AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); - if (bridge != null) { - DeviceMonitor deviceMonitor = bridge.getDeviceMonitor(); - if (deviceMonitor != null) { - deviceMonitor.addClientToDropAndReopen(client, newPort); - client = null; - } - } - } - } - - return client; - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Client.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Client.java deleted file mode 100644 index f927dd7..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Client.java +++ /dev/null @@ -1,869 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.ClientData.MethodProfilingStatus; -import com.android.ddmlib.DebugPortManager.IDebugPortProvider; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; - -import java.io.IOException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.util.HashMap; - -/** - * This represents a single client, usually a DAlvik VM process. - * <p/>This class gives access to basic client information, as well as methods to perform actions - * on the client. - * <p/>More detailed information, usually updated in real time, can be access through the - * {@link ClientData} class. Each <code>Client</code> object has its own <code>ClientData</code> - * accessed through {@link #getClientData()}. - */ -public class Client { - - private static final int SERVER_PROTOCOL_VERSION = 1; - - /** Client change bit mask: application name change */ - public static final int CHANGE_NAME = 0x0001; - /** Client change bit mask: debugger status change */ - public static final int CHANGE_DEBUGGER_STATUS = 0x0002; - /** Client change bit mask: debugger port change */ - public static final int CHANGE_PORT = 0x0004; - /** Client change bit mask: thread update flag change */ - public static final int CHANGE_THREAD_MODE = 0x0008; - /** Client change bit mask: thread data updated */ - public static final int CHANGE_THREAD_DATA = 0x0010; - /** Client change bit mask: heap update flag change */ - public static final int CHANGE_HEAP_MODE = 0x0020; - /** Client change bit mask: head data updated */ - public static final int CHANGE_HEAP_DATA = 0x0040; - /** Client change bit mask: native heap data updated */ - public static final int CHANGE_NATIVE_HEAP_DATA = 0x0080; - /** Client change bit mask: thread stack trace updated */ - public static final int CHANGE_THREAD_STACKTRACE = 0x0100; - /** Client change bit mask: allocation information updated */ - public static final int CHANGE_HEAP_ALLOCATIONS = 0x0200; - /** Client change bit mask: allocation information updated */ - public static final int CHANGE_HEAP_ALLOCATION_STATUS = 0x0400; - /** Client change bit mask: allocation information updated */ - public static final int CHANGE_METHOD_PROFILING_STATUS = 0x0800; - - /** Client change bit mask: combination of {@link Client#CHANGE_NAME}, - * {@link Client#CHANGE_DEBUGGER_STATUS}, and {@link Client#CHANGE_PORT}. - */ - public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_STATUS | CHANGE_PORT; - - private SocketChannel mChan; - - // debugger we're associated with, if any - private Debugger mDebugger; - private int mDebuggerListenPort; - - // list of IDs for requests we have sent to the client - private HashMap<Integer,ChunkHandler> mOutstandingReqs; - - // chunk handlers stash state data in here - private ClientData mClientData; - - // User interface state. Changing the value causes a message to be - // sent to the client. - private boolean mThreadUpdateEnabled; - private boolean mHeapUpdateEnabled; - - /* - * Read/write buffers. We can get large quantities of data from the - * client, e.g. the response to a "give me the list of all known classes" - * request from the debugger. Requests from the debugger, and from us, - * are much smaller. - * - * Pass-through debugger traffic is sent without copying. "mWriteBuffer" - * is only used for data generated within Client. - */ - private static final int INITIAL_BUF_SIZE = 2*1024; - private static final int MAX_BUF_SIZE = 200*1024*1024; - private ByteBuffer mReadBuffer; - - private static final int WRITE_BUF_SIZE = 256; - private ByteBuffer mWriteBuffer; - - private Device mDevice; - - private int mConnState; - - private static final int ST_INIT = 1; - private static final int ST_NOT_JDWP = 2; - private static final int ST_AWAIT_SHAKE = 10; - private static final int ST_NEED_DDM_PKT = 11; - private static final int ST_NOT_DDM = 12; - private static final int ST_READY = 13; - private static final int ST_ERROR = 20; - private static final int ST_DISCONNECTED = 21; - - - /** - * Create an object for a new client connection. - * - * @param device the device this client belongs to - * @param chan the connected {@link SocketChannel}. - * @param pid the client pid. - */ - Client(Device device, SocketChannel chan, int pid) { - mDevice = device; - mChan = chan; - - mReadBuffer = ByteBuffer.allocate(INITIAL_BUF_SIZE); - mWriteBuffer = ByteBuffer.allocate(WRITE_BUF_SIZE); - - mOutstandingReqs = new HashMap<Integer,ChunkHandler>(); - - mConnState = ST_INIT; - - mClientData = new ClientData(pid); - - mThreadUpdateEnabled = DdmPreferences.getInitialThreadUpdate(); - mHeapUpdateEnabled = DdmPreferences.getInitialHeapUpdate(); - } - - /** - * Returns a string representation of the {@link Client} object. - */ - @Override - public String toString() { - return "[Client pid: " + mClientData.getPid() + "]"; - } - - /** - * Returns the {@link IDevice} on which this Client is running. - */ - public IDevice getDevice() { - return mDevice; - } - - /** Returns the {@link Device} on which this Client is running. - */ - Device getDeviceImpl() { - return mDevice; - } - - /** - * Returns the debugger port for this client. - */ - public int getDebuggerListenPort() { - return mDebuggerListenPort; - } - - /** - * Returns <code>true</code> if the client VM is DDM-aware. - * - * Calling here is only allowed after the connection has been - * established. - */ - public boolean isDdmAware() { - switch (mConnState) { - case ST_INIT: - case ST_NOT_JDWP: - case ST_AWAIT_SHAKE: - case ST_NEED_DDM_PKT: - case ST_NOT_DDM: - case ST_ERROR: - case ST_DISCONNECTED: - return false; - case ST_READY: - return true; - default: - assert false; - return false; - } - } - - /** - * Returns <code>true</code> if a debugger is currently attached to the client. - */ - public boolean isDebuggerAttached() { - return mDebugger.isDebuggerAttached(); - } - - /** - * Return the Debugger object associated with this client. - */ - Debugger getDebugger() { - return mDebugger; - } - - /** - * Returns the {@link ClientData} object containing this client information. - */ - public ClientData getClientData() { - return mClientData; - } - - /** - * Forces the client to execute its garbage collector. - */ - public void executeGarbageCollector() { - try { - HandleHeap.sendHPGC(this); - } catch (IOException ioe) { - Log.w("ddms", "Send of HPGC message failed"); - // ignore - } - } - - /** - * Makes the VM dump an HPROF file - */ - public void dumpHprof() { - boolean canStream = mClientData.hasFeature(ClientData.FEATURE_HPROF_STREAMING); - try { - if (canStream) { - HandleHeap.sendHPDS(this); - } else { - String file = "/sdcard/" + mClientData.getClientDescription().replaceAll( - "\\:.*", "") + ".hprof"; - HandleHeap.sendHPDU(this, file); - } - } catch (IOException e) { - Log.w("ddms", "Send of HPDU message failed"); - // ignore - } - } - - public void toggleMethodProfiling() { - boolean canStream = mClientData.hasFeature(ClientData.FEATURE_PROFILING_STREAMING); - try { - if (mClientData.getMethodProfilingStatus() == MethodProfilingStatus.ON) { - if (canStream) { - HandleProfiling.sendMPSE(this); - } else { - HandleProfiling.sendMPRE(this); - } - } else { - int bufferSize = DdmPreferences.getProfilerBufferSizeMb() * 1024 * 1024; - if (canStream) { - HandleProfiling.sendMPSS(this, bufferSize, 0 /*flags*/); - } else { - String file = "/sdcard/" + - mClientData.getClientDescription().replaceAll("\\:.*", "") + - DdmConstants.DOT_TRACE; - HandleProfiling.sendMPRS(this, file, bufferSize, 0 /*flags*/); - } - } - } catch (IOException e) { - Log.w("ddms", "Toggle method profiling failed"); - // ignore - } - } - - public boolean startOpenGlTracing() { - boolean canTraceOpenGl = mClientData.hasFeature(ClientData.FEATURE_OPENGL_TRACING); - if (!canTraceOpenGl) { - return false; - } - - try { - OpenGlTraceChunkHandler.sendStartGlTracing(this); - return true; - } catch (IOException e) { - Log.w("ddms", "Start OpenGL Tracing failed"); - return false; - } - } - - public boolean stopOpenGlTracing() { - boolean canTraceOpenGl = mClientData.hasFeature(ClientData.FEATURE_OPENGL_TRACING); - if (!canTraceOpenGl) { - return false; - } - - try { - OpenGlTraceChunkHandler.sendStopGlTracing(this); - return true; - } catch (IOException e) { - Log.w("ddms", "Stop OpenGL Tracing failed"); - return false; - } - } - - /** - * Sends a request to the VM to send the enable status of the method profiling. - * This is asynchronous. - * <p/>The allocation status can be accessed by {@link ClientData#getAllocationStatus()}. - * The notification that the new status is available will be received through - * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code> - * containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}. - */ - public void requestMethodProfilingStatus() { - try { - HandleHeap.sendREAQ(this); - } catch (IOException e) { - Log.e("ddmlib", e); - } - } - - - /** - * Enables or disables the thread update. - * <p/>If <code>true</code> the VM will be able to send thread information. Thread information - * must be requested with {@link #requestThreadUpdate()}. - * @param enabled the enable flag. - */ - public void setThreadUpdateEnabled(boolean enabled) { - mThreadUpdateEnabled = enabled; - if (enabled == false) { - mClientData.clearThreads(); - } - - try { - HandleThread.sendTHEN(this, enabled); - } catch (IOException ioe) { - // ignore it here; client will clean up shortly - ioe.printStackTrace(); - } - - update(CHANGE_THREAD_MODE); - } - - /** - * Returns whether the thread update is enabled. - */ - public boolean isThreadUpdateEnabled() { - return mThreadUpdateEnabled; - } - - /** - * Sends a thread update request. This is asynchronous. - * <p/>The thread info can be accessed by {@link ClientData#getThreads()}. The notification - * that the new data is available will be received through - * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code> - * containing the mask {@link #CHANGE_THREAD_DATA}. - */ - public void requestThreadUpdate() { - HandleThread.requestThreadUpdate(this); - } - - /** - * Sends a thread stack trace update request. This is asynchronous. - * <p/>The thread info can be accessed by {@link ClientData#getThreads()} and - * {@link ThreadInfo#getStackTrace()}. - * <p/>The notification that the new data is available - * will be received through {@link IClientChangeListener#clientChanged(Client, int)} - * with a <code>changeMask</code> containing the mask {@link #CHANGE_THREAD_STACKTRACE}. - */ - public void requestThreadStackTrace(int threadId) { - HandleThread.requestThreadStackCallRefresh(this, threadId); - } - - /** - * Enables or disables the heap update. - * <p/>If <code>true</code>, any GC will cause the client to send its heap information. - * <p/>The heap information can be accessed by {@link ClientData#getVmHeapData()}. - * <p/>The notification that the new data is available - * will be received through {@link IClientChangeListener#clientChanged(Client, int)} - * with a <code>changeMask</code> containing the value {@link #CHANGE_HEAP_DATA}. - * @param enabled the enable flag - */ - public void setHeapUpdateEnabled(boolean enabled) { - mHeapUpdateEnabled = enabled; - - try { - HandleHeap.sendHPIF(this, - enabled ? HandleHeap.HPIF_WHEN_EVERY_GC : HandleHeap.HPIF_WHEN_NEVER); - - HandleHeap.sendHPSG(this, - enabled ? HandleHeap.WHEN_GC : HandleHeap.WHEN_DISABLE, - HandleHeap.WHAT_MERGE); - } catch (IOException ioe) { - // ignore it here; client will clean up shortly - } - - update(CHANGE_HEAP_MODE); - } - - /** - * Returns whether the heap update is enabled. - * @see #setHeapUpdateEnabled(boolean) - */ - public boolean isHeapUpdateEnabled() { - return mHeapUpdateEnabled; - } - - /** - * Sends a native heap update request. this is asynchronous. - * <p/>The native heap info can be accessed by {@link ClientData#getNativeAllocationList()}. - * The notification that the new data is available will be received through - * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code> - * containing the mask {@link #CHANGE_NATIVE_HEAP_DATA}. - */ - public boolean requestNativeHeapInformation() { - try { - HandleNativeHeap.sendNHGT(this); - return true; - } catch (IOException e) { - Log.e("ddmlib", e); - } - - return false; - } - - /** - * Enables or disables the Allocation tracker for this client. - * <p/>If enabled, the VM will start tracking allocation informations. A call to - * {@link #requestAllocationDetails()} will make the VM sends the information about all the - * allocations that happened between the enabling and the request. - * @param enable - * @see #requestAllocationDetails() - */ - public void enableAllocationTracker(boolean enable) { - try { - HandleHeap.sendREAE(this, enable); - } catch (IOException e) { - Log.e("ddmlib", e); - } - } - - /** - * Sends a request to the VM to send the enable status of the allocation tracking. - * This is asynchronous. - * <p/>The allocation status can be accessed by {@link ClientData#getAllocationStatus()}. - * The notification that the new status is available will be received through - * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code> - * containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}. - */ - public void requestAllocationStatus() { - try { - HandleHeap.sendREAQ(this); - } catch (IOException e) { - Log.e("ddmlib", e); - } - } - - /** - * Sends a request to the VM to send the information about all the allocations that have - * happened since the call to {@link #enableAllocationTracker(boolean)} with <var>enable</var> - * set to <code>null</code>. This is asynchronous. - * <p/>The allocation information can be accessed by {@link ClientData#getAllocations()}. - * The notification that the new data is available will be received through - * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code> - * containing the mask {@link #CHANGE_HEAP_ALLOCATIONS}. - */ - public void requestAllocationDetails() { - try { - HandleHeap.sendREAL(this); - } catch (IOException e) { - Log.e("ddmlib", e); - } - } - - /** - * Sends a kill message to the VM. - */ - public void kill() { - try { - HandleExit.sendEXIT(this, 1); - } catch (IOException ioe) { - Log.w("ddms", "Send of EXIT message failed"); - // ignore - } - } - - /** - * Registers the client with a Selector. - */ - void register(Selector sel) throws IOException { - if (mChan != null) { - mChan.register(sel, SelectionKey.OP_READ, this); - } - } - - /** - * Sets the client to accept debugger connection on the "selected debugger port". - * - * @see AndroidDebugBridge#setSelectedClient(Client) - * @see DdmPreferences#setSelectedDebugPort(int) - */ - public void setAsSelectedClient() { - MonitorThread monitorThread = MonitorThread.getInstance(); - if (monitorThread != null) { - monitorThread.setSelectedClient(this); - } - } - - /** - * Returns whether this client is the current selected client, accepting debugger connection - * on the "selected debugger port". - * - * @see #setAsSelectedClient() - * @see AndroidDebugBridge#setSelectedClient(Client) - * @see DdmPreferences#setSelectedDebugPort(int) - */ - public boolean isSelectedClient() { - MonitorThread monitorThread = MonitorThread.getInstance(); - if (monitorThread != null) { - return monitorThread.getSelectedClient() == this; - } - - return false; - } - - /** - * Tell the client to open a server socket channel and listen for - * connections on the specified port. - */ - void listenForDebugger(int listenPort) throws IOException { - mDebuggerListenPort = listenPort; - mDebugger = new Debugger(this, listenPort); - } - - /** - * Initiate the JDWP handshake. - * - * On failure, closes the socket and returns false. - */ - boolean sendHandshake() { - assert mWriteBuffer.position() == 0; - - try { - // assume write buffer can hold 14 bytes - JdwpPacket.putHandshake(mWriteBuffer); - int expectedLen = mWriteBuffer.position(); - mWriteBuffer.flip(); - if (mChan.write(mWriteBuffer) != expectedLen) - throw new IOException("partial handshake write"); - } - catch (IOException ioe) { - Log.e("ddms-client", "IO error during handshake: " + ioe.getMessage()); - mConnState = ST_ERROR; - close(true /* notify */); - return false; - } - finally { - mWriteBuffer.clear(); - } - - mConnState = ST_AWAIT_SHAKE; - - return true; - } - - - /** - * Send a non-DDM packet to the client. - * - * Equivalent to sendAndConsume(packet, null). - */ - void sendAndConsume(JdwpPacket packet) throws IOException { - sendAndConsume(packet, null); - } - - /** - * Send a DDM packet to the client. - * - * Ideally, we can do this with a single channel write. If that doesn't - * happen, we have to prevent anybody else from writing to the channel - * until this packet completes, so we synchronize on the channel. - * - * Another goal is to avoid unnecessary buffer copies, so we write - * directly out of the JdwpPacket's ByteBuffer. - */ - void sendAndConsume(JdwpPacket packet, ChunkHandler replyHandler) - throws IOException { - - if (mChan == null) { - // can happen for e.g. THST packets - Log.v("ddms", "Not sending packet -- client is closed"); - return; - } - - if (replyHandler != null) { - /* - * Add the ID to the list of outstanding requests. We have to do - * this before sending the packet, in case the response comes back - * before our thread returns from the packet-send function. - */ - addRequestId(packet.getId(), replyHandler); - } - - synchronized (mChan) { - try { - packet.writeAndConsume(mChan); - } - catch (IOException ioe) { - removeRequestId(packet.getId()); - throw ioe; - } - } - } - - /** - * Forward the packet to the debugger (if still connected to one). - * - * Consumes the packet. - */ - void forwardPacketToDebugger(JdwpPacket packet) - throws IOException { - - Debugger dbg = mDebugger; - - if (dbg == null) { - Log.d("ddms", "Discarding packet"); - packet.consume(); - } else { - dbg.sendAndConsume(packet); - } - } - - /** - * Read data from our channel. - * - * This is called when data is known to be available, and we don't yet - * have a full packet in the buffer. If the buffer is at capacity, - * expand it. - */ - void read() - throws IOException, BufferOverflowException { - - int count; - - if (mReadBuffer.position() == mReadBuffer.capacity()) { - if (mReadBuffer.capacity() * 2 > MAX_BUF_SIZE) { - Log.e("ddms", "Exceeded MAX_BUF_SIZE!"); - throw new BufferOverflowException(); - } - Log.d("ddms", "Expanding read buffer to " - + mReadBuffer.capacity() * 2); - - ByteBuffer newBuffer = ByteBuffer.allocate(mReadBuffer.capacity() * 2); - - // copy entire buffer to new buffer - mReadBuffer.position(0); - newBuffer.put(mReadBuffer); // leaves "position" at end of copied - - mReadBuffer = newBuffer; - } - - count = mChan.read(mReadBuffer); - if (count < 0) - throw new IOException("read failed"); - - if (Log.Config.LOGV) Log.v("ddms", "Read " + count + " bytes from " + this); - //Log.hexDump("ddms", Log.DEBUG, mReadBuffer.array(), - // mReadBuffer.arrayOffset(), mReadBuffer.position()); - } - - /** - * Return information for the first full JDWP packet in the buffer. - * - * If we don't yet have a full packet, return null. - * - * If we haven't yet received the JDWP handshake, we watch for it here - * and consume it without admitting to have done so. Upon receipt - * we send out the "HELO" message, which is why this can throw an - * IOException. - */ - JdwpPacket getJdwpPacket() throws IOException { - - /* - * On entry, the data starts at offset 0 and ends at "position". - * "limit" is set to the buffer capacity. - */ - if (mConnState == ST_AWAIT_SHAKE) { - /* - * The first thing we get from the client is a response to our - * handshake. It doesn't look like a packet, so we have to - * handle it specially. - */ - int result; - - result = JdwpPacket.findHandshake(mReadBuffer); - //Log.v("ddms", "findHand: " + result); - switch (result) { - case JdwpPacket.HANDSHAKE_GOOD: - Log.d("ddms", - "Good handshake from client, sending HELO to " + mClientData.getPid()); - JdwpPacket.consumeHandshake(mReadBuffer); - mConnState = ST_NEED_DDM_PKT; - HandleHello.sendHelloCommands(this, SERVER_PROTOCOL_VERSION); - // see if we have another packet in the buffer - return getJdwpPacket(); - case JdwpPacket.HANDSHAKE_BAD: - Log.d("ddms", "Bad handshake from client"); - if (MonitorThread.getInstance().getRetryOnBadHandshake()) { - // we should drop the client, but also attempt to reopen it. - // This is done by the DeviceMonitor. - mDevice.getMonitor().addClientToDropAndReopen(this, - IDebugPortProvider.NO_STATIC_PORT); - } else { - // mark it as bad, close the socket, and don't retry - mConnState = ST_NOT_JDWP; - close(true /* notify */); - } - break; - case JdwpPacket.HANDSHAKE_NOTYET: - Log.d("ddms", "No handshake from client yet."); - break; - default: - Log.e("ddms", "Unknown packet while waiting for client handshake"); - } - return null; - } else if (mConnState == ST_NEED_DDM_PKT || - mConnState == ST_NOT_DDM || - mConnState == ST_READY) { - /* - * Normal packet traffic. - */ - if (mReadBuffer.position() != 0) { - if (Log.Config.LOGV) Log.v("ddms", - "Checking " + mReadBuffer.position() + " bytes"); - } - return JdwpPacket.findPacket(mReadBuffer); - } else { - /* - * Not expecting data when in this state. - */ - Log.e("ddms", "Receiving data in state = " + mConnState); - } - - return null; - } - - /* - * Add the specified ID to the list of request IDs for which we await - * a response. - */ - private void addRequestId(int id, ChunkHandler handler) { - synchronized (mOutstandingReqs) { - if (Log.Config.LOGV) Log.v("ddms", - "Adding req 0x" + Integer.toHexString(id) +" to set"); - mOutstandingReqs.put(id, handler); - } - } - - /* - * Remove the specified ID from the list, if present. - */ - void removeRequestId(int id) { - synchronized (mOutstandingReqs) { - if (Log.Config.LOGV) Log.v("ddms", - "Removing req 0x" + Integer.toHexString(id) + " from set"); - mOutstandingReqs.remove(id); - } - - //Log.w("ddms", "Request " + Integer.toHexString(id) - // + " could not be removed from " + this); - } - - /** - * Determine whether this is a response to a request we sent earlier. - * If so, return the ChunkHandler responsible. - */ - ChunkHandler isResponseToUs(int id) { - - synchronized (mOutstandingReqs) { - ChunkHandler handler = mOutstandingReqs.get(id); - if (handler != null) { - if (Log.Config.LOGV) Log.v("ddms", - "Found 0x" + Integer.toHexString(id) - + " in request set - " + handler); - return handler; - } - } - - return null; - } - - /** - * An earlier request resulted in a failure. This is the expected - * response to a HELO message when talking to a non-DDM client. - */ - void packetFailed(JdwpPacket reply) { - if (mConnState == ST_NEED_DDM_PKT) { - Log.d("ddms", "Marking " + this + " as non-DDM client"); - mConnState = ST_NOT_DDM; - } else if (mConnState != ST_NOT_DDM) { - Log.w("ddms", "WEIRD: got JDWP failure packet on DDM req"); - } - } - - /** - * The MonitorThread calls this when it sees a DDM request or reply. - * If we haven't seen a DDM packet before, we advance the state to - * ST_READY and return "false". Otherwise, just return true. - * - * The idea is to let the MonitorThread know when we first see a DDM - * packet, so we can send a broadcast to the handlers when a client - * connection is made. This method is synchronized so that we only - * send the broadcast once. - */ - synchronized boolean ddmSeen() { - if (mConnState == ST_NEED_DDM_PKT) { - mConnState = ST_READY; - return false; - } else if (mConnState != ST_READY) { - Log.w("ddms", "WEIRD: in ddmSeen with state=" + mConnState); - } - return true; - } - - /** - * Close the client socket channel. If there is a debugger associated - * with us, close that too. - * - * Closing a channel automatically unregisters it from the selector. - * However, we have to iterate through the selector loop before it - * actually lets them go and allows the file descriptors to close. - * The caller is expected to manage that. - * @param notify Whether or not to notify the listeners of a change. - */ - void close(boolean notify) { - Log.d("ddms", "Closing " + this.toString()); - - mOutstandingReqs.clear(); - - try { - if (mChan != null) { - mChan.close(); - mChan = null; - } - - if (mDebugger != null) { - mDebugger.close(); - mDebugger = null; - } - } - catch (IOException ioe) { - Log.w("ddms", "failed to close " + this); - // swallow it -- not much else to do - } - - mDevice.removeClient(this, notify); - } - - /** - * Returns whether this {@link Client} has a valid connection to the application VM. - */ - public boolean isValid() { - return mChan != null; - } - - void update(int changeMask) { - mDevice.update(this, changeMask); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ClientData.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ClientData.java deleted file mode 100644 index f490c1a..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ClientData.java +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.HeapSegment.HeapSegmentElement; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.TreeSet; - - -/** - * Contains the data of a {@link Client}. - */ -public class ClientData { - /* This is a place to stash data associated with a Client, such as thread - * states or heap data. ClientData maps 1:1 to Client, but it's a little - * cleaner if we separate the data out. - * - * Message handlers are welcome to stash arbitrary data here. - * - * IMPORTANT: The data here is written by HandleFoo methods and read by - * FooPanel methods, which run in different threads. All non-trivial - * access should be synchronized against the ClientData object. - */ - - - /** Temporary name of VM to be ignored. */ - private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$ - - public static enum DebuggerStatus { - /** Debugger connection status: not waiting on one, not connected to one, but accepting - * new connections. This is the default value. */ - DEFAULT, - /** - * Debugger connection status: the application's VM is paused, waiting for a debugger to - * connect to it before resuming. */ - WAITING, - /** Debugger connection status : Debugger is connected */ - ATTACHED, - /** Debugger connection status: The listening port for debugger connection failed to listen. - * No debugger will be able to connect. */ - ERROR; - } - - public static enum AllocationTrackingStatus { - /** - * Allocation tracking status: unknown. - * <p/>This happens right after a {@link Client} is discovered - * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query - * regarding its allocation tracking status. - * @see Client#requestAllocationStatus() - */ - UNKNOWN, - /** Allocation tracking status: the {@link Client} is not tracking allocations. */ - OFF, - /** Allocation tracking status: the {@link Client} is tracking allocations. */ - ON; - } - - public static enum MethodProfilingStatus { - /** - * Method profiling status: unknown. - * <p/>This happens right after a {@link Client} is discovered - * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query - * regarding its method profiling status. - * @see Client#requestMethodProfilingStatus() - */ - UNKNOWN, - /** Method profiling status: the {@link Client} is not profiling method calls. */ - OFF, - /** Method profiling status: the {@link Client} is profiling method calls. */ - ON; - } - - /** - * Name of the value representing the max size of the heap, in the {@link Map} returned by - * {@link #getVmHeapInfo(int)} - */ - public final static String HEAP_MAX_SIZE_BYTES = "maxSizeInBytes"; //$NON-NLS-1$ - /** - * Name of the value representing the size of the heap, in the {@link Map} returned by - * {@link #getVmHeapInfo(int)} - */ - public final static String HEAP_SIZE_BYTES = "sizeInBytes"; //$NON-NLS-1$ - /** - * Name of the value representing the number of allocated bytes of the heap, in the - * {@link Map} returned by {@link #getVmHeapInfo(int)} - */ - public final static String HEAP_BYTES_ALLOCATED = "bytesAllocated"; //$NON-NLS-1$ - /** - * Name of the value representing the number of objects in the heap, in the {@link Map} - * returned by {@link #getVmHeapInfo(int)} - */ - public final static String HEAP_OBJECTS_ALLOCATED = "objectsAllocated"; //$NON-NLS-1$ - - /** - * String for feature enabling starting/stopping method profiling - * @see #hasFeature(String) - */ - public final static String FEATURE_PROFILING = "method-trace-profiling"; //$NON-NLS-1$ - - /** - * String for feature enabling direct streaming of method profiling data - * @see #hasFeature(String) - */ - public final static String FEATURE_PROFILING_STREAMING = "method-trace-profiling-streaming"; //$NON-NLS-1$ - - /** - * String for feature indicating support for tracing OpenGL calls. - * @see #hasFeature(String) - */ - public final static String FEATURE_OPENGL_TRACING = "opengl-tracing"; //$NON-NLS-1$ - - /** - * String for feature allowing to dump hprof files - * @see #hasFeature(String) - */ - public final static String FEATURE_HPROF = "hprof-heap-dump"; //$NON-NLS-1$ - - /** - * String for feature allowing direct streaming of hprof dumps - * @see #hasFeature(String) - */ - public final static String FEATURE_HPROF_STREAMING = "hprof-heap-dump-streaming"; //$NON-NLS-1$ - - private static IHprofDumpHandler sHprofDumpHandler; - private static IMethodProfilingHandler sMethodProfilingHandler; - - // is this a DDM-aware client? - private boolean mIsDdmAware; - - // the client's process ID - private final int mPid; - - // Java VM identification string - private String mVmIdentifier; - - // client's self-description - private String mClientDescription; - - // client's user id (on device in a multi user environment) - private int mUserId; - - // client's user id is valid - private boolean mValidUserId; - - // how interested are we in a debugger? - private DebuggerStatus mDebuggerInterest; - - // List of supported features by the client. - private final HashSet<String> mFeatures = new HashSet<String>(); - - // Thread tracking (THCR, THDE). - private TreeMap<Integer,ThreadInfo> mThreadMap; - - /** VM Heap data */ - private final HeapData mHeapData = new HeapData(); - /** Native Heap data */ - private final HeapData mNativeHeapData = new HeapData(); - - private HashMap<Integer, HashMap<String, Long>> mHeapInfoMap = - new HashMap<Integer, HashMap<String, Long>>(); - - - /** library map info. Stored here since the backtrace data - * is computed on a need to display basis. - */ - private ArrayList<NativeLibraryMapInfo> mNativeLibMapInfo = - new ArrayList<NativeLibraryMapInfo>(); - - /** Native Alloc info list */ - private ArrayList<NativeAllocationInfo> mNativeAllocationList = - new ArrayList<NativeAllocationInfo>(); - private int mNativeTotalMemory; - - private AllocationInfo[] mAllocations; - private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN; - - private String mPendingHprofDump; - - private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN; - private String mPendingMethodProfiling; - - /** - * Heap Information. - * <p/>The heap is composed of several {@link HeapSegment} objects. - * <p/>A call to {@link #isHeapDataComplete()} will indicate if the segments (available through - * {@link #getHeapSegments()}) represent the full heap. - */ - public static class HeapData { - private TreeSet<HeapSegment> mHeapSegments = new TreeSet<HeapSegment>(); - private boolean mHeapDataComplete = false; - private byte[] mProcessedHeapData; - private Map<Integer, ArrayList<HeapSegmentElement>> mProcessedHeapMap; - - /** - * Abandon the current list of heap segments. - */ - public synchronized void clearHeapData() { - /* Abandon the old segments instead of just calling .clear(). - * This lets the user hold onto the old set if it wants to. - */ - mHeapSegments = new TreeSet<HeapSegment>(); - mHeapDataComplete = false; - } - - /** - * Add raw HPSG chunk data to the list of heap segments. - * - * @param data The raw data from an HPSG chunk. - */ - synchronized void addHeapData(ByteBuffer data) { - HeapSegment hs; - - if (mHeapDataComplete) { - clearHeapData(); - } - - try { - hs = new HeapSegment(data); - } catch (BufferUnderflowException e) { - System.err.println("Discarding short HPSG data (length " + data.limit() + ")"); - return; - } - - mHeapSegments.add(hs); - } - - /** - * Called when all heap data has arrived. - */ - synchronized void sealHeapData() { - mHeapDataComplete = true; - } - - /** - * Returns whether the heap data has been sealed. - */ - public boolean isHeapDataComplete() { - return mHeapDataComplete; - } - - /** - * Get the collected heap data, if sealed. - * - * @return The list of heap segments if the heap data has been sealed, or null if it hasn't. - */ - public Collection<HeapSegment> getHeapSegments() { - if (isHeapDataComplete()) { - return mHeapSegments; - } - return null; - } - - /** - * Sets the processed heap data. - * - * @param heapData The new heap data (can be null) - */ - public void setProcessedHeapData(byte[] heapData) { - mProcessedHeapData = heapData; - } - - /** - * Get the processed heap data, if present. - * - * @return the processed heap data, or null. - */ - public byte[] getProcessedHeapData() { - return mProcessedHeapData; - } - - public void setProcessedHeapMap(Map<Integer, ArrayList<HeapSegmentElement>> heapMap) { - mProcessedHeapMap = heapMap; - } - - public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() { - return mProcessedHeapMap; - } - } - - /** - * Handlers able to act on HPROF dumps. - */ - public interface IHprofDumpHandler { - /** - * Called when a HPROF dump succeeded. - * @param remoteFilePath the device-side path of the HPROF file. - * @param client the client for which the HPROF file was. - */ - void onSuccess(String remoteFilePath, Client client); - - /** - * Called when a HPROF dump was successful. - * @param data the data containing the HPROF file, streamed from the VM - * @param client the client that was profiled. - */ - void onSuccess(byte[] data, Client client); - - /** - * Called when a hprof dump failed to end on the VM side - * @param client the client that was profiled. - * @param message an optional (<code>null<code> ok) error message to be displayed. - */ - void onEndFailure(Client client, String message); - } - - /** - * Handlers able to act on Method profiling info - */ - public interface IMethodProfilingHandler { - /** - * Called when a method tracing was successful. - * @param remoteFilePath the device-side path of the trace file. - * @param client the client that was profiled. - */ - void onSuccess(String remoteFilePath, Client client); - - /** - * Called when a method tracing was successful. - * @param data the data containing the trace file, streamed from the VM - * @param client the client that was profiled. - */ - void onSuccess(byte[] data, Client client); - - /** - * Called when method tracing failed to start - * @param client the client that was profiled. - * @param message an optional (<code>null<code> ok) error message to be displayed. - */ - void onStartFailure(Client client, String message); - - /** - * Called when method tracing failed to end on the VM side - * @param client the client that was profiled. - * @param message an optional (<code>null<code> ok) error message to be displayed. - */ - void onEndFailure(Client client, String message); - } - - /** - * Sets the handler to receive notifications when an HPROF dump succeeded or failed. - */ - public static void setHprofDumpHandler(IHprofDumpHandler handler) { - sHprofDumpHandler = handler; - } - - static IHprofDumpHandler getHprofDumpHandler() { - return sHprofDumpHandler; - } - - /** - * Sets the handler to receive notifications when an HPROF dump succeeded or failed. - */ - public static void setMethodProfilingHandler(IMethodProfilingHandler handler) { - sMethodProfilingHandler = handler; - } - - static IMethodProfilingHandler getMethodProfilingHandler() { - return sMethodProfilingHandler; - } - - /** - * Generic constructor. - */ - ClientData(int pid) { - mPid = pid; - - mDebuggerInterest = DebuggerStatus.DEFAULT; - mThreadMap = new TreeMap<Integer,ThreadInfo>(); - } - - /** - * Returns whether the process is DDM-aware. - */ - public boolean isDdmAware() { - return mIsDdmAware; - } - - /** - * Sets DDM-aware status. - */ - void isDdmAware(boolean aware) { - mIsDdmAware = aware; - } - - /** - * Returns the process ID. - */ - public int getPid() { - return mPid; - } - - /** - * Returns the Client's VM identifier. - */ - public String getVmIdentifier() { - return mVmIdentifier; - } - - /** - * Sets VM identifier. - */ - void setVmIdentifier(String ident) { - mVmIdentifier = ident; - } - - /** - * Returns the client description. - * <p/>This is generally the name of the package defined in the - * <code>AndroidManifest.xml</code>. - * - * @return the client description or <code>null</code> if not the description was not yet - * sent by the client. - */ - public String getClientDescription() { - return mClientDescription; - } - - /** - * Returns the client's user id. - * @return user id if set, -1 otherwise - */ - public int getUserId() { - return mUserId; - } - - /** - * Returns true if the user id of this client was set. Only devices that support multiple - * users will actually return the user id to ddms. For other/older devices, this will not - * be set. - */ - public boolean isValidUserId() { - return mValidUserId; - } - - /** - * Sets client description. - * - * There may be a race between HELO and APNM. Rather than try - * to enforce ordering on the device, we just don't allow an empty - * name to replace a specified one. - */ - void setClientDescription(String description) { - if (mClientDescription == null && description.length() > 0) { - /* - * The application VM is first named <pre-initialized> before being assigned - * its real name. - * Depending on the timing, we can get an APNM chunk setting this name before - * another one setting the final actual name. So if we get a SetClientDescription - * with this value we ignore it. - */ - if (PRE_INITIALIZED.equals(description) == false) { - mClientDescription = description; - } - } - } - - void setUserId(int id) { - mUserId = id; - mValidUserId = true; - } - - /** - * Returns the debugger connection status. - */ - public DebuggerStatus getDebuggerConnectionStatus() { - return mDebuggerInterest; - } - - /** - * Sets debugger connection status. - */ - void setDebuggerConnectionStatus(DebuggerStatus status) { - mDebuggerInterest = status; - } - - /** - * Sets the current heap info values for the specified heap. - * - * @param heapId The heap whose info to update - * @param sizeInBytes The size of the heap, in bytes - * @param bytesAllocated The number of bytes currently allocated in the heap - * @param objectsAllocated The number of objects currently allocated in - * the heap - */ - // TODO: keep track of timestamp, reason - synchronized void setHeapInfo(int heapId, long maxSizeInBytes, - long sizeInBytes, long bytesAllocated, long objectsAllocated) { - HashMap<String, Long> heapInfo = new HashMap<String, Long>(); - heapInfo.put(HEAP_MAX_SIZE_BYTES, maxSizeInBytes); - heapInfo.put(HEAP_SIZE_BYTES, sizeInBytes); - heapInfo.put(HEAP_BYTES_ALLOCATED, bytesAllocated); - heapInfo.put(HEAP_OBJECTS_ALLOCATED, objectsAllocated); - mHeapInfoMap.put(heapId, heapInfo); - } - - /** - * Returns the {@link HeapData} object for the VM. - */ - public HeapData getVmHeapData() { - return mHeapData; - } - - /** - * Returns the {@link HeapData} object for the native code. - */ - HeapData getNativeHeapData() { - return mNativeHeapData; - } - - /** - * Returns an iterator over the list of known VM heap ids. - * <p/> - * The caller must synchronize on the {@link ClientData} object while iterating. - * - * @return an iterator over the list of heap ids - */ - public synchronized Iterator<Integer> getVmHeapIds() { - return mHeapInfoMap.keySet().iterator(); - } - - /** - * Returns the most-recent info values for the specified VM heap. - * - * @param heapId The heap whose info should be returned - * @return a map containing the info values for the specified heap. - * Returns <code>null</code> if the heap ID is unknown. - */ - public synchronized Map<String, Long> getVmHeapInfo(int heapId) { - return mHeapInfoMap.get(heapId); - } - - /** - * Adds a new thread to the list. - */ - synchronized void addThread(int threadId, String threadName) { - ThreadInfo attr = new ThreadInfo(threadId, threadName); - mThreadMap.put(threadId, attr); - } - - /** - * Removes a thread from the list. - */ - synchronized void removeThread(int threadId) { - mThreadMap.remove(threadId); - } - - /** - * Returns the list of threads as {@link ThreadInfo} objects. - * <p/>The list is empty until a thread update was requested with - * {@link Client#requestThreadUpdate()}. - */ - public synchronized ThreadInfo[] getThreads() { - Collection<ThreadInfo> threads = mThreadMap.values(); - return threads.toArray(new ThreadInfo[threads.size()]); - } - - /** - * Returns the {@link ThreadInfo} by thread id. - */ - synchronized ThreadInfo getThread(int threadId) { - return mThreadMap.get(threadId); - } - - synchronized void clearThreads() { - mThreadMap.clear(); - } - - /** - * Returns the list of {@link NativeAllocationInfo}. - * @see Client#requestNativeHeapInformation() - */ - public synchronized List<NativeAllocationInfo> getNativeAllocationList() { - return Collections.unmodifiableList(mNativeAllocationList); - } - - /** - * adds a new {@link NativeAllocationInfo} to the {@link Client} - * @param allocInfo The {@link NativeAllocationInfo} to add. - */ - synchronized void addNativeAllocation(NativeAllocationInfo allocInfo) { - mNativeAllocationList.add(allocInfo); - } - - /** - * Clear the current malloc info. - */ - synchronized void clearNativeAllocationInfo() { - mNativeAllocationList.clear(); - } - - /** - * Returns the total native memory. - * @see Client#requestNativeHeapInformation() - */ - public synchronized int getTotalNativeMemory() { - return mNativeTotalMemory; - } - - synchronized void setTotalNativeMemory(int totalMemory) { - mNativeTotalMemory = totalMemory; - } - - synchronized void addNativeLibraryMapInfo(long startAddr, long endAddr, String library) { - mNativeLibMapInfo.add(new NativeLibraryMapInfo(startAddr, endAddr, library)); - } - - /** - * Returns the list of native libraries mapped in memory for this client. - */ - public synchronized List<NativeLibraryMapInfo> getMappedNativeLibraries() { - return Collections.unmodifiableList(mNativeLibMapInfo); - } - - synchronized void setAllocationStatus(AllocationTrackingStatus status) { - mAllocationStatus = status; - } - - /** - * Returns the allocation tracking status. - * @see Client#requestAllocationStatus() - */ - public synchronized AllocationTrackingStatus getAllocationStatus() { - return mAllocationStatus; - } - - synchronized void setAllocations(AllocationInfo[] allocs) { - mAllocations = allocs; - } - - /** - * Returns the list of tracked allocations. - * @see Client#requestAllocationDetails() - */ - public synchronized AllocationInfo[] getAllocations() { - return mAllocations; - } - - void addFeature(String feature) { - mFeatures.add(feature); - } - - /** - * Returns true if the {@link Client} supports the given <var>feature</var> - * @param feature The feature to test. - * @return true if the feature is supported - * - * @see ClientData#FEATURE_PROFILING - * @see ClientData#FEATURE_HPROF - */ - public boolean hasFeature(String feature) { - return mFeatures.contains(feature); - } - - /** - * Sets the device-side path to the hprof file being written - * @param pendingHprofDump the file to the hprof file - */ - void setPendingHprofDump(String pendingHprofDump) { - mPendingHprofDump = pendingHprofDump; - } - - /** - * Returns the path to the device-side hprof file being written. - */ - String getPendingHprofDump() { - return mPendingHprofDump; - } - - public boolean hasPendingHprofDump() { - return mPendingHprofDump != null; - } - - synchronized void setMethodProfilingStatus(MethodProfilingStatus status) { - mProfilingStatus = status; - } - - /** - * Returns the method profiling status. - * @see Client#requestMethodProfilingStatus() - */ - public synchronized MethodProfilingStatus getMethodProfilingStatus() { - return mProfilingStatus; - } - - /** - * Sets the device-side path to the method profile file being written - * @param pendingMethodProfiling the file being written - */ - void setPendingMethodProfiling(String pendingMethodProfiling) { - mPendingMethodProfiling = pendingMethodProfiling; - } - - /** - * Returns the path to the device-side method profiling file being written. - */ - String getPendingMethodProfiling() { - return mPendingMethodProfiling; - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java deleted file mode 100644 index 80aa8e1..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.UnsupportedEncodingException; -import java.util.concurrent.CountDownLatch; - -/** - * A {@link IShellOutputReceiver} which collects the whole shell output into one - * {@link String}. - */ -public class CollectingOutputReceiver implements IShellOutputReceiver { - private CountDownLatch mCompletionLatch; - private StringBuffer mOutputBuffer = new StringBuffer(); - private boolean mIsCanceled = false; - - public CollectingOutputReceiver() { - } - - public CollectingOutputReceiver(CountDownLatch commandCompleteLatch) { - mCompletionLatch = commandCompleteLatch; - } - - public String getOutput() { - return mOutputBuffer.toString(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCancelled() { - return mIsCanceled; - } - - /** - * Cancel the output collection - */ - public void cancel() { - mIsCanceled = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void addOutput(byte[] data, int offset, int length) { - if (!isCancelled()) { - String s = null; - try { - s = new String(data, offset, length, "UTF-8"); //$NON-NLS-1$ - } catch (UnsupportedEncodingException e) { - // normal encoding didn't work, try the default one - s = new String(data, offset,length); - } - mOutputBuffer.append(s); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void flush() { - if (mCompletionLatch != null) { - mCompletionLatch.countDown(); - } - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java deleted file mode 100644 index 0b107e4..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2009 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; - -public final class DdmConstants { - - public final static int PLATFORM_UNKNOWN = 0; - public final static int PLATFORM_LINUX = 1; - public final static int PLATFORM_WINDOWS = 2; - public final static int PLATFORM_DARWIN = 3; - - /** - * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public final static int CURRENT_PLATFORM = currentPlatform(); - - /** - * Extension for Traceview files. - */ - public final static String DOT_TRACE = ".trace"; - - /** hprof-conv executable (with extension for the current OS) */ - public final static String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "hprof-conv.exe" : "hprof-conv"; //$NON-NLS-1$ //$NON-NLS-2$ - - /** traceview executable (with extension for the current OS) */ - public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "traceview.bat" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$ - - /** - * Returns current platform - * - * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public static int currentPlatform() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS")) { //$NON-NLS-1$ - return PLATFORM_DARWIN; - } else if (os.startsWith("Windows")) { //$NON-NLS-1$ - return PLATFORM_WINDOWS; - } else if (os.startsWith("Linux")) { //$NON-NLS-1$ - return PLATFORM_LINUX; - } - - return PLATFORM_UNKNOWN; - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmPreferences.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmPreferences.java deleted file mode 100644 index d286917..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DdmPreferences.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.Log.LogLevel; - -/** - * Preferences for the ddm library. - * <p/>This class does not handle storing the preferences. It is merely a central point for - * applications using the ddmlib to override the default values. - * <p/>Various components of the ddmlib query this class to get their values. - * <p/>Calls to some <code>set##()</code> methods will update the components using the values - * right away, while other methods will have no effect once {@link AndroidDebugBridge#init(boolean)} - * has been called. - * <p/>Check the documentation of each method. - */ -public final class DdmPreferences { - - /** Default value for thread update flag upon client connection. */ - public final static boolean DEFAULT_INITIAL_THREAD_UPDATE = false; - /** Default value for heap update flag upon client connection. */ - public final static boolean DEFAULT_INITIAL_HEAP_UPDATE = false; - /** Default value for the selected client debug port */ - public final static int DEFAULT_SELECTED_DEBUG_PORT = 8700; - /** Default value for the debug port base */ - public final static int DEFAULT_DEBUG_PORT_BASE = 8600; - /** Default value for the logcat {@link LogLevel} */ - public final static LogLevel DEFAULT_LOG_LEVEL = LogLevel.ERROR; - /** Default timeout values for adb connection (milliseconds) */ - public static final int DEFAULT_TIMEOUT = 5000; // standard delay, in ms - /** Default profiler buffer size (megabytes) */ - public static final int DEFAULT_PROFILER_BUFFER_SIZE_MB = 8; - /** Default values for the use of the ADBHOST environment variable. */ - public final static boolean DEFAULT_USE_ADBHOST = false; - public final static String DEFAULT_ADBHOST_VALUE = "127.0.0.1"; - - private static boolean sThreadUpdate = DEFAULT_INITIAL_THREAD_UPDATE; - private static boolean sInitialHeapUpdate = DEFAULT_INITIAL_HEAP_UPDATE; - - private static int sSelectedDebugPort = DEFAULT_SELECTED_DEBUG_PORT; - private static int sDebugPortBase = DEFAULT_DEBUG_PORT_BASE; - private static LogLevel sLogLevel = DEFAULT_LOG_LEVEL; - private static int sTimeOut = DEFAULT_TIMEOUT; - private static int sProfilerBufferSizeMb = DEFAULT_PROFILER_BUFFER_SIZE_MB; - - private static boolean sUseAdbHost = DEFAULT_USE_ADBHOST; - private static String sAdbHostValue = DEFAULT_ADBHOST_VALUE; - - /** - * Returns the initial {@link Client} flag for thread updates. - * @see #setInitialThreadUpdate(boolean) - */ - public static boolean getInitialThreadUpdate() { - return sThreadUpdate; - } - - /** - * Sets the initial {@link Client} flag for thread updates. - * <p/>This change takes effect right away, for newly created {@link Client} objects. - */ - public static void setInitialThreadUpdate(boolean state) { - sThreadUpdate = state; - } - - /** - * Returns the initial {@link Client} flag for heap updates. - * @see #setInitialHeapUpdate(boolean) - */ - public static boolean getInitialHeapUpdate() { - return sInitialHeapUpdate; - } - - /** - * Sets the initial {@link Client} flag for heap updates. - * <p/>If <code>true</code>, the {@link ClientData} will automatically be updated with - * the VM heap information whenever a GC happens. - * <p/>This change takes effect right away, for newly created {@link Client} objects. - */ - public static void setInitialHeapUpdate(boolean state) { - sInitialHeapUpdate = state; - } - - /** - * Returns the debug port used by the selected {@link Client}. - */ - public static int getSelectedDebugPort() { - return sSelectedDebugPort; - } - - /** - * Sets the debug port used by the selected {@link Client}. - * <p/>This change takes effect right away. - * @param port the new port to use. - */ - public static void setSelectedDebugPort(int port) { - sSelectedDebugPort = port; - - MonitorThread monitorThread = MonitorThread.getInstance(); - if (monitorThread != null) { - monitorThread.setDebugSelectedPort(port); - } - } - - /** - * Returns the debug port used by the first {@link Client}. Following clients, will use the - * next port. - */ - public static int getDebugPortBase() { - return sDebugPortBase; - } - - /** - * Sets the debug port used by the first {@link Client}. - * <p/>Once a port is used, the next Client will use port + 1. Quitting applications will - * release their debug port, and new clients will be able to reuse them. - * <p/>This must be called before {@link AndroidDebugBridge#init(boolean)}. - */ - public static void setDebugPortBase(int port) { - sDebugPortBase = port; - } - - /** - * Returns the minimum {@link LogLevel} being displayed. - */ - public static LogLevel getLogLevel() { - return sLogLevel; - } - - /** - * Sets the minimum {@link LogLevel} to display. - * <p/>This change takes effect right away. - */ - public static void setLogLevel(String value) { - sLogLevel = LogLevel.getByString(value); - - Log.setLevel(sLogLevel); - } - - /** - * Returns the timeout to be used in adb connections (milliseconds). - */ - public static int getTimeOut() { - return sTimeOut; - } - - /** - * Sets the timeout value for adb connection. - * <p/>This change takes effect for newly created connections only. - * @param timeOut the timeout value (milliseconds). - */ - public static void setTimeOut(int timeOut) { - sTimeOut = timeOut; - } - - /** - * Returns the profiler buffer size (megabytes). - */ - public static int getProfilerBufferSizeMb() { - return sProfilerBufferSizeMb; - } - - /** - * Sets the profiler buffer size value. - * @param bufferSizeMb the buffer size (megabytes). - */ - public static void setProfilerBufferSizeMb(int bufferSizeMb) { - sProfilerBufferSizeMb = bufferSizeMb; - } - - /** - * Returns a boolean indicating that the user uses or not the variable ADBHOST. - */ - public static boolean getUseAdbHost() { - return sUseAdbHost; - } - - /** - * Sets the value of the boolean indicating that the user uses or not the variable ADBHOST. - * @param useAdbHost true if the user uses ADBHOST - */ - public static void setUseAdbHost(boolean useAdbHost) { - sUseAdbHost = useAdbHost; - } - - /** - * Returns the value of the ADBHOST variable set by the user. - */ - public static String getAdbHostValue() { - return sAdbHostValue; - } - - /** - * Sets the value of the ADBHOST variable. - * @param adbHostValue - */ - public static void setAdbHostValue(String adbHostValue) { - sAdbHostValue = adbHostValue; - } - - /** - * Non accessible constructor. - */ - private DdmPreferences() { - // pass, only static methods in the class. - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DebugPortManager.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DebugPortManager.java deleted file mode 100644 index defdc0e..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DebugPortManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.Device; - -/** - * Centralized point to provide a {@link IDebugPortProvider} to ddmlib. - * - * <p/>When {@link Client} objects are created, they start listening for debuggers on a specific - * port. The default behavior is to start with {@link DdmPreferences#getDebugPortBase()} and - * increment this value for each new <code>Client</code>. - * - * <p/>This {@link DebugPortManager} allows applications using ddmlib to provide a custom - * port provider on a per-<code>Client</code> basis, depending on the device/emulator they are - * running on, and/or their names. - */ -public class DebugPortManager { - - /** - * Classes which implement this interface provide a method that provides a non random - * debugger port for a newly created {@link Client}. - */ - public interface IDebugPortProvider { - - public static final int NO_STATIC_PORT = -1; - - /** - * Returns a non-random debugger port for the specified application running on the - * specified {@link Device}. - * @param device The device the application is running on. - * @param appName The application name, as defined in the <code>AndroidManifest.xml</code> - * <var>package</var> attribute of the <var>manifest</var> node. - * @return The non-random debugger port or {@link #NO_STATIC_PORT} if the {@link Client} - * should use the automatic debugger port provider. - */ - public int getPort(IDevice device, String appName); - } - - private static IDebugPortProvider sProvider = null; - - /** - * Sets the {@link IDebugPortProvider} that will be used when a new {@link Client} requests - * a debugger port. - * @param provider the <code>IDebugPortProvider</code> to use. - */ - public static void setProvider(IDebugPortProvider provider) { - sProvider = provider; - } - - /** - * Returns the - * @return - */ - static IDebugPortProvider getProvider() { - return sProvider; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Debugger.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Debugger.java deleted file mode 100644 index 9356c13..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Debugger.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.ClientData.DebuggerStatus; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; - -/** - * This represents a pending or established connection with a JDWP debugger. - */ -class Debugger { - - /* - * Messages from the debugger should be pretty small; may not even - * need an expanding-buffer implementation for this. - */ - private static final int INITIAL_BUF_SIZE = 1 * 1024; - private static final int MAX_BUF_SIZE = 32 * 1024; - private ByteBuffer mReadBuffer; - - private static final int PRE_DATA_BUF_SIZE = 256; - private ByteBuffer mPreDataBuffer; - - /* connection state */ - private int mConnState; - private static final int ST_NOT_CONNECTED = 1; - private static final int ST_AWAIT_SHAKE = 2; - private static final int ST_READY = 3; - - /* peer */ - private Client mClient; // client we're forwarding to/from - private int mListenPort; // listen to me - private ServerSocketChannel mListenChannel; - - /* this goes up and down; synchronize methods that access the field */ - private SocketChannel mChannel; - - /** - * Create a new Debugger object, configured to listen for connections - * on a specific port. - */ - Debugger(Client client, int listenPort) throws IOException { - - mClient = client; - mListenPort = listenPort; - - mListenChannel = ServerSocketChannel.open(); - mListenChannel.configureBlocking(false); // required for Selector - - InetSocketAddress addr = new InetSocketAddress( - InetAddress.getByName("localhost"), //$NON-NLS-1$ - listenPort); - mListenChannel.socket().setReuseAddress(true); // enable SO_REUSEADDR - mListenChannel.socket().bind(addr); - - mReadBuffer = ByteBuffer.allocate(INITIAL_BUF_SIZE); - mPreDataBuffer = ByteBuffer.allocate(PRE_DATA_BUF_SIZE); - mConnState = ST_NOT_CONNECTED; - - Log.d("ddms", "Created: " + this.toString()); - } - - /** - * Returns "true" if a debugger is currently attached to us. - */ - boolean isDebuggerAttached() { - return mChannel != null; - } - - /** - * Represent the Debugger as a string. - */ - @Override - public String toString() { - // mChannel != null means we have connection, ST_READY means it's going - return "[Debugger " + mListenPort + "-->" + mClient.getClientData().getPid() - + ((mConnState != ST_READY) ? " inactive]" : " active]"); - } - - /** - * Register the debugger's listen socket with the Selector. - */ - void registerListener(Selector sel) throws IOException { - mListenChannel.register(sel, SelectionKey.OP_ACCEPT, this); - } - - /** - * Return the Client being debugged. - */ - Client getClient() { - return mClient; - } - - /** - * Accept a new connection, but only if we don't already have one. - * - * Must be synchronized with other uses of mChannel and mPreBuffer. - * - * Returns "null" if we're already talking to somebody. - */ - synchronized SocketChannel accept() throws IOException { - return accept(mListenChannel); - } - - /** - * Accept a new connection from the specified listen channel. This - * is so we can listen on a dedicated port for the "current" client, - * where "current" is constantly in flux. - * - * Must be synchronized with other uses of mChannel and mPreBuffer. - * - * Returns "null" if we're already talking to somebody. - */ - synchronized SocketChannel accept(ServerSocketChannel listenChan) - throws IOException { - - if (listenChan != null) { - SocketChannel newChan; - - newChan = listenChan.accept(); - if (mChannel != null) { - Log.w("ddms", "debugger already talking to " + mClient - + " on " + mListenPort); - newChan.close(); - return null; - } - mChannel = newChan; - mChannel.configureBlocking(false); // required for Selector - mConnState = ST_AWAIT_SHAKE; - return mChannel; - } - - return null; - } - - /** - * Close the data connection only. - */ - synchronized void closeData() { - try { - if (mChannel != null) { - mChannel.close(); - mChannel = null; - mConnState = ST_NOT_CONNECTED; - - ClientData cd = mClient.getClientData(); - cd.setDebuggerConnectionStatus(DebuggerStatus.DEFAULT); - mClient.update(Client.CHANGE_DEBUGGER_STATUS); - } - } catch (IOException ioe) { - Log.w("ddms", "Failed to close data " + this); - } - } - - /** - * Close the socket that's listening for new connections and (if - * we're connected) the debugger data socket. - */ - synchronized void close() { - try { - if (mListenChannel != null) { - mListenChannel.close(); - } - mListenChannel = null; - closeData(); - } catch (IOException ioe) { - Log.w("ddms", "Failed to close listener " + this); - } - } - - // TODO: ?? add a finalizer that verifies the channel was closed - - /** - * Read data from our channel. - * - * This is called when data is known to be available, and we don't yet - * have a full packet in the buffer. If the buffer is at capacity, - * expand it. - */ - void read() throws IOException { - int count; - - if (mReadBuffer.position() == mReadBuffer.capacity()) { - if (mReadBuffer.capacity() * 2 > MAX_BUF_SIZE) { - throw new BufferOverflowException(); - } - Log.d("ddms", "Expanding read buffer to " - + mReadBuffer.capacity() * 2); - - ByteBuffer newBuffer = - ByteBuffer.allocate(mReadBuffer.capacity() * 2); - mReadBuffer.position(0); - newBuffer.put(mReadBuffer); // leaves "position" at end - - mReadBuffer = newBuffer; - } - - count = mChannel.read(mReadBuffer); - Log.v("ddms", "Read " + count + " bytes from " + this); - if (count < 0) throw new IOException("read failed"); - } - - /** - * Return information for the first full JDWP packet in the buffer. - * - * If we don't yet have a full packet, return null. - * - * If we haven't yet received the JDWP handshake, we watch for it here - * and consume it without admitting to have done so. We also send - * the handshake response to the debugger, along with any pending - * pre-connection data, which is why this can throw an IOException. - */ - JdwpPacket getJdwpPacket() throws IOException { - /* - * On entry, the data starts at offset 0 and ends at "position". - * "limit" is set to the buffer capacity. - */ - if (mConnState == ST_AWAIT_SHAKE) { - int result; - - result = JdwpPacket.findHandshake(mReadBuffer); - //Log.v("ddms", "findHand: " + result); - switch (result) { - case JdwpPacket.HANDSHAKE_GOOD: - Log.d("ddms", "Good handshake from debugger"); - JdwpPacket.consumeHandshake(mReadBuffer); - sendHandshake(); - mConnState = ST_READY; - - ClientData cd = mClient.getClientData(); - cd.setDebuggerConnectionStatus(DebuggerStatus.ATTACHED); - mClient.update(Client.CHANGE_DEBUGGER_STATUS); - - // see if we have another packet in the buffer - return getJdwpPacket(); - case JdwpPacket.HANDSHAKE_BAD: - // not a debugger, throw an exception so we drop the line - Log.d("ddms", "Bad handshake from debugger"); - throw new IOException("bad handshake"); - case JdwpPacket.HANDSHAKE_NOTYET: - break; - default: - Log.e("ddms", "Unknown packet while waiting for client handshake"); - } - return null; - } else if (mConnState == ST_READY) { - if (mReadBuffer.position() != 0) { - Log.v("ddms", "Checking " + mReadBuffer.position() + " bytes"); - } - return JdwpPacket.findPacket(mReadBuffer); - } else { - Log.e("ddms", "Receiving data in state = " + mConnState); - } - - return null; - } - - /** - * Forward a packet to the client. - * - * "mClient" will never be null, though it's possible that the channel - * in the client has closed and our send attempt will fail. - * - * Consumes the packet. - */ - void forwardPacketToClient(JdwpPacket packet) throws IOException { - mClient.sendAndConsume(packet); - } - - /** - * Send the handshake to the debugger. We also send along any packets - * we already received from the client (usually just a VM_START event, - * if anything at all). - */ - private synchronized void sendHandshake() throws IOException { - ByteBuffer tempBuffer = ByteBuffer.allocate(JdwpPacket.HANDSHAKE_LEN); - JdwpPacket.putHandshake(tempBuffer); - int expectedLength = tempBuffer.position(); - tempBuffer.flip(); - if (mChannel.write(tempBuffer) != expectedLength) { - throw new IOException("partial handshake write"); - } - - expectedLength = mPreDataBuffer.position(); - if (expectedLength > 0) { - Log.d("ddms", "Sending " + mPreDataBuffer.position() - + " bytes of saved data"); - mPreDataBuffer.flip(); - if (mChannel.write(mPreDataBuffer) != expectedLength) { - throw new IOException("partial pre-data write"); - } - mPreDataBuffer.clear(); - } - } - - /** - * Send a packet to the debugger. - * - * Ideally, we can do this with a single channel write. If that doesn't - * happen, we have to prevent anybody else from writing to the channel - * until this packet completes, so we synchronize on the channel. - * - * Another goal is to avoid unnecessary buffer copies, so we write - * directly out of the JdwpPacket's ByteBuffer. - * - * We must synchronize on "mChannel" before writing to it. We want to - * coordinate the buffered data with mChannel creation, so this whole - * method is synchronized. - */ - synchronized void sendAndConsume(JdwpPacket packet) - throws IOException { - - if (mChannel == null) { - /* - * Buffer this up so we can send it to the debugger when it - * finally does connect. This is essential because the VM_START - * message might be telling the debugger that the VM is - * suspended. The alternative approach would be for us to - * capture and interpret VM_START and send it later if we - * didn't choose to un-suspend the VM for our own purposes. - */ - Log.d("ddms", "Saving packet 0x" - + Integer.toHexString(packet.getId())); - packet.movePacket(mPreDataBuffer); - } else { - packet.writeAndConsume(mChannel); - } - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Device.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Device.java deleted file mode 100644 index 0566275..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Device.java +++ /dev/null @@ -1,851 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.log.LogReceiver; - -import java.io.File; -import java.io.IOException; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -/** - * A Device. It can be a physical device or an emulator. - */ -final class Device implements IDevice { - private static final String DEVICE_MODEL_PROPERTY = "ro.product.model"; //$NON-NLS-1$ - private static final String DEVICE_MANUFACTURER_PROPERTY = "ro.product.manufacturer"; //$NON-NLS-1$ - - private final static int INSTALL_TIMEOUT = 2*60*1000; //2min - private static final int BATTERY_TIMEOUT = 2*1000; //2 seconds - private static final int GETPROP_TIMEOUT = 2*1000; //2 seconds - - /** Emulator Serial Number regexp. */ - final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$ - - /** Serial number of the device */ - private String mSerialNumber = null; - - /** Name of the AVD */ - private String mAvdName = null; - - /** State of the device. */ - private DeviceState mState = null; - - /** Device properties. */ - private final Map<String, String> mProperties = new HashMap<String, String>(); - private final Map<String, String> mMountPoints = new HashMap<String, String>(); - - private final ArrayList<Client> mClients = new ArrayList<Client>(); - private DeviceMonitor mMonitor; - - private static final String LOG_TAG = "Device"; - private static final char SEPARATOR = '-'; - - /** - * Socket for the connection monitoring client connection/disconnection. - */ - private SocketChannel mSocketChannel; - - private boolean mArePropertiesSet = false; - - private Integer mLastBatteryLevel = null; - private long mLastBatteryCheckTime = 0; - - private String mName; - - /** - * Output receiver for "pm install package.apk" command line. - */ - private static final class InstallReceiver extends MultiLineReceiver { - - private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ - private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ - - private String mErrorMessage = null; - - public InstallReceiver() { - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.length() > 0) { - if (line.startsWith(SUCCESS_OUTPUT)) { - mErrorMessage = null; - } else { - Matcher m = FAILURE_PATTERN.matcher(line); - if (m.matches()) { - mErrorMessage = m.group(1); - } - } - } - } - } - - @Override - public boolean isCancelled() { - return false; - } - - public String getErrorMessage() { - return mErrorMessage; - } - } - - /** - * Output receiver for "dumpsys battery" command line. - */ - private static final class BatteryReceiver extends MultiLineReceiver { - private static final Pattern BATTERY_LEVEL = Pattern.compile("\\s*level: (\\d+)"); - private static final Pattern SCALE = Pattern.compile("\\s*scale: (\\d+)"); - - private Integer mBatteryLevel = null; - private Integer mBatteryScale = null; - - /** - * Get the parsed percent battery level. - * @return - */ - public Integer getBatteryLevel() { - if (mBatteryLevel != null && mBatteryScale != null) { - return (mBatteryLevel * 100) / mBatteryScale; - } - return null; - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - Matcher batteryMatch = BATTERY_LEVEL.matcher(line); - if (batteryMatch.matches()) { - try { - mBatteryLevel = Integer.parseInt(batteryMatch.group(1)); - } catch (NumberFormatException e) { - Log.w(LOG_TAG, String.format("Failed to parse %s as an integer", - batteryMatch.group(1))); - } - } - Matcher scaleMatch = SCALE.matcher(line); - if (scaleMatch.matches()) { - try { - mBatteryScale = Integer.parseInt(scaleMatch.group(1)); - } catch (NumberFormatException e) { - Log.w(LOG_TAG, String.format("Failed to parse %s as an integer", - batteryMatch.group(1))); - } - } - } - } - - @Override - public boolean isCancelled() { - return false; - } - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getSerialNumber() - */ - @Override - public String getSerialNumber() { - return mSerialNumber; - } - - /** {@inheritDoc} */ - @Override - public String getAvdName() { - return mAvdName; - } - - /** - * Sets the name of the AVD - */ - void setAvdName(String avdName) { - if (isEmulator() == false) { - throw new IllegalArgumentException( - "Cannot set the AVD name of the device is not an emulator"); - } - - mAvdName = avdName; - } - - @Override - public String getName() { - if (mName == null) { - mName = constructName(); - } - - return mName; - } - - private String constructName() { - if (isEmulator()) { - String avdName = getAvdName(); - if (avdName != null) { - return String.format("%s [%s]", avdName, getSerialNumber()); - } else { - return getSerialNumber(); - } - } else { - String manufacturer = cleanupStringForDisplay( - getProperty(DEVICE_MANUFACTURER_PROPERTY)); - String model = cleanupStringForDisplay( - getProperty(DEVICE_MODEL_PROPERTY)); - - StringBuilder sb = new StringBuilder(20); - - if (manufacturer != null) { - sb.append(manufacturer); - sb.append(SEPARATOR); - } - - if (model != null) { - sb.append(model); - sb.append(SEPARATOR); - } - - sb.append(getSerialNumber()); - return sb.toString(); - } - } - - private String cleanupStringForDisplay(String s) { - if (s == null) { - return null; - } - - StringBuilder sb = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - - if (Character.isLetterOrDigit(c)) { - sb.append(Character.toLowerCase(c)); - } else { - sb.append('_'); - } - } - - return sb.toString(); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getState() - */ - @Override - public DeviceState getState() { - return mState; - } - - /** - * Changes the state of the device. - */ - void setState(DeviceState state) { - mState = state; - } - - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getProperties() - */ - @Override - public Map<String, String> getProperties() { - return Collections.unmodifiableMap(mProperties); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getPropertyCount() - */ - @Override - public int getPropertyCount() { - return mProperties.size(); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getProperty(java.lang.String) - */ - @Override - public String getProperty(String name) { - return mProperties.get(name); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean arePropertiesSet() { - return mArePropertiesSet; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPropertyCacheOrSync(String name) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { - if (mArePropertiesSet) { - return getProperty(name); - } else { - return getPropertySync(name); - } - } - - /** - * {@inheritDoc} - */ - @Override - public String getPropertySync(String name) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { - CollectingOutputReceiver receiver = new CollectingOutputReceiver(); - executeShellCommand(String.format("getprop '%s'", name), receiver, GETPROP_TIMEOUT); - String value = receiver.getOutput().trim(); - if (value.isEmpty()) { - return null; - } - return value; - } - - @Override - public String getMountPoint(String name) { - return mMountPoints.get(name); - } - - - @Override - public String toString() { - return mSerialNumber; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isOnline() - */ - @Override - public boolean isOnline() { - return mState == DeviceState.ONLINE; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isEmulator() - */ - @Override - public boolean isEmulator() { - return mSerialNumber.matches(RE_EMULATOR_SN); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isOffline() - */ - @Override - public boolean isOffline() { - return mState == DeviceState.OFFLINE; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isBootLoader() - */ - @Override - public boolean isBootLoader() { - return mState == DeviceState.BOOTLOADER; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#hasClients() - */ - @Override - public boolean hasClients() { - return mClients.size() > 0; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClients() - */ - @Override - public Client[] getClients() { - synchronized (mClients) { - return mClients.toArray(new Client[mClients.size()]); - } - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClient(java.lang.String) - */ - @Override - public Client getClient(String applicationName) { - synchronized (mClients) { - for (Client c : mClients) { - if (applicationName.equals(c.getClientData().getClientDescription())) { - return c; - } - } - - } - - return null; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getSyncService() - */ - @Override - public SyncService getSyncService() - throws TimeoutException, AdbCommandRejectedException, IOException { - SyncService syncService = new SyncService(AndroidDebugBridge.getSocketAddress(), this); - if (syncService.openSync()) { - return syncService; - } - - return null; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getFileListingService() - */ - @Override - public FileListingService getFileListingService() { - return new FileListingService(this); - } - - @Override - public RawImage getScreenshot() - throws TimeoutException, AdbCommandRejectedException, IOException { - return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this); - } - - @Override - public void executeShellCommand(String command, IShellOutputReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, - receiver, DdmPreferences.getTimeOut()); - } - - @Override - public void executeShellCommand(String command, IShellOutputReceiver receiver, - int maxTimeToOutputResponse) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, - receiver, maxTimeToOutputResponse); - } - - @Override - public void runEventLogService(LogReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver); - } - - @Override - public void runLogService(String logname, LogReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver); - } - - @Override - public void createForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, - String.format("tcp:%d", localPort), //$NON-NLS-1$ - String.format("tcp:%d", remotePort)); //$NON-NLS-1$ - } - - @Override - public void createForward(int localPort, String remoteSocketName, - DeviceUnixSocketNamespace namespace) throws TimeoutException, - AdbCommandRejectedException, IOException { - AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, - String.format("tcp:%d", localPort), //$NON-NLS-1$ - String.format("%s:%s", namespace.getType(), remoteSocketName)); //$NON-NLS-1$ - } - - @Override - public void removeForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, - String.format("tcp:%d", localPort), //$NON-NLS-1$ - String.format("tcp:%d", remotePort)); //$NON-NLS-1$ - } - - @Override - public void removeForward(int localPort, String remoteSocketName, - DeviceUnixSocketNamespace namespace) throws TimeoutException, - AdbCommandRejectedException, IOException { - AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, - String.format("tcp:%d", localPort), //$NON-NLS-1$ - String.format("%s:%s", namespace.getType(), remoteSocketName)); //$NON-NLS-1$ - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClientName(int) - */ - @Override - public String getClientName(int pid) { - synchronized (mClients) { - for (Client c : mClients) { - if (c.getClientData().getPid() == pid) { - return c.getClientData().getClientDescription(); - } - } - } - - return null; - } - - - Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) { - mMonitor = monitor; - mSerialNumber = serialNumber; - mState = deviceState; - } - - DeviceMonitor getMonitor() { - return mMonitor; - } - - void addClient(Client client) { - synchronized (mClients) { - mClients.add(client); - } - } - - List<Client> getClientList() { - return mClients; - } - - boolean hasClient(int pid) { - synchronized (mClients) { - for (Client client : mClients) { - if (client.getClientData().getPid() == pid) { - return true; - } - } - } - - return false; - } - - void clearClientList() { - synchronized (mClients) { - mClients.clear(); - } - } - - /** - * Sets the client monitoring socket. - * @param socketChannel the sockets - */ - void setClientMonitoringSocket(SocketChannel socketChannel) { - mSocketChannel = socketChannel; - } - - /** - * Returns the client monitoring socket. - */ - SocketChannel getClientMonitoringSocket() { - return mSocketChannel; - } - - /** - * Removes a {@link Client} from the list. - * @param client the client to remove. - * @param notify Whether or not to notify the listeners of a change. - */ - void removeClient(Client client, boolean notify) { - mMonitor.addPortToAvailableList(client.getDebuggerListenPort()); - synchronized (mClients) { - mClients.remove(client); - } - if (notify) { - mMonitor.getServer().deviceChanged(this, CHANGE_CLIENT_LIST); - } - } - - void update(int changeMask) { - if ((changeMask & CHANGE_BUILD_INFO) != 0) { - mArePropertiesSet = true; - } - mMonitor.getServer().deviceChanged(this, changeMask); - } - - void update(Client client, int changeMask) { - mMonitor.getServer().clientChanged(client, changeMask); - } - - void addProperty(String label, String value) { - mProperties.put(label, value); - } - - void setMountingPoint(String name, String value) { - mMountPoints.put(name, value); - } - - @Override - public void pushFile(String local, String remote) - throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { - SyncService sync = null; - try { - String targetFileName = getFileName(local); - - Log.d(targetFileName, String.format("Uploading %1$s onto device '%2$s'", - targetFileName, getSerialNumber())); - - sync = getSyncService(); - if (sync != null) { - String message = String.format("Uploading file onto device '%1$s'", - getSerialNumber()); - Log.d(LOG_TAG, message); - sync.pushFile(local, remote, SyncService.getNullProgressMonitor()); - } else { - throw new IOException("Unable to open sync connection!"); - } - } catch (TimeoutException e) { - Log.e(LOG_TAG, "Error during Sync: timeout."); - throw e; - - } catch (SyncException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } catch (IOException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } finally { - if (sync != null) { - sync.close(); - } - } - } - - @Override - public void pullFile(String remote, String local) - throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { - SyncService sync = null; - try { - String targetFileName = getFileName(remote); - - Log.d(targetFileName, String.format("Downloading %1$s from device '%2$s'", - targetFileName, getSerialNumber())); - - sync = getSyncService(); - if (sync != null) { - String message = String.format("Downloding file from device '%1$s'", - getSerialNumber()); - Log.d(LOG_TAG, message); - sync.pullFile(remote, local, SyncService.getNullProgressMonitor()); - } else { - throw new IOException("Unable to open sync connection!"); - } - } catch (TimeoutException e) { - Log.e(LOG_TAG, "Error during Sync: timeout."); - throw e; - - } catch (SyncException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } catch (IOException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } finally { - if (sync != null) { - sync.close(); - } - } - } - - @Override - public String installPackage(String packageFilePath, boolean reinstall, String... extraArgs) - throws InstallException { - try { - String remoteFilePath = syncPackageToDevice(packageFilePath); - String result = installRemotePackage(remoteFilePath, reinstall, extraArgs); - removeRemotePackage(remoteFilePath); - return result; - } catch (IOException e) { - throw new InstallException(e); - } catch (AdbCommandRejectedException e) { - throw new InstallException(e); - } catch (TimeoutException e) { - throw new InstallException(e); - } catch (SyncException e) { - throw new InstallException(e); - } - } - - @Override - public String syncPackageToDevice(String localFilePath) - throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { - SyncService sync = null; - try { - String packageFileName = getFileName(localFilePath); - String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$ - - Log.d(packageFileName, String.format("Uploading %1$s onto device '%2$s'", - packageFileName, getSerialNumber())); - - sync = getSyncService(); - if (sync != null) { - String message = String.format("Uploading file onto device '%1$s'", - getSerialNumber()); - Log.d(LOG_TAG, message); - sync.pushFile(localFilePath, remoteFilePath, SyncService.getNullProgressMonitor()); - } else { - throw new IOException("Unable to open sync connection!"); - } - return remoteFilePath; - } catch (TimeoutException e) { - Log.e(LOG_TAG, "Error during Sync: timeout."); - throw e; - - } catch (SyncException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } catch (IOException e) { - Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); - throw e; - - } finally { - if (sync != null) { - sync.close(); - } - } - } - - /** - * Helper method to retrieve the file name given a local file path - * @param filePath full directory path to file - * @return {@link String} file name - */ - private String getFileName(String filePath) { - return new File(filePath).getName(); - } - - @Override - public String installRemotePackage(String remoteFilePath, boolean reinstall, - String... extraArgs) throws InstallException { - try { - InstallReceiver receiver = new InstallReceiver(); - StringBuilder optionString = new StringBuilder(); - if (reinstall) { - optionString.append("-r "); - } - for (String arg : extraArgs) { - optionString.append(arg); - optionString.append(' '); - } - String cmd = String.format("pm install %1$s \"%2$s\"", optionString.toString(), - remoteFilePath); - executeShellCommand(cmd, receiver, INSTALL_TIMEOUT); - return receiver.getErrorMessage(); - } catch (TimeoutException e) { - throw new InstallException(e); - } catch (AdbCommandRejectedException e) { - throw new InstallException(e); - } catch (ShellCommandUnresponsiveException e) { - throw new InstallException(e); - } catch (IOException e) { - throw new InstallException(e); - } - } - - @Override - public void removeRemotePackage(String remoteFilePath) throws InstallException { - try { - executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver(), INSTALL_TIMEOUT); - } catch (IOException e) { - throw new InstallException(e); - } catch (TimeoutException e) { - throw new InstallException(e); - } catch (AdbCommandRejectedException e) { - throw new InstallException(e); - } catch (ShellCommandUnresponsiveException e) { - throw new InstallException(e); - } - } - - @Override - public String uninstallPackage(String packageName) throws InstallException { - try { - InstallReceiver receiver = new InstallReceiver(); - executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT); - return receiver.getErrorMessage(); - } catch (TimeoutException e) { - throw new InstallException(e); - } catch (AdbCommandRejectedException e) { - throw new InstallException(e); - } catch (ShellCommandUnresponsiveException e) { - throw new InstallException(e); - } catch (IOException e) { - throw new InstallException(e); - } - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#reboot() - */ - @Override - public void reboot(String into) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this); - } - - @Override - public Integer getBatteryLevel() throws TimeoutException, AdbCommandRejectedException, - IOException, ShellCommandUnresponsiveException { - // use default of 5 minutes - return getBatteryLevel(5 * 60 * 1000); - } - - @Override - public Integer getBatteryLevel(long freshnessMs) throws TimeoutException, - AdbCommandRejectedException, IOException, ShellCommandUnresponsiveException { - if (mLastBatteryLevel != null - && mLastBatteryCheckTime > (System.currentTimeMillis() - freshnessMs)) { - return mLastBatteryLevel; - } - BatteryReceiver receiver = new BatteryReceiver(); - executeShellCommand("dumpsys battery", receiver, BATTERY_TIMEOUT); - mLastBatteryLevel = receiver.getBatteryLevel(); - mLastBatteryCheckTime = System.currentTimeMillis(); - return mLastBatteryLevel; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java deleted file mode 100644 index f70627c..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java +++ /dev/null @@ -1,947 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.AdbHelper.AdbResponse; -import com.android.ddmlib.ClientData.DebuggerStatus; -import com.android.ddmlib.DebugPortManager.IDebugPortProvider; -import com.android.ddmlib.IDevice.DeviceState; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A Device monitor. This connects to the Android Debug Bridge and get device and - * debuggable process information from it. - */ -final class DeviceMonitor { - private byte[] mLengthBuffer = new byte[4]; - private byte[] mLengthBuffer2 = new byte[4]; - - private boolean mQuit = false; - - private AndroidDebugBridge mServer; - - private SocketChannel mMainAdbConnection = null; - private boolean mMonitoring = false; - private int mConnectionAttempt = 0; - private int mRestartAttemptCount = 0; - private boolean mInitialDeviceListDone = false; - - private Selector mSelector; - - private final ArrayList<Device> mDevices = new ArrayList<Device>(); - - private final ArrayList<Integer> mDebuggerPorts = new ArrayList<Integer>(); - - private final HashMap<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>(); - - /** - * Creates a new {@link DeviceMonitor} object and links it to the running - * {@link AndroidDebugBridge} object. - * @param server the running {@link AndroidDebugBridge}. - */ - DeviceMonitor(AndroidDebugBridge server) { - mServer = server; - - mDebuggerPorts.add(DdmPreferences.getDebugPortBase()); - } - - /** - * Starts the monitoring. - */ - void start() { - new Thread("Device List Monitor") { //$NON-NLS-1$ - @Override - public void run() { - deviceMonitorLoop(); - } - }.start(); - } - - /** - * Stops the monitoring. - */ - void stop() { - mQuit = true; - - // wakeup the main loop thread by closing the main connection to adb. - try { - if (mMainAdbConnection != null) { - mMainAdbConnection.close(); - } - } catch (IOException e1) { - } - - // wake up the secondary loop by closing the selector. - if (mSelector != null) { - mSelector.wakeup(); - } - } - - - - /** - * Returns if the monitor is currently connected to the debug bridge server. - * @return - */ - boolean isMonitoring() { - return mMonitoring; - } - - int getConnectionAttemptCount() { - return mConnectionAttempt; - } - - int getRestartAttemptCount() { - return mRestartAttemptCount; - } - - /** - * Returns the devices. - */ - Device[] getDevices() { - synchronized (mDevices) { - return mDevices.toArray(new Device[mDevices.size()]); - } - } - - boolean hasInitialDeviceList() { - return mInitialDeviceListDone; - } - - AndroidDebugBridge getServer() { - return mServer; - } - - void addClientToDropAndReopen(Client client, int port) { - synchronized (mClientsToReopen) { - Log.d("DeviceMonitor", - "Adding " + client + " to list of client to reopen (" + port +")."); - if (mClientsToReopen.get(client) == null) { - mClientsToReopen.put(client, port); - } - } - mSelector.wakeup(); - } - - /** - * Monitors the devices. This connects to the Debug Bridge - */ - private void deviceMonitorLoop() { - do { - try { - if (mMainAdbConnection == null) { - Log.d("DeviceMonitor", "Opening adb connection"); - mMainAdbConnection = openAdbConnection(); - if (mMainAdbConnection == null) { - mConnectionAttempt++; - Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt); - if (mConnectionAttempt > 10) { - if (mServer.startAdb() == false) { - mRestartAttemptCount++; - Log.e("DeviceMonitor", - "adb restart attempts: " + mRestartAttemptCount); - } else { - mRestartAttemptCount = 0; - } - } - waitABit(); - } else { - Log.d("DeviceMonitor", "Connected to adb for device monitoring"); - mConnectionAttempt = 0; - } - } - - if (mMainAdbConnection != null && mMonitoring == false) { - mMonitoring = sendDeviceListMonitoringRequest(); - } - - if (mMonitoring) { - // read the length of the incoming message - int length = readLength(mMainAdbConnection, mLengthBuffer); - - if (length >= 0) { - // read the incoming message - processIncomingDeviceData(length); - - // flag the fact that we have build the list at least once. - mInitialDeviceListDone = true; - } - } - } 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) { - 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; - - // remove all devices from list - // 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()) { - synchronized (mDevices) { - for (int n = mDevices.size() - 1; n >= 0; n--) { - Device device = mDevices.get(0); - removeDevice(device); - mServer.deviceDisconnected(device); - } - } - } - } - } - } - - /** - * Sleeps for a little bit. - */ - private void waitABit() { - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - } - } - - /** - * Attempts to connect to the debug bridge server. - * @return a connect socket if success, null otherwise - */ - private SocketChannel openAdbConnection() { - Log.d("DeviceMonitor", "Connecting to adb for Device List Monitoring..."); - - SocketChannel adbChannel = null; - try { - adbChannel = SocketChannel.open(AndroidDebugBridge.getSocketAddress()); - adbChannel.socket().setTcpNoDelay(true); - } catch (IOException e) { - } - - return adbChannel; - } - - /** - * - * @return - * @throws IOException - */ - private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException { - byte[] request = AdbHelper.formAdbRequest("host:track-devices"); //$NON-NLS-1$ - - try { - AdbHelper.write(mMainAdbConnection, request); - - AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection, - false /* readDiagString */); - - 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; - } - } - - /** - * Processes an incoming device message from the socket - * @param socket - * @param length - * @throws IOException - */ - private void processIncomingDeviceData(int length) throws IOException { - ArrayList<Device> list = new ArrayList<Device>(); - - if (length > 0) { - byte[] buffer = new byte[length]; - String result = read(mMainAdbConnection, buffer); - - String[] devices = result.split("\n"); //$NON-NLS-1$ - - for (String d : devices) { - String[] param = d.split("\t"); //$NON-NLS-1$ - if (param.length == 2) { - // new adb uses only serial numbers to identify devices - Device device = new Device(this, param[0] /*serialnumber*/, - DeviceState.getState(param[1])); - - //add the device to the list - list.add(device); - } - } - } - - // now merge the new devices with the old ones. - updateDevices(list); - } - - /** - * Updates the device list with the new items received from the monitoring service. - */ - private void updateDevices(ArrayList<Device> newList) { - // 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 - // there is - // (mostly state change, if the device becomes ready, we query for build info). - // We also remove the device from the new list to mark it as "processed" - // * if we do not find it, we remove it from the current list. - // Once this is done, the new list contains device we aren't monitoring yet, so we - // add them to the list, and start monitoring them. - - for (int d = 0 ; d < mDevices.size() ;) { - Device device = mDevices.get(d); - - // look for a similar device in the new list. - int count = newList.size(); - boolean foundMatch = false; - for (int dd = 0 ; dd < count ; dd++) { - Device newDevice = newList.get(dd); - // see if it matches in id and serial number. - if (newDevice.getSerialNumber().equals(device.getSerialNumber())) { - foundMatch = true; - - // update the state if needed. - if (device.getState() != newDevice.getState()) { - device.setState(newDevice.getState()); - device.update(Device.CHANGE_STATE); - - // if the device just got ready/online, we need to start - // monitoring it. - if (device.isOnline()) { - if (AndroidDebugBridge.getClientSupport() == true) { - if (startMonitoringDevice(device) == false) { - Log.e("DeviceMonitor", - "Failed to start monitoring " - + device.getSerialNumber()); - } - } - - if (device.getPropertyCount() == 0) { - devicesToQuery.add(device); - } - } - } - - // remove the new device from the list since it's been used - newList.remove(dd); - break; - } - } - - if (foundMatch == false) { - // the device is gone, we need to remove it, and keep current index - // to process the next one. - removeDevice(device); - mServer.deviceDisconnected(device); - } else { - // process the next one - d++; - } - } - - // at this point we should still have some new devices in newList, so we - // process them. - for (Device newDevice : newList) { - // add them to the list - mDevices.add(newDevice); - mServer.deviceConnected(newDevice); - - // start monitoring them. - if (AndroidDebugBridge.getClientSupport() == true) { - if (newDevice.isOnline()) { - startMonitoringDevice(newDevice); - } - } - - // look for their build info. - if (newDevice.isOnline()) { - devicesToQuery.add(newDevice); - } - } - } - - // query the new devices for info. - for (Device d : devicesToQuery) { - queryNewDeviceForInfo(d); - } - } - newList.clear(); - } - - private void removeDevice(Device device) { - device.clearClientList(); - mDevices.remove(device); - - SocketChannel channel = device.getClientMonitoringSocket(); - if (channel != null) { - try { - channel.close(); - } catch (IOException e) { - // doesn't really matter if the close fails. - } - } - } - - /** - * Queries a device for its build info. - * @param device the device to query. - */ - private void queryNewDeviceForInfo(Device device) { - // TODO: do this in a separate thread. - try { - // first get the list of properties. - device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND, - new GetPropReceiver(device)); - - queryNewDeviceForMountingPoint(device, IDevice.MNT_EXTERNAL_STORAGE); - queryNewDeviceForMountingPoint(device, IDevice.MNT_DATA); - queryNewDeviceForMountingPoint(device, IDevice.MNT_ROOT); - - // now get the emulator Virtual Device name (if applicable). - if (device.isEmulator()) { - EmulatorConsole console = EmulatorConsole.getConsole(device); - if (console != null) { - 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) { - Log.w("DeviceMonitor", String.format( - "IO Error getting info for device %s", - device.getSerialNumber())); - } - } - - private void queryNewDeviceForMountingPoint(final Device device, final String name) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - device.executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$ - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.length() > 0) { - // this should be the only one. - device.setMountingPoint(name, line); - } - } - } - }); - } - - /** - * Starts a monitoring service for a device. - * @param device the device to monitor. - * @return true if success. - */ - private boolean startMonitoringDevice(Device device) { - SocketChannel socketChannel = openAdbConnection(); - - if (socketChannel != null) { - try { - boolean result = sendDeviceMonitoringRequest(socketChannel, device); - if (result) { - - if (mSelector == null) { - startDeviceMonitorThread(); - } - - device.setClientMonitoringSocket(socketChannel); - - synchronized (mDevices) { - // always wakeup before doing the register. The synchronized block - // ensure that the selector won't select() before the end of this block. - // @see deviceClientMonitorLoop - mSelector.wakeup(); - - socketChannel.configureBlocking(false); - socketChannel.register(mSelector, SelectionKey.OP_READ, device); - } - - 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. - 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 + "' : " + e.getMessage()); - } - } - - return false; - } - - private void startDeviceMonitorThread() throws IOException { - mSelector = Selector.open(); - new Thread("Device Client Monitor") { //$NON-NLS-1$ - @Override - public void run() { - deviceClientMonitorLoop(); - } - }.start(); - } - - private void deviceClientMonitorLoop() { - do { - try { - // This synchronized block stops us from doing the select() if a new - // Device is being added. - // @see startMonitoringDevice() - synchronized (mDevices) { - } - - int count = mSelector.select(); - - if (mQuit) { - return; - } - - synchronized (mClientsToReopen) { - if (mClientsToReopen.size() > 0) { - Set<Client> clients = mClientsToReopen.keySet(); - MonitorThread monitorThread = MonitorThread.getInstance(); - - for (Client client : clients) { - Device device = client.getDeviceImpl(); - int pid = client.getClientData().getPid(); - - monitorThread.dropClient(client, false /* notify */); - - // This is kinda bad, but if we don't wait a bit, the client - // will never answer the second handshake! - waitABit(); - - int port = mClientsToReopen.get(client); - - if (port == IDebugPortProvider.NO_STATIC_PORT) { - port = getNextDebuggerPort(); - } - Log.d("DeviceMonitor", "Reopening " + client); - openClient(device, pid, port, monitorThread); - device.update(Device.CHANGE_CLIENT_LIST); - } - - mClientsToReopen.clear(); - } - } - - if (count == 0) { - continue; - } - - Set<SelectionKey> keys = mSelector.selectedKeys(); - Iterator<SelectionKey> iter = keys.iterator(); - - while (iter.hasNext()) { - SelectionKey key = iter.next(); - iter.remove(); - - if (key.isValid() && key.isReadable()) { - Object attachment = key.attachment(); - - if (attachment instanceof Device) { - Device device = (Device)attachment; - - SocketChannel socket = device.getClientMonitoringSocket(); - - if (socket != null) { - try { - int length = readLength(socket, mLengthBuffer2); - - processIncomingJdwpData(device, socket, length); - } catch (IOException ioe) { - Log.d("DeviceMonitor", - "Error reading jdwp list: " + ioe.getMessage()); - socket.close(); - - // restart the monitoring of that device - synchronized (mDevices) { - if (mDevices.contains(device)) { - Log.d("DeviceMonitor", - "Restarting monitoring service for " + device); - startMonitoringDevice(device); - } - } - } - } - } - } - } - } catch (IOException e) { - if (mQuit == false) { - - } - } - - } while (mQuit == false); - } - - private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device) - throws TimeoutException, AdbCommandRejectedException, IOException { - - try { - AdbHelper.setDevice(socket, device); - - byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$ - - AdbHelper.write(socket, request); - - AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */); - - 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; - } - } - - private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length) - throws IOException { - - // This methods reads @length bytes from the @monitorSocket channel. - // These bytes correspond to the pids of the current set of processes on the device. - // It takes this set of pids and compares them with the existing set of clients - // for the device. Clients that correspond to pids that are not alive anymore are - // dropped, and new clients are created for pids that don't have a corresponding Client. - - if (length >= 0) { - // array for the current pids. - Set<Integer> newPids = new HashSet<Integer>(); - - // get the string data if there are any - if (length > 0) { - byte[] buffer = new byte[length]; - String result = read(monitorSocket, buffer); - - // split each line in its own list and create an array of integer pid - String[] pids = result.split("\n"); //$NON-NLS-1$ - - for (String pid : pids) { - try { - newPids.add(Integer.valueOf(pid)); - } catch (NumberFormatException nfe) { - // looks like this pid is not really a number. Lets ignore it. - continue; - } - } - } - - MonitorThread monitorThread = MonitorThread.getInstance(); - - List<Client> clients = device.getClientList(); - Map<Integer, Client> existingClients = new HashMap<Integer, Client>(); - - synchronized (clients) { - for (Client c : clients) { - existingClients.put( - Integer.valueOf(c.getClientData().getPid()), - c); - } - } - - Set<Client> clientsToRemove = new HashSet<Client>(); - for (Integer pid : existingClients.keySet()) { - if (!newPids.contains(pid)) { - clientsToRemove.add(existingClients.get(pid)); - } - } - - Set<Integer> pidsToAdd = new HashSet<Integer>(newPids); - pidsToAdd.removeAll(existingClients.keySet()); - - monitorThread.dropClients(clientsToRemove, false); - - // at this point whatever pid is left in the list needs to be converted into Clients. - for (int newPid : pidsToAdd) { - openClient(device, newPid, getNextDebuggerPort(), monitorThread); - } - - if (pidsToAdd.size() > 0 || clientsToRemove.size() > 0) { - mServer.deviceChanged(device, Device.CHANGE_CLIENT_LIST); - } - } - } - - /** - * Opens and creates a new client. - * @return - */ - private void openClient(Device device, int pid, int port, MonitorThread monitorThread) { - - SocketChannel clientSocket; - try { - clientSocket = AdbHelper.createPassThroughConnection( - AndroidDebugBridge.getSocketAddress(), device, pid); - - // required for Selector - clientSocket.configureBlocking(false); - } 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()); - return ; - } - - createClient(device, pid, clientSocket, port, monitorThread); - } - - /** - * Creates a client and register it to the monitor thread - * @param device - * @param pid - * @param socket - * @param debuggerPort the debugger port. - * @param monitorThread the {@link MonitorThread} object. - */ - private void createClient(Device device, int pid, SocketChannel socket, int debuggerPort, - MonitorThread monitorThread) { - - /* - * Successfully connected to something. Create a Client object, add - * it to the list, and initiate the JDWP handshake. - */ - - Client client = new Client(device, socket, pid); - - if (client.sendHandshake()) { - try { - if (AndroidDebugBridge.getClientSupport()) { - client.listenForDebugger(debuggerPort); - } - } catch (IOException ioe) { - client.getClientData().setDebuggerConnectionStatus(DebuggerStatus.ERROR); - Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger"); - // oh well - } - - client.requestAllocationStatus(); - } else { - Log.e("ddms", "Handshake with " + client + " failed!"); - /* - * The handshake send failed. We could remove it now, but if the - * failure is "permanent" we'll just keep banging on it and - * getting the same result. Keep it in the list with its "error" - * state so we don't try to reopen it. - */ - } - - if (client.isValid()) { - device.addClient(client); - monitorThread.addClient(client); - } else { - client = null; - } - } - - private int getNextDebuggerPort() { - // get the first port and remove it - synchronized (mDebuggerPorts) { - if (mDebuggerPorts.size() > 0) { - int port = mDebuggerPorts.get(0); - - // remove it. - mDebuggerPorts.remove(0); - - // if there's nothing left, add the next port to the list - if (mDebuggerPorts.size() == 0) { - mDebuggerPorts.add(port+1); - } - - return port; - } - } - - return -1; - } - - void addPortToAvailableList(int port) { - if (port > 0) { - synchronized (mDebuggerPorts) { - // because there could be case where clients are closed twice, we have to make - // sure the port number is not already in the list. - if (mDebuggerPorts.indexOf(port) == -1) { - // add the port to the list while keeping it sorted. It's not like there's - // going to be tons of objects so we do it linearly. - int count = mDebuggerPorts.size(); - for (int i = 0 ; i < count ; i++) { - if (port < mDebuggerPorts.get(i)) { - mDebuggerPorts.add(i, port); - break; - } - } - // TODO: check if we can compact the end of the list. - } - } - } - } - - /** - * Reads the length of the next message from a socket. - * @param socket The {@link SocketChannel} to read from. - * @return the length, or 0 (zero) if no data is available from the socket. - * @throws IOException if the connection failed. - */ - private int readLength(SocketChannel socket, byte[] buffer) throws IOException { - String msg = read(socket, buffer); - - if (msg != null) { - try { - return Integer.parseInt(msg, 16); - } catch (NumberFormatException nfe) { - // we'll throw an exception below. - } - } - - // we receive something we can't read. It's better to reset the connection at this point. - throw new IOException("Unable to read length"); - } - - /** - * Fills a buffer from a socket. - * @param socket - * @param buffer - * @return the content of the buffer as a string, or null if it failed to convert the buffer. - * @throws IOException - */ - private String read(SocketChannel socket, byte[] buffer) throws IOException { - ByteBuffer buf = ByteBuffer.wrap(buffer, 0, buffer.length); - - while (buf.position() != buf.limit()) { - int count; - - count = socket.read(buf); - if (count < 0) { - throw new IOException("EOF"); - } - } - - try { - return new String(buffer, 0, buf.position(), AdbHelper.DEFAULT_ENCODING); - } catch (UnsupportedEncodingException e) { - // we'll return null below. - } - - return null; - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/EmulatorConsole.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/EmulatorConsole.java deleted file mode 100644 index 2f4175f..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/EmulatorConsole.java +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; -import java.security.InvalidParameterException; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Provides control over emulated hardware of the Android emulator. - * <p/>This is basically a wrapper around the command line console normally used with telnet. - *<p/> - * Regarding line termination handling:<br> - * One of the issues is that the telnet protocol <b>requires</b> usage of <code>\r\n</code>. Most - * implementations don't enforce it (the dos one does). In this particular case, this is mostly - * irrelevant since we don't use telnet in Java, but that means we want to make - * sure we use the same line termination than what the console expects. The console - * code removes <code>\r</code> and waits for <code>\n</code>. - * <p/>However this means you <i>may</i> receive <code>\r\n</code> when reading from the console. - * <p/> - * <b>This API will change in the near future.</b> - */ -public final class EmulatorConsole { - - private final static String DEFAULT_ENCODING = "ISO-8859-1"; //$NON-NLS-1$ - - private final static int WAIT_TIME = 5; // spin-wait sleep, in ms - - private final static int STD_TIMEOUT = 5000; // standard delay, in ms - - private final static String HOST = "127.0.0.1"; //$NON-NLS-1$ - - private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$ - private final static String COMMAND_AVD_NAME = "avd name\r\n"; //$NON-NLS-1$ - private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GSM_CANCEL_CALL = "gsm cancel %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GSM_DATA = "gsm data %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GSM_VOICE = "gsm voice %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_SMS_SEND = "sms send %1$s %2$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_NETWORK_STATUS = "network status\r\n"; //$NON-NLS-1$ - private final static String COMMAND_NETWORK_SPEED = "network speed %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_NETWORK_LATENCY = "network delay %1$s\r\n"; //$NON-NLS-1$ - private final static String COMMAND_GPS = "geo fix %1$f %2$f %3$f\r\n"; //$NON-NLS-1$ - - private final static Pattern RE_KO = Pattern.compile("KO:\\s+(.*)"); //$NON-NLS-1$ - - /** - * Array of delay values: no delay, gprs, edge/egprs, umts/3d - */ - public final static int[] MIN_LATENCIES = new int[] { - 0, // No delay - 150, // gprs - 80, // edge/egprs - 35 // umts/3g - }; - - /** - * Array of download speeds: full speed, gsm, hscsd, gprs, edge/egprs, umts/3g, hsdpa. - */ - public final int[] DOWNLOAD_SPEEDS = new int[] { - 0, // full speed - 14400, // gsm - 43200, // hscsd - 80000, // gprs - 236800, // edge/egprs - 1920000, // umts/3g - 14400000 // hsdpa - }; - - /** Arrays of valid network speeds */ - public final static String[] NETWORK_SPEEDS = new String[] { - "full", //$NON-NLS-1$ - "gsm", //$NON-NLS-1$ - "hscsd", //$NON-NLS-1$ - "gprs", //$NON-NLS-1$ - "edge", //$NON-NLS-1$ - "umts", //$NON-NLS-1$ - "hsdpa", //$NON-NLS-1$ - }; - - /** Arrays of valid network latencies */ - public final static String[] NETWORK_LATENCIES = new String[] { - "none", //$NON-NLS-1$ - "gprs", //$NON-NLS-1$ - "edge", //$NON-NLS-1$ - "umts", //$NON-NLS-1$ - }; - - /** Gsm Mode enum. */ - public static enum GsmMode { - UNKNOWN((String)null), - UNREGISTERED(new String[] { "unregistered", "off" }), - HOME(new String[] { "home", "on" }), - ROAMING("roaming"), - SEARCHING("searching"), - DENIED("denied"); - - private final String[] tags; - - GsmMode(String tag) { - if (tag != null) { - this.tags = new String[] { tag }; - } else { - this.tags = new String[0]; - } - } - - GsmMode(String[] tags) { - this.tags = tags; - } - - public static GsmMode getEnum(String tag) { - for (GsmMode mode : values()) { - for (String t : mode.tags) { - if (t.equals(tag)) { - return mode; - } - } - } - return UNKNOWN; - } - - /** - * Returns the first tag of the enum. - */ - public String getTag() { - if (tags.length > 0) { - return tags[0]; - } - return null; - } - } - - public final static String RESULT_OK = null; - - private final static Pattern sEmulatorRegexp = Pattern.compile(Device.RE_EMULATOR_SN); - private final static Pattern sVoiceStatusRegexp = Pattern.compile( - "gsm\\s+voice\\s+state:\\s*([a-z]+)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - private final static Pattern sDataStatusRegexp = Pattern.compile( - "gsm\\s+data\\s+state:\\s*([a-z]+)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - private final static Pattern sDownloadSpeedRegexp = Pattern.compile( - "\\s+download\\s+speed:\\s+(\\d+)\\s+bits.*", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - private final static Pattern sMinLatencyRegexp = Pattern.compile( - "\\s+minimum\\s+latency:\\s+(\\d+)\\s+ms", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - - private final static HashMap<Integer, EmulatorConsole> sEmulators = - new HashMap<Integer, EmulatorConsole>(); - - /** Gsm Status class */ - public static class GsmStatus { - /** Voice status. */ - public GsmMode voice = GsmMode.UNKNOWN; - /** Data status. */ - public GsmMode data = GsmMode.UNKNOWN; - } - - /** Network Status class */ - public static class NetworkStatus { - /** network speed status. This is an index in the {@link #DOWNLOAD_SPEEDS} array. */ - public int speed = -1; - /** network latency status. This is an index in the {@link #MIN_LATENCIES} array. */ - public int latency = -1; - } - - private int mPort; - - private SocketChannel mSocketChannel; - - private byte[] mBuffer = new byte[1024]; - - /** - * Returns an {@link EmulatorConsole} object for the given {@link Device}. This can - * be an already existing console, or a new one if it hadn't been created yet. - * @param d The device that the console links to. - * @return an <code>EmulatorConsole</code> object or <code>null</code> if the connection failed. - */ - public static synchronized EmulatorConsole getConsole(IDevice d) { - // we need to make sure that the device is an emulator - // get the port number. This is the console port. - Integer port = getEmulatorPort(d.getSerialNumber()); - if (port == null) { - return null; - } - - EmulatorConsole console = sEmulators.get(port); - - if (console != null) { - // if the console exist, we ping the emulator to check the connection. - if (console.ping() == false) { - RemoveConsole(console.mPort); - console = null; - } - } - - if (console == null) { - // no console object exists for this port so we create one, and start - // the connection. - console = new EmulatorConsole(port); - if (console.start()) { - sEmulators.put(port, console); - } else { - console = null; - } - } - - return console; - } - - /** - * Return port of emulator given its serial number. - * - * @param serialNumber the emulator's serial number - * @return the integer port or <code>null</code> if it could not be determined - */ - public static Integer getEmulatorPort(String serialNumber) { - Matcher m = sEmulatorRegexp.matcher(serialNumber); - if (m.matches()) { - // get the port number. This is the console port. - int port; - try { - port = Integer.parseInt(m.group(1)); - if (port > 0) { - return port; - } - } catch (NumberFormatException e) { - // looks like we failed to get the port number. This is a bit strange since - // it's coming from a regexp that only accept digit, but we handle the case - // and return null. - } - } - return null; - } - - /** - * Removes the console object associated with a port from the map. - * @param port The port of the console to remove. - */ - private static synchronized void RemoveConsole(int port) { - sEmulators.remove(port); - } - - private EmulatorConsole(int port) { - super(); - mPort = port; - } - - /** - * Starts the connection of the console. - * @return true if success. - */ - private boolean start() { - - InetSocketAddress socketAddr; - try { - InetAddress hostAddr = InetAddress.getByName(HOST); - socketAddr = new InetSocketAddress(hostAddr, mPort); - } catch (UnknownHostException e) { - return false; - } - - try { - mSocketChannel = SocketChannel.open(socketAddr); - } catch (IOException e1) { - return false; - } - - // read some stuff from it - readLines(); - - return true; - } - - /** - * Ping the emulator to check if the connection is still alive. - * @return true if the connection is alive. - */ - private synchronized boolean ping() { - // it looks like we can send stuff, even when the emulator quit, but we can't read - // from the socket. So we check the return of readLines() - if (sendCommand(COMMAND_PING)) { - return readLines() != null; - } - - return false; - } - - /** - * Sends a KILL command to the emulator. - */ - public synchronized void kill() { - if (sendCommand(COMMAND_KILL)) { - RemoveConsole(mPort); - } - } - - public synchronized String getAvdName() { - if (sendCommand(COMMAND_AVD_NAME)) { - String[] result = readLines(); - if (result != null && result.length == 2) { // this should be the name on first line, - // and ok on 2nd line - return result[0]; - } else { - // try to see if there's a message after KO - Matcher m = RE_KO.matcher(result[result.length-1]); - if (m.matches()) { - return m.group(1); - } - } - } - - return null; - } - - /** - * Get the network status of the emulator. - * @return a {@link NetworkStatus} object containing the {@link GsmStatus}, or - * <code>null</code> if the query failed. - */ - public synchronized NetworkStatus getNetworkStatus() { - if (sendCommand(COMMAND_NETWORK_STATUS)) { - /* Result is in the format - Current network status: - download speed: 14400 bits/s (1.8 KB/s) - upload speed: 14400 bits/s (1.8 KB/s) - minimum latency: 0 ms - maximum latency: 0 ms - */ - String[] result = readLines(); - - if (isValid(result)) { - // we only compare agains the min latency and the download speed - // let's not rely on the order of the output, and simply loop through - // the line testing the regexp. - NetworkStatus status = new NetworkStatus(); - for (String line : result) { - Matcher m = sDownloadSpeedRegexp.matcher(line); - if (m.matches()) { - // get the string value - String value = m.group(1); - - // get the index from the list - status.speed = getSpeedIndex(value); - - // move on to next line. - continue; - } - - m = sMinLatencyRegexp.matcher(line); - if (m.matches()) { - // get the string value - String value = m.group(1); - - // get the index from the list - status.latency = getLatencyIndex(value); - - // move on to next line. - continue; - } - } - - return status; - } - } - - return null; - } - - /** - * Returns the current gsm status of the emulator - * @return a {@link GsmStatus} object containing the gms status, or <code>null</code> - * if the query failed. - */ - public synchronized GsmStatus getGsmStatus() { - if (sendCommand(COMMAND_GSM_STATUS)) { - /* - * result is in the format: - * gsm status - * gsm voice state: home - * gsm data state: home - */ - - String[] result = readLines(); - if (isValid(result)) { - - GsmStatus status = new GsmStatus(); - - // let's not rely on the order of the output, and simply loop through - // the line testing the regexp. - for (String line : result) { - Matcher m = sVoiceStatusRegexp.matcher(line); - if (m.matches()) { - // get the string value - String value = m.group(1); - - // get the index from the list - status.voice = GsmMode.getEnum(value.toLowerCase(Locale.US)); - - // move on to next line. - continue; - } - - m = sDataStatusRegexp.matcher(line); - if (m.matches()) { - // get the string value - String value = m.group(1); - - // get the index from the list - status.data = GsmMode.getEnum(value.toLowerCase(Locale.US)); - - // move on to next line. - continue; - } - } - - return status; - } - } - - return null; - } - - /** - * Sets the GSM voice mode. - * @param mode the {@link GsmMode} value. - * @return RESULT_OK if success, an error String otherwise. - * @throws InvalidParameterException if mode is an invalid value. - */ - public synchronized String setGsmVoiceMode(GsmMode mode) throws InvalidParameterException { - if (mode == GsmMode.UNKNOWN) { - throw new InvalidParameterException(); - } - - String command = String.format(COMMAND_GSM_VOICE, mode.getTag()); - return processCommand(command); - } - - /** - * Sets the GSM data mode. - * @param mode the {@link GsmMode} value - * @return {@link #RESULT_OK} if success, an error String otherwise. - * @throws InvalidParameterException if mode is an invalid value. - */ - public synchronized String setGsmDataMode(GsmMode mode) throws InvalidParameterException { - if (mode == GsmMode.UNKNOWN) { - throw new InvalidParameterException(); - } - - String command = String.format(COMMAND_GSM_DATA, mode.getTag()); - return processCommand(command); - } - - /** - * Initiate an incoming call on the emulator. - * @param number a string representing the calling number. - * @return {@link #RESULT_OK} if success, an error String otherwise. - */ - public synchronized String call(String number) { - String command = String.format(COMMAND_GSM_CALL, number); - return processCommand(command); - } - - /** - * Cancels a current call. - * @param number the number of the call to cancel - * @return {@link #RESULT_OK} if success, an error String otherwise. - */ - public synchronized String cancelCall(String number) { - String command = String.format(COMMAND_GSM_CANCEL_CALL, number); - return processCommand(command); - } - - /** - * Sends an SMS to the emulator - * @param number The sender phone number - * @param message The SMS message. \ characters must be escaped. The carriage return is - * the 2 character sequence {'\', 'n' } - * - * @return {@link #RESULT_OK} if success, an error String otherwise. - */ - public synchronized String sendSms(String number, String message) { - String command = String.format(COMMAND_SMS_SEND, number, message); - return processCommand(command); - } - - /** - * Sets the network speed. - * @param selectionIndex The index in the {@link #NETWORK_SPEEDS} table. - * @return {@link #RESULT_OK} if success, an error String otherwise. - */ - public synchronized String setNetworkSpeed(int selectionIndex) { - String command = String.format(COMMAND_NETWORK_SPEED, NETWORK_SPEEDS[selectionIndex]); - return processCommand(command); - } - - /** - * Sets the network latency. - * @param selectionIndex The index in the {@link #NETWORK_LATENCIES} table. - * @return {@link #RESULT_OK} if success, an error String otherwise. - */ - public synchronized String setNetworkLatency(int selectionIndex) { - String command = String.format(COMMAND_NETWORK_LATENCY, NETWORK_LATENCIES[selectionIndex]); - return processCommand(command); - } - - public synchronized String sendLocation(double longitude, double latitude, double elevation) { - - // need to make sure the string format uses dot and not comma - Formatter formatter = new Formatter(Locale.US); - try { - formatter.format(COMMAND_GPS, longitude, latitude, elevation); - - return processCommand(formatter.toString()); - } finally { - formatter.close(); - } - } - - /** - * Sends a command to the emulator console. - * @param command The command string. <b>MUST BE TERMINATED BY \n</b>. - * @return true if success - */ - private boolean sendCommand(String command) { - boolean result = false; - try { - byte[] bCommand; - try { - bCommand = command.getBytes(DEFAULT_ENCODING); - } catch (UnsupportedEncodingException e) { - // wrong encoding... - return result; - } - - // write the command - AdbHelper.write(mSocketChannel, bCommand, bCommand.length, DdmPreferences.getTimeOut()); - - result = true; - } catch (Exception e) { - return false; - } finally { - if (result == false) { - // FIXME connection failed somehow, we need to disconnect the console. - RemoveConsole(mPort); - } - } - - return result; - } - - /** - * Sends a command to the emulator and parses its answer. - * @param command the command to send. - * @return {@link #RESULT_OK} if success, an error message otherwise. - */ - private String processCommand(String command) { - if (sendCommand(command)) { - String[] result = readLines(); - - if (result != null && result.length > 0) { - Matcher m = RE_KO.matcher(result[result.length-1]); - if (m.matches()) { - return m.group(1); - } - return RESULT_OK; - } - - return "Unable to communicate with the emulator"; - } - - return "Unable to send command to the emulator"; - } - - /** - * Reads line from the console socket. This call is blocking until we read the lines: - * <ul> - * <li>OK\r\n</li> - * <li>KO<msg>\r\n</li> - * </ul> - * @return the array of strings read from the emulator. - */ - private String[] readLines() { - try { - ByteBuffer buf = ByteBuffer.wrap(mBuffer, 0, mBuffer.length); - int numWaits = 0; - boolean stop = false; - - while (buf.position() != buf.limit() && stop == false) { - int count; - - count = mSocketChannel.read(buf); - if (count < 0) { - return null; - } else if (count == 0) { - if (numWaits * WAIT_TIME > STD_TIMEOUT) { - return null; - } - // non-blocking spin - try { - Thread.sleep(WAIT_TIME); - } catch (InterruptedException ie) { - } - numWaits++; - } else { - numWaits = 0; - } - - // check the last few char aren't OK. For a valid message to test - // we need at least 4 bytes (OK/KO + \r\n) - if (buf.position() >= 4) { - int pos = buf.position(); - if (endsWithOK(pos) || lastLineIsKO(pos)) { - stop = true; - } - } - } - - String msg = new String(mBuffer, 0, buf.position(), DEFAULT_ENCODING); - return msg.split("\r\n"); //$NON-NLS-1$ - } catch (IOException e) { - return null; - } - } - - /** - * Returns true if the 4 characters *before* the current position are "OK\r\n" - * @param currentPosition The current position - */ - private boolean endsWithOK(int currentPosition) { - if (mBuffer[currentPosition-1] == '\n' && - mBuffer[currentPosition-2] == '\r' && - mBuffer[currentPosition-3] == 'K' && - mBuffer[currentPosition-4] == 'O') { - return true; - } - - return false; - } - - /** - * Returns true if the last line starts with KO and is also terminated by \r\n - * @param currentPosition the current position - */ - private boolean lastLineIsKO(int currentPosition) { - // first check that the last 2 characters are CRLF - if (mBuffer[currentPosition-1] != '\n' || - mBuffer[currentPosition-2] != '\r') { - return false; - } - - // now loop backward looking for the previous CRLF, or the beginning of the buffer - int i = 0; - for (i = currentPosition-3 ; i >= 0; i--) { - if (mBuffer[i] == '\n') { - // found \n! - if (i > 0 && mBuffer[i-1] == '\r') { - // found \r! - break; - } - } - } - - // here it is either -1 if we reached the start of the buffer without finding - // a CRLF, or the position of \n. So in both case we look at the characters at i+1 and i+2 - if (mBuffer[i+1] == 'K' && mBuffer[i+2] == 'O') { - // found error! - return true; - } - - return false; - } - - /** - * Returns true if the last line of the result does not start with KO - */ - private boolean isValid(String[] result) { - if (result != null && result.length > 0) { - return !(RE_KO.matcher(result[result.length-1]).matches()); - } - return false; - } - - private int getLatencyIndex(String value) { - try { - // get the int value - int latency = Integer.parseInt(value); - - // check for the speed from the index - for (int i = 0 ; i < MIN_LATENCIES.length; i++) { - if (MIN_LATENCIES[i] == latency) { - return i; - } - } - } catch (NumberFormatException e) { - // Do nothing, we'll just return -1. - } - - return -1; - } - - private int getSpeedIndex(String value) { - try { - // get the int value - int speed = Integer.parseInt(value); - - // check for the speed from the index - for (int i = 0 ; i < DOWNLOAD_SPEEDS.length; i++) { - if (DOWNLOAD_SPEEDS[i] == speed) { - return i; - } - } - } catch (NumberFormatException e) { - // Do nothing, we'll just return -1. - } - - return -1; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/FileListingService.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/FileListingService.java deleted file mode 100644 index 97c57b9..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/FileListingService.java +++ /dev/null @@ -1,855 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Provides {@link Device} side file listing service. - * <p/>To get an instance for a known {@link Device}, call {@link Device#getFileListingService()}. - */ -public final class FileListingService { - - /** Pattern to find filenames that match "*.apk" */ - private final static Pattern sApkPattern = - Pattern.compile(".*\\.apk", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - - private final static String PM_FULL_LISTING = "pm list packages -f"; //$NON-NLS-1$ - - /** Pattern to parse the output of the 'pm -lf' command.<br> - * The output format looks like:<br> - * /data/app/myapp.apk=com.mypackage.myapp */ - private final static Pattern sPmPattern = Pattern.compile("^package:(.+?)=(.+)$"); //$NON-NLS-1$ - - /** Top level data folder. */ - public final static String DIRECTORY_DATA = "data"; //$NON-NLS-1$ - /** Top level sdcard folder. */ - public final static String DIRECTORY_SDCARD = "sdcard"; //$NON-NLS-1$ - /** Top level mount folder. */ - public final static String DIRECTORY_MNT = "mnt"; //$NON-NLS-1$ - /** Top level system folder. */ - public final static String DIRECTORY_SYSTEM = "system"; //$NON-NLS-1$ - /** Top level temp folder. */ - public final static String DIRECTORY_TEMP = "tmp"; //$NON-NLS-1$ - /** Application folder. */ - public final static String DIRECTORY_APP = "app"; //$NON-NLS-1$ - - public static final long REFRESH_RATE = 5000L; - /** - * Refresh test has to be slightly lower for precision issue. - */ - static final long REFRESH_TEST = (long)(REFRESH_RATE * .8); - - /** Entry type: File */ - public static final int TYPE_FILE = 0; - /** Entry type: Directory */ - public static final int TYPE_DIRECTORY = 1; - /** Entry type: Directory Link */ - public static final int TYPE_DIRECTORY_LINK = 2; - /** Entry type: Block */ - public static final int TYPE_BLOCK = 3; - /** Entry type: Character */ - public static final int TYPE_CHARACTER = 4; - /** Entry type: Link */ - public static final int TYPE_LINK = 5; - /** Entry type: Socket */ - public static final int TYPE_SOCKET = 6; - /** Entry type: FIFO */ - public static final int TYPE_FIFO = 7; - /** Entry type: Other */ - public static final int TYPE_OTHER = 8; - - /** Device side file separator. */ - public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$ - - private static final String FILE_ROOT = "/"; //$NON-NLS-1$ - - - /** - * Regexp pattern to parse the result from ls. - */ - private static final Pattern LS_L_PATTERN = Pattern.compile( - "^([bcdlsp-][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xstST])\\s+(\\S+)\\s+(\\S+)\\s+" + - "([\\d\\s,]*)\\s+(\\d{4}-\\d\\d-\\d\\d)\\s+(\\d\\d:\\d\\d)\\s+(.*)$"); //$NON-NLS-1$ - - private static final Pattern LS_LD_PATTERN = Pattern.compile( - "d[rwx-]{9}\\s+\\S+\\s+\\S+\\s+[0-9-]{10}\\s+\\d{2}:\\d{2}$"); //$NON-NLS-1$ - - - private Device mDevice; - private FileEntry mRoot; - - private ArrayList<Thread> mThreadList = new ArrayList<Thread>(); - - /** - * Represents an entry in a directory. This can be a file or a directory. - */ - public final static class FileEntry { - /** Pattern to escape filenames for shell command consumption. - * This pattern identifies any special characters that need to be escaped with a - * backslash. */ - private final static Pattern sEscapePattern = Pattern.compile( - "([\\\\()*+?\"'&#/\\s])"); //$NON-NLS-1$ - - /** - * Comparator object for FileEntry - */ - private static Comparator<FileEntry> sEntryComparator = new Comparator<FileEntry>() { - @Override - public int compare(FileEntry o1, FileEntry o2) { - if (o1 instanceof FileEntry && o2 instanceof FileEntry) { - FileEntry fe1 = o1; - FileEntry fe2 = o2; - return fe1.name.compareTo(fe2.name); - } - return 0; - } - }; - - FileEntry parent; - String name; - String info; - String permissions; - String size; - String date; - String time; - String owner; - String group; - int type; - boolean isAppPackage; - - boolean isRoot; - - /** - * Indicates whether the entry content has been fetched yet, or not. - */ - long fetchTime = 0; - - final ArrayList<FileEntry> mChildren = new ArrayList<FileEntry>(); - - /** - * Creates a new file entry. - * @param parent parent entry or null if entry is root - * @param name name of the entry. - * @param type entry type. Can be one of the following: {@link FileListingService#TYPE_FILE}, - * {@link FileListingService#TYPE_DIRECTORY}, {@link FileListingService#TYPE_OTHER}. - */ - private FileEntry(FileEntry parent, String name, int type, boolean isRoot) { - this.parent = parent; - this.name = name; - this.type = type; - this.isRoot = isRoot; - - checkAppPackageStatus(); - } - - /** - * Returns the name of the entry - */ - public String getName() { - return name; - } - - /** - * Returns the size string of the entry, as returned by <code>ls</code>. - */ - public String getSize() { - return size; - } - - /** - * Returns the size of the entry. - */ - public int getSizeValue() { - return Integer.parseInt(size); - } - - /** - * Returns the date string of the entry, as returned by <code>ls</code>. - */ - public String getDate() { - return date; - } - - /** - * Returns the time string of the entry, as returned by <code>ls</code>. - */ - public String getTime() { - return time; - } - - /** - * Returns the permission string of the entry, as returned by <code>ls</code>. - */ - public String getPermissions() { - return permissions; - } - - /** - * Returns the owner string of the entry, as returned by <code>ls</code>. - */ - public String getOwner() { - return owner; - } - - /** - * Returns the group owner of the entry, as returned by <code>ls</code>. - */ - public String getGroup() { - return group; - } - - /** - * Returns the extra info for the entry. - * <p/>For a link, it will be a description of the link. - * <p/>For an application apk file it will be the application package as returned - * by the Package Manager. - */ - public String getInfo() { - return info; - } - - /** - * Return the full path of the entry. - * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator. - */ - public String getFullPath() { - if (isRoot) { - return FILE_ROOT; - } - StringBuilder pathBuilder = new StringBuilder(); - fillPathBuilder(pathBuilder, false); - - return pathBuilder.toString(); - } - - /** - * Return the fully escaped path of the entry. This path is safe to use in a - * shell command line. - * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator - */ - public String getFullEscapedPath() { - StringBuilder pathBuilder = new StringBuilder(); - fillPathBuilder(pathBuilder, true); - - return pathBuilder.toString(); - } - - /** - * Returns the path as a list of segments. - */ - public String[] getPathSegments() { - ArrayList<String> list = new ArrayList<String>(); - fillPathSegments(list); - - return list.toArray(new String[list.size()]); - } - - /** - * Returns the Entry type as an int, which will match one of the TYPE_(...) constants - */ - public int getType() { - return type; - } - - /** - * Sets a new type. - */ - public void setType(int type) { - this.type = type; - } - - /** - * Returns if the entry is a folder or a link to a folder. - */ - public boolean isDirectory() { - return type == TYPE_DIRECTORY || type == TYPE_DIRECTORY_LINK; - } - - /** - * Returns the parent entry. - */ - public FileEntry getParent() { - return parent; - } - - /** - * Returns the cached children of the entry. This returns the cache created from calling - * <code>FileListingService.getChildren()</code>. - */ - public FileEntry[] getCachedChildren() { - return mChildren.toArray(new FileEntry[mChildren.size()]); - } - - /** - * Returns the child {@link FileEntry} matching the name. - * This uses the cached children list. - * @param name the name of the child to return. - * @return the FileEntry matching the name or null. - */ - public FileEntry findChild(String name) { - for (FileEntry entry : mChildren) { - if (entry.name.equals(name)) { - return entry; - } - } - return null; - } - - /** - * Returns whether the entry is the root. - */ - public boolean isRoot() { - return isRoot; - } - - void addChild(FileEntry child) { - mChildren.add(child); - } - - void setChildren(ArrayList<FileEntry> newChildren) { - mChildren.clear(); - mChildren.addAll(newChildren); - } - - boolean needFetch() { - if (fetchTime == 0) { - return true; - } - long current = System.currentTimeMillis(); - if (current-fetchTime > REFRESH_TEST) { - return true; - } - - return false; - } - - /** - * Returns if the entry is a valid application package. - */ - public boolean isApplicationPackage() { - return isAppPackage; - } - - /** - * Returns if the file name is an application package name. - */ - public boolean isAppFileName() { - Matcher m = sApkPattern.matcher(name); - return m.matches(); - } - - /** - * Recursively fills the pathBuilder with the full path - * @param pathBuilder a StringBuilder used to create the path. - * @param escapePath Whether the path need to be escaped for consumption by - * a shell command line. - */ - protected void fillPathBuilder(StringBuilder pathBuilder, boolean escapePath) { - if (isRoot) { - return; - } - - if (parent != null) { - parent.fillPathBuilder(pathBuilder, escapePath); - } - pathBuilder.append(FILE_SEPARATOR); - pathBuilder.append(escapePath ? escape(name) : name); - } - - /** - * Recursively fills the segment list with the full path. - * @param list The list of segments to fill. - */ - protected void fillPathSegments(ArrayList<String> list) { - if (isRoot) { - return; - } - - if (parent != null) { - parent.fillPathSegments(list); - } - - list.add(name); - } - - /** - * Sets the internal app package status flag. This checks whether the entry is in an app - * directory like /data/app or /system/app - */ - private void checkAppPackageStatus() { - isAppPackage = false; - - String[] segments = getPathSegments(); - if (type == TYPE_FILE && segments.length == 3 && isAppFileName()) { - isAppPackage = DIRECTORY_APP.equals(segments[1]) && - (DIRECTORY_SYSTEM.equals(segments[0]) || DIRECTORY_DATA.equals(segments[0])); - } - } - - /** - * Returns an escaped version of the entry name. - * @param entryName - */ - public static String escape(String entryName) { - return sEscapePattern.matcher(entryName).replaceAll("\\\\$1"); //$NON-NLS-1$ - } - } - - private static class LsReceiver extends MultiLineReceiver { - - private ArrayList<FileEntry> mEntryList; - private ArrayList<String> mLinkList; - private FileEntry[] mCurrentChildren; - private FileEntry mParentEntry; - - /** - * Create an ls receiver/parser. - * @param currentChildren The list of current children. To prevent - * collapse during update, reusing the same FileEntry objects for - * files that were already there is paramount. - * @param entryList the list of new children to be filled by the - * receiver. - * @param linkList the list of link path to compute post ls, to figure - * out if the link pointed to a file or to a directory. - */ - public LsReceiver(FileEntry parentEntry, ArrayList<FileEntry> entryList, - ArrayList<String> linkList) { - mParentEntry = parentEntry; - mCurrentChildren = parentEntry.getCachedChildren(); - mEntryList = entryList; - mLinkList = linkList; - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - // no need to handle empty lines. - if (line.length() == 0) { - continue; - } - - // run the line through the regexp - Matcher m = LS_L_PATTERN.matcher(line); - if (m.matches() == false) { - continue; - } - - // get the name - String name = m.group(7); - - // get the rest of the groups - String permissions = m.group(1); - String owner = m.group(2); - String group = m.group(3); - String size = m.group(4); - String date = m.group(5); - String time = m.group(6); - String info = null; - - // and the type - int objectType = TYPE_OTHER; - switch (permissions.charAt(0)) { - case '-' : - objectType = TYPE_FILE; - break; - case 'b' : - objectType = TYPE_BLOCK; - break; - case 'c' : - objectType = TYPE_CHARACTER; - break; - case 'd' : - objectType = TYPE_DIRECTORY; - break; - case 'l' : - objectType = TYPE_LINK; - break; - case 's' : - objectType = TYPE_SOCKET; - break; - case 'p' : - objectType = TYPE_FIFO; - break; - } - - - // now check what we may be linking to - if (objectType == TYPE_LINK) { - String[] segments = name.split("\\s->\\s"); //$NON-NLS-1$ - - // we should have 2 segments - if (segments.length == 2) { - // update the entry name to not contain the link - name = segments[0]; - - // and the link name - info = segments[1]; - - // now get the path to the link - String[] pathSegments = info.split(FILE_SEPARATOR); - if (pathSegments.length == 1) { - // the link is to something in the same directory, - // unless the link is .. - if ("..".equals(pathSegments[0])) { //$NON-NLS-1$ - // set the type and we're done. - objectType = TYPE_DIRECTORY_LINK; - } else { - // either we found the object already - // or we'll find it later. - } - } - } - - // add an arrow in front to specify it's a link. - info = "-> " + info; //$NON-NLS-1$; - } - - // get the entry, either from an existing one, or a new one - FileEntry entry = getExistingEntry(name); - if (entry == null) { - entry = new FileEntry(mParentEntry, name, objectType, false /* isRoot */); - } - - // add some misc info - entry.permissions = permissions; - entry.size = size; - entry.date = date; - entry.time = time; - entry.owner = owner; - entry.group = group; - if (objectType == TYPE_LINK) { - entry.info = info; - } - - mEntryList.add(entry); - } - } - - /** - * Queries for an already existing Entry per name - * @param name the name of the entry - * @return the existing FileEntry or null if no entry with a matching - * name exists. - */ - private FileEntry getExistingEntry(String name) { - for (int i = 0 ; i < mCurrentChildren.length; i++) { - FileEntry e = mCurrentChildren[i]; - - // since we're going to "erase" the one we use, we need to - // check that the item is not null. - if (e != null) { - // compare per name, case-sensitive. - if (name.equals(e.name)) { - // erase from the list - mCurrentChildren[i] = null; - - // and return the object - return e; - } - } - } - - // couldn't find any matching object, return null - return null; - } - - @Override - public boolean isCancelled() { - return false; - } - - /** - * Determine if any symlinks in the <code entries> list are links-to-directories, and if so - * mark them as such. This allows us to traverse them properly later on. - */ - public void finishLinks(IDevice device, ArrayList<FileEntry> entries) - throws TimeoutException, AdbCommandRejectedException, - ShellCommandUnresponsiveException, IOException { - final int[] nLines = {0}; - MultiLineReceiver receiver = new MultiLineReceiver() { - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - Matcher m = LS_LD_PATTERN.matcher(line); - if (m.matches()) { - nLines[0]++; - } - } - } - - @Override - public boolean isCancelled() { - return false; - } - }; - - for (FileEntry entry : entries) { - if (entry.getType() != TYPE_LINK) continue; - - // We simply need to determine whether the referent is a directory or not. - // We do this by running `ls -ld ${link}/`. If the referent exists and is a - // directory, we'll see the normal directory listing. Otherwise, we'll see an - // error of some sort. - nLines[0] = 0; - - final String command = String.format("ls -l -d %s%s", entry.getFullEscapedPath(), - FILE_SEPARATOR); - - device.executeShellCommand(command, receiver); - - if (nLines[0] > 0) { - // We saw lines matching the directory pattern, so it's a directory! - entry.setType(TYPE_DIRECTORY_LINK); - } - } - } - } - - /** - * Classes which implement this interface provide a method that deals with asynchronous - * result from <code>ls</code> command on the device. - * - * @see FileListingService#getChildren(com.android.ddmlib.FileListingService.FileEntry, boolean, com.android.ddmlib.FileListingService.IListingReceiver) - */ - public interface IListingReceiver { - public void setChildren(FileEntry entry, FileEntry[] children); - - public void refreshEntry(FileEntry entry); - } - - /** - * Creates a File Listing Service for a specified {@link Device}. - * @param device The Device the service is connected to. - */ - FileListingService(Device device) { - mDevice = device; - } - - /** - * Returns the root element. - * @return the {@link FileEntry} object representing the root element or - * <code>null</code> if the device is invalid. - */ - public FileEntry getRoot() { - if (mDevice != null) { - if (mRoot == null) { - mRoot = new FileEntry(null /* parent */, "" /* name */, TYPE_DIRECTORY, - true /* isRoot */); - } - - return mRoot; - } - - return null; - } - - /** - * Returns the children of a {@link FileEntry}. - * <p/> - * This method supports a cache mechanism and synchronous and asynchronous modes. - * <p/> - * If <var>receiver</var> is <code>null</code>, the device side <code>ls</code> - * command is done synchronously, and the method will return upon completion of the command.<br> - * If <var>receiver</var> is non <code>null</code>, the command is launched is a separate - * thread and upon completion, the receiver will be notified of the result. - * <p/> - * The result for each <code>ls</code> command is cached in the parent - * <code>FileEntry</code>. <var>useCache</var> allows usage of this cache, but only if the - * cache is valid. The cache is valid only for {@link FileListingService#REFRESH_RATE} ms. - * After that a new <code>ls</code> command is always executed. - * <p/> - * If the cache is valid and <code>useCache == true</code>, the method will always simply - * return the value of the cache, whether a {@link IListingReceiver} has been provided or not. - * - * @param entry The parent entry. - * @param useCache A flag to use the cache or to force a new ls command. - * @param receiver A receiver for asynchronous calls. - * @return The list of children or <code>null</code> for asynchronous calls. - * - * @see FileEntry#getCachedChildren() - */ - public FileEntry[] getChildren(final FileEntry entry, boolean useCache, - final IListingReceiver receiver) { - // first thing we do is check the cache, and if we already have a recent - // enough children list, we just return that. - if (useCache && entry.needFetch() == false) { - return entry.getCachedChildren(); - } - - // if there's no receiver, then this is a synchronous call, and we - // return the result of ls - if (receiver == null) { - doLs(entry); - return entry.getCachedChildren(); - } - - // this is a asynchronous call. - // we launch a thread that will do ls and give the listing - // to the receiver - Thread t = new Thread("ls " + entry.getFullPath()) { //$NON-NLS-1$ - @Override - public void run() { - doLs(entry); - - receiver.setChildren(entry, entry.getCachedChildren()); - - final FileEntry[] children = entry.getCachedChildren(); - if (children.length > 0 && children[0].isApplicationPackage()) { - final HashMap<String, FileEntry> map = new HashMap<String, FileEntry>(); - - for (FileEntry child : children) { - String path = child.getFullPath(); - map.put(path, child); - } - - // call pm. - String command = PM_FULL_LISTING; - try { - mDevice.executeShellCommand(command, new MultiLineReceiver() { - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.length() > 0) { - // get the filepath and package from the line - Matcher m = sPmPattern.matcher(line); - if (m.matches()) { - // get the children with that path - FileEntry entry = map.get(m.group(1)); - if (entry != null) { - entry.info = m.group(2); - receiver.refreshEntry(entry); - } - } - } - } - } - @Override - public boolean isCancelled() { - return false; - } - }); - } catch (Exception e) { - // adb failed somehow, we do nothing. - } - } - - - // if another thread is pending, launch it - synchronized (mThreadList) { - // first remove ourselves from the list - mThreadList.remove(this); - - // then launch the next one if applicable. - if (mThreadList.size() > 0) { - Thread t = mThreadList.get(0); - t.start(); - } - } - } - }; - - // we don't want to run multiple ls on the device at the same time, so we - // store the thread in a list and launch it only if there's no other thread running. - // the thread will launch the next one once it's done. - synchronized (mThreadList) { - // add to the list - mThreadList.add(t); - - // if it's the only one, launch it. - if (mThreadList.size() == 1) { - t.start(); - } - } - - // and we return null. - return null; - } - - /** - * Returns the children of a {@link FileEntry}. - * <p/> - * This method is the explicit synchronous version of - * {@link #getChildren(FileEntry, boolean, IListingReceiver)}. It is roughly equivalent to - * calling - * getChildren(FileEntry, false, null) - * - * @param entry The parent entry. - * @return The list of children - * @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. - */ - public FileEntry[] getChildrenSync(final FileEntry entry) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { - doLsAndThrow(entry); - return entry.getCachedChildren(); - } - - private void doLs(FileEntry entry) { - try { - doLsAndThrow(entry); - } catch (Exception e) { - // do nothing - } - } - - private void doLsAndThrow(FileEntry entry) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { - // create a list that will receive the list of the entries - ArrayList<FileEntry> entryList = new ArrayList<FileEntry>(); - - // create a list that will receive the link to compute post ls; - ArrayList<String> linkList = new ArrayList<String>(); - - try { - // create the command - String command = "ls -l " + entry.getFullEscapedPath(); //$NON-NLS-1$ - if (entry.isDirectory()) { - // If we expect a file to behave like a directory, we should stick a "/" at the end. - // This is a good habit, and is mandatory for symlinks-to-directories, which will - // otherwise behave like symlinks. - command += FILE_SEPARATOR; - } - - // create the receiver object that will parse the result from ls - LsReceiver receiver = new LsReceiver(entry, entryList, linkList); - - // call ls. - mDevice.executeShellCommand(command, receiver); - - // finish the process of the receiver to handle links - receiver.finishLinks(mDevice, entryList); - } finally { - // at this point we need to refresh the viewer - entry.fetchTime = System.currentTimeMillis(); - - // sort the children and set them as the new children - Collections.sort(entryList, FileEntry.sEntryComparator); - entry.setChildren(entryList); - } - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/GetPropReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/GetPropReceiver.java deleted file mode 100644 index 2033f04..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/GetPropReceiver.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2007 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.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A receiver able to parse the result of the execution of - * {@link #GETPROP_COMMAND} on a device. - */ -final class GetPropReceiver extends MultiLineReceiver { - final static String GETPROP_COMMAND = "getprop"; //$NON-NLS-1$ - - private final static Pattern GETPROP_PATTERN = Pattern.compile("^\\[([^]]+)\\]\\:\\s*\\[(.*)\\]$"); //$NON-NLS-1$ - - /** indicates if we need to read the first */ - private Device mDevice = null; - - /** - * Creates the receiver with the device the receiver will modify. - * @param device The device to modify - */ - public GetPropReceiver(Device device) { - mDevice = device; - } - - @Override - public void processNewLines(String[] lines) { - // We receive an array of lines. We're expecting - // to have the build info in the first line, and the build - // date in the 2nd line. There seems to be an empty line - // after all that. - - for (String line : lines) { - if (line.length() == 0 || line.startsWith("#")) { - continue; - } - - Matcher m = GETPROP_PATTERN.matcher(line); - if (m.matches()) { - String label = m.group(1); - String value = m.group(2); - - if (label.length() > 0) { - mDevice.addProperty(label, value); - } - } - } - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void done() { - mDevice.update(Device.CHANGE_BUILD_INFO); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleAppName.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleAppName.java deleted file mode 100644 index da4ade3..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleAppName.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; - -/** - * Handle the "app name" chunk (APNM). - */ -final class HandleAppName extends ChunkHandler { - - public static final int CHUNK_APNM = ChunkHandler.type("APNM"); - - private static final HandleAppName mInst = new HandleAppName(); - - - private HandleAppName() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_APNM, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, - boolean isReply, int msgId) { - - Log.d("ddm-appname", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_APNM) { - assert !isReply; - handleAPNM(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a reply to our APNM message. - */ - private static void handleAPNM(Client client, ByteBuffer data) { - int appNameLen; - String appName; - - appNameLen = data.getInt(); - appName = getString(data, appNameLen); - - // Newer devices send user id in the APNM packet. - int userId = -1; - boolean validUserId = false; - if (data.hasRemaining()) { - try { - userId = data.getInt(); - validUserId = true; - } catch (BufferUnderflowException e) { - // two integers + utf-16 string - int expectedPacketLength = 8 + appNameLen * 2; - - Log.e("ddm-appname", "Insufficient data in APNM chunk to retrieve user id."); - Log.e("ddm-appname", "Actual chunk length: " + data.capacity()); - Log.e("ddm-appname", "Expected chunk length: " + expectedPacketLength); - } - } - - Log.d("ddm-appname", "APNM: app='" + appName + "'"); - - ClientData cd = client.getClientData(); - synchronized (cd) { - cd.setClientDescription(appName); - - if (validUserId) { - cd.setUserId(userId); - } - } - - client = checkDebuggerPortForAppName(client, appName); - - if (client != null) { - client.update(Client.CHANGE_NAME); - } - } - } - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleExit.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleExit.java deleted file mode 100644 index adeedbb..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleExit.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.nio.ByteBuffer; - -/** - * Submit an exit request. - */ -final class HandleExit extends ChunkHandler { - - public static final int CHUNK_EXIT = type("EXIT"); - - private static final HandleExit mInst = new HandleExit(); - - - private HandleExit() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) {} - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - handleUnknownChunk(client, type, data, isReply, msgId); - } - - /** - * Send an EXIT request to the client. - */ - public static void sendEXIT(Client client, int status) - throws IOException - { - ByteBuffer rawBuf = allocBuffer(4); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(status); - - finishChunkPacket(packet, CHUNK_EXIT, buf.position()); - Log.d("ddm-exit", "Sending " + name(CHUNK_EXIT) + ": " + status); - client.sendAndConsume(packet, mInst); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHeap.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHeap.java deleted file mode 100644 index 1761b79..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHeap.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.ClientData.AllocationTrackingStatus; -import com.android.ddmlib.ClientData.IHprofDumpHandler; - -import java.io.IOException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.ArrayList; - -/** - * Handle heap status updates. - */ -final class HandleHeap extends ChunkHandler { - - public static final int CHUNK_HPIF = type("HPIF"); - public static final int CHUNK_HPST = type("HPST"); - public static final int CHUNK_HPEN = type("HPEN"); - public static final int CHUNK_HPSG = type("HPSG"); - public static final int CHUNK_HPGC = type("HPGC"); - public static final int CHUNK_HPDU = type("HPDU"); - public static final int CHUNK_HPDS = type("HPDS"); - public static final int CHUNK_REAE = type("REAE"); - public static final int CHUNK_REAQ = type("REAQ"); - public static final int CHUNK_REAL = type("REAL"); - - // args to sendHPSG - public static final int WHEN_DISABLE = 0; - public static final int WHEN_GC = 1; - public static final int WHAT_MERGE = 0; // merge adjacent objects - public static final int WHAT_OBJ = 1; // keep objects distinct - - // args to sendHPIF - public static final int HPIF_WHEN_NEVER = 0; - public static final int HPIF_WHEN_NOW = 1; - public static final int HPIF_WHEN_NEXT_GC = 2; - public static final int HPIF_WHEN_EVERY_GC = 3; - - private static final HandleHeap mInst = new HandleHeap(); - - private HandleHeap() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_HPIF, mInst); - mt.registerChunkHandler(CHUNK_HPST, mInst); - mt.registerChunkHandler(CHUNK_HPEN, mInst); - mt.registerChunkHandler(CHUNK_HPSG, mInst); - mt.registerChunkHandler(CHUNK_HPDS, mInst); - mt.registerChunkHandler(CHUNK_REAQ, mInst); - mt.registerChunkHandler(CHUNK_REAL, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException { - if (client.isHeapUpdateEnabled()) { - //sendHPSG(client, WHEN_GC, WHAT_MERGE); - sendHPIF(client, HPIF_WHEN_EVERY_GC); - } - } - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - Log.d("ddm-heap", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_HPIF) { - handleHPIF(client, data); - } else if (type == CHUNK_HPST) { - handleHPST(client, data); - } else if (type == CHUNK_HPEN) { - handleHPEN(client, data); - } else if (type == CHUNK_HPSG) { - handleHPSG(client, data); - } else if (type == CHUNK_HPDU) { - handleHPDU(client, data); - } else if (type == CHUNK_HPDS) { - handleHPDS(client, data); - } else if (type == CHUNK_REAQ) { - handleREAQ(client, data); - } else if (type == CHUNK_REAL) { - handleREAL(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a heap info message. - */ - private void handleHPIF(Client client, ByteBuffer data) { - Log.d("ddm-heap", "HPIF!"); - try { - int numHeaps = data.getInt(); - - for (int i = 0; i < numHeaps; i++) { - int heapId = data.getInt(); - @SuppressWarnings("unused") - long timeStamp = data.getLong(); - @SuppressWarnings("unused") - byte reason = data.get(); - long maxHeapSize = (long)data.getInt() & 0x00ffffffff; - long heapSize = (long)data.getInt() & 0x00ffffffff; - long bytesAllocated = (long)data.getInt() & 0x00ffffffff; - long objectsAllocated = (long)data.getInt() & 0x00ffffffff; - - client.getClientData().setHeapInfo(heapId, maxHeapSize, - heapSize, bytesAllocated, objectsAllocated); - client.update(Client.CHANGE_HEAP_DATA); - } - } catch (BufferUnderflowException ex) { - Log.w("ddm-heap", "malformed HPIF chunk from client"); - } - } - - /** - * Send an HPIF (HeaP InFo) request to the client. - */ - public static void sendHPIF(Client client, int when) throws IOException { - ByteBuffer rawBuf = allocBuffer(1); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.put((byte)when); - - finishChunkPacket(packet, CHUNK_HPIF, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_HPIF) + ": when=" + when); - client.sendAndConsume(packet, mInst); - } - - /* - * Handle a heap segment series start message. - */ - private void handleHPST(Client client, ByteBuffer data) { - /* Clear out any data that's sitting around to - * get ready for the chunks that are about to come. - */ -//xxx todo: only clear data that belongs to the heap mentioned in <data>. - client.getClientData().getVmHeapData().clearHeapData(); - } - - /* - * Handle a heap segment series end message. - */ - private void handleHPEN(Client client, ByteBuffer data) { - /* Let the UI know that we've received all of the - * data for this heap. - */ -//xxx todo: only seal data that belongs to the heap mentioned in <data>. - client.getClientData().getVmHeapData().sealHeapData(); - client.update(Client.CHANGE_HEAP_DATA); - } - - /* - * Handle a heap segment message. - */ - private void handleHPSG(Client client, ByteBuffer data) { - byte dataCopy[] = new byte[data.limit()]; - data.rewind(); - data.get(dataCopy); - data = ByteBuffer.wrap(dataCopy); - client.getClientData().getVmHeapData().addHeapData(data); -//xxx todo: add to the heap mentioned in <data> - } - - /** - * Sends an HPSG (HeaP SeGment) request to the client. - */ - public static void sendHPSG(Client client, int when, int what) - throws IOException { - - ByteBuffer rawBuf = allocBuffer(2); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.put((byte)when); - buf.put((byte)what); - - finishChunkPacket(packet, CHUNK_HPSG, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_HPSG) + ": when=" - + when + ", what=" + what); - client.sendAndConsume(packet, mInst); - } - - /** - * Sends an HPGC request to the client. - */ - public static void sendHPGC(Client client) - throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_HPGC, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_HPGC)); - client.sendAndConsume(packet, mInst); - } - - /** - * Sends an HPDU request to the client. - * - * We will get an HPDU response when the heap dump has completed. On - * failure we get a generic failure response. - * - * @param fileName name of output file (on device) - */ - public static void sendHPDU(Client client, String fileName) - throws IOException { - ByteBuffer rawBuf = allocBuffer(4 + fileName.length() * 2); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(fileName.length()); - putString(buf, fileName); - - finishChunkPacket(packet, CHUNK_HPDU, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'"); - client.sendAndConsume(packet, mInst); - client.getClientData().setPendingHprofDump(fileName); - } - - /** - * Sends an HPDS request to the client. - * - * We will get an HPDS response when the heap dump has completed. On - * failure we get a generic failure response. - * - * This is more expensive for the device than HPDU, because the entire - * heap dump is held in RAM instead of spooled out to a temp file. On - * the other hand, permission to write to /sdcard is not required. - * - * @param fileName name of output file (on device) - */ - public static void sendHPDS(Client client) - throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - finishChunkPacket(packet, CHUNK_HPDS, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_HPDS)); - client.sendAndConsume(packet, mInst); - } - - /* - * Handle notification of completion of a HeaP DUmp. - */ - private void handleHPDU(Client client, ByteBuffer data) { - byte result; - - // get the filename and make the client not have pending HPROF dump anymore. - String filename = client.getClientData().getPendingHprofDump(); - client.getClientData().setPendingHprofDump(null); - - // get the dump result - result = data.get(); - - // get the app-level handler for HPROF dump - IHprofDumpHandler handler = ClientData.getHprofDumpHandler(); - if (handler != null) { - if (result == 0) { - handler.onSuccess(filename, client); - - Log.d("ddm-heap", "Heap dump request has finished"); - } else { - handler.onEndFailure(client, null); - Log.w("ddm-heap", "Heap dump request failed (check device log)"); - } - } - } - - /* - * Handle HeaP Dump Streaming response. "data" contains the full - * hprof dump. - */ - private void handleHPDS(Client client, ByteBuffer data) { - IHprofDumpHandler handler = ClientData.getHprofDumpHandler(); - if (handler != null) { - byte[] stuff = new byte[data.capacity()]; - data.get(stuff, 0, stuff.length); - - Log.d("ddm-hprof", "got hprof file, size: " + data.capacity() + " bytes"); - - handler.onSuccess(stuff, client); - } - } - - /** - * Sends a REAE (REcent Allocation Enable) request to the client. - */ - public static void sendREAE(Client client, boolean enable) - throws IOException { - ByteBuffer rawBuf = allocBuffer(1); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.put((byte) (enable ? 1 : 0)); - - finishChunkPacket(packet, CHUNK_REAE, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_REAE) + ": " + enable); - client.sendAndConsume(packet, mInst); - } - - /** - * Sends a REAQ (REcent Allocation Query) request to the client. - */ - public static void sendREAQ(Client client) - throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_REAQ, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_REAQ)); - client.sendAndConsume(packet, mInst); - } - - /** - * Sends a REAL (REcent ALlocation) request to the client. - */ - public static void sendREAL(Client client) - throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_REAL, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_REAL)); - client.sendAndConsume(packet, mInst); - } - - /* - * Handle the response from our REcent Allocation Query message. - */ - private void handleREAQ(Client client, ByteBuffer data) { - boolean enabled; - - enabled = (data.get() != 0); - Log.d("ddm-heap", "REAQ says: enabled=" + enabled); - - client.getClientData().setAllocationStatus(enabled ? - AllocationTrackingStatus.ON : AllocationTrackingStatus.OFF); - client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS); - } - - /** - * Converts a VM class descriptor string ("Landroid/os/Debug;") to - * a dot-notation class name ("android.os.Debug"). - */ - private String descriptorToDot(String str) { - // count the number of arrays. - int array = 0; - while (str.startsWith("[")) { - str = str.substring(1); - array++; - } - - int len = str.length(); - - /* strip off leading 'L' and trailing ';' if appropriate */ - if (len >= 2 && str.charAt(0) == 'L' && str.charAt(len - 1) == ';') { - str = str.substring(1, len-1); - str = str.replace('/', '.'); - } else { - // convert the basic types - if ("C".equals(str)) { - str = "char"; - } else if ("B".equals(str)) { - str = "byte"; - } else if ("Z".equals(str)) { - str = "boolean"; - } else if ("S".equals(str)) { - str = "short"; - } else if ("I".equals(str)) { - str = "int"; - } else if ("J".equals(str)) { - str = "long"; - } else if ("F".equals(str)) { - str = "float"; - } else if ("D".equals(str)) { - str = "double"; - } - } - - // now add the array part - for (int a = 0 ; a < array; a++) { - str = str + "[]"; - } - - return str; - } - - /** - * Reads a string table out of "data". - * - * This is just a serial collection of strings, each of which is a - * four-byte length followed by UTF-16 data. - */ - private void readStringTable(ByteBuffer data, String[] strings) { - int count = strings.length; - int i; - - for (i = 0; i < count; i++) { - int nameLen = data.getInt(); - String descriptor = getString(data, nameLen); - strings[i] = descriptorToDot(descriptor); - } - } - - /* - * Handle a REcent ALlocation response. - * - * Message header (all values big-endian): - * (1b) message header len (to allow future expansion); includes itself - * (1b) entry header len - * (1b) stack frame len - * (2b) number of entries - * (4b) offset to string table from start of message - * (2b) number of class name strings - * (2b) number of method name strings - * (2b) number of source file name strings - * For each entry: - * (4b) total allocation size - * (2b) threadId - * (2b) allocated object's class name index - * (1b) stack depth - * For each stack frame: - * (2b) method's class name - * (2b) method name - * (2b) method source file - * (2b) line number, clipped to 32767; -2 if native; -1 if no source - * (xb) class name strings - * (xb) method name strings - * (xb) source file strings - * - * As with other DDM traffic, strings are sent as a 4-byte length - * followed by UTF-16 data. - */ - private void handleREAL(Client client, ByteBuffer data) { - Log.e("ddm-heap", "*** Received " + name(CHUNK_REAL)); - int messageHdrLen, entryHdrLen, stackFrameLen; - int numEntries, offsetToStrings; - int numClassNames, numMethodNames, numFileNames; - - /* - * Read the header. - */ - messageHdrLen = (data.get() & 0xff); - entryHdrLen = (data.get() & 0xff); - stackFrameLen = (data.get() & 0xff); - numEntries = (data.getShort() & 0xffff); - offsetToStrings = data.getInt(); - numClassNames = (data.getShort() & 0xffff); - numMethodNames = (data.getShort() & 0xffff); - numFileNames = (data.getShort() & 0xffff); - - - /* - * Skip forward to the strings and read them. - */ - data.position(offsetToStrings); - - String[] classNames = new String[numClassNames]; - String[] methodNames = new String[numMethodNames]; - String[] fileNames = new String[numFileNames]; - - readStringTable(data, classNames); - readStringTable(data, methodNames); - //System.out.println("METHODS: " - // + java.util.Arrays.deepToString(methodNames)); - readStringTable(data, fileNames); - - /* - * Skip back to a point just past the header and start reading - * entries. - */ - data.position(messageHdrLen); - - ArrayList<AllocationInfo> list = new ArrayList<AllocationInfo>(numEntries); - int allocNumber = numEntries; // order value for the entry. This is sent in reverse order. - for (int i = 0; i < numEntries; i++) { - int totalSize; - int threadId, classNameIndex, stackDepth; - - totalSize = data.getInt(); - threadId = (data.getShort() & 0xffff); - classNameIndex = (data.getShort() & 0xffff); - stackDepth = (data.get() & 0xff); - /* we've consumed 9 bytes; gobble up any extra */ - for (int skip = 9; skip < entryHdrLen; skip++) - data.get(); - - StackTraceElement[] steArray = new StackTraceElement[stackDepth]; - - /* - * Pull out the stack trace. - */ - for (int sti = 0; sti < stackDepth; sti++) { - int methodClassNameIndex, methodNameIndex; - int methodSourceFileIndex; - short lineNumber; - String methodClassName, methodName, methodSourceFile; - - methodClassNameIndex = (data.getShort() & 0xffff); - methodNameIndex = (data.getShort() & 0xffff); - methodSourceFileIndex = (data.getShort() & 0xffff); - lineNumber = data.getShort(); - - methodClassName = classNames[methodClassNameIndex]; - methodName = methodNames[methodNameIndex]; - methodSourceFile = fileNames[methodSourceFileIndex]; - - steArray[sti] = new StackTraceElement(methodClassName, - methodName, methodSourceFile, lineNumber); - - /* we've consumed 8 bytes; gobble up any extra */ - for (int skip = 9; skip < stackFrameLen; skip++) - data.get(); - } - - list.add(new AllocationInfo(allocNumber--, classNames[classNameIndex], - totalSize, (short) threadId, steArray)); - } - - client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries])); - client.update(Client.CHANGE_HEAP_ALLOCATIONS); - } - - /* - * For debugging: dump the contents of an AllocRecord array. - * - * The array starts with the oldest known allocation and ends with - * the most recent allocation. - */ - @SuppressWarnings("unused") - private static void dumpRecords(AllocationInfo[] records) { - System.out.println("Found " + records.length + " records:"); - - for (AllocationInfo rec: records) { - System.out.println("tid=" + rec.getThreadId() + " " - + rec.getAllocatedClass() + " (" + rec.getSize() + " bytes)"); - - for (StackTraceElement ste: rec.getStackTrace()) { - if (ste.isNativeMethod()) { - System.out.println(" " + ste.getClassName() - + "." + ste.getMethodName() - + " (Native method)"); - } else { - System.out.println(" " + ste.getClassName() - + "." + ste.getMethodName() - + " (" + ste.getFileName() - + ":" + ste.getLineNumber() + ")"); - } - } - } - } - -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHello.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHello.java deleted file mode 100644 index b5c2968..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleHello.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; - -/** - * Handle the "hello" chunk (HELO) and feature discovery. - */ -final class HandleHello extends ChunkHandler { - - public static final int CHUNK_HELO = ChunkHandler.type("HELO"); - public static final int CHUNK_FEAT = ChunkHandler.type("FEAT"); - - private static final HandleHello mInst = new HandleHello(); - - private HandleHello() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_HELO, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException { - Log.d("ddm-hello", "Now ready: " + client); - } - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) { - Log.d("ddm-hello", "Now disconnected: " + client); - } - - /** - * Sends HELLO-type commands to the VM after a good handshake. - * @param client - * @param serverProtocolVersion - * @throws IOException - */ - public static void sendHelloCommands(Client client, int serverProtocolVersion) - throws IOException { - sendHELO(client, serverProtocolVersion); - sendFEAT(client); - HandleProfiling.sendMPRQ(client); - } - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - - Log.d("ddm-hello", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_HELO) { - assert isReply; - handleHELO(client, data); - } else if (type == CHUNK_FEAT) { - handleFEAT(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a reply to our HELO message. - */ - private static void handleHELO(Client client, ByteBuffer data) { - int version, pid, vmIdentLen, appNameLen; - String vmIdent, appName; - - version = data.getInt(); - pid = data.getInt(); - vmIdentLen = data.getInt(); - appNameLen = data.getInt(); - - vmIdent = getString(data, vmIdentLen); - appName = getString(data, appNameLen); - - // Newer devices send user id in the APNM packet. - int userId = -1; - boolean validUserId = false; - if (data.hasRemaining()) { - try { - userId = data.getInt(); - validUserId = true; - } catch (BufferUnderflowException e) { - // five integers + two utf-16 strings - int expectedPacketLength = 20 + appNameLen * 2 + vmIdentLen * 2; - - Log.e("ddm-hello", "Insufficient data in HELO chunk to retrieve user id."); - Log.e("ddm-hello", "Actual chunk length: " + data.capacity()); - Log.e("ddm-hello", "Expected chunk length: " + expectedPacketLength); - } - } - - Log.d("ddm-hello", "HELO: v=" + version + ", pid=" + pid - + ", vm='" + vmIdent + "', app='" + appName + "'"); - - ClientData cd = client.getClientData(); - - synchronized (cd) { - if (cd.getPid() == pid) { - cd.setVmIdentifier(vmIdent); - cd.setClientDescription(appName); - cd.isDdmAware(true); - - if (validUserId) { - cd.setUserId(userId); - } - } else { - Log.e("ddm-hello", "Received pid (" + pid + ") does not match client pid (" - + cd.getPid() + ")"); - } - } - - client = checkDebuggerPortForAppName(client, appName); - - if (client != null) { - client.update(Client.CHANGE_NAME); - } - } - - - /** - * Send a HELO request to the client. - */ - public static void sendHELO(Client client, int serverProtocolVersion) - throws IOException - { - ByteBuffer rawBuf = allocBuffer(4); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(serverProtocolVersion); - - finishChunkPacket(packet, CHUNK_HELO, buf.position()); - Log.d("ddm-hello", "Sending " + name(CHUNK_HELO) - + " ID=0x" + Integer.toHexString(packet.getId())); - client.sendAndConsume(packet, mInst); - } - - /** - * Handle a reply to our FEAT request. - */ - private static void handleFEAT(Client client, ByteBuffer data) { - int featureCount; - int i; - - featureCount = data.getInt(); - for (i = 0; i < featureCount; i++) { - int len = data.getInt(); - String feature = getString(data, len); - client.getClientData().addFeature(feature); - - Log.d("ddm-hello", "Feature: " + feature); - } - } - - /** - * Send a FEAT request to the client. - */ - public static void sendFEAT(Client client) throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_FEAT, buf.position()); - Log.d("ddm-heap", "Sending " + name(CHUNK_FEAT)); - client.sendAndConsume(packet, mInst); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleNativeHeap.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleNativeHeap.java deleted file mode 100644 index 5c176cf..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleNativeHeap.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2007 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.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Handle thread status updates. - */ -final class HandleNativeHeap extends ChunkHandler { - - public static final int CHUNK_NHGT = type("NHGT"); //$NON-NLS-1$ - public static final int CHUNK_NHSG = type("NHSG"); //$NON-NLS-1$ - public static final int CHUNK_NHST = type("NHST"); //$NON-NLS-1$ - public static final int CHUNK_NHEN = type("NHEN"); //$NON-NLS-1$ - - private static final HandleNativeHeap mInst = new HandleNativeHeap(); - - private HandleNativeHeap() { - } - - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_NHGT, mInst); - mt.registerChunkHandler(CHUNK_NHSG, mInst); - mt.registerChunkHandler(CHUNK_NHST, mInst); - mt.registerChunkHandler(CHUNK_NHEN, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - - Log.d("ddm-nativeheap", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_NHGT) { - handleNHGT(client, data); - } else if (type == CHUNK_NHST) { - // start chunk before any NHSG chunk(s) - client.getClientData().getNativeHeapData().clearHeapData(); - } else if (type == CHUNK_NHEN) { - // end chunk after NHSG chunk(s) - client.getClientData().getNativeHeapData().sealHeapData(); - } else if (type == CHUNK_NHSG) { - handleNHSG(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - - client.update(Client.CHANGE_NATIVE_HEAP_DATA); - } - - /** - * Send an NHGT (Native Thread GeT) request to the client. - */ - public static void sendNHGT(Client client) throws IOException { - - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data in request message - - finishChunkPacket(packet, CHUNK_NHGT, buf.position()); - Log.d("ddm-nativeheap", "Sending " + name(CHUNK_NHGT)); - client.sendAndConsume(packet, mInst); - - rawBuf = allocBuffer(2); - packet = new JdwpPacket(rawBuf); - buf = getChunkDataBuf(rawBuf); - - buf.put((byte)HandleHeap.WHEN_DISABLE); - buf.put((byte)HandleHeap.WHAT_OBJ); - - finishChunkPacket(packet, CHUNK_NHSG, buf.position()); - Log.d("ddm-nativeheap", "Sending " + name(CHUNK_NHSG)); - client.sendAndConsume(packet, mInst); - } - - /* - * Handle our native heap data. - */ - private void handleNHGT(Client client, ByteBuffer data) { - ClientData cd = client.getClientData(); - - Log.d("ddm-nativeheap", "NHGT: " + data.limit() + " bytes"); - - // TODO - process incoming data and save in "cd" - byte[] copy = new byte[data.limit()]; - data.get(copy); - - // clear the previous run - cd.clearNativeAllocationInfo(); - - ByteBuffer buffer = ByteBuffer.wrap(copy); - buffer.order(ByteOrder.LITTLE_ENDIAN); - -// read the header -// typedef struct Header { -// uint32_t mapSize; -// uint32_t allocSize; -// uint32_t allocInfoSize; -// uint32_t totalMemory; -// uint32_t backtraceSize; -// }; - - int mapSize = buffer.getInt(); - int allocSize = buffer.getInt(); - int allocInfoSize = buffer.getInt(); - int totalMemory = buffer.getInt(); - int backtraceSize = buffer.getInt(); - - Log.d("ddms", "mapSize: " + mapSize); - Log.d("ddms", "allocSize: " + allocSize); - Log.d("ddms", "allocInfoSize: " + allocInfoSize); - Log.d("ddms", "totalMemory: " + totalMemory); - - cd.setTotalNativeMemory(totalMemory); - - // this means that updates aren't turned on. - if (allocInfoSize == 0) - return; - - if (mapSize > 0) { - byte[] maps = new byte[mapSize]; - buffer.get(maps, 0, mapSize); - parseMaps(cd, maps); - } - - int iterations = allocSize / allocInfoSize; - - for (int i = 0 ; i < iterations ; i++) { - NativeAllocationInfo info = new NativeAllocationInfo( - buffer.getInt() /* size */, - buffer.getInt() /* allocations */); - - for (int j = 0 ; j < backtraceSize ; j++) { - long addr = (buffer.getInt()) & 0x00000000ffffffffL; - - if (addr == 0x0) { - // skip past null addresses - continue; - } - - info.addStackCallAddress(addr);; - } - - cd.addNativeAllocation(info); - } - } - - private void handleNHSG(Client client, ByteBuffer data) { - byte dataCopy[] = new byte[data.limit()]; - data.rewind(); - data.get(dataCopy); - data = ByteBuffer.wrap(dataCopy); - client.getClientData().getNativeHeapData().addHeapData(data); - - if (true) { - return; - } - - // WORK IN PROGRESS - -// Log.e("ddm-nativeheap", "NHSG: ----------------------------------"); -// Log.e("ddm-nativeheap", "NHSG: " + data.limit() + " bytes"); - - byte[] copy = new byte[data.limit()]; - data.get(copy); - - ByteBuffer buffer = ByteBuffer.wrap(copy); - buffer.order(ByteOrder.BIG_ENDIAN); - - int id = buffer.getInt(); - int unitsize = buffer.get(); - long startAddress = buffer.getInt() & 0x00000000ffffffffL; - int offset = buffer.getInt(); - int allocationUnitCount = buffer.getInt(); - -// Log.e("ddm-nativeheap", "id: " + id); -// Log.e("ddm-nativeheap", "unitsize: " + unitsize); -// Log.e("ddm-nativeheap", "startAddress: 0x" + Long.toHexString(startAddress)); -// Log.e("ddm-nativeheap", "offset: " + offset); -// Log.e("ddm-nativeheap", "allocationUnitCount: " + allocationUnitCount); -// Log.e("ddm-nativeheap", "end: 0x" + -// Long.toHexString(startAddress + unitsize * allocationUnitCount)); - - // read the usage - while (buffer.position() < buffer.limit()) { - int eState = buffer.get() & 0x000000ff; - int eLen = (buffer.get() & 0x000000ff) + 1; - //Log.e("ddm-nativeheap", "solidity: " + (eState & 0x7) + " - kind: " - // + ((eState >> 3) & 0x7) + " - len: " + eLen); - } - - -// count += unitsize * allocationUnitCount; -// Log.e("ddm-nativeheap", "count = " + count); - - } - - private void parseMaps(ClientData cd, byte[] maps) { - InputStreamReader input = new InputStreamReader(new ByteArrayInputStream(maps)); - BufferedReader reader = new BufferedReader(input); - - String line; - - try { - - // most libraries are defined on several lines, so we need to make sure we parse - // all the library lines and only add the library at the end - long startAddr = 0; - long endAddr = 0; - String library = null; - - while ((line = reader.readLine()) != null) { - Log.d("ddms", "line: " + line); - if (line.length() < 16) { - continue; - } - - try { - long tmpStart = Long.parseLong(line.substring(0, 8), 16); - long tmpEnd = Long.parseLong(line.substring(9, 17), 16); - - int index = line.indexOf('/'); - - if (index == -1) - continue; - - String tmpLib = line.substring(index); - - if (library == null || - (library != null && tmpLib.equals(library) == false)) { - - if (library != null) { - cd.addNativeLibraryMapInfo(startAddr, endAddr, library); - Log.d("ddms", library + "(" + Long.toHexString(startAddr) + - " - " + Long.toHexString(endAddr) + ")"); - } - - // now init the new library - library = tmpLib; - startAddr = tmpStart; - endAddr = tmpEnd; - } else { - // add the new end - endAddr = tmpEnd; - } - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - - if (library != null) { - cd.addNativeLibraryMapInfo(startAddr, endAddr, library); - Log.d("ddms", library + "(" + Long.toHexString(startAddr) + - " - " + Long.toHexString(endAddr) + ")"); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleProfiling.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleProfiling.java deleted file mode 100644 index 9d01fdf..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleProfiling.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2009 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 com.android.ddmlib.ClientData.IMethodProfilingHandler; -import com.android.ddmlib.ClientData.MethodProfilingStatus; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Handle heap status updates. - */ -final class HandleProfiling extends ChunkHandler { - - public static final int CHUNK_MPRS = type("MPRS"); - public static final int CHUNK_MPRE = type("MPRE"); - public static final int CHUNK_MPSS = type("MPSS"); - public static final int CHUNK_MPSE = type("MPSE"); - public static final int CHUNK_MPRQ = type("MPRQ"); - public static final int CHUNK_FAIL = type("FAIL"); - - private static final HandleProfiling mInst = new HandleProfiling(); - - private HandleProfiling() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_MPRE, mInst); - mt.registerChunkHandler(CHUNK_MPSE, mInst); - mt.registerChunkHandler(CHUNK_MPRQ, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, - boolean isReply, int msgId) { - - Log.d("ddm-prof", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_MPRE) { - handleMPRE(client, data); - } else if (type == CHUNK_MPSE) { - handleMPSE(client, data); - } else if (type == CHUNK_MPRQ) { - handleMPRQ(client, data); - } else if (type == CHUNK_FAIL) { - handleFAIL(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /** - * Send a MPRS (Method PRofiling Start) request to the client. - * - * The arguments to this method will eventually be passed to - * android.os.Debug.startMethodTracing() on the device. - * - * @param fileName is the name of the file to which profiling data - * will be written (on the device); it will have {@link DdmConstants#DOT_TRACE} - * appended if necessary - * @param bufferSize is the desired buffer size in bytes (8MB is good) - * @param flags see startMethodTracing() docs; use 0 for default behavior - */ - public static void sendMPRS(Client client, String fileName, int bufferSize, - int flags) throws IOException { - - ByteBuffer rawBuf = allocBuffer(3*4 + fileName.length() * 2); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(bufferSize); - buf.putInt(flags); - buf.putInt(fileName.length()); - putString(buf, fileName); - - finishChunkPacket(packet, CHUNK_MPRS, buf.position()); - Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName - + "', size=" + bufferSize + ", flags=" + flags); - client.sendAndConsume(packet, mInst); - - // record the filename we asked for. - client.getClientData().setPendingMethodProfiling(fileName); - - // send a status query. this ensure that the status is properly updated if for some - // reason starting the tracing failed. - sendMPRQ(client); - } - - /** - * Send a MPRE (Method PRofiling End) request to the client. - */ - public static void sendMPRE(Client client) throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_MPRE, buf.position()); - Log.d("ddm-prof", "Sending " + name(CHUNK_MPRE)); - client.sendAndConsume(packet, mInst); - } - - /** - * Handle notification that method profiling has finished writing - * data to disk. - */ - private void handleMPRE(Client client, ByteBuffer data) { - byte result; - - // get the filename and make the client not have pending HPROF dump anymore. - String filename = client.getClientData().getPendingMethodProfiling(); - client.getClientData().setPendingMethodProfiling(null); - - result = data.get(); - - // get the app-level handler for method tracing dump - IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); - if (handler != null) { - if (result == 0) { - handler.onSuccess(filename, client); - - Log.d("ddm-prof", "Method profiling has finished"); - } else { - handler.onEndFailure(client, null /*message*/); - - Log.w("ddm-prof", "Method profiling has failed (check device log)"); - } - } - - client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); - client.update(Client.CHANGE_METHOD_PROFILING_STATUS); - } - - /** - * Send a MPSS (Method Profiling Streaming Start) request to the client. - * - * The arguments to this method will eventually be passed to - * android.os.Debug.startMethodTracing() on the device. - * - * @param bufferSize is the desired buffer size in bytes (8MB is good) - * @param flags see startMethodTracing() docs; use 0 for default behavior - */ - public static void sendMPSS(Client client, int bufferSize, - int flags) throws IOException { - - ByteBuffer rawBuf = allocBuffer(2*4); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(bufferSize); - buf.putInt(flags); - - finishChunkPacket(packet, CHUNK_MPSS, buf.position()); - Log.d("ddm-prof", "Sending " + name(CHUNK_MPSS) - + "', size=" + bufferSize + ", flags=" + flags); - client.sendAndConsume(packet, mInst); - - // send a status query. this ensure that the status is properly updated if for some - // reason starting the tracing failed. - sendMPRQ(client); - } - - /** - * Send a MPSE (Method Profiling Streaming End) request to the client. - */ - public static void sendMPSE(Client client) throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_MPSE, buf.position()); - Log.d("ddm-prof", "Sending " + name(CHUNK_MPSE)); - client.sendAndConsume(packet, mInst); - } - - /** - * Handle incoming profiling data. The MPSE packet includes the - * complete .trace file. - */ - private void handleMPSE(Client client, ByteBuffer data) { - IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); - if (handler != null) { - byte[] stuff = new byte[data.capacity()]; - data.get(stuff, 0, stuff.length); - - Log.d("ddm-prof", "got trace file, size: " + stuff.length + " bytes"); - - handler.onSuccess(stuff, client); - } - - client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); - client.update(Client.CHANGE_METHOD_PROFILING_STATUS); - } - - /** - * Send a MPRQ (Method PRofiling Query) request to the client. - */ - public static void sendMPRQ(Client client) throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // no data - - finishChunkPacket(packet, CHUNK_MPRQ, buf.position()); - Log.d("ddm-prof", "Sending " + name(CHUNK_MPRQ)); - client.sendAndConsume(packet, mInst); - } - - /** - * Receive response to query. - */ - private void handleMPRQ(Client client, ByteBuffer data) { - byte result; - - result = data.get(); - - if (result == 0) { - client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); - Log.d("ddm-prof", "Method profiling is not running"); - } else { - client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.ON); - Log.d("ddm-prof", "Method profiling is running"); - } - client.update(Client.CHANGE_METHOD_PROFILING_STATUS); - } - - private void handleFAIL(Client client, ByteBuffer data) { - /*int errorCode =*/ data.getInt(); - int length = data.getInt() * 2; - String message = null; - if (length > 0) { - byte[] messageBuffer = new byte[length]; - data.get(messageBuffer, 0, length); - message = new String(messageBuffer); - } - - // this can be sent if - // - MPRS failed (like wrong permission) - // - MPSE failed for whatever reason - - String filename = client.getClientData().getPendingMethodProfiling(); - if (filename != null) { - // reset the pending file. - client.getClientData().setPendingMethodProfiling(null); - - // and notify of failure - IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); - if (handler != null) { - handler.onStartFailure(client, message); - } - } else { - // this is MPRE - // notify of failure - IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); - if (handler != null) { - handler.onEndFailure(client, message); - } - } - - // send a query to know the current status - try { - sendMPRQ(client); - } catch (IOException e) { - Log.e("HandleProfiling", e); - } - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleTest.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleTest.java deleted file mode 100644 index b9f3a74..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.Log.LogLevel; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Handle thread status updates. - */ -final class HandleTest extends ChunkHandler { - - public static final int CHUNK_TEST = type("TEST"); - - private static final HandleTest mInst = new HandleTest(); - - - private HandleTest() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_TEST, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - - Log.d("ddm-test", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_TEST) { - handleTEST(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a thread creation message. - */ - private void handleTEST(Client client, ByteBuffer data) - { - /* - * Can't call data.array() on a read-only ByteBuffer, so we make - * a copy. - */ - byte[] copy = new byte[data.limit()]; - data.get(copy); - - Log.d("ddm-test", "Received:"); - Log.hexDump("ddm-test", LogLevel.DEBUG, copy, 0, copy.length); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleThread.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleThread.java deleted file mode 100644 index 8430c95..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleThread.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2007 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; -import java.nio.ByteBuffer; - -/** - * Handle thread status updates. - */ -final class HandleThread extends ChunkHandler { - - public static final int CHUNK_THEN = type("THEN"); - public static final int CHUNK_THCR = type("THCR"); - public static final int CHUNK_THDE = type("THDE"); - public static final int CHUNK_THST = type("THST"); - public static final int CHUNK_THNM = type("THNM"); - public static final int CHUNK_STKL = type("STKL"); - - private static final HandleThread mInst = new HandleThread(); - - // only read/written by requestThreadUpdates() - private static volatile boolean mThreadStatusReqRunning = false; - private static volatile boolean mThreadStackTraceReqRunning = false; - - private HandleThread() {} - - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_THCR, mInst); - mt.registerChunkHandler(CHUNK_THDE, mInst); - mt.registerChunkHandler(CHUNK_THST, mInst); - mt.registerChunkHandler(CHUNK_THNM, mInst); - mt.registerChunkHandler(CHUNK_STKL, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException { - Log.d("ddm-thread", "Now ready: " + client); - if (client.isThreadUpdateEnabled()) - sendTHEN(client, true); - } - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - - Log.d("ddm-thread", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_THCR) { - handleTHCR(client, data); - } else if (type == CHUNK_THDE) { - handleTHDE(client, data); - } else if (type == CHUNK_THST) { - handleTHST(client, data); - } else if (type == CHUNK_THNM) { - handleTHNM(client, data); - } else if (type == CHUNK_STKL) { - handleSTKL(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a thread creation message. - * - * We should be tolerant of receiving a duplicate create message. (It - * shouldn't happen with the current implementation.) - */ - private void handleTHCR(Client client, ByteBuffer data) { - int threadId, nameLen; - String name; - - threadId = data.getInt(); - nameLen = data.getInt(); - name = getString(data, nameLen); - - Log.v("ddm-thread", "THCR: " + threadId + " '" + name + "'"); - - client.getClientData().addThread(threadId, name); - client.update(Client.CHANGE_THREAD_DATA); - } - - /* - * Handle a thread death message. - */ - private void handleTHDE(Client client, ByteBuffer data) { - int threadId; - - threadId = data.getInt(); - Log.v("ddm-thread", "THDE: " + threadId); - - client.getClientData().removeThread(threadId); - client.update(Client.CHANGE_THREAD_DATA); - } - - /* - * Handle a thread status update message. - * - * Response has: - * (1b) header len - * (1b) bytes per entry - * (2b) thread count - * Then, for each thread: - * (4b) threadId (matches value from THCR) - * (1b) thread status - * (4b) tid - * (4b) utime - * (4b) stime - */ - private void handleTHST(Client client, ByteBuffer data) { - int headerLen, bytesPerEntry, extraPerEntry; - int threadCount; - - headerLen = (data.get() & 0xff); - bytesPerEntry = (data.get() & 0xff); - threadCount = data.getShort(); - - headerLen -= 4; // we've read 4 bytes - while (headerLen-- > 0) - data.get(); - - extraPerEntry = bytesPerEntry - 18; // we want 18 bytes - - Log.v("ddm-thread", "THST: threadCount=" + threadCount); - - /* - * For each thread, extract the data, find the appropriate - * client, and add it to the ClientData. - */ - for (int i = 0; i < threadCount; i++) { - int threadId, status, tid, utime, stime; - boolean isDaemon = false; - - threadId = data.getInt(); - status = data.get(); - tid = data.getInt(); - utime = data.getInt(); - stime = data.getInt(); - if (bytesPerEntry >= 18) - isDaemon = (data.get() != 0); - - Log.v("ddm-thread", " id=" + threadId - + ", status=" + status + ", tid=" + tid - + ", utime=" + utime + ", stime=" + stime); - - ClientData cd = client.getClientData(); - ThreadInfo threadInfo = cd.getThread(threadId); - if (threadInfo != null) - threadInfo.updateThread(status, tid, utime, stime, isDaemon); - else - Log.d("ddms", "Thread with id=" + threadId + " not found"); - - // slurp up any extra - for (int slurp = extraPerEntry; slurp > 0; slurp--) - data.get(); - } - - client.update(Client.CHANGE_THREAD_DATA); - } - - /* - * Handle a THNM (THread NaMe) message. We get one of these after - * somebody calls Thread.setName() on a running thread. - */ - private void handleTHNM(Client client, ByteBuffer data) { - int threadId, nameLen; - String name; - - threadId = data.getInt(); - nameLen = data.getInt(); - name = getString(data, nameLen); - - Log.v("ddm-thread", "THNM: " + threadId + " '" + name + "'"); - - ThreadInfo threadInfo = client.getClientData().getThread(threadId); - if (threadInfo != null) { - threadInfo.setThreadName(name); - client.update(Client.CHANGE_THREAD_DATA); - } else { - Log.d("ddms", "Thread with id=" + threadId + " not found"); - } - } - - - /** - * Parse an incoming STKL. - */ - private void handleSTKL(Client client, ByteBuffer data) { - StackTraceElement[] trace; - int i, threadId, stackDepth; - @SuppressWarnings("unused") - int future; - - future = data.getInt(); - threadId = data.getInt(); - - Log.v("ddms", "STKL: " + threadId); - - /* un-serialize the StackTraceElement[] */ - stackDepth = data.getInt(); - trace = new StackTraceElement[stackDepth]; - for (i = 0; i < stackDepth; i++) { - String className, methodName, fileName; - int len, lineNumber; - - len = data.getInt(); - className = getString(data, len); - len = data.getInt(); - methodName = getString(data, len); - len = data.getInt(); - if (len == 0) { - fileName = null; - } else { - fileName = getString(data, len); - } - lineNumber = data.getInt(); - - trace[i] = new StackTraceElement(className, methodName, fileName, - lineNumber); - } - - ThreadInfo threadInfo = client.getClientData().getThread(threadId); - if (threadInfo != null) { - threadInfo.setStackCall(trace); - client.update(Client.CHANGE_THREAD_STACKTRACE); - } else { - Log.d("STKL", String.format( - "Got stackcall for thread %1$d, which does not exists (anymore?).", //$NON-NLS-1$ - threadId)); - } - } - - - /** - * Send a THEN (THread notification ENable) request to the client. - */ - public static void sendTHEN(Client client, boolean enable) - throws IOException { - - ByteBuffer rawBuf = allocBuffer(1); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - if (enable) - buf.put((byte)1); - else - buf.put((byte)0); - - finishChunkPacket(packet, CHUNK_THEN, buf.position()); - Log.d("ddm-thread", "Sending " + name(CHUNK_THEN) + ": " + enable); - client.sendAndConsume(packet, mInst); - } - - - /** - * Send a STKL (STacK List) request to the client. The VM will suspend - * the target thread, obtain its stack, and return it. If the thread - * is no longer running, a failure result will be returned. - */ - public static void sendSTKL(Client client, int threadId) - throws IOException { - - if (false) { - Log.d("ddm-thread", "would send STKL " + threadId); - return; - } - - ByteBuffer rawBuf = allocBuffer(4); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - buf.putInt(threadId); - - finishChunkPacket(packet, CHUNK_STKL, buf.position()); - Log.d("ddm-thread", "Sending " + name(CHUNK_STKL) + ": " + threadId); - client.sendAndConsume(packet, mInst); - } - - - /** - * This is called periodically from the UI thread. To avoid locking - * the UI while we request the updates, we create a new thread. - * - */ - static void requestThreadUpdate(final Client client) { - if (client.isDdmAware() && client.isThreadUpdateEnabled()) { - if (mThreadStatusReqRunning) { - Log.w("ddms", "Waiting for previous thread update req to finish"); - return; - } - - new Thread("Thread Status Req") { - @Override - public void run() { - mThreadStatusReqRunning = true; - try { - sendTHST(client); - } catch (IOException ioe) { - Log.d("ddms", "Unable to request thread updates from " - + client + ": " + ioe.getMessage()); - } finally { - mThreadStatusReqRunning = false; - } - } - }.start(); - } - } - - static void requestThreadStackCallRefresh(final Client client, final int threadId) { - if (client.isDdmAware() && client.isThreadUpdateEnabled()) { - if (mThreadStackTraceReqRunning ) { - Log.w("ddms", "Waiting for previous thread stack call req to finish"); - return; - } - - new Thread("Thread Status Req") { - @Override - public void run() { - mThreadStackTraceReqRunning = true; - try { - sendSTKL(client, threadId); - } catch (IOException ioe) { - Log.d("ddms", "Unable to request thread stack call updates from " - + client + ": " + ioe.getMessage()); - } finally { - mThreadStackTraceReqRunning = false; - } - } - }.start(); - } - - } - - /* - * Send a THST request to the specified client. - */ - private static void sendTHST(Client client) throws IOException { - ByteBuffer rawBuf = allocBuffer(0); - JdwpPacket packet = new JdwpPacket(rawBuf); - ByteBuffer buf = getChunkDataBuf(rawBuf); - - // nothing much to say - - finishChunkPacket(packet, CHUNK_THST, buf.position()); - Log.d("ddm-thread", "Sending " + name(CHUNK_THST)); - client.sendAndConsume(packet, mInst); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleWait.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleWait.java deleted file mode 100644 index 934cbea..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HandleWait.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.ClientData.DebuggerStatus; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Handle the "wait" chunk (WAIT). These are sent up when the client is - * waiting for something, e.g. for a debugger to attach. - */ -final class HandleWait extends ChunkHandler { - - public static final int CHUNK_WAIT = ChunkHandler.type("WAIT"); - - private static final HandleWait mInst = new HandleWait(); - - - private HandleWait() {} - - /** - * Register for the packets we expect to get from the client. - */ - public static void register(MonitorThread mt) { - mt.registerChunkHandler(CHUNK_WAIT, mInst); - } - - /** - * Client is ready. - */ - @Override - public void clientReady(Client client) throws IOException {} - - /** - * Client went away. - */ - @Override - public void clientDisconnected(Client client) {} - - /** - * Chunk handler entry point. - */ - @Override - public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - - Log.d("ddm-wait", "handling " + ChunkHandler.name(type)); - - if (type == CHUNK_WAIT) { - assert !isReply; - handleWAIT(client, data); - } else { - handleUnknownChunk(client, type, data, isReply, msgId); - } - } - - /* - * Handle a reply to our WAIT message. - */ - private static void handleWAIT(Client client, ByteBuffer data) { - byte reason; - - reason = data.get(); - - Log.d("ddm-wait", "WAIT: reason=" + reason); - - - ClientData cd = client.getClientData(); - synchronized (cd) { - cd.setDebuggerConnectionStatus(DebuggerStatus.WAITING); - } - - client.update(Client.CHANGE_DEBUGGER_STATUS); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HeapSegment.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HeapSegment.java deleted file mode 100644 index 42f740c..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/HeapSegment.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2007 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.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.text.ParseException; - -/** - * Describes the types and locations of objects in a segment of a heap. - */ -public final class HeapSegment implements Comparable<HeapSegment> { - - /** - * Describes an object/region encoded in the HPSG data. - */ - public static class HeapSegmentElement implements Comparable<HeapSegmentElement> { - - /* - * Solidity values, which must match the values in - * the HPSG data. - */ - - /** The element describes a free block. */ - public static int SOLIDITY_FREE = 0; - - /** The element is strongly-reachable. */ - public static int SOLIDITY_HARD = 1; - - /** The element is softly-reachable. */ - public static int SOLIDITY_SOFT = 2; - - /** The element is weakly-reachable. */ - public static int SOLIDITY_WEAK = 3; - - /** The element is phantom-reachable. */ - public static int SOLIDITY_PHANTOM = 4; - - /** The element is pending finalization. */ - public static int SOLIDITY_FINALIZABLE = 5; - - /** The element is not reachable, and is about to be swept/freed. */ - public static int SOLIDITY_SWEEP = 6; - - /** The reachability of the object is unknown. */ - public static int SOLIDITY_INVALID = -1; - - - /* - * Kind values, which must match the values in - * the HPSG data. - */ - - /** The element describes a data object. */ - public static int KIND_OBJECT = 0; - - /** The element describes a class object. */ - public static int KIND_CLASS_OBJECT = 1; - - /** The element describes an array of 1-byte elements. */ - public static int KIND_ARRAY_1 = 2; - - /** The element describes an array of 2-byte elements. */ - public static int KIND_ARRAY_2 = 3; - - /** The element describes an array of 4-byte elements. */ - public static int KIND_ARRAY_4 = 4; - - /** The element describes an array of 8-byte elements. */ - public static int KIND_ARRAY_8 = 5; - - /** The element describes an unknown type of object. */ - public static int KIND_UNKNOWN = 6; - - /** The element describes a native object. */ - public static int KIND_NATIVE = 7; - - /** The object kind is unknown or unspecified. */ - public static int KIND_INVALID = -1; - - - /** - * A bit in the HPSG data that indicates that an element should - * be combined with the element that follows, typically because - * an element is too large to be described by a single element. - */ - private static int PARTIAL_MASK = 1 << 7; - - - /** - * Describes the reachability/solidity of the element. Must - * be set to one of the SOLIDITY_* values. - */ - private int mSolidity; - - /** - * Describes the type/kind of the element. Must be set to one - * of the KIND_* values. - */ - private int mKind; - - /** - * Describes the length of the element, in bytes. - */ - private int mLength; - - - /** - * Creates an uninitialized element. - */ - public HeapSegmentElement() { - setSolidity(SOLIDITY_INVALID); - setKind(KIND_INVALID); - setLength(-1); - } - - /** - * Create an element describing the entry at the current - * position of hpsgData. - * - * @param hs The heap segment to pull the entry from. - * @throws BufferUnderflowException if there is not a whole entry - * following the current position - * of hpsgData. - * @throws ParseException if the provided data is malformed. - */ - public HeapSegmentElement(HeapSegment hs) - throws BufferUnderflowException, ParseException { - set(hs); - } - - /** - * Replace the element with the entry at the current position of - * hpsgData. - * - * @param hs The heap segment to pull the entry from. - * @return this object. - * @throws BufferUnderflowException if there is not a whole entry - * following the current position of - * hpsgData. - * @throws ParseException if the provided data is malformed. - */ - public HeapSegmentElement set(HeapSegment hs) - throws BufferUnderflowException, ParseException { - - /* TODO: Maybe keep track of the virtual address of each element - * so that they can be examined independently. - */ - ByteBuffer data = hs.mUsageData; - int eState = data.get() & 0x000000ff; - int eLen = (data.get() & 0x000000ff) + 1; - - while ((eState & PARTIAL_MASK) != 0) { - - /* If the partial bit was set, the next byte should describe - * the same object as the current one. - */ - int nextState = data.get() & 0x000000ff; - if ((nextState & ~PARTIAL_MASK) != (eState & ~PARTIAL_MASK)) { - throw new ParseException("State mismatch", data.position()); - } - eState = nextState; - eLen += (data.get() & 0x000000ff) + 1; - } - - setSolidity(eState & 0x7); - setKind((eState >> 3) & 0x7); - setLength(eLen * hs.mAllocationUnitSize); - - return this; - } - - public int getSolidity() { - return mSolidity; - } - - public void setSolidity(int solidity) { - this.mSolidity = solidity; - } - - public int getKind() { - return mKind; - } - - public void setKind(int kind) { - this.mKind = kind; - } - - public int getLength() { - return mLength; - } - - public void setLength(int length) { - this.mLength = length; - } - - @Override - public int compareTo(HeapSegmentElement other) { - if (mLength != other.mLength) { - return mLength < other.mLength ? -1 : 1; - } - return 0; - } - } - - //* The ID of the heap that this segment belongs to. - protected int mHeapId; - - //* The size of an allocation unit, in bytes. (e.g., 8 bytes) - protected int mAllocationUnitSize; - - //* The virtual address of the start of this segment. - protected long mStartAddress; - - //* The offset of this pices from mStartAddress, in bytes. - protected int mOffset; - - //* The number of allocation units described in this segment. - protected int mAllocationUnitCount; - - //* The raw data that describes the contents of this segment. - protected ByteBuffer mUsageData; - - //* mStartAddress is set to this value when the segment becomes invalid. - private final static long INVALID_START_ADDRESS = -1; - - /** - * Create a new HeapSegment based on the raw contents - * of an HPSG chunk. - * - * @param hpsgData The raw data from an HPSG chunk. - * @throws BufferUnderflowException if hpsgData is too small - * to hold the HPSG chunk header data. - */ - public HeapSegment(ByteBuffer hpsgData) throws BufferUnderflowException { - /* Read the HPSG chunk header. - * These get*() calls may throw a BufferUnderflowException - * if the underlying data isn't big enough. - */ - hpsgData.order(ByteOrder.BIG_ENDIAN); - mHeapId = hpsgData.getInt(); - mAllocationUnitSize = hpsgData.get(); - mStartAddress = hpsgData.getInt() & 0x00000000ffffffffL; - mOffset = hpsgData.getInt(); - mAllocationUnitCount = hpsgData.getInt(); - - // Hold onto the remainder of the data. - mUsageData = hpsgData.slice(); - mUsageData.order(ByteOrder.BIG_ENDIAN); // doesn't actually matter - - // Validate the data. -//xxx do it -//xxx make sure the number of elements matches mAllocationUnitCount. -//xxx make sure the last element doesn't have P set - } - - /** - * See if this segment still contains data, and has not been - * appended to another segment. - * - * @return true if this segment has not been appended to - * another segment. - */ - public boolean isValid() { - return mStartAddress != INVALID_START_ADDRESS; - } - - /** - * See if <code>other</code> comes immediately after this segment. - * - * @param other The HeapSegment to check. - * @return true if <code>other</code> comes immediately after this - * segment. - */ - public boolean canAppend(HeapSegment other) { - return isValid() && other.isValid() && mHeapId == other.mHeapId && - mAllocationUnitSize == other.mAllocationUnitSize && - getEndAddress() == other.getStartAddress(); - } - - /** - * Append the contents of <code>other</code> to this segment - * if it describes the segment immediately after this one. - * - * @param other The segment to append to this segment, if possible. - * If appended, <code>other</code> will be invalid - * when this method returns. - * @return true if <code>other</code> was successfully appended to - * this segment. - */ - public boolean append(HeapSegment other) { - if (canAppend(other)) { - /* Preserve the position. The mark is not preserved, - * but we don't use it anyway. - */ - int pos = mUsageData.position(); - - // Guarantee that we have enough room for the new data. - if (mUsageData.capacity() - mUsageData.limit() < - other.mUsageData.limit()) { - /* Grow more than necessary in case another append() - * is about to happen. - */ - int newSize = mUsageData.limit() + other.mUsageData.limit(); - ByteBuffer newData = ByteBuffer.allocate(newSize * 2); - - mUsageData.rewind(); - newData.put(mUsageData); - mUsageData = newData; - } - - // Copy the data from the other segment and restore the position. - other.mUsageData.rewind(); - mUsageData.put(other.mUsageData); - mUsageData.position(pos); - - // Fix this segment's header to cover the new data. - mAllocationUnitCount += other.mAllocationUnitCount; - - // Mark the other segment as invalid. - other.mStartAddress = INVALID_START_ADDRESS; - other.mUsageData = null; - - return true; - } else { - return false; - } - } - - public long getStartAddress() { - return mStartAddress + mOffset; - } - - public int getLength() { - return mAllocationUnitSize * mAllocationUnitCount; - } - - public long getEndAddress() { - return getStartAddress() + getLength(); - } - - public void rewindElements() { - if (mUsageData != null) { - mUsageData.rewind(); - } - } - - public HeapSegmentElement getNextElement(HeapSegmentElement reuse) { - try { - if (reuse != null) { - return reuse.set(this); - } else { - return new HeapSegmentElement(this); - } - } catch (BufferUnderflowException ex) { - /* Normal "end of buffer" situation. - */ - } catch (ParseException ex) { - /* Malformed data. - */ -//TODO: we should catch this in the constructor - } - return null; - } - - /* - * Method overrides for Comparable - */ - @Override - public boolean equals(Object o) { - if (o instanceof HeapSegment) { - return compareTo((HeapSegment) o) == 0; - } - return false; - } - - @Override - public int hashCode() { - return mHeapId * 31 + - mAllocationUnitSize * 31 + - (int) mStartAddress * 31 + - mOffset * 31 + - mAllocationUnitCount * 31 + - mUsageData.hashCode(); - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - - str.append("HeapSegment { heap ").append(mHeapId) - .append(", start 0x") - .append(Integer.toHexString((int) getStartAddress())) - .append(", length ").append(getLength()) - .append(" }"); - - return str.toString(); - } - - @Override - public int compareTo(HeapSegment other) { - if (mHeapId != other.mHeapId) { - return mHeapId < other.mHeapId ? -1 : 1; - } - if (getStartAddress() != other.getStartAddress()) { - return getStartAddress() < other.getStartAddress() ? -1 : 1; - } - - /* If two segments have the same start address, the rest of - * the fields should be equal. Go through the motions, though. - * Note that we re-check the components of getStartAddress() - * (mStartAddress and mOffset) to make sure that all fields in - * an equal segment are equal. - */ - - if (mAllocationUnitSize != other.mAllocationUnitSize) { - return mAllocationUnitSize < other.mAllocationUnitSize ? -1 : 1; - } - if (mStartAddress != other.mStartAddress) { - return mStartAddress < other.mStartAddress ? -1 : 1; - } - if (mOffset != other.mOffset) { - return mOffset < other.mOffset ? -1 : 1; - } - if (mAllocationUnitCount != other.mAllocationUnitCount) { - return mAllocationUnitCount < other.mAllocationUnitCount ? -1 : 1; - } - if (mUsageData != other.mUsageData) { - return mUsageData.compareTo(other.mUsageData); - } - return 0; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IDevice.java deleted file mode 100644 index 452d032..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IDevice.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2008 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 com.android.ddmlib.log.LogReceiver; - -import java.io.IOException; -import java.util.Map; - -/** - * A Device. It can be a physical device or an emulator. - */ -public interface IDevice { - - public final static String PROP_BUILD_VERSION = "ro.build.version.release"; - public final static String PROP_BUILD_API_LEVEL = "ro.build.version.sdk"; - public final static String PROP_BUILD_CODENAME = "ro.build.version.codename"; - - public final static String PROP_DEBUGGABLE = "ro.debuggable"; - - /** Serial number of the first connected emulator. */ - public final static String FIRST_EMULATOR_SN = "emulator-5554"; //$NON-NLS-1$ - /** Device change bit mask: {@link DeviceState} change. */ - public static final int CHANGE_STATE = 0x0001; - /** Device change bit mask: {@link Client} list change. */ - public static final int CHANGE_CLIENT_LIST = 0x0002; - /** Device change bit mask: build info change. */ - public static final int CHANGE_BUILD_INFO = 0x0004; - - /** @deprecated Use {@link #PROP_BUILD_API_LEVEL}. */ - @Deprecated - public final static String PROP_BUILD_VERSION_NUMBER = PROP_BUILD_API_LEVEL; - - public final static String MNT_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"; //$NON-NLS-1$ - public final static String MNT_ROOT = "ANDROID_ROOT"; //$NON-NLS-1$ - public final static String MNT_DATA = "ANDROID_DATA"; //$NON-NLS-1$ - - /** - * The state of a device. - */ - public static enum DeviceState { - BOOTLOADER("bootloader"), //$NON-NLS-1$ - OFFLINE("offline"), //$NON-NLS-1$ - ONLINE("device"), //$NON-NLS-1$ - RECOVERY("recovery"); //$NON-NLS-1$ - - private String mState; - - DeviceState(String state) { - mState = state; - } - - /** - * 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. - */ - public static DeviceState getState(String state) { - for (DeviceState deviceState : values()) { - if (deviceState.mState.equals(state)) { - return deviceState; - } - } - return null; - } - } - - /** - * Namespace of a Unix Domain Socket created on the device. - */ - public static enum DeviceUnixSocketNamespace { - ABSTRACT("localabstract"), //$NON-NLS-1$ - FILESYSTEM("localfilesystem"), //$NON-NLS-1$ - RESERVED("localreserved"); //$NON-NLS-1$ - - private String mType; - - private DeviceUnixSocketNamespace(String type) { - mType = type; - } - - String getType() { - return mType; - } - }; - - /** - * Returns the serial number of the device. - */ - public String getSerialNumber(); - - /** - * Returns the name of the AVD the emulator is running. - * <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(); - - /** - * Returns a (humanized) name for this device. Typically this is the AVD name for AVD's, and - * a combination of the manufacturer name, model name & serial number for devices. - */ - public String getName(); - - /** - * Returns the state of the device. - */ - public DeviceState getState(); - - /** - * Returns the device properties. It contains the whole output of 'getprop' - */ - public Map<String, String> getProperties(); - - /** - * Returns the number of property for this device. - */ - public int getPropertyCount(); - - /** - * Returns the cached property value. - * - * @param name the name of the value to return. - * @return the value or <code>null</code> if the property does not exist or has not yet been - * cached. - */ - public String getProperty(String name); - - /** - * Returns <code>true></code> if properties have been cached - */ - public boolean arePropertiesSet(); - - /** - * A variant of {@link #getProperty(String)} that will attempt to retrieve the given - * property from device directly, without using cache. - * - * @param name the name of the value to return. - * @return the value or <code>null</code> if the property does not exist - * @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. - */ - public String getPropertySync(String name) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException; - - /** - * A combination of {@link #getProperty(String)} and {@link #getPropertySync(String)} that - * will attempt to retrieve the property from cache if available, and if not, will query the - * device directly. - * - * @param name the name of the value to return. - * @return the value or <code>null</code> if the property does not exist - * @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. - */ - public String getPropertyCacheOrSync(String name) throws TimeoutException, - AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException; - - /** - * Returns a mount point. - * - * @param name the name of the mount point to return - * - * @see #MNT_EXTERNAL_STORAGE - * @see #MNT_ROOT - * @see #MNT_DATA - */ - public String getMountPoint(String name); - - /** - * Returns if the device is ready. - * - * @return <code>true</code> if {@link #getState()} returns {@link DeviceState#ONLINE}. - */ - public boolean isOnline(); - - /** - * Returns <code>true</code> if the device is an emulator. - */ - public boolean isEmulator(); - - /** - * 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(); - - /** - * Returns whether the {@link Device} has {@link Client}s. - */ - public boolean hasClients(); - - /** - * Returns the array of clients. - */ - public Client[] getClients(); - - /** - * 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. - */ - public Client getClient(String applicationName); - - /** - * 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). - * @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 TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Returns a {@link FileListingService} for this device. - */ - public FileListingService getFileListingService(); - - /** - * 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 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 TimeoutException, AdbCommandRejectedException, - 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, - 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 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 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 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 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. - * @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 void createForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Creates a port forwarding between a local TCP port and a remote Unix Domain Socket. - * - * @param localPort the local port to forward - * @param remoteSocketName name of the unix domain socket created on the device - * @param namespace namespace in which the unix domain socket was created - * @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 void createForward(int localPort, String remoteSocketName, - DeviceUnixSocketNamespace namespace) - 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. - * @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 void removeForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Removes an existing port forwarding between a local and a remote port. - * - * @param localPort the local port to forward - * @param remoteSocketName the remote unix domain socket name. - * @param namespace namespace in which the unix domain socket was created - * @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 void removeForward(int localPort, String remoteSocketName, - DeviceUnixSocketNamespace namespace) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Returns the name of the client by pid or <code>null</code> if pid is unknown - * @param pid the pid of the client. - */ - public String getClientName(int pid); - - /** - * Push a single file. - * @param local the local filepath. - * @param remote The remote filepath. - * - * @throws IOException in case of I/O error on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws TimeoutException in case of a timeout reading responses from the device. - * @throws SyncException if file could not be pushed - */ - public void pushFile(String local, String remote) - throws IOException, AdbCommandRejectedException, TimeoutException, SyncException; - - /** - * Pulls a single file. - * - * @param remote the full path to the remote file - * @param local The local destination. - * - * @throws IOException in case of an IO exception. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws TimeoutException in case of a timeout reading responses from the device. - * @throws SyncException in case of a sync exception. - */ - public void pullFile(String remote, String local) - throws IOException, AdbCommandRejectedException, TimeoutException, SyncException; - - /** - * 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 - * @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for - * available options. - * @return a {@link String} with an error code, or <code>null</code> if success. - * @throws InstallException if the installation fails. - */ - public String installPackage(String packageFilePath, boolean reinstall, String... extraArgs) - throws InstallException; - - /** - * 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 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. - * @throws SyncException if an error happens during the push of the package on the device. - */ - public String syncPackageToDevice(String localFilePath) - throws TimeoutException, AdbCommandRejectedException, IOException, SyncException; - - /** - * 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 - * @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for - * available options. - * @throws InstallException if the installation fails. - */ - public String installRemotePackage(String remoteFilePath, boolean reinstall, - String... extraArgs) throws InstallException; - - /** - * Removes a file from device. - * - * @param remoteFilePath path on device of file to remove - * @throws InstallException if the installation fails. - */ - public void removeRemotePackage(String remoteFilePath) throws InstallException; - - /** - * 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 InstallException if the uninstallation fails. - */ - public String uninstallPackage(String packageName) throws InstallException; - - /** - * 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 TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Return the device's battery level, from 0 to 100 percent. - * <p/> - * The battery level may be cached. Only queries the device for its - * battery level if 5 minutes have expired since the last successful query. - * - * @return the battery level or <code>null</code> if it could not be retrieved - */ - public Integer getBatteryLevel() throws TimeoutException, - AdbCommandRejectedException, IOException, ShellCommandUnresponsiveException; - - /** - * Return the device's battery level, from 0 to 100 percent. - * <p/> - * The battery level may be cached. Only queries the device for its - * battery level if <code>freshnessMs</code> ms have expired since the last successful query. - * - * @param freshnessMs - * @return the battery level or <code>null</code> if it could not be retrieved - * @throws ShellCommandUnresponsiveException - */ - public Integer getBatteryLevel(long freshnessMs) throws TimeoutException, - AdbCommandRejectedException, IOException, ShellCommandUnresponsiveException; - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IShellOutputReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IShellOutputReceiver.java deleted file mode 100644 index fb671bb..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IShellOutputReceiver.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * Classes which implement this interface provide methods that deal with out from a remote shell - * command on a device/emulator. - */ -public interface IShellOutputReceiver { - /** - * Called every time some new data is available. - * @param data The new data. - * @param offset The offset at which the new data starts. - * @param length The length of the new data. - */ - public void addOutput(byte[] data, int offset, int length); - - /** - * Called at the end of the process execution (unless the process was - * canceled). This allows the receiver to terminate and flush whatever - * data was not yet processed. - */ - public void flush(); - - /** - * Cancel method to stop the execution of the remote shell command. - * @return true to cancel the execution of the command. - */ - public boolean isCancelled(); -}; diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IStackTraceInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IStackTraceInfo.java deleted file mode 100644 index 3b9d730..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/IStackTraceInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * Classes which implement this interface provide a method that returns a stack trace. - */ -public interface IStackTraceInfo { - - /** - * Returns the stack trace. This can be <code>null</code>. - */ - public StackTraceElement[] getStackTrace(); - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/InstallException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/InstallException.java deleted file mode 100644 index 7aa718f..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/InstallException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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; - -/** - * Thrown if installation or uninstallation of application fails. - */ -public class InstallException extends CanceledException { - private static final long serialVersionUID = 1L; - - public InstallException(Throwable cause) { - super(cause.getMessage(), cause); - } - - public InstallException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Returns true if the installation was canceled by user input. This can typically only - * happen in the sync phase. - */ - @Override - public boolean wasCanceled() { - Throwable cause = getCause(); - return cause instanceof SyncException && ((SyncException)cause).wasCanceled(); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/JdwpPacket.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/JdwpPacket.java deleted file mode 100644 index 92bbb82..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/JdwpPacket.java +++ /dev/null @@ -1,371 +0,0 @@ -/* //device/tools/ddms/libs/ddmlib/src/com/android/ddmlib/JdwpPacket.java -** -** Copyright 2007, 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; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.SocketChannel; - -/** - * A JDWP packet, sitting at the start of a ByteBuffer somewhere. - * - * This allows us to wrap a "pointer" to the data with the results of - * decoding the packet. - * - * None of the operations here are synchronized. If multiple threads will - * be accessing the same ByteBuffers, external sync will be required. - * - * Use the constructor to create an empty packet, or "findPacket()" to - * wrap a JdwpPacket around existing data. - */ -final class JdwpPacket { - // header len - public static final int JDWP_HEADER_LEN = 11; - - // results from findHandshake - public static final int HANDSHAKE_GOOD = 1; - public static final int HANDSHAKE_NOTYET = 2; - public static final int HANDSHAKE_BAD = 3; - - // our cmdSet/cmd - private static final int DDMS_CMD_SET = 0xc7; // 'G' + 128 - private static final int DDMS_CMD = 0x01; - - // "flags" field - private static final int REPLY_PACKET = 0x80; - - // this is sent and expected at the start of a JDWP connection - private static final byte[] mHandshake = { - 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e' - }; - - public static final int HANDSHAKE_LEN = mHandshake.length; - - private ByteBuffer mBuffer; - private int mLength, mId, mFlags, mCmdSet, mCmd, mErrCode; - private boolean mIsNew; - - private static int mSerialId = 0x40000000; - - - /** - * Create a new, empty packet, in "buf". - */ - JdwpPacket(ByteBuffer buf) { - mBuffer = buf; - mIsNew = true; - } - - /** - * Finish a packet created with newPacket(). - * - * This always creates a command packet, with the next serial number - * in sequence. - * - * We have to take "payloadLength" as an argument because we can't - * see the position in the "slice" returned by getPayload(). We could - * fish it out of the chunk header, but it's legal for there to be - * more than one chunk in a JDWP packet. - * - * On exit, "position" points to the end of the data. - */ - void finishPacket(int payloadLength) { - assert mIsNew; - - ByteOrder oldOrder = mBuffer.order(); - mBuffer.order(ChunkHandler.CHUNK_ORDER); - - mLength = JDWP_HEADER_LEN + payloadLength; - mId = getNextSerial(); - mFlags = 0; - mCmdSet = DDMS_CMD_SET; - mCmd = DDMS_CMD; - - mBuffer.putInt(0x00, mLength); - mBuffer.putInt(0x04, mId); - mBuffer.put(0x08, (byte) mFlags); - mBuffer.put(0x09, (byte) mCmdSet); - mBuffer.put(0x0a, (byte) mCmd); - - mBuffer.order(oldOrder); - mBuffer.position(mLength); - } - - /** - * Get the next serial number. This creates a unique serial number - * across all connections, not just for the current connection. This - * is a useful property when debugging, but isn't necessary. - * - * We can't synchronize on an int, so we use a sync method. - */ - private static synchronized int getNextSerial() { - return mSerialId++; - } - - /** - * Return a slice of the byte buffer, positioned past the JDWP header - * to the start of the chunk header. The buffer's limit will be set - * to the size of the payload if the size is known; if this is a - * packet under construction the limit will be set to the end of the - * buffer. - * - * Doesn't examine the packet at all -- works on empty buffers. - */ - ByteBuffer getPayload() { - ByteBuffer buf; - int oldPosn = mBuffer.position(); - - mBuffer.position(JDWP_HEADER_LEN); - buf = mBuffer.slice(); // goes from position to limit - mBuffer.position(oldPosn); - - if (mLength > 0) - buf.limit(mLength - JDWP_HEADER_LEN); - else - assert mIsNew; - buf.order(ChunkHandler.CHUNK_ORDER); - return buf; - } - - /** - * Returns "true" if this JDWP packet has a JDWP command type. - * - * This never returns "true" for reply packets. - */ - boolean isDdmPacket() { - return (mFlags & REPLY_PACKET) == 0 && - mCmdSet == DDMS_CMD_SET && - mCmd == DDMS_CMD; - } - - /** - * Returns "true" if this JDWP packet is tagged as a reply. - */ - boolean isReply() { - return (mFlags & REPLY_PACKET) != 0; - } - - /** - * Returns "true" if this JDWP packet is a reply with a nonzero - * error code. - */ - boolean isError() { - return isReply() && mErrCode != 0; - } - - /** - * Returns "true" if this JDWP packet has no data. - */ - boolean isEmpty() { - return (mLength == JDWP_HEADER_LEN); - } - - /** - * Return the packet's ID. For a reply packet, this allows us to - * match the reply with the original request. - */ - int getId() { - return mId; - } - - /** - * Return the length of a packet. This includes the header, so an - * empty packet is 11 bytes long. - */ - int getLength() { - return mLength; - } - - /** - * Write our packet to "chan". Consumes the packet as part of the - * write. - * - * The JDWP packet starts at offset 0 and ends at mBuffer.position(). - */ - void writeAndConsume(SocketChannel chan) throws IOException { - int oldLimit; - - //Log.i("ddms", "writeAndConsume: pos=" + mBuffer.position() - // + ", limit=" + mBuffer.limit()); - - assert mLength > 0; - - mBuffer.flip(); // limit<-posn, posn<-0 - oldLimit = mBuffer.limit(); - mBuffer.limit(mLength); - while (mBuffer.position() != mBuffer.limit()) { - chan.write(mBuffer); - } - // position should now be at end of packet - assert mBuffer.position() == mLength; - - mBuffer.limit(oldLimit); - mBuffer.compact(); // shift posn...limit, posn<-pending data - - //Log.i("ddms", " : pos=" + mBuffer.position() - // + ", limit=" + mBuffer.limit()); - } - - /** - * "Move" the packet data out of the buffer we're sitting on and into - * buf at the current position. - */ - void movePacket(ByteBuffer buf) { - Log.v("ddms", "moving " + mLength + " bytes"); - int oldPosn = mBuffer.position(); - - mBuffer.position(0); - mBuffer.limit(mLength); - buf.put(mBuffer); - mBuffer.position(mLength); - mBuffer.limit(oldPosn); - mBuffer.compact(); // shift posn...limit, posn<-pending data - } - - /** - * Consume the JDWP packet. - * - * On entry and exit, "position" is the #of bytes in the buffer. - */ - void consume() - { - //Log.d("ddms", "consuming " + mLength + " bytes"); - //Log.d("ddms", " posn=" + mBuffer.position() - // + ", limit=" + mBuffer.limit()); - - /* - * The "flip" call sets "limit" equal to the position (usually the - * end of data) and "position" equal to zero. - * - * compact() copies everything from "position" and "limit" to the - * start of the buffer, sets "position" to the end of data, and - * sets "limit" to the capacity. - * - * On entry, "position" is set to the amount of data in the buffer - * and "limit" is set to the capacity. We want to call flip() - * so that position..limit spans our data, advance "position" past - * the current packet, then compact. - */ - mBuffer.flip(); // limit<-posn, posn<-0 - mBuffer.position(mLength); - mBuffer.compact(); // shift posn...limit, posn<-pending data - mLength = 0; - //Log.d("ddms", " after compact, posn=" + mBuffer.position() - // + ", limit=" + mBuffer.limit()); - } - - /** - * Find the JDWP packet at the start of "buf". The start is known, - * but the length has to be parsed out. - * - * On entry, the packet data in "buf" must start at offset 0 and end - * at "position". "limit" should be set to the buffer capacity. This - * method does not alter "buf"s attributes. - * - * Returns a new JdwpPacket if a full one is found in the buffer. If - * not, returns null. Throws an exception if the data doesn't look like - * a valid JDWP packet. - */ - static JdwpPacket findPacket(ByteBuffer buf) { - int count = buf.position(); - int length, id, flags, cmdSet, cmd; - - if (count < JDWP_HEADER_LEN) - return null; - - ByteOrder oldOrder = buf.order(); - buf.order(ChunkHandler.CHUNK_ORDER); - - length = buf.getInt(0x00); - id = buf.getInt(0x04); - flags = buf.get(0x08) & 0xff; - cmdSet = buf.get(0x09) & 0xff; - cmd = buf.get(0x0a) & 0xff; - - buf.order(oldOrder); - - if (length < JDWP_HEADER_LEN) - throw new BadPacketException(); - if (count < length) - return null; - - JdwpPacket pkt = new JdwpPacket(buf); - //pkt.mBuffer = buf; - pkt.mLength = length; - pkt.mId = id; - pkt.mFlags = flags; - - if ((flags & REPLY_PACKET) == 0) { - pkt.mCmdSet = cmdSet; - pkt.mCmd = cmd; - pkt.mErrCode = -1; - } else { - pkt.mCmdSet = -1; - pkt.mCmd = -1; - pkt.mErrCode = cmdSet | (cmd << 8); - } - - return pkt; - } - - /** - * Like findPacket(), but when we're expecting the JDWP handshake. - * - * Returns one of: - * HANDSHAKE_GOOD - found handshake, looks good - * HANDSHAKE_BAD - found enough data, but it's wrong - * HANDSHAKE_NOTYET - not enough data has been read yet - */ - static int findHandshake(ByteBuffer buf) { - int count = buf.position(); - int i; - - if (count < mHandshake.length) - return HANDSHAKE_NOTYET; - - for (i = mHandshake.length -1; i >= 0; --i) { - if (buf.get(i) != mHandshake[i]) - return HANDSHAKE_BAD; - } - - return HANDSHAKE_GOOD; - } - - /** - * Remove the handshake string from the buffer. - * - * On entry and exit, "position" is the #of bytes in the buffer. - */ - static void consumeHandshake(ByteBuffer buf) { - // in theory, nothing else can have arrived, so this is overkill - buf.flip(); // limit<-posn, posn<-0 - buf.position(mHandshake.length); - buf.compact(); // shift posn...limit, posn<-pending data - } - - /** - * Copy the handshake string into the output buffer. - * - * On exit, "buf"s position will be advanced. - */ - static void putHandshake(ByteBuffer buf) { - buf.put(mHandshake); - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Log.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Log.java deleted file mode 100644 index 55f7aab..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/Log.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2007 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.PrintWriter; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * Log class that mirrors the API in main Android sources. - * <p/>Default behavior outputs the log to {@link System#out}. Use - * {@link #setLogOutput(com.android.ddmlib.Log.ILogOutput)} to redirect the log somewhere else. - */ -public final class Log { - - /** - * Log Level enum. - */ - public enum LogLevel { - VERBOSE(2, "verbose", 'V'), //$NON-NLS-1$ - DEBUG(3, "debug", 'D'), //$NON-NLS-1$ - INFO(4, "info", 'I'), //$NON-NLS-1$ - WARN(5, "warn", 'W'), //$NON-NLS-1$ - ERROR(6, "error", 'E'), //$NON-NLS-1$ - ASSERT(7, "assert", 'A'); //$NON-NLS-1$ - - private int mPriorityLevel; - private String mStringValue; - private char mPriorityLetter; - - LogLevel(int intPriority, String stringValue, char priorityChar) { - mPriorityLevel = intPriority; - mStringValue = stringValue; - mPriorityLetter = priorityChar; - } - - public static LogLevel getByString(String value) { - for (LogLevel mode : values()) { - if (mode.mStringValue.equals(value)) { - return mode; - } - } - - return null; - } - - /** - * Returns the {@link LogLevel} enum matching the specified letter. - * @param letter the letter matching a <code>LogLevel</code> enum - * @return a <code>LogLevel</code> object or <code>null</code> if no match were found. - */ - public static LogLevel getByLetter(char letter) { - for (LogLevel mode : values()) { - if (mode.mPriorityLetter == letter) { - return mode; - } - } - - return null; - } - - /** - * Returns the {@link LogLevel} enum matching the specified letter. - * <p/> - * The letter is passed as a {@link String} argument, but only the first character - * is used. - * @param letter the letter matching a <code>LogLevel</code> enum - * @return a <code>LogLevel</code> object or <code>null</code> if no match were found. - */ - public static LogLevel getByLetterString(String letter) { - if (letter.length() > 0) { - return getByLetter(letter.charAt(0)); - } - - return null; - } - - /** - * Returns the letter identifying the priority of the {@link LogLevel}. - */ - public char getPriorityLetter() { - return mPriorityLetter; - } - - /** - * Returns the numerical value of the priority. - */ - public int getPriority() { - return mPriorityLevel; - } - - /** - * Returns a non translated string representing the LogLevel. - */ - public String getStringValue() { - return mStringValue; - } - } - - /** - * Classes which implement this interface provides methods that deal with outputting log - * messages. - */ - public interface ILogOutput { - /** - * Sent when a log message needs to be printed. - * @param logLevel The {@link LogLevel} enum representing the priority of the message. - * @param tag The tag associated with the message. - * @param message The message to display. - */ - public void printLog(LogLevel logLevel, String tag, String message); - - /** - * Sent when a log message needs to be printed, and, if possible, displayed to the user - * in a dialog box. - * @param logLevel The {@link LogLevel} enum representing the priority of the message. - * @param tag The tag associated with the message. - * @param message The message to display. - */ - public void printAndPromptLog(LogLevel logLevel, String tag, String message); - } - - private static LogLevel mLevel = DdmPreferences.getLogLevel(); - - private static ILogOutput sLogOutput; - - private static final char[] mSpaceLine = new char[72]; - private static final char[] mHexDigit = new char[] - { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; - static { - /* prep for hex dump */ - int i = mSpaceLine.length-1; - while (i >= 0) - mSpaceLine[i--] = ' '; - mSpaceLine[0] = mSpaceLine[1] = mSpaceLine[2] = mSpaceLine[3] = '0'; - mSpaceLine[4] = '-'; - } - - static final class Config { - static final boolean LOGV = true; - static final boolean LOGD = true; - }; - - private Log() {} - - /** - * Outputs a {@link LogLevel#VERBOSE} level message. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void v(String tag, String message) { - println(LogLevel.VERBOSE, tag, message); - } - - /** - * Outputs a {@link LogLevel#DEBUG} level message. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void d(String tag, String message) { - println(LogLevel.DEBUG, tag, message); - } - - /** - * Outputs a {@link LogLevel#INFO} level message. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void i(String tag, String message) { - println(LogLevel.INFO, tag, message); - } - - /** - * Outputs a {@link LogLevel#WARN} level message. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void w(String tag, String message) { - println(LogLevel.WARN, tag, message); - } - - /** - * Outputs a {@link LogLevel#ERROR} level message. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void e(String tag, String message) { - println(LogLevel.ERROR, tag, message); - } - - /** - * Outputs a log message and attempts to display it in a dialog. - * @param tag The tag associated with the message. - * @param message The message to output. - */ - public static void logAndDisplay(LogLevel logLevel, String tag, String message) { - if (sLogOutput != null) { - sLogOutput.printAndPromptLog(logLevel, tag, message); - } else { - println(logLevel, tag, message); - } - } - - /** - * Outputs a {@link LogLevel#ERROR} level {@link Throwable} information. - * @param tag The tag associated with the message. - * @param throwable The {@link Throwable} to output. - */ - public static void e(String tag, Throwable throwable) { - if (throwable != null) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - - throwable.printStackTrace(pw); - println(LogLevel.ERROR, tag, throwable.getMessage() + '\n' + sw.toString()); - } - } - - static void setLevel(LogLevel logLevel) { - mLevel = logLevel; - } - - /** - * Sets the {@link ILogOutput} to use to print the logs. If not set, {@link System#out} - * will be used. - * @param logOutput The {@link ILogOutput} to use to print the log. - */ - public static void setLogOutput(ILogOutput logOutput) { - sLogOutput = logOutput; - } - - /** - * Show hex dump. - * <p/> - * Local addition. Output looks like: - * 1230- 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef - * <p/> - * Uses no string concatenation; creates one String object per line. - */ - static void hexDump(String tag, LogLevel level, byte[] data, int offset, int length) { - - int kHexOffset = 6; - int kAscOffset = 55; - char[] line = new char[mSpaceLine.length]; - int addr, baseAddr, count; - int i, ch; - boolean needErase = true; - - //Log.w(tag, "HEX DUMP: off=" + offset + ", length=" + length); - - baseAddr = 0; - while (length != 0) { - if (length > 16) { - // full line - count = 16; - } else { - // partial line; re-copy blanks to clear end - count = length; - needErase = true; - } - - if (needErase) { - System.arraycopy(mSpaceLine, 0, line, 0, mSpaceLine.length); - needErase = false; - } - - // output the address (currently limited to 4 hex digits) - addr = baseAddr; - addr &= 0xffff; - ch = 3; - while (addr != 0) { - line[ch] = mHexDigit[addr & 0x0f]; - ch--; - addr >>>= 4; - } - - // output hex digits and ASCII chars - ch = kHexOffset; - for (i = 0; i < count; i++) { - byte val = data[offset + i]; - - line[ch++] = mHexDigit[(val >>> 4) & 0x0f]; - line[ch++] = mHexDigit[val & 0x0f]; - ch++; - - if (val >= 0x20 && val < 0x7f) - line[kAscOffset + i] = (char) val; - else - line[kAscOffset + i] = '.'; - } - - println(level, tag, new String(line)); - - // advance to next chunk of data - length -= count; - offset += count; - baseAddr += count; - } - - } - - /** - * Dump the entire contents of a byte array with DEBUG priority. - */ - static void hexDump(byte[] data) { - hexDump("ddms", LogLevel.DEBUG, data, 0, data.length); - } - - /* currently prints to stdout; could write to a log window */ - private static void println(LogLevel logLevel, String tag, String message) { - if (logLevel.getPriority() >= mLevel.getPriority()) { - if (sLogOutput != null) { - sLogOutput.printLog(logLevel, tag, message); - } else { - printLog(logLevel, tag, message); - } - } - } - - /** - * Prints a log message. - * @param logLevel - * @param tag - * @param message - */ - public static void printLog(LogLevel logLevel, String tag, String message) { - System.out.print(getLogFormatString(logLevel, tag, message)); - } - - /** - * Formats a log message. - * @param logLevel - * @param tag - * @param message - */ - public static String getLogFormatString(LogLevel logLevel, String tag, String message) { - SimpleDateFormat formatter = new SimpleDateFormat("hh:mm:ss"); - return String.format("%s %c/%s: %s\n", formatter.format(new Date()), - logLevel.getPriorityLetter(), tag, message); - } -} - - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MonitorThread.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MonitorThread.java deleted file mode 100644 index eae4707..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MonitorThread.java +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.DebugPortManager.IDebugPortProvider; -import com.android.ddmlib.Log.LogLevel; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.NotYetBoundException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * Monitor open connections. - */ -final class MonitorThread extends Thread { - - // For broadcasts to message handlers - //private static final int CLIENT_CONNECTED = 1; - - private static final int CLIENT_READY = 2; - - private static final int CLIENT_DISCONNECTED = 3; - - private volatile boolean mQuit = false; - - // List of clients we're paying attention to - private ArrayList<Client> mClientList; - - // The almighty mux - private Selector mSelector; - - // Map chunk types to handlers - private HashMap<Integer, ChunkHandler> mHandlerMap; - - // port for "debug selected" - private ServerSocketChannel mDebugSelectedChan; - - private int mNewDebugSelectedPort; - - private int mDebugSelectedPort = -1; - - /** - * "Selected" client setup to answer debugging connection to the mNewDebugSelectedPort port. - */ - private Client mSelectedClient = null; - - // singleton - private static MonitorThread mInstance; - - /** - * Generic constructor. - */ - private MonitorThread() { - super("Monitor"); - mClientList = new ArrayList<Client>(); - mHandlerMap = new HashMap<Integer, ChunkHandler>(); - - mNewDebugSelectedPort = DdmPreferences.getSelectedDebugPort(); - } - - /** - * Creates and return the singleton instance of the client monitor thread. - */ - static MonitorThread createInstance() { - return mInstance = new MonitorThread(); - } - - /** - * Get singleton instance of the client monitor thread. - */ - static MonitorThread getInstance() { - return mInstance; - } - - - /** - * Sets or changes the port number for "debug selected". - */ - synchronized void setDebugSelectedPort(int port) throws IllegalStateException { - if (mInstance == null) { - return; - } - - if (AndroidDebugBridge.getClientSupport() == false) { - return; - } - - if (mDebugSelectedChan != null) { - Log.d("ddms", "Changing debug-selected port to " + port); - mNewDebugSelectedPort = port; - wakeup(); - } else { - // we set mNewDebugSelectedPort instead of mDebugSelectedPort so that it's automatically - // opened on the first run loop. - mNewDebugSelectedPort = port; - } - } - - /** - * Sets the client to accept debugger connection on the custom "Selected debug port". - * @param selectedClient the client. Can be null. - */ - synchronized void setSelectedClient(Client selectedClient) { - if (mInstance == null) { - return; - } - - if (mSelectedClient != selectedClient) { - Client oldClient = mSelectedClient; - mSelectedClient = selectedClient; - - if (oldClient != null) { - oldClient.update(Client.CHANGE_PORT); - } - - if (mSelectedClient != null) { - mSelectedClient.update(Client.CHANGE_PORT); - } - } - } - - /** - * Returns the client accepting debugger connection on the custom "Selected debug port". - */ - Client getSelectedClient() { - return mSelectedClient; - } - - - /** - * Returns "true" if we want to retry connections to clients if we get a bad - * JDWP handshake back, "false" if we want to just mark them as bad and - * leave them alone. - */ - boolean getRetryOnBadHandshake() { - return true; // TODO? make configurable - } - - /** - * Get an array of known clients. - */ - Client[] getClients() { - synchronized (mClientList) { - return mClientList.toArray(new Client[0]); - } - } - - /** - * Register "handler" as the handler for type "type". - */ - synchronized void registerChunkHandler(int type, ChunkHandler handler) { - if (mInstance == null) { - return; - } - - synchronized (mHandlerMap) { - if (mHandlerMap.get(type) == null) { - mHandlerMap.put(type, handler); - } - } - } - - /** - * Watch for activity from clients and debuggers. - */ - @Override - public void run() { - Log.d("ddms", "Monitor is up"); - - // create a selector - try { - mSelector = Selector.open(); - } catch (IOException ioe) { - Log.logAndDisplay(LogLevel.ERROR, "ddms", - "Failed to initialize Monitor Thread: " + ioe.getMessage()); - return; - } - - while (!mQuit) { - - try { - /* - * sync with new registrations: we wait until addClient is done before going through - * and doing mSelector.select() again. - * @see {@link #addClient(Client)} - */ - synchronized (mClientList) { - } - - // (re-)open the "debug selected" port, if it's not opened yet or - // if the port changed. - try { - if (AndroidDebugBridge.getClientSupport()) { - if ((mDebugSelectedChan == null || - mNewDebugSelectedPort != mDebugSelectedPort) && - mNewDebugSelectedPort != -1) { - if (reopenDebugSelectedPort()) { - mDebugSelectedPort = mNewDebugSelectedPort; - } - } - } - } catch (IOException ioe) { - Log.e("ddms", - "Failed to reopen debug port for Selected Client to: " + mNewDebugSelectedPort); - Log.e("ddms", ioe); - mNewDebugSelectedPort = mDebugSelectedPort; // no retry - } - - int count; - try { - count = mSelector.select(); - } catch (IOException ioe) { - ioe.printStackTrace(); - continue; - } catch (CancelledKeyException cke) { - continue; - } - - if (count == 0) { - // somebody called wakeup() ? - // Log.i("ddms", "selector looping"); - continue; - } - - Set<SelectionKey> keys = mSelector.selectedKeys(); - Iterator<SelectionKey> iter = keys.iterator(); - - while (iter.hasNext()) { - SelectionKey key = iter.next(); - iter.remove(); - - try { - if (key.attachment() instanceof Client) { - processClientActivity(key); - } - else if (key.attachment() instanceof Debugger) { - processDebuggerActivity(key); - } - else if (key.attachment() instanceof MonitorThread) { - processDebugSelectedActivity(key); - } - else { - Log.e("ddms", "unknown activity key"); - } - } catch (Exception e) { - // we don't want to have our thread be killed because of any uncaught - // exception, so we intercept all here. - Log.e("ddms", "Exception during activity from Selector."); - Log.e("ddms", e); - } - } - } catch (Exception e) { - // we don't want to have our thread be killed because of any uncaught - // exception, so we intercept all here. - Log.e("ddms", "Exception MonitorThread.run()"); - Log.e("ddms", e); - } - } - } - - - /** - * Returns the port on which the selected client listen for debugger - */ - int getDebugSelectedPort() { - return mDebugSelectedPort; - } - - /* - * Something happened. Figure out what. - */ - private void processClientActivity(SelectionKey key) { - Client client = (Client)key.attachment(); - - try { - if (key.isReadable() == false || key.isValid() == false) { - Log.d("ddms", "Invalid key from " + client + ". Dropping client."); - dropClient(client, true /* notify */); - return; - } - - client.read(); - - /* - * See if we have a full packet in the buffer. It's possible we have - * more than one packet, so we have to loop. - */ - JdwpPacket packet = client.getJdwpPacket(); - while (packet != null) { - if (packet.isDdmPacket()) { - // unsolicited DDM request - hand it off - assert !packet.isReply(); - callHandler(client, packet, null); - packet.consume(); - } else if (packet.isReply() - && client.isResponseToUs(packet.getId()) != null) { - // reply to earlier DDM request - ChunkHandler handler = client - .isResponseToUs(packet.getId()); - if (packet.isError()) - client.packetFailed(packet); - else if (packet.isEmpty()) - Log.d("ddms", "Got empty reply for 0x" - + Integer.toHexString(packet.getId()) - + " from " + client); - else - callHandler(client, packet, handler); - packet.consume(); - client.removeRequestId(packet.getId()); - } else { - Log.v("ddms", "Forwarding client " - + (packet.isReply() ? "reply" : "event") + " 0x" - + Integer.toHexString(packet.getId()) + " to " - + client.getDebugger()); - client.forwardPacketToDebugger(packet); - } - - // find next - packet = client.getJdwpPacket(); - } - } catch (CancelledKeyException e) { - // key was canceled probably due to a disconnected client before we could - // read stuff coming from the client, so we drop it. - dropClient(client, true /* notify */); - } catch (IOException ex) { - // something closed down, no need to print anything. The client is simply dropped. - dropClient(client, true /* notify */); - } catch (Exception ex) { - Log.e("ddms", ex); - - /* close the client; automatically un-registers from selector */ - dropClient(client, true /* notify */); - - if (ex instanceof BufferOverflowException) { - Log.w("ddms", - "Client data packet exceeded maximum buffer size " - + client); - } else { - // don't know what this is, display it - Log.e("ddms", ex); - } - } - } - - /* - * Process an incoming DDM packet. If this is a reply to an earlier request, - * "handler" will be set to the handler responsible for the original - * request. The spec allows a JDWP message to include multiple DDM chunks. - */ - private void callHandler(Client client, JdwpPacket packet, - ChunkHandler handler) { - - // on first DDM packet received, broadcast a "ready" message - if (!client.ddmSeen()) - broadcast(CLIENT_READY, client); - - ByteBuffer buf = packet.getPayload(); - int type, length; - boolean reply = true; - - type = buf.getInt(); - length = buf.getInt(); - - if (handler == null) { - // not a reply, figure out who wants it - synchronized (mHandlerMap) { - handler = mHandlerMap.get(type); - reply = false; - } - } - - if (handler == null) { - Log.w("ddms", "Received unsupported chunk type " - + ChunkHandler.name(type) + " (len=" + length + ")"); - } else { - Log.d("ddms", "Calling handler for " + ChunkHandler.name(type) - + " [" + handler + "] (len=" + length + ")"); - ByteBuffer ibuf = buf.slice(); - ByteBuffer roBuf = ibuf.asReadOnlyBuffer(); // enforce R/O - roBuf.order(ChunkHandler.CHUNK_ORDER); - // do the handling of the chunk synchronized on the client list - // to be sure there's no concurrency issue when we look for HOME - // in hasApp() - synchronized (mClientList) { - handler.handleChunk(client, type, roBuf, reply, packet.getId()); - } - } - } - - /** - * Drops a client from the monitor. - * <p/>This will lock the {@link Client} list of the {@link Device} running <var>client</var>. - * @param client - * @param notify - */ - synchronized void dropClient(Client client, boolean notify) { - if (mInstance == null) { - return; - } - - synchronized (mClientList) { - if (mClientList.remove(client) == false) { - return; - } - } - client.close(notify); - broadcast(CLIENT_DISCONNECTED, client); - - /* - * http://forum.java.sun.com/thread.jspa?threadID=726715&start=0 - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5073504 - */ - wakeup(); - } - - /** - * Drops the provided list of clients from the monitor. This will lock the {@link Client} - * list of the {@link Device} running each of the clients. - */ - synchronized void dropClients(Collection<? extends Client> clients, boolean notify) { - for (Client c : clients) { - dropClient(c, notify); - } - } - - /* - * Process activity from one of the debugger sockets. This could be a new - * connection or a data packet. - */ - private void processDebuggerActivity(SelectionKey key) { - Debugger dbg = (Debugger)key.attachment(); - - try { - if (key.isAcceptable()) { - try { - acceptNewDebugger(dbg, null); - } catch (IOException ioe) { - Log.w("ddms", "debugger accept() failed"); - ioe.printStackTrace(); - } - } else if (key.isReadable()) { - processDebuggerData(key); - } else { - Log.d("ddm-debugger", "key in unknown state"); - } - } catch (CancelledKeyException cke) { - // key has been cancelled we can ignore that. - } - } - - /* - * Accept a new connection from a debugger. If successful, register it with - * the Selector. - */ - private void acceptNewDebugger(Debugger dbg, ServerSocketChannel acceptChan) - throws IOException { - - synchronized (mClientList) { - SocketChannel chan; - - if (acceptChan == null) - chan = dbg.accept(); - else - chan = dbg.accept(acceptChan); - - if (chan != null) { - chan.socket().setTcpNoDelay(true); - - wakeup(); - - try { - chan.register(mSelector, SelectionKey.OP_READ, dbg); - } catch (IOException ioe) { - // failed, drop the connection - dbg.closeData(); - throw ioe; - } catch (RuntimeException re) { - // failed, drop the connection - dbg.closeData(); - throw re; - } - } else { - Log.w("ddms", "ignoring duplicate debugger"); - // new connection already closed - } - } - } - - /* - * We have incoming data from the debugger. Forward it to the client. - */ - private void processDebuggerData(SelectionKey key) { - Debugger dbg = (Debugger)key.attachment(); - - try { - /* - * Read pending data. - */ - dbg.read(); - - /* - * See if we have a full packet in the buffer. It's possible we have - * more than one packet, so we have to loop. - */ - JdwpPacket packet = dbg.getJdwpPacket(); - while (packet != null) { - Log.v("ddms", "Forwarding dbg req 0x" - + Integer.toHexString(packet.getId()) + " to " - + dbg.getClient()); - - dbg.forwardPacketToClient(packet); - - packet = dbg.getJdwpPacket(); - } - } catch (IOException ioe) { - /* - * Close data connection; automatically un-registers dbg from - * selector. The failure could be caused by the debugger going away, - * or by the client going away and failing to accept our data. - * Either way, the debugger connection does not need to exist any - * longer. We also need to recycle the connection to the client, so - * that the VM sees the debugger disconnect. For a DDM-aware client - * this won't be necessary, and we can just send a "debugger - * disconnected" message. - */ - Log.d("ddms", "Closing connection to debugger " + dbg); - dbg.closeData(); - Client client = dbg.getClient(); - if (client.isDdmAware()) { - // TODO: soft-disconnect DDM-aware clients - Log.d("ddms", " (recycling client connection as well)"); - - // we should drop the client, but also attempt to reopen it. - // This is done by the DeviceMonitor. - client.getDeviceImpl().getMonitor().addClientToDropAndReopen(client, - IDebugPortProvider.NO_STATIC_PORT); - } else { - Log.d("ddms", " (recycling client connection as well)"); - // we should drop the client, but also attempt to reopen it. - // This is done by the DeviceMonitor. - client.getDeviceImpl().getMonitor().addClientToDropAndReopen(client, - IDebugPortProvider.NO_STATIC_PORT); - } - } - } - - /* - * Tell the thread that something has changed. - */ - private void wakeup() { - mSelector.wakeup(); - } - - /** - * Tell the thread to stop. Called from UI thread. - */ - synchronized void quit() { - mQuit = true; - wakeup(); - Log.d("ddms", "Waiting for Monitor thread"); - try { - this.join(); - // since we're quitting, lets drop all the client and disconnect - // the DebugSelectedPort - synchronized (mClientList) { - for (Client c : mClientList) { - c.close(false /* notify */); - broadcast(CLIENT_DISCONNECTED, c); - } - mClientList.clear(); - } - - if (mDebugSelectedChan != null) { - mDebugSelectedChan.close(); - mDebugSelectedChan.socket().close(); - mDebugSelectedChan = null; - } - mSelector.close(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - mInstance = null; - } - - /** - * Add a new Client to the list of things we monitor. Also adds the client's - * channel and the client's debugger listener to the selection list. This - * should only be called from one thread (the VMWatcherThread) to avoid a - * race between "alreadyOpen" and Client creation. - */ - synchronized void addClient(Client client) { - if (mInstance == null) { - return; - } - - Log.d("ddms", "Adding new client " + client); - - synchronized (mClientList) { - mClientList.add(client); - - /* - * Register the Client's socket channel with the selector. We attach - * the Client to the SelectionKey. If you try to register a new - * channel with the Selector while it is waiting for I/O, you will - * block. The solution is to call wakeup() and then hold a lock to - * ensure that the registration happens before the Selector goes - * back to sleep. - */ - try { - wakeup(); - - client.register(mSelector); - - Debugger dbg = client.getDebugger(); - if (dbg != null) { - dbg.registerListener(mSelector); - } - } catch (IOException ioe) { - // not really expecting this to happen - ioe.printStackTrace(); - } - } - } - - /* - * Broadcast an event to all message handlers. - */ - private void broadcast(int event, Client client) { - Log.d("ddms", "broadcast " + event + ": " + client); - - /* - * The handler objects appear once in mHandlerMap for each message they - * handle. We want to notify them once each, so we convert the HashMap - * to a HashSet before we iterate. - */ - HashSet<ChunkHandler> set; - synchronized (mHandlerMap) { - Collection<ChunkHandler> values = mHandlerMap.values(); - set = new HashSet<ChunkHandler>(values); - } - - Iterator<ChunkHandler> iter = set.iterator(); - while (iter.hasNext()) { - ChunkHandler handler = iter.next(); - switch (event) { - case CLIENT_READY: - try { - handler.clientReady(client); - } catch (IOException ioe) { - // Something failed with the client. It should - // fall out of the list the next time we try to - // do something with it, so we discard the - // exception here and assume cleanup will happen - // later. May need to propagate farther. The - // trouble is that not all values for "event" may - // actually throw an exception. - Log.w("ddms", - "Got exception while broadcasting 'ready'"); - return; - } - break; - case CLIENT_DISCONNECTED: - handler.clientDisconnected(client); - break; - default: - throw new UnsupportedOperationException(); - } - } - - } - - /** - * Opens (or reopens) the "debug selected" port and listen for connections. - * @return true if the port was opened successfully. - * @throws IOException - */ - private boolean reopenDebugSelectedPort() throws IOException { - - Log.d("ddms", "reopen debug-selected port: " + mNewDebugSelectedPort); - if (mDebugSelectedChan != null) { - mDebugSelectedChan.close(); - } - - mDebugSelectedChan = ServerSocketChannel.open(); - mDebugSelectedChan.configureBlocking(false); // required for Selector - - InetSocketAddress addr = new InetSocketAddress( - InetAddress.getByName("localhost"), //$NON-NLS-1$ - mNewDebugSelectedPort); - mDebugSelectedChan.socket().setReuseAddress(true); // enable SO_REUSEADDR - - try { - mDebugSelectedChan.socket().bind(addr); - if (mSelectedClient != null) { - mSelectedClient.update(Client.CHANGE_PORT); - } - - mDebugSelectedChan.register(mSelector, SelectionKey.OP_ACCEPT, this); - - return true; - } catch (java.net.BindException e) { - displayDebugSelectedBindError(mNewDebugSelectedPort); - - // do not attempt to reopen it. - mDebugSelectedChan = null; - mNewDebugSelectedPort = -1; - - return false; - } - } - - /* - * We have some activity on the "debug selected" port. Handle it. - */ - private void processDebugSelectedActivity(SelectionKey key) { - assert key.isAcceptable(); - - ServerSocketChannel acceptChan = (ServerSocketChannel)key.channel(); - - /* - * Find the debugger associated with the currently-selected client. - */ - if (mSelectedClient != null) { - Debugger dbg = mSelectedClient.getDebugger(); - - if (dbg != null) { - Log.d("ddms", "Accepting connection on 'debug selected' port"); - try { - acceptNewDebugger(dbg, acceptChan); - } catch (IOException ioe) { - // client should be gone, keep going - } - - return; - } - } - - Log.w("ddms", - "Connection on 'debug selected' port, but none selected"); - try { - SocketChannel chan = acceptChan.accept(); - chan.close(); - } catch (IOException ioe) { - // not expected; client should be gone, keep going - } catch (NotYetBoundException e) { - displayDebugSelectedBindError(mDebugSelectedPort); - } - } - - private void displayDebugSelectedBindError(int port) { - String message = String.format( - "Could not open Selected VM debug port (%1$d). Make sure you do not have another instance of DDMS or of the eclipse plugin running. If it's being used by something else, choose a new port number in the preferences.", - port); - - Log.logAndDisplay(LogLevel.ERROR, "ddms", message); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java deleted file mode 100644 index ac1fa91..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2007 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.UnsupportedEncodingException; -import java.util.ArrayList; - -/** - * Base implementation of {@link IShellOutputReceiver}, that takes the raw data coming from the - * socket, and convert it into {@link String} objects. - * <p/>Additionally, it splits the string by lines. - * <p/>Classes extending it must implement {@link #processNewLines(String[])} which receives - * new parsed lines as they become available. - */ -public abstract class MultiLineReceiver implements IShellOutputReceiver { - - private boolean mTrimLines = true; - - /** unfinished message line, stored for next packet */ - private String mUnfinishedLine = null; - - private final ArrayList<String> mArray = new ArrayList<String>(); - - /** - * Set the trim lines flag. - * @param trim hether the lines are trimmed, or not. - */ - public void setTrimLine(boolean trim) { - mTrimLines = trim; - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput( - * byte[], int, int) - */ - @Override - public final void addOutput(byte[] data, int offset, int length) { - if (isCancelled() == false) { - String s = null; - try { - s = new String(data, offset, length, "UTF-8"); //$NON-NLS-1$ - } catch (UnsupportedEncodingException e) { - // normal encoding didn't work, try the default one - s = new String(data, offset,length); - } - - // ok we've got a string - if (s != null) { - // if we had an unfinished line we add it. - if (mUnfinishedLine != null) { - s = mUnfinishedLine + s; - mUnfinishedLine = null; - } - - // now we split the lines - mArray.clear(); - int start = 0; - do { - int index = s.indexOf("\r\n", start); //$NON-NLS-1$ - - // if \r\n was not found, this is an unfinished line - // and we store it to be processed for the next packet - if (index == -1) { - mUnfinishedLine = s.substring(start); - break; - } - - // so we found a \r\n; - // extract the line - String line = s.substring(start, index); - if (mTrimLines) { - line = line.trim(); - } - mArray.add(line); - - // move start to after the \r\n we found - start = index + 2; - } while (true); - - if (mArray.size() > 0) { - // at this point we've split all the lines. - // make the array - String[] lines = mArray.toArray(new String[mArray.size()]); - - // send it for final processing - processNewLines(lines); - } - } - } - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#flush() - */ - @Override - public final void flush() { - if (mUnfinishedLine != null) { - processNewLines(new String[] { mUnfinishedLine }); - } - - done(); - } - - /** - * Terminates the process. This is called after the last lines have been through - * {@link #processNewLines(String[])}. - */ - public void done() { - // do nothing. - } - - /** - * Called when new lines are being received by the remote process. - * <p/>It is guaranteed that the lines are complete when they are given to this method. - * @param lines The array containing the new lines. - */ - public abstract void processNewLines(String[] lines); -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeAllocationInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeAllocationInfo.java deleted file mode 100644 index 9b104ba..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeAllocationInfo.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2007 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.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Stores native allocation information. - * <p/>Contains number of allocations, their size and the stack trace. - * <p/>Note: the ddmlib does not resolve the stack trace automatically. While this class provides - * storage for resolved stack trace, this is merely for convenience. - */ -public final class NativeAllocationInfo { - /* Keywords used as delimiters in the string representation of a NativeAllocationInfo */ - public static final String END_STACKTRACE_KW = "EndStacktrace"; - public static final String BEGIN_STACKTRACE_KW = "BeginStacktrace:"; - public static final String TOTAL_SIZE_KW = "TotalSize:"; - public static final String SIZE_KW = "Size:"; - public static final String ALLOCATIONS_KW = "Allocations:"; - - /* constants for flag bits */ - private static final int FLAG_ZYGOTE_CHILD = (1<<31); - private static final int FLAG_MASK = (FLAG_ZYGOTE_CHILD); - - /** Libraries whose methods will be assumed to be not part of the user code. */ - private static final List<String> FILTERED_LIBRARIES = Arrays.asList(new String[] { - "libc.so", - "libc_malloc_debug_leak.so", - }); - - /** Method names that should be assumed to be not part of the user code. */ - private static final List<Pattern> FILTERED_METHOD_NAME_PATTERNS = Arrays.asList(new Pattern[] { - Pattern.compile("malloc", Pattern.CASE_INSENSITIVE), - Pattern.compile("calloc", Pattern.CASE_INSENSITIVE), - Pattern.compile("realloc", Pattern.CASE_INSENSITIVE), - Pattern.compile("operator new", Pattern.CASE_INSENSITIVE), - Pattern.compile("memalign", Pattern.CASE_INSENSITIVE), - }); - - private final int mSize; - - private final boolean mIsZygoteChild; - - private final int mAllocations; - - private final ArrayList<Long> mStackCallAddresses = new ArrayList<Long>(); - - private ArrayList<NativeStackCallInfo> mResolvedStackCall = null; - - private boolean mIsStackCallResolved = false; - - /** - * Constructs a new {@link NativeAllocationInfo}. - * @param size The size of the allocations. - * @param allocations the allocation count - */ - public NativeAllocationInfo(int size, int allocations) { - this.mSize = size & ~FLAG_MASK; - this.mIsZygoteChild = ((size & FLAG_ZYGOTE_CHILD) != 0); - this.mAllocations = allocations; - } - - /** - * Adds a stack call address for this allocation. - * @param address The address to add. - */ - public void addStackCallAddress(long address) { - mStackCallAddresses.add(address); - } - - /** - * Returns the total size of this allocation. - */ - public int getSize() { - return mSize; - } - - /** - * Returns whether the allocation happened in a child of the zygote - * process. - */ - public boolean isZygoteChild() { - return mIsZygoteChild; - } - - /** - * Returns the allocation count. - */ - public int getAllocationCount() { - return mAllocations; - } - - /** - * Returns whether the stack call addresses have been resolved into - * {@link NativeStackCallInfo} objects. - */ - public boolean isStackCallResolved() { - return mIsStackCallResolved; - } - - /** - * Returns the stack call of this allocation as raw addresses. - * @return the list of addresses where the allocation happened. - */ - public List<Long> getStackCallAddresses() { - return mStackCallAddresses; - } - - /** - * Sets the resolved stack call for this allocation. - * <p/> - * If <code>resolvedStackCall</code> is non <code>null</code> then - * {@link #isStackCallResolved()} will return <code>true</code> after this call. - * @param resolvedStackCall The list of {@link NativeStackCallInfo}. - */ - public synchronized void setResolvedStackCall(List<NativeStackCallInfo> resolvedStackCall) { - if (mResolvedStackCall == null) { - mResolvedStackCall = new ArrayList<NativeStackCallInfo>(); - } else { - mResolvedStackCall.clear(); - } - mResolvedStackCall.addAll(resolvedStackCall); - mIsStackCallResolved = mResolvedStackCall.size() != 0; - } - - /** - * Returns the resolved stack call. - * @return An array of {@link NativeStackCallInfo} or <code>null</code> if the stack call - * was not resolved. - * @see #setResolvedStackCall(List) - * @see #isStackCallResolved() - */ - public synchronized List<NativeStackCallInfo> getResolvedStackCall() { - if (mIsStackCallResolved) { - return mResolvedStackCall; - } - - return null; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - * @return <code>true</code> if this object is equal to the obj argument; - * <code>false</code> otherwise. - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (obj == this) - return true; - if (obj instanceof NativeAllocationInfo) { - NativeAllocationInfo mi = (NativeAllocationInfo)obj; - // quick compare of size, alloc, and stackcall size - if (mSize != mi.mSize || mAllocations != mi.mAllocations || - mStackCallAddresses.size() != mi.mStackCallAddresses.size()) { - return false; - } - // compare the stack addresses - int count = mStackCallAddresses.size(); - for (int i = 0 ; i < count ; i++) { - long a = mStackCallAddresses.get(i); - long b = mi.mStackCallAddresses.get(i); - if (a != b) { - return false; - } - } - - return true; - } - return false; - } - - - @Override - public int hashCode() { - // Follow Effective Java's recipe re hash codes. - // Includes all the fields looked at by equals(). - - int result = 17; // arbitrary starting point - - result = 31 * result + mSize; - result = 31 * result + mAllocations; - result = 31 * result + mStackCallAddresses.size(); - - for (long addr : mStackCallAddresses) { - result = 31 * result + (int) (addr ^ (addr >>> 32)); - } - - return result; - } - - /** - * Returns a string representation of the object. - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(ALLOCATIONS_KW); - buffer.append(' '); - buffer.append(mAllocations); - buffer.append('\n'); - - buffer.append(SIZE_KW); - buffer.append(' '); - buffer.append(mSize); - buffer.append('\n'); - - buffer.append(TOTAL_SIZE_KW); - buffer.append(' '); - buffer.append(mSize * mAllocations); - buffer.append('\n'); - - if (mResolvedStackCall != null) { - buffer.append(BEGIN_STACKTRACE_KW); - buffer.append('\n'); - for (NativeStackCallInfo source : mResolvedStackCall) { - long addr = source.getAddress(); - if (addr == 0) { - continue; - } - - if (source.getLineNumber() != -1) { - buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d\n", addr, - source.getLibraryName(), source.getMethodName(), - source.getSourceFile(), source.getLineNumber())); - } else { - buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s\n", addr, - source.getLibraryName(), source.getMethodName(), source.getSourceFile())); - } - } - buffer.append(END_STACKTRACE_KW); - buffer.append('\n'); - } - - return buffer.toString(); - } - - /** - * Returns the first {@link NativeStackCallInfo} that is relevant. - * <p/> - * A relevant <code>NativeStackCallInfo</code> is a stack call that is not deep in the - * lower level of the libc, but the actual method that performed the allocation. - * @return a <code>NativeStackCallInfo</code> or <code>null</code> if the stack call has not - * been processed from the raw addresses. - * @see #setResolvedStackCall(List) - * @see #isStackCallResolved() - */ - public synchronized NativeStackCallInfo getRelevantStackCallInfo() { - if (mIsStackCallResolved && mResolvedStackCall != null) { - for (NativeStackCallInfo info : mResolvedStackCall) { - if (isRelevantLibrary(info.getLibraryName()) - && isRelevantMethod(info.getMethodName())) { - return info; - } - } - - // couldnt find a relevant one, so we'll return the first one if it exists. - if (mResolvedStackCall.size() > 0) - return mResolvedStackCall.get(0); - } - - return null; - } - - private boolean isRelevantLibrary(String libPath) { - for (String l : FILTERED_LIBRARIES) { - if (libPath.endsWith(l)) { - return false; - } - } - - return true; - } - - private boolean isRelevantMethod(String methodName) { - for (Pattern p : FILTERED_METHOD_NAME_PATTERNS) { - Matcher m = p.matcher(methodName); - if (m.find()) { - return false; - } - } - - return true; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeLibraryMapInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeLibraryMapInfo.java deleted file mode 100644 index 5a26317..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeLibraryMapInfo.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * Memory address to library mapping for native libraries. - * <p/> - * Each instance represents a single native library and its start and end memory addresses. - */ -public final class NativeLibraryMapInfo { - private long mStartAddr; - private long mEndAddr; - - private String mLibrary; - - /** - * Constructs a new native library map info. - * @param startAddr The start address of the library. - * @param endAddr The end address of the library. - * @param library The name of the library. - */ - NativeLibraryMapInfo(long startAddr, long endAddr, String library) { - this.mStartAddr = startAddr; - this.mEndAddr = endAddr; - this.mLibrary = library; - } - - /** - * Returns the name of the library. - */ - public String getLibraryName() { - return mLibrary; - } - - /** - * Returns the start address of the library. - */ - public long getStartAddress() { - return mStartAddr; - } - - /** - * Returns the end address of the library. - */ - public long getEndAddress() { - return mEndAddr; - } - - /** - * Returns whether the specified address is inside the library. - * @param address The address to test. - * @return <code>true</code> if the address is between the start and end address of the library. - * @see #getStartAddress() - * @see #getEndAddress() - */ - public boolean isWithinLibrary(long address) { - return address >= mStartAddr && address <= mEndAddr; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java deleted file mode 100644 index 1d4af86..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2007 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.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Represents a stack call. This is used to return all of the call - * information as one object. - */ -public final class NativeStackCallInfo { - private final static Pattern SOURCE_NAME_PATTERN = Pattern.compile("^(.+):(\\d+)$"); - - /** address of this stack frame */ - private long mAddress; - - /** name of the library */ - private String mLibrary; - - /** name of the method */ - private String mMethod; - - /** - * name of the source file + line number in the format<br> - * <sourcefile>:<linenumber> - */ - private String mSourceFile; - - private int mLineNumber = -1; - - /** - * Basic constructor with library, method, and sourcefile information - * - * @param address address of this stack frame - * @param lib The name of the library - * @param method the name of the method - * @param sourceFile the name of the source file and the line number - * as "[sourcefile]:[fileNumber]" - */ - public NativeStackCallInfo(long address, String lib, String method, String sourceFile) { - mAddress = address; - mLibrary = lib; - mMethod = method; - - Matcher m = SOURCE_NAME_PATTERN.matcher(sourceFile); - if (m.matches()) { - mSourceFile = m.group(1); - try { - mLineNumber = Integer.parseInt(m.group(2)); - } catch (NumberFormatException e) { - // do nothing, the line number will stay at -1 - } - } else { - mSourceFile = sourceFile; - } - } - - /** - * Returns the address of this stack frame. - */ - public long getAddress() { - return mAddress; - } - - /** - * Returns the name of the library name. - */ - public String getLibraryName() { - return mLibrary; - } - - /** - * Returns the name of the method. - */ - public String getMethodName() { - return mMethod; - } - - /** - * Returns the name of the source file. - */ - public String getSourceFile() { - return mSourceFile; - } - - /** - * Returns the line number, or -1 if unknown. - */ - public int getLineNumber() { - return mLineNumber; - } - - @Override - public String toString() { - return String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d", - getAddress(), getLibraryName(), getMethodName(), getSourceFile(), getLineNumber()); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NullOutputReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NullOutputReceiver.java deleted file mode 100644 index a963a64..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/NullOutputReceiver.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * Implementation of {@link IShellOutputReceiver} that does nothing. - * <p/>This can be used to execute a remote shell command when the output is not needed. - */ -public final class NullOutputReceiver implements IShellOutputReceiver { - - private static NullOutputReceiver sReceiver = new NullOutputReceiver(); - - public static IShellOutputReceiver getReceiver() { - return sReceiver; - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput(byte[], int, int) - */ - @Override - public void addOutput(byte[] data, int offset, int length) { - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#flush() - */ - @Override - public void flush() { - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#isCancelled() - */ - @Override - public boolean isCancelled() { - return false; - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/OpenGlTraceChunkHandler.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/OpenGlTraceChunkHandler.java deleted file mode 100644 index 12ba142..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/OpenGlTraceChunkHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2012 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; -import java.nio.ByteBuffer; - -public class OpenGlTraceChunkHandler extends ChunkHandler { - /** GL TRace Control: data in the packet enables or disables tracing. */ - public static final int CHUNK_GLTR = type("GLTR"); - - private OpenGlTraceChunkHandler() { - } - - @Override - void clientReady(Client client) throws IOException { - } - - @Override - void clientDisconnected(Client client) { - } - - @Override - void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) { - handleUnknownChunk(client, type, data, isReply, msgId); - } - - public static void sendStartGlTracing(Client client) throws IOException { - ByteBuffer buf = allocBuffer(4); - JdwpPacket packet = new JdwpPacket(buf); - - ByteBuffer chunkBuf = getChunkDataBuf(buf); - chunkBuf.putInt(1); - finishChunkPacket(packet, CHUNK_GLTR, chunkBuf.position()); - - client.sendAndConsume(packet); - } - - public static void sendStopGlTracing(Client client) throws IOException { - ByteBuffer buf = allocBuffer(4); - JdwpPacket packet = new JdwpPacket(buf); - - ByteBuffer chunkBuf = getChunkDataBuf(buf); - chunkBuf.putInt(0); - finishChunkPacket(packet, CHUNK_GLTR, chunkBuf.position()); - - client.sendAndConsume(packet); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/RawImage.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/RawImage.java deleted file mode 100644 index adb0cc9..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/RawImage.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2007 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.nio.ByteBuffer; - -/** - * Data representing an image taken from a device frame buffer. - */ -public final class RawImage { - public int version; - public int bpp; - public int size; - public int width; - public int height; - public int red_offset; - public int red_length; - public int blue_offset; - public int blue_length; - public int green_offset; - public int green_length; - public int alpha_offset; - public int alpha_length; - - public byte[] data; - - /** - * Reads the header of a RawImage from a {@link ByteBuffer}. - * <p/>The way the data is sent over adb is defined in system/core/adb/framebuffer_service.c - * @param version the version of the protocol. - * @param buf the buffer to read from. - * @return true if success - */ - public boolean readHeader(int version, ByteBuffer buf) { - this.version = version; - - if (version == 16) { - // compatibility mode with original protocol - this.bpp = 16; - - // read actual values. - this.size = buf.getInt(); - this.width = buf.getInt(); - this.height = buf.getInt(); - - // create default values for the rest. Format is 565 - this.red_offset = 11; - this.red_length = 5; - this.green_offset = 5; - this.green_length = 6; - this.blue_offset = 0; - this.blue_length = 5; - this.alpha_offset = 0; - this.alpha_length = 0; - } else if (version == 1) { - this.bpp = buf.getInt(); - this.size = buf.getInt(); - this.width = buf.getInt(); - this.height = buf.getInt(); - this.red_offset = buf.getInt(); - this.red_length = buf.getInt(); - this.blue_offset = buf.getInt(); - this.blue_length = buf.getInt(); - this.green_offset = buf.getInt(); - this.green_length = buf.getInt(); - this.alpha_offset = buf.getInt(); - this.alpha_length = buf.getInt(); - } else { - // unsupported protocol! - return false; - } - - return true; - } - - /** - * Returns the mask value for the red color. - * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData - */ - public int getRedMask() { - return getMask(red_length, red_offset); - } - - /** - * Returns the mask value for the green color. - * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData - */ - public int getGreenMask() { - return getMask(green_length, green_offset); - } - - /** - * Returns the mask value for the blue color. - * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData - */ - public int getBlueMask() { - return getMask(blue_length, blue_offset); - } - - /** - * Returns the size of the header for a specific version of the framebuffer adb protocol. - * @param version the version of the protocol - * @return the number of int that makes up the header. - */ - public static int getHeaderSize(int version) { - switch (version) { - case 16: // compatibility mode - return 3; // size, width, height - case 1: - return 12; // bpp, size, width, height, 4*(length, offset) - } - - return 0; - } - - /** - * Returns a rotated version of the image - * The image is rotated counter-clockwise. - */ - public RawImage getRotated() { - RawImage rotated = new RawImage(); - rotated.version = this.version; - rotated.bpp = this.bpp; - rotated.size = this.size; - rotated.red_offset = this.red_offset; - rotated.red_length = this.red_length; - rotated.blue_offset = this.blue_offset; - rotated.blue_length = this.blue_length; - rotated.green_offset = this.green_offset; - rotated.green_length = this.green_length; - rotated.alpha_offset = this.alpha_offset; - rotated.alpha_length = this.alpha_length; - - rotated.width = this.height; - rotated.height = this.width; - - int count = this.data.length; - rotated.data = new byte[count]; - - int byteCount = this.bpp >> 3; // bpp is in bits, we want bytes to match our array - final int w = this.width; - final int h = this.height; - for (int y = 0 ; y < h ; y++) { - for (int x = 0 ; x < w ; x++) { - System.arraycopy( - this.data, (y * w + x) * byteCount, - rotated.data, ((w-x-1) * h + y) * byteCount, - byteCount); - } - } - - return rotated; - } - - /** - * Returns an ARGB integer value for the pixel at <var>index</var> in {@link #data}. - */ - public int getARGB(int index) { - int value; - if (bpp == 16) { - value = data[index] & 0x00FF; - value |= (data[index+1] << 8) & 0x0FF00; - } else if (bpp == 32) { - value = data[index] & 0x00FF; - value |= (data[index+1] & 0x00FF) << 8; - value |= (data[index+2] & 0x00FF) << 16; - value |= (data[index+3] & 0x00FF) << 24; - } else { - throw new UnsupportedOperationException("RawImage.getARGB(int) only works in 16 and 32 bit mode."); - } - - int r = ((value >>> red_offset) & getMask(red_length)) << (8 - red_length); - int g = ((value >>> green_offset) & getMask(green_length)) << (8 - green_length); - int b = ((value >>> blue_offset) & getMask(blue_length)) << (8 - blue_length); - int a; - if (alpha_length == 0) { - a = 0xFF; // force alpha to opaque if there's no alpha value in the framebuffer. - } else { - a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length); - } - - return a << 24 | r << 16 | g << 8 | b; - } - - /** - * creates a mask value based on a length and offset. - * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData - */ - private int getMask(int length, int offset) { - int res = getMask(length) << offset; - - // if the bpp is 32 bits then we need to invert it because the buffer is in little endian - if (bpp == 32) { - return Integer.reverseBytes(res); - } - - return res; - } - - /** - * Creates a mask value based on a length. - * @param length - * @return - */ - private static int getMask(int length) { - return (1 << length) - 1; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ShellCommandUnresponsiveException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ShellCommandUnresponsiveException.java deleted file mode 100644 index 09823c4..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ShellCommandUnresponsiveException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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; - - -/** - * 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 Exception { - private static final long serialVersionUID = 1L; -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncException.java deleted file mode 100644 index 76de367..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncException.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 transfer using {@link SyncService} doesn't complete. - * <p/>This is different from an {@link IOException} because it's not the underlying connection - * that triggered the error, but the adb transfer protocol that didn't work somehow, or that the - * targets (local and/or remote) were wrong. - */ -public class SyncException extends CanceledException { - private static final long serialVersionUID = 1L; - - public enum SyncError { - /** canceled transfer */ - CANCELED("Operation was canceled by the user."), - /** Transfer error */ - TRANSFER_PROTOCOL_ERROR("Adb Transfer Protocol Error."), - /** unknown remote object during a pull */ - NO_REMOTE_OBJECT("Remote object doesn't exist!"), - /** Result code when attempting to pull multiple files into a file */ - TARGET_IS_FILE("Target object is a file."), - /** Result code when attempting to pull multiple into a directory that does not exist. */ - NO_DIR_TARGET("Target directory doesn't exist."), - /** wrong encoding on the remote path. */ - REMOTE_PATH_ENCODING("Remote Path encoding is not supported."), - /** remote path that is too long. */ - REMOTE_PATH_LENGTH("Remote path is too long."), - /** error while reading local file. */ - FILE_READ_ERROR("Reading local file failed!"), - /** error while writing local file. */ - FILE_WRITE_ERROR("Writing local file failed!"), - /** attempting to push a directory. */ - LOCAL_IS_DIRECTORY("Local path is a directory."), - /** attempting to push a non-existent file. */ - NO_LOCAL_FILE("Local path doesn't exist."), - /** when the target path of a multi file push is a file. */ - REMOTE_IS_FILE("Remote path is a file."), - /** receiving too much data from the remove device at once */ - BUFFER_OVERRUN("Receiving too much data."); - - private final String mMessage; - - private SyncError(String message) { - mMessage = message; - } - - public String getMessage() { - return mMessage; - } - } - - private final SyncError mError; - - public SyncException(SyncError error) { - super(error.getMessage()); - mError = error; - } - - public SyncException(SyncError error, String message) { - super(message); - mError = error; - } - - public SyncException(SyncError error, Throwable cause) { - super(error.getMessage(), cause); - mError = error; - } - - public SyncError getErrorCode() { - return mError; - } - - /** - * Returns true if the sync was canceled by user input. - */ - @Override - public boolean wasCanceled() { - return mError == SyncError.CANCELED; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncService.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncService.java deleted file mode 100644 index f207567..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/SyncService.java +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.ddmlib.AdbHelper.AdbResponse; -import com.android.ddmlib.FileListingService.FileEntry; -import com.android.ddmlib.SyncException.SyncError; -import com.android.ddmlib.utils.ArrayHelper; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.InetSocketAddress; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; - -/** - * Sync service class to push/pull to/from devices/emulators, through the debug bridge. - * <p/> - * To get a {@link SyncService} object, use {@link Device#getSyncService()}. - */ -public final class SyncService { - - private final static byte[] ID_OKAY = { 'O', 'K', 'A', 'Y' }; - private final static byte[] ID_FAIL = { 'F', 'A', 'I', 'L' }; - private final static byte[] ID_STAT = { 'S', 'T', 'A', 'T' }; - private final static byte[] ID_RECV = { 'R', 'E', 'C', 'V' }; - private final static byte[] ID_DATA = { 'D', 'A', 'T', 'A' }; - private final static byte[] ID_DONE = { 'D', 'O', 'N', 'E' }; - private final static byte[] ID_SEND = { 'S', 'E', 'N', 'D' }; -// private final static byte[] ID_LIST = { 'L', 'I', 'S', 'T' }; -// private final static byte[] ID_DENT = { 'D', 'E', 'N', 'T' }; - - private final static NullSyncProgresMonitor sNullSyncProgressMonitor = - new NullSyncProgresMonitor(); - - private final static int S_ISOCK = 0xC000; // type: symbolic link - private final static int S_IFLNK = 0xA000; // type: symbolic link - private final static int S_IFREG = 0x8000; // type: regular file - private final static int S_IFBLK = 0x6000; // type: block device - private final static int S_IFDIR = 0x4000; // type: directory - private final static int S_IFCHR = 0x2000; // type: character device - private final static int S_IFIFO = 0x1000; // type: fifo -/* - private final static int S_ISUID = 0x0800; // set-uid bit - private final static int S_ISGID = 0x0400; // set-gid bit - private final static int S_ISVTX = 0x0200; // sticky bit - private final static int S_IRWXU = 0x01C0; // user permissions - private final static int S_IRUSR = 0x0100; // user: read - private final static int S_IWUSR = 0x0080; // user: write - private final static int S_IXUSR = 0x0040; // user: execute - private final static int S_IRWXG = 0x0038; // group permissions - private final static int S_IRGRP = 0x0020; // group: read - private final static int S_IWGRP = 0x0010; // group: write - private final static int S_IXGRP = 0x0008; // group: execute - private final static int S_IRWXO = 0x0007; // other permissions - private final static int S_IROTH = 0x0004; // other: read - private final static int S_IWOTH = 0x0002; // other: write - private final static int S_IXOTH = 0x0001; // other: execute -*/ - - private final static int SYNC_DATA_MAX = 64*1024; - private final static int REMOTE_PATH_MAX_LENGTH = 1024; - - /** - * Classes which implement this interface provide methods that deal - * with displaying transfer progress. - */ - public interface ISyncProgressMonitor { - /** - * Sent when the transfer starts - * @param totalWork the total amount of work. - */ - public void start(int totalWork); - /** - * Sent when the transfer is finished or interrupted. - */ - public void stop(); - /** - * Sent to query for possible cancellation. - * @return true if the transfer should be stopped. - */ - public boolean isCanceled(); - /** - * Sent when a sub task is started. - * @param name the name of the sub task. - */ - public void startSubTask(String name); - /** - * Sent when some progress have been made. - * @param work the amount of work done. - */ - public void advance(int work); - } - - /** - * A Sync progress monitor that does nothing - */ - private static class NullSyncProgresMonitor implements ISyncProgressMonitor { - @Override - public void advance(int work) { - } - @Override - public boolean isCanceled() { - return false; - } - - @Override - public void start(int totalWork) { - } - @Override - public void startSubTask(String name) { - } - @Override - public void stop() { - } - } - - private InetSocketAddress mAddress; - private Device mDevice; - private SocketChannel mChannel; - - /** - * Buffer used to send data. Allocated when needed and reused afterward. - */ - private byte[] mBuffer; - - /** - * Creates a Sync service object. - * @param address The address to connect to - * @param device the {@link Device} that the service connects to. - */ - SyncService(InetSocketAddress address, Device device) { - mAddress = address; - mDevice = device; - } - - /** - * 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 TimeoutException, AdbCommandRejectedException, IOException { - try { - mChannel = SocketChannel.open(mAddress); - mChannel.configureBlocking(false); - - // target a specific device - AdbHelper.setDevice(mChannel, mDevice); - - byte[] request = AdbHelper.formAdbRequest("sync:"); //$NON-NLS-1$ - AdbHelper.write(mChannel, request, -1, DdmPreferences.getTimeOut()); - - AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false /* readDiagString */); - - 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 { - mChannel.close(); - } catch (IOException e2) { - // we want to throw the original exception, so we ignore this one. - } - mChannel = null; - } - - throw e; - } - - return true; - } - - /** - * Closes the connection. - */ - public void close() { - if (mChannel != null) { - try { - mChannel.close(); - } catch (IOException e) { - // nothing to be done really... - } - mChannel = null; - } - } - - /** - * Returns a sync progress monitor that does nothing. This allows background tasks that don't - * want/need to display ui, to pass a valid {@link ISyncProgressMonitor}. - * <p/>This object can be reused multiple times and can be used by concurrent threads. - */ - public static ISyncProgressMonitor getNullProgressMonitor() { - return sNullSyncProgressMonitor; - } - - /** - * Pulls file(s) or folder(s). - * @param entries the remote item(s) to pull - * @param localPath The local destination. If the entries count is > 1 or - * if the unique entry is a folder, this should be a folder. - * @param monitor The progress monitor. Cannot be null. - * @throws SyncException - * @throws IOException - * @throws TimeoutException - * - * @see FileListingService.FileEntry - * @see #getNullProgressMonitor() - */ - public void pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor) - throws SyncException, IOException, TimeoutException { - - // first we check the destination is a directory and exists - File f = new File(localPath); - if (f.exists() == false) { - throw new SyncException(SyncError.NO_DIR_TARGET); - } - if (f.isDirectory() == false) { - throw new SyncException(SyncError.TARGET_IS_FILE); - } - - // get a FileListingService object - FileListingService fls = new FileListingService(mDevice); - - // compute the number of file to move - int total = getTotalRemoteFileSize(entries, fls); - - // start the monitor - monitor.start(total); - - doPull(entries, localPath, fls, monitor); - - monitor.stop(); - } - - /** - * Pulls a single file. - * @param remote the remote file - * @param localFilename The local destination. - * @param monitor The progress monitor. Cannot be null. - * - * @throws IOException in case of an IO exception. - * @throws TimeoutException in case of a timeout reading responses from the device. - * @throws SyncException in case of a sync exception. - * - * @see FileListingService.FileEntry - * @see #getNullProgressMonitor() - */ - public void pullFile(FileEntry remote, String localFilename, ISyncProgressMonitor monitor) - throws IOException, SyncException, TimeoutException { - int total = remote.getSizeValue(); - monitor.start(total); - - doPullFile(remote.getFullPath(), localFilename, monitor); - - monitor.stop(); - } - - /** - * Pulls a single file. - * <p/>Because this method just deals with a String for the remote file instead of a - * {@link FileEntry}, the size of the file being pulled is unknown and the - * {@link ISyncProgressMonitor} will not properly show the progress - * @param remoteFilepath the full path to the remote file - * @param localFilename The local destination. - * @param monitor The progress monitor. Cannot be null. - * - * @throws IOException in case of an IO exception. - * @throws TimeoutException in case of a timeout reading responses from the device. - * @throws SyncException in case of a sync exception. - * - * @see #getNullProgressMonitor() - */ - public void pullFile(String remoteFilepath, String localFilename, - ISyncProgressMonitor monitor) throws TimeoutException, IOException, SyncException { - Integer mode = readMode(remoteFilepath); - if (mode == null) { - // attempts to download anyway - } else if (mode == 0) { - throw new SyncException(SyncError.NO_REMOTE_OBJECT); - } - - monitor.start(0); - //TODO: use the {@link FileListingService} to get the file size. - - doPullFile(remoteFilepath, localFilename, monitor); - - monitor.stop(); - } - - /** - * Push several files. - * @param local An array of loca files to push - * @param remote the remote {@link FileEntry} representing a directory. - * @param monitor The progress monitor. Cannot be null. - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - public void push(String[] local, FileEntry remote, ISyncProgressMonitor monitor) - throws SyncException, IOException, TimeoutException { - if (remote.isDirectory() == false) { - throw new SyncException(SyncError.REMOTE_IS_FILE); - } - - // make a list of File from the list of String - ArrayList<File> files = new ArrayList<File>(); - for (String path : local) { - files.add(new File(path)); - } - - // get the total count of the bytes to transfer - File[] fileArray = files.toArray(new File[files.size()]); - int total = getTotalLocalFileSize(fileArray); - - monitor.start(total); - - doPush(fileArray, remote.getFullPath(), monitor); - - monitor.stop(); - } - - /** - * Push a single file. - * @param local the local filepath. - * @param remote The remote filepath. - * @param monitor The progress monitor. Cannot be null. - * - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - public void pushFile(String local, String remote, ISyncProgressMonitor monitor) - throws SyncException, IOException, TimeoutException { - File f = new File(local); - if (f.exists() == false) { - throw new SyncException(SyncError.NO_LOCAL_FILE); - } - - if (f.isDirectory()) { - throw new SyncException(SyncError.LOCAL_IS_DIRECTORY); - } - - monitor.start((int)f.length()); - - doPushFile(local, remote, monitor); - - monitor.stop(); - } - - /** - * compute the recursive file size of all the files in the list. Folder - * have a weight of 1. - * @param entries - * @param fls - * @return - */ - private int getTotalRemoteFileSize(FileEntry[] entries, FileListingService fls) { - int count = 0; - for (FileEntry e : entries) { - int type = e.getType(); - if (type == FileListingService.TYPE_DIRECTORY) { - // get the children - FileEntry[] children = fls.getChildren(e, false, null); - count += getTotalRemoteFileSize(children, fls) + 1; - } else if (type == FileListingService.TYPE_FILE) { - count += e.getSizeValue(); - } - } - - return count; - } - - /** - * compute the recursive file size of all the files in the list. Folder - * have a weight of 1. - * This does not check for circular links. - * @param files - * @return - */ - private int getTotalLocalFileSize(File[] files) { - int count = 0; - - for (File f : files) { - if (f.exists()) { - if (f.isDirectory()) { - return getTotalLocalFileSize(f.listFiles()) + 1; - } else if (f.isFile()) { - count += f.length(); - } - } - } - - return count; - } - - /** - * Pulls multiple files/folders recursively. - * @param entries The list of entry to pull - * @param localPath the localpath to a directory - * @param fileListingService a FileListingService object to browse through remote directories. - * @param monitor the progress monitor. Must be started already. - * - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - private void doPull(FileEntry[] entries, String localPath, - FileListingService fileListingService, - ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException { - - for (FileEntry e : entries) { - // check if we're cancelled - if (monitor.isCanceled() == true) { - throw new SyncException(SyncError.CANCELED); - } - - // get type (we only pull directory and files for now) - int type = e.getType(); - if (type == FileListingService.TYPE_DIRECTORY) { - monitor.startSubTask(e.getFullPath()); - String dest = localPath + File.separator + e.getName(); - - // make the directory - File d = new File(dest); - d.mkdir(); - - // then recursively call the content. Since we did a ls command - // to get the number of files, we can use the cache - FileEntry[] children = fileListingService.getChildren(e, true, null); - doPull(children, dest, fileListingService, monitor); - monitor.advance(1); - } else if (type == FileListingService.TYPE_FILE) { - monitor.startSubTask(e.getFullPath()); - String dest = localPath + File.separator + e.getName(); - doPullFile(e.getFullPath(), dest, monitor); - } - } - } - - /** - * Pulls a remote file - * @param remotePath the remote file (length max is 1024) - * @param localPath the local destination - * @param monitor the monitor. The monitor must be started already. - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - private void doPullFile(String remotePath, String localPath, - ISyncProgressMonitor monitor) throws IOException, SyncException, TimeoutException { - byte[] msg = null; - byte[] pullResult = new byte[8]; - - final int timeOut = DdmPreferences.getTimeOut(); - - try { - byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING); - - if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) { - throw new SyncException(SyncError.REMOTE_PATH_LENGTH); - } - - // create the full request message - msg = createFileReq(ID_RECV, remotePathContent); - - // and send it. - AdbHelper.write(mChannel, msg, -1, timeOut); - - // read the result, in a byte array containing 2 ints - // (id, size) - AdbHelper.read(mChannel, pullResult, -1, timeOut); - - // check we have the proper data back - if (checkResult(pullResult, ID_DATA) == false && - checkResult(pullResult, ID_DONE) == false) { - throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR, - readErrorMessage(pullResult, timeOut)); - } - } catch (UnsupportedEncodingException e) { - throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e); - } - - // access the destination file - File f = new File(localPath); - - // create the stream to write in the file. We use a new try/catch block to differentiate - // between file and network io exceptions. - FileOutputStream fos = null; - try { - fos = new FileOutputStream(f); - - // the buffer to read the data - byte[] data = new byte[SYNC_DATA_MAX]; - - // loop to get data until we're done. - while (true) { - // check if we're cancelled - if (monitor.isCanceled() == true) { - throw new SyncException(SyncError.CANCELED); - } - - // if we're done, we stop the loop - if (checkResult(pullResult, ID_DONE)) { - break; - } - if (checkResult(pullResult, ID_DATA) == false) { - // hmm there's an error - throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR, - readErrorMessage(pullResult, timeOut)); - } - int length = ArrayHelper.swap32bitFromArray(pullResult, 4); - if (length > SYNC_DATA_MAX) { - // buffer overrun! - // error and exit - throw new SyncException(SyncError.BUFFER_OVERRUN); - } - - // now read the length we received - AdbHelper.read(mChannel, data, length, timeOut); - - // get the header for the next packet. - AdbHelper.read(mChannel, pullResult, -1, timeOut); - - // write the content in the file - fos.write(data, 0, length); - - monitor.advance(length); - } - - fos.flush(); - } catch (IOException e) { - Log.e("ddms", String.format("Failed to open local file %s for writing, Reason: %s", - f.getAbsolutePath(), e.toString())); - throw new SyncException(SyncError.FILE_WRITE_ERROR); - } finally { - if (fos != null) { - fos.close(); - } - } - } - - - /** - * Push multiple files - * @param fileArray - * @param remotePath - * @param monitor - * - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - private void doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor) - throws SyncException, IOException, TimeoutException { - for (File f : fileArray) { - // check if we're canceled - if (monitor.isCanceled() == true) { - throw new SyncException(SyncError.CANCELED); - } - if (f.exists()) { - if (f.isDirectory()) { - // append the name of the directory to the remote path - String dest = remotePath + "/" + f.getName(); // $NON-NLS-1S - monitor.startSubTask(dest); - doPush(f.listFiles(), dest, monitor); - - monitor.advance(1); - } else if (f.isFile()) { - // append the name of the file to the remote path - String remoteFile = remotePath + "/" + f.getName(); // $NON-NLS-1S - monitor.startSubTask(remoteFile); - doPushFile(f.getAbsolutePath(), remoteFile, monitor); - } - } - } - } - - /** - * Push a single file - * @param localPath the local file to push - * @param remotePath the remote file (length max is 1024) - * @param monitor the monitor. The monitor must be started already. - * - * @throws SyncException if file could not be pushed - * @throws IOException in case of I/O error on the connection. - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - private void doPushFile(String localPath, String remotePath, - ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException { - FileInputStream fis = null; - byte[] msg; - - final int timeOut = DdmPreferences.getTimeOut(); - - try { - byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING); - - if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) { - throw new SyncException(SyncError.REMOTE_PATH_LENGTH); - } - - File f = new File(localPath); - - // create the stream to read the file - fis = new FileInputStream(f); - - // create the header for the action - msg = createSendFileReq(ID_SEND, remotePathContent, 0644); - - // and send it. We use a custom try/catch block to make the difference between - // file and network IO exceptions. - AdbHelper.write(mChannel, msg, -1, timeOut); - - // create the buffer used to read. - // we read max SYNC_DATA_MAX, but we need 2 4 bytes at the beginning. - if (mBuffer == null) { - mBuffer = new byte[SYNC_DATA_MAX + 8]; - } - System.arraycopy(ID_DATA, 0, mBuffer, 0, ID_DATA.length); - - // look while there is something to read - while (true) { - // check if we're canceled - if (monitor.isCanceled() == true) { - throw new SyncException(SyncError.CANCELED); - } - - // read up to SYNC_DATA_MAX - int readCount = fis.read(mBuffer, 8, SYNC_DATA_MAX); - - if (readCount == -1) { - // we reached the end of the file - break; - } - - // now send the data to the device - // first write the amount read - ArrayHelper.swap32bitsToArray(readCount, mBuffer, 4); - - // now write it - AdbHelper.write(mChannel, mBuffer, readCount+8, timeOut); - - // and advance the monitor - monitor.advance(readCount); - } - } catch (UnsupportedEncodingException e) { - throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e); - } finally { - // close the local file - if (fis != null) { - fis.close(); - } - } - - // create the DONE message - long time = System.currentTimeMillis() / 1000; - msg = createReq(ID_DONE, (int)time); - - // and send it. - AdbHelper.write(mChannel, msg, -1, timeOut); - - // read the result, in a byte array containing 2 ints - // (id, size) - byte[] result = new byte[8]; - AdbHelper.read(mChannel, result, -1 /* full length */, timeOut); - - if (checkResult(result, ID_OKAY) == false) { - throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR, - readErrorMessage(result, timeOut)); - } - } - - /** - * Reads an error message from the opened {@link #mChannel}. - * @param result the current adb result. Must contain both FAIL and the length of the message. - * @param timeOut - * @return - * @throws TimeoutException in case of a timeout reading responses from the device. - * @throws IOException - */ - private String readErrorMessage(byte[] result, final int timeOut) throws TimeoutException, - IOException { - if (checkResult(result, ID_FAIL)) { - int len = ArrayHelper.swap32bitFromArray(result, 4); - - if (len > 0) { - AdbHelper.read(mChannel, mBuffer, len, timeOut); - - String message = new String(mBuffer, 0, len); - Log.e("ddms", "transfer error: " + message); - - return message; - } - } - - return null; - } - - /** - * Returns the mode of the remote file. - * @param path the remote file - * @return an Integer containing the mode if all went well or null - * otherwise - * @throws IOException - * @throws TimeoutException in case of a timeout reading responses from the device. - */ - private Integer readMode(String path) throws TimeoutException, IOException { - // create the stat request message. - byte[] msg = createFileReq(ID_STAT, path); - - AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut()); - - // 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()); - - // 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); - } - - /** - * Create a command with a code and an int values - * @param command - * @param value - * @return - */ - private static byte[] createReq(byte[] command, int value) { - byte[] array = new byte[8]; - - System.arraycopy(command, 0, array, 0, 4); - ArrayHelper.swap32bitsToArray(value, array, 4); - - return array; - } - - /** - * Creates the data array for a stat request. - * @param command the 4 byte command (ID_STAT, ID_RECV, ...) - * @param path The path of the remote file on which to execute the command - * @return the byte[] to send to the device through adb - */ - private static byte[] createFileReq(byte[] command, String path) { - byte[] pathContent = null; - try { - pathContent = path.getBytes(AdbHelper.DEFAULT_ENCODING); - } catch (UnsupportedEncodingException e) { - return null; - } - - return createFileReq(command, pathContent); - } - - /** - * Creates the data array for a file request. This creates an array with a 4 byte command + the - * remote file name. - * @param command the 4 byte command (ID_STAT, ID_RECV, ...). - * @param path The path, as a byte array, of the remote file on which to - * execute the command. - * @return the byte[] to send to the device through adb - */ - private static byte[] createFileReq(byte[] command, byte[] path) { - byte[] array = new byte[8 + path.length]; - - System.arraycopy(command, 0, array, 0, 4); - ArrayHelper.swap32bitsToArray(path.length, array, 4); - System.arraycopy(path, 0, array, 8, path.length); - - return array; - } - - private static byte[] createSendFileReq(byte[] command, byte[] path, int mode) { - // make the mode into a string - String modeStr = "," + (mode & 0777); // $NON-NLS-1S - byte[] modeContent = null; - try { - modeContent = modeStr.getBytes(AdbHelper.DEFAULT_ENCODING); - } catch (UnsupportedEncodingException e) { - return null; - } - - byte[] array = new byte[8 + path.length + modeContent.length]; - - System.arraycopy(command, 0, array, 0, 4); - ArrayHelper.swap32bitsToArray(path.length + modeContent.length, array, 4); - System.arraycopy(path, 0, array, 8, path.length); - System.arraycopy(modeContent, 0, array, 8 + path.length, modeContent.length); - - return array; - - - } - - /** - * Checks the result array starts with the provided code - * @param result The result array to check - * @param code The 4 byte code. - * @return true if the code matches. - */ - private static boolean checkResult(byte[] result, byte[] code) { - if (result[0] != code[0] || - result[1] != code[1] || - result[2] != code[2] || - result[3] != code[3]) { - return false; - } - - return true; - - } - - private static int getFileType(int mode) { - if ((mode & S_ISOCK) == S_ISOCK) { - return FileListingService.TYPE_SOCKET; - } - - if ((mode & S_IFLNK) == S_IFLNK) { - return FileListingService.TYPE_LINK; - } - - if ((mode & S_IFREG) == S_IFREG) { - return FileListingService.TYPE_FILE; - } - - if ((mode & S_IFBLK) == S_IFBLK) { - return FileListingService.TYPE_BLOCK; - } - - if ((mode & S_IFDIR) == S_IFDIR) { - return FileListingService.TYPE_DIRECTORY; - } - - if ((mode & S_IFCHR) == S_IFCHR) { - return FileListingService.TYPE_CHARACTER; - } - - if ((mode & S_IFIFO) == S_IFIFO) { - return FileListingService.TYPE_FIFO; - } - - return FileListingService.TYPE_OTHER; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ThreadInfo.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ThreadInfo.java deleted file mode 100644 index 93db931..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/ThreadInfo.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * Holds a thread information. - */ -public final class ThreadInfo implements IStackTraceInfo { - private int mThreadId; - private String mThreadName; - private int mStatus; - private int mTid; - private int mUtime; - private int mStime; - private boolean mIsDaemon; - private StackTraceElement[] mTrace; - private long mTraceTime; - - // priority? - // total CPU used? - // method at top of stack? - - /** - * Construct with basic identification. - */ - ThreadInfo(int threadId, String threadName) { - mThreadId = threadId; - mThreadName = threadName; - - mStatus = -1; - //mTid = mUtime = mStime = 0; - //mIsDaemon = false; - } - - /** - * Set with the values we get from a THST chunk. - */ - void updateThread(int status, int tid, int utime, int stime, boolean isDaemon) { - - mStatus = status; - mTid = tid; - mUtime = utime; - mStime = stime; - mIsDaemon = isDaemon; - } - - /** - * Sets the stack call of the thread. - * @param trace stackcall information. - */ - void setStackCall(StackTraceElement[] trace) { - mTrace = trace; - mTraceTime = System.currentTimeMillis(); - } - - /** - * Returns the thread's ID. - */ - public int getThreadId() { - return mThreadId; - } - - /** - * Returns the thread's name. - */ - public String getThreadName() { - return mThreadName; - } - - void setThreadName(String name) { - mThreadName = name; - } - - /** - * Returns the system tid. - */ - public int getTid() { - return mTid; - } - - /** - * Returns the VM thread status. - */ - public int getStatus() { - return mStatus; - } - - /** - * Returns the cumulative user time. - */ - public int getUtime() { - return mUtime; - } - - /** - * Returns the cumulative system time. - */ - public int getStime() { - return mStime; - } - - /** - * Returns whether this is a daemon thread. - */ - public boolean isDaemon() { - return mIsDaemon; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IStackTraceInfo#getStackTrace() - */ - @Override - public StackTraceElement[] getStackTrace() { - return mTrace; - } - - /** - * Returns the approximate time of the stacktrace data. - * @see #getStackTrace() - */ - public long getStackCallTime() { - return mTraceTime; - } -} - diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/TimeoutException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/TimeoutException.java deleted file mode 100644 index 78f5db7..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/TimeoutException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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; - - -/** - * Exception thrown when a connection to Adb failed with a timeout. - * - */ -public class TimeoutException extends Exception { - private static final long serialVersionUID = 1L; -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java deleted file mode 100644 index 0afdf5d..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import com.android.ddmlib.log.LogReceiver.LogEntry; - -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Represents an event and its data. - */ -public class EventContainer { - - /** - * Comparison method for {@link EventContainer#testValue(int, Object, com.android.ddmlib.log.EventContainer.CompareMethod)} - * - */ - public enum CompareMethod { - EQUAL_TO("equals", "=="), - LESSER_THAN("less than or equals to", "<="), - LESSER_THAN_STRICT("less than", "<"), - GREATER_THAN("greater than or equals to", ">="), - GREATER_THAN_STRICT("greater than", ">"), - BIT_CHECK("bit check", "&"); - - private final String mName; - private final String mTestString; - - private CompareMethod(String name, String testString) { - mName = name; - mTestString = testString; - } - - /** - * Returns the display string. - */ - @Override - public String toString() { - return mName; - } - - /** - * Returns a short string representing the comparison. - */ - public String testString() { - return mTestString; - } - } - - - /** - * Type for event data. - */ - public static enum EventValueType { - UNKNOWN(0), - INT(1), - LONG(2), - STRING(3), - LIST(4), - TREE(5); - - private final static Pattern STORAGE_PATTERN = Pattern.compile("^(\\d+)@(.*)$"); //$NON-NLS-1$ - - private int mValue; - - /** - * Returns a {@link EventValueType} from an integer value, or <code>null</code> if no match - * was found. - * @param value the integer value. - */ - static EventValueType getEventValueType(int value) { - for (EventValueType type : values()) { - if (type.mValue == value) { - return type; - } - } - - return null; - } - - /** - * Returns a storage string for an {@link Object} of type supported by - * {@link EventValueType}. - * <p/> - * Strings created by this method can be reloaded with - * {@link #getObjectFromStorageString(String)}. - * <p/> - * NOTE: for now, only {@link #STRING}, {@link #INT}, and {@link #LONG} are supported. - * @param object the object to "convert" into a storage string. - * @return a string storing the object and its type or null if the type was not recognized. - */ - public static String getStorageString(Object object) { - if (object instanceof String) { - return STRING.mValue + "@" + (String)object; //$NON-NLS-1$ - } else if (object instanceof Integer) { - return INT.mValue + "@" + object.toString(); //$NON-NLS-1$ - } else if (object instanceof Long) { - return LONG.mValue + "@" + object.toString(); //$NON-NLS-1$ - } - - return null; - } - - /** - * Creates an {@link Object} from a storage string created with - * {@link #getStorageString(Object)}. - * @param value the storage string - * @return an {@link Object} or null if the string or type were not recognized. - */ - public static Object getObjectFromStorageString(String value) { - Matcher m = STORAGE_PATTERN.matcher(value); - if (m.matches()) { - try { - EventValueType type = getEventValueType(Integer.parseInt(m.group(1))); - - if (type == null) { - return null; - } - - switch (type) { - case STRING: - return m.group(2); - case INT: - return Integer.valueOf(m.group(2)); - case LONG: - return Long.valueOf(m.group(2)); - } - } catch (NumberFormatException nfe) { - return null; - } - } - - return null; - } - - - /** - * Returns the integer value of the enum. - */ - public int getValue() { - return mValue; - } - - @Override - public String toString() { - return super.toString().toLowerCase(Locale.US); - } - - private EventValueType(int value) { - mValue = value; - } - } - - public int mTag; - public int pid; /* generating process's pid */ - public int tid; /* generating process's tid */ - public int sec; /* seconds since Epoch */ - public int nsec; /* nanoseconds */ - - private Object mData; - - /** - * Creates an {@link EventContainer} from a {@link LogEntry}. - * @param entry the LogEntry from which pid, tid, and time info is copied. - * @param tag the event tag value - * @param data the data of the EventContainer. - */ - EventContainer(LogEntry entry, int tag, Object data) { - getType(data); - mTag = tag; - mData = data; - - pid = entry.pid; - tid = entry.tid; - sec = entry.sec; - nsec = entry.nsec; - } - - /** - * Creates an {@link EventContainer} with raw data - */ - EventContainer(int tag, int pid, int tid, int sec, int nsec, Object data) { - getType(data); - mTag = tag; - mData = data; - - this.pid = pid; - this.tid = tid; - this.sec = sec; - this.nsec = nsec; - } - - /** - * Returns the data as an int. - * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}. - * @see #getType() - */ - public final Integer getInt() throws InvalidTypeException { - if (getType(mData) == EventValueType.INT) { - return (Integer)mData; - } - - throw new InvalidTypeException(); - } - - /** - * Returns the data as a long. - * @throws InvalidTypeException if the data type is not {@link EventValueType#LONG}. - * @see #getType() - */ - public final Long getLong() throws InvalidTypeException { - if (getType(mData) == EventValueType.LONG) { - return (Long)mData; - } - - throw new InvalidTypeException(); - } - - /** - * Returns the data as a String. - * @throws InvalidTypeException if the data type is not {@link EventValueType#STRING}. - * @see #getType() - */ - public final String getString() throws InvalidTypeException { - if (getType(mData) == EventValueType.STRING) { - return (String)mData; - } - - throw new InvalidTypeException(); - } - - /** - * Returns a value by index. The return type is defined by its type. - * @param valueIndex the index of the value. If the data is not a list, this is ignored. - */ - public Object getValue(int valueIndex) { - return getValue(mData, valueIndex, true); - } - - /** - * Returns a value by index as a double. - * @param valueIndex the index of the value. If the data is not a list, this is ignored. - * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}, - * {@link EventValueType#LONG}, {@link EventValueType#LIST}, or if the item in the - * list at index <code>valueIndex</code> is not of type {@link EventValueType#INT} or - * {@link EventValueType#LONG}. - * @see #getType() - */ - public double getValueAsDouble(int valueIndex) throws InvalidTypeException { - return getValueAsDouble(mData, valueIndex, true); - } - - /** - * Returns a value by index as a String. - * @param valueIndex the index of the value. If the data is not a list, this is ignored. - * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}, - * {@link EventValueType#LONG}, {@link EventValueType#STRING}, {@link EventValueType#LIST}, - * or if the item in the list at index <code>valueIndex</code> is not of type - * {@link EventValueType#INT}, {@link EventValueType#LONG}, or {@link EventValueType#STRING} - * @see #getType() - */ - public String getValueAsString(int valueIndex) throws InvalidTypeException { - return getValueAsString(mData, valueIndex, true); - } - - /** - * Returns the type of the data. - */ - public EventValueType getType() { - return getType(mData); - } - - /** - * Returns the type of an object. - */ - public final EventValueType getType(Object data) { - if (data instanceof Integer) { - return EventValueType.INT; - } else if (data instanceof Long) { - return EventValueType.LONG; - } else if (data instanceof String) { - return EventValueType.STRING; - } else if (data instanceof Object[]) { - // loop through the list to see if we have another list - Object[] objects = (Object[])data; - for (Object obj : objects) { - EventValueType type = getType(obj); - if (type == EventValueType.LIST || type == EventValueType.TREE) { - return EventValueType.TREE; - } - } - return EventValueType.LIST; - } - - return EventValueType.UNKNOWN; - } - - /** - * Checks that the <code>index</code>-th value of this event against a provided value. - * @param index the index of the value to test - * @param value the value to test against - * @param compareMethod the method of testing - * @return true if the test passed. - * @throws InvalidTypeException in case of type mismatch between the value to test and the value - * to test against, or if the compare method is incompatible with the type of the values. - * @see CompareMethod - */ - public boolean testValue(int index, Object value, - CompareMethod compareMethod) throws InvalidTypeException { - EventValueType type = getType(mData); - if (index > 0 && type != EventValueType.LIST) { - throw new InvalidTypeException(); - } - - Object data = mData; - if (type == EventValueType.LIST) { - data = ((Object[])mData)[index]; - } - - if (data.getClass().equals(data.getClass()) == false) { - throw new InvalidTypeException(); - } - - switch (compareMethod) { - case EQUAL_TO: - return data.equals(value); - case LESSER_THAN: - if (data instanceof Integer) { - return (((Integer)data).compareTo((Integer)value) <= 0); - } else if (data instanceof Long) { - return (((Long)data).compareTo((Long)value) <= 0); - } - - // other types can't use this compare method. - throw new InvalidTypeException(); - case LESSER_THAN_STRICT: - if (data instanceof Integer) { - return (((Integer)data).compareTo((Integer)value) < 0); - } else if (data instanceof Long) { - return (((Long)data).compareTo((Long)value) < 0); - } - - // other types can't use this compare method. - throw new InvalidTypeException(); - case GREATER_THAN: - if (data instanceof Integer) { - return (((Integer)data).compareTo((Integer)value) >= 0); - } else if (data instanceof Long) { - return (((Long)data).compareTo((Long)value) >= 0); - } - - // other types can't use this compare method. - throw new InvalidTypeException(); - case GREATER_THAN_STRICT: - if (data instanceof Integer) { - return (((Integer)data).compareTo((Integer)value) > 0); - } else if (data instanceof Long) { - return (((Long)data).compareTo((Long)value) > 0); - } - - // other types can't use this compare method. - throw new InvalidTypeException(); - case BIT_CHECK: - if (data instanceof Integer) { - return (((Integer)data).intValue() & ((Integer)value).intValue()) != 0; - } else if (data instanceof Long) { - return (((Long)data).longValue() & ((Long)value).longValue()) != 0; - } - - // other types can't use this compare method. - throw new InvalidTypeException(); - default : - throw new InvalidTypeException(); - } - } - - private final Object getValue(Object data, int valueIndex, boolean recursive) { - EventValueType type = getType(data); - - switch (type) { - case INT: - case LONG: - case STRING: - return data; - case LIST: - if (recursive) { - Object[] list = (Object[]) data; - if (valueIndex >= 0 && valueIndex < list.length) { - return getValue(list[valueIndex], valueIndex, false); - } - } - } - - return null; - } - - private final double getValueAsDouble(Object data, int valueIndex, boolean recursive) - throws InvalidTypeException { - EventValueType type = getType(data); - - switch (type) { - case INT: - return ((Integer)data).doubleValue(); - case LONG: - return ((Long)data).doubleValue(); - case STRING: - throw new InvalidTypeException(); - case LIST: - if (recursive) { - Object[] list = (Object[]) data; - if (valueIndex >= 0 && valueIndex < list.length) { - return getValueAsDouble(list[valueIndex], valueIndex, false); - } - } - } - - throw new InvalidTypeException(); - } - - private final String getValueAsString(Object data, int valueIndex, boolean recursive) - throws InvalidTypeException { - EventValueType type = getType(data); - - switch (type) { - case INT: - return ((Integer)data).toString(); - case LONG: - return ((Long)data).toString(); - case STRING: - return (String)data; - case LIST: - if (recursive) { - Object[] list = (Object[]) data; - if (valueIndex >= 0 && valueIndex < list.length) { - return getValueAsString(list[valueIndex], valueIndex, false); - } - } else { - throw new InvalidTypeException( - "getValueAsString() doesn't support EventValueType.TREE"); - } - } - - throw new InvalidTypeException( - "getValueAsString() unsupported type:" + type); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventLogParser.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventLogParser.java deleted file mode 100644 index b2d8256..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventLogParser.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import com.android.ddmlib.IDevice; -import com.android.ddmlib.Log; -import com.android.ddmlib.MultiLineReceiver; -import com.android.ddmlib.log.EventContainer.EventValueType; -import com.android.ddmlib.log.EventValueDescription.ValueType; -import com.android.ddmlib.log.LogReceiver.LogEntry; -import com.android.ddmlib.utils.ArrayHelper; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Parser for the "event" log. - */ -public final class EventLogParser { - - /** Location of the tag map file on the device */ - private final static String EVENT_TAG_MAP_FILE = "/system/etc/event-log-tags"; //$NON-NLS-1$ - - /** - * Event log entry types. These must match up with the declarations in - * java/android/android/util/EventLog.java. - */ - private final static int EVENT_TYPE_INT = 0; - private final static int EVENT_TYPE_LONG = 1; - private final static int EVENT_TYPE_STRING = 2; - private final static int EVENT_TYPE_LIST = 3; - - private final static Pattern PATTERN_SIMPLE_TAG = Pattern.compile( - "^(\\d+)\\s+([A-Za-z0-9_]+)\\s*$"); //$NON-NLS-1$ - private final static Pattern PATTERN_TAG_WITH_DESC = Pattern.compile( - "^(\\d+)\\s+([A-Za-z0-9_]+)\\s*(.*)\\s*$"); //$NON-NLS-1$ - private final static Pattern PATTERN_DESCRIPTION = Pattern.compile( - "\\(([A-Za-z0-9_\\s]+)\\|(\\d+)(\\|\\d+){0,1}\\)"); //$NON-NLS-1$ - - private final static Pattern TEXT_LOG_LINE = Pattern.compile( - "(\\d\\d)-(\\d\\d)\\s(\\d\\d):(\\d\\d):(\\d\\d).(\\d{3})\\s+I/([a-zA-Z0-9_]+)\\s*\\(\\s*(\\d+)\\):\\s+(.*)"); //$NON-NLS-1$ - - private final TreeMap<Integer, String> mTagMap = new TreeMap<Integer, String>(); - - private final TreeMap<Integer, EventValueDescription[]> mValueDescriptionMap = - new TreeMap<Integer, EventValueDescription[]>(); - - public EventLogParser() { - } - - /** - * Inits the parser for a specific Device. - * <p/> - * This methods reads the event-log-tags located on the device to find out - * what tags are being written to the event log and what their format is. - * @param device The device. - * @return <code>true</code> if success, <code>false</code> if failure or cancellation. - */ - public boolean init(IDevice device) { - // read the event tag map file on the device. - try { - device.executeShellCommand("cat " + EVENT_TAG_MAP_FILE, //$NON-NLS-1$ - new MultiLineReceiver() { - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - processTagLine(line); - } - } - @Override - public boolean isCancelled() { - return false; - } - }); - } catch (Exception e) { - // catch all possible exceptions and return false. - return false; - } - - return true; - } - - /** - * Inits the parser with the content of a tag file. - * @param tagFileContent the lines of a tag file. - * @return <code>true</code> if success, <code>false</code> if failure. - */ - public boolean init(String[] tagFileContent) { - for (String line : tagFileContent) { - processTagLine(line); - } - return true; - } - - /** - * Inits the parser with a specified event-log-tags file. - * @param filePath - * @return <code>true</code> if success, <code>false</code> if failure. - */ - public boolean init(String filePath) { - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(filePath)); - - String line = null; - do { - line = reader.readLine(); - if (line != null) { - processTagLine(line); - } - } while (line != null); - - return true; - } catch (IOException e) { - return false; - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException e) { - // ignore - } - } - } - - /** - * Processes a line from the event-log-tags file. - * @param line the line to process - */ - private void processTagLine(String line) { - // ignore empty lines and comment lines - if (line.length() > 0 && line.charAt(0) != '#') { - Matcher m = PATTERN_TAG_WITH_DESC.matcher(line); - if (m.matches()) { - try { - int value = Integer.parseInt(m.group(1)); - String name = m.group(2); - if (name != null && mTagMap.get(value) == null) { - mTagMap.put(value, name); - } - - // special case for the GC tag. We ignore what is in the file, - // and take what the custom GcEventContainer class tells us. - // This is due to the event encoding several values on 2 longs. - // @see GcEventContainer - if (value == GcEventContainer.GC_EVENT_TAG) { - mValueDescriptionMap.put(value, - GcEventContainer.getValueDescriptions()); - } else { - - String description = m.group(3); - if (description != null && description.length() > 0) { - EventValueDescription[] desc = - processDescription(description); - - if (desc != null) { - mValueDescriptionMap.put(value, desc); - } - } - } - } catch (NumberFormatException e) { - // failed to convert the number into a string. just ignore it. - } - } else { - m = PATTERN_SIMPLE_TAG.matcher(line); - if (m.matches()) { - int value = Integer.parseInt(m.group(1)); - String name = m.group(2); - if (name != null && mTagMap.get(value) == null) { - mTagMap.put(value, name); - } - } - } - } - } - - private EventValueDescription[] processDescription(String description) { - String[] descriptions = description.split("\\s*,\\s*"); //$NON-NLS-1$ - - ArrayList<EventValueDescription> list = new ArrayList<EventValueDescription>(); - - for (String desc : descriptions) { - Matcher m = PATTERN_DESCRIPTION.matcher(desc); - if (m.matches()) { - try { - String name = m.group(1); - - String typeString = m.group(2); - int typeValue = Integer.parseInt(typeString); - EventValueType eventValueType = EventValueType.getEventValueType(typeValue); - if (eventValueType == null) { - // just ignore this description if the value is not recognized. - // TODO: log the error. - } - - typeString = m.group(3); - if (typeString != null && typeString.length() > 0) { - //skip the | - typeString = typeString.substring(1); - - typeValue = Integer.parseInt(typeString); - ValueType valueType = ValueType.getValueType(typeValue); - - list.add(new EventValueDescription(name, eventValueType, valueType)); - } else { - list.add(new EventValueDescription(name, eventValueType)); - } - } catch (NumberFormatException nfe) { - // just ignore this description if one number is malformed. - // TODO: log the error. - } catch (InvalidValueTypeException e) { - // just ignore this description if data type and data unit don't match - // TODO: log the error. - } - } else { - Log.e("EventLogParser", //$NON-NLS-1$ - String.format("Can't parse %1$s", description)); //$NON-NLS-1$ - } - } - - if (list.size() == 0) { - return null; - } - - return list.toArray(new EventValueDescription[list.size()]); - - } - - public EventContainer parse(LogEntry entry) { - if (entry.len < 4) { - return null; - } - - int inOffset = 0; - - int tagValue = ArrayHelper.swap32bitFromArray(entry.data, inOffset); - inOffset += 4; - - String tag = mTagMap.get(tagValue); - if (tag == null) { - Log.e("EventLogParser", String.format("unknown tag number: %1$d", tagValue)); - } - - ArrayList<Object> list = new ArrayList<Object>(); - if (parseBinaryEvent(entry.data, inOffset, list) == -1) { - return null; - } - - Object data; - if (list.size() == 1) { - data = list.get(0); - } else{ - data = list.toArray(); - } - - EventContainer event = null; - if (tagValue == GcEventContainer.GC_EVENT_TAG) { - event = new GcEventContainer(entry, tagValue, data); - } else { - event = new EventContainer(entry, tagValue, data); - } - - return event; - } - - public EventContainer parse(String textLogLine) { - // line will look like - // 04-29 23:16:16.691 I/dvm_gc_info( 427): <data> - // where <data> is either - // [value1,value2...] - // or - // value - if (textLogLine.length() == 0) { - return null; - } - - // parse the header first - Matcher m = TEXT_LOG_LINE.matcher(textLogLine); - if (m.matches()) { - try { - int month = Integer.parseInt(m.group(1)); - int day = Integer.parseInt(m.group(2)); - int hours = Integer.parseInt(m.group(3)); - int minutes = Integer.parseInt(m.group(4)); - int seconds = Integer.parseInt(m.group(5)); - int milliseconds = Integer.parseInt(m.group(6)); - - // convert into seconds since epoch and nano-seconds. - Calendar cal = Calendar.getInstance(); - cal.set(cal.get(Calendar.YEAR), month-1, day, hours, minutes, seconds); - int sec = (int)Math.floor(cal.getTimeInMillis()/1000); - int nsec = milliseconds * 1000000; - - String tag = m.group(7); - - // get the numerical tag value - int tagValue = -1; - Set<Entry<Integer, String>> tagSet = mTagMap.entrySet(); - for (Entry<Integer, String> entry : tagSet) { - if (tag.equals(entry.getValue())) { - tagValue = entry.getKey(); - break; - } - } - - if (tagValue == -1) { - return null; - } - - int pid = Integer.parseInt(m.group(8)); - - Object data = parseTextData(m.group(9), tagValue); - if (data == null) { - return null; - } - - // now we can allocate and return the EventContainer - EventContainer event = null; - if (tagValue == GcEventContainer.GC_EVENT_TAG) { - event = new GcEventContainer(tagValue, pid, -1 /* tid */, sec, nsec, data); - } else { - event = new EventContainer(tagValue, pid, -1 /* tid */, sec, nsec, data); - } - - return event; - } catch (NumberFormatException e) { - return null; - } - } - - return null; - } - - public Map<Integer, String> getTagMap() { - return mTagMap; - } - - public Map<Integer, EventValueDescription[]> getEventInfoMap() { - return mValueDescriptionMap; - } - - /** - * Recursively convert binary log data to printable form. - * - * This needs to be recursive because you can have lists of lists. - * - * If we run out of room, we stop processing immediately. It's important - * for us to check for space on every output element to avoid producing - * garbled output. - * - * Returns the amount read on success, -1 on failure. - */ - private static int parseBinaryEvent(byte[] eventData, int dataOffset, ArrayList<Object> list) { - - if (eventData.length - dataOffset < 1) - return -1; - - int offset = dataOffset; - - int type = eventData[offset++]; - - //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); - - switch (type) { - case EVENT_TYPE_INT: { /* 32-bit signed int */ - int ival; - - if (eventData.length - offset < 4) - return -1; - ival = ArrayHelper.swap32bitFromArray(eventData, offset); - offset += 4; - - list.add(new Integer(ival)); - } - break; - case EVENT_TYPE_LONG: { /* 64-bit signed long */ - long lval; - - if (eventData.length - offset < 8) - return -1; - lval = ArrayHelper.swap64bitFromArray(eventData, offset); - offset += 8; - - list.add(new Long(lval)); - } - break; - case EVENT_TYPE_STRING: { /* UTF-8 chars, not NULL-terminated */ - int strLen; - - if (eventData.length - offset < 4) - return -1; - strLen = ArrayHelper.swap32bitFromArray(eventData, offset); - offset += 4; - - if (eventData.length - offset < strLen) - return -1; - - // get the string - try { - String str = new String(eventData, offset, strLen, "UTF-8"); //$NON-NLS-1$ - list.add(str); - } catch (UnsupportedEncodingException e) { - } - offset += strLen; - break; - } - case EVENT_TYPE_LIST: { /* N items, all different types */ - - if (eventData.length - offset < 1) - return -1; - - int count = eventData[offset++]; - - // make a new temp list - ArrayList<Object> subList = new ArrayList<Object>(); - for (int i = 0; i < count; i++) { - int result = parseBinaryEvent(eventData, offset, subList); - if (result == -1) { - return result; - } - - offset += result; - } - - list.add(subList.toArray()); - } - break; - default: - Log.e("EventLogParser", //$NON-NLS-1$ - String.format("Unknown binary event type %1$d", type)); //$NON-NLS-1$ - return -1; - } - - return offset - dataOffset; - } - - private Object parseTextData(String data, int tagValue) { - // first, get the description of what we're supposed to parse - EventValueDescription[] desc = mValueDescriptionMap.get(tagValue); - - if (desc == null) { - // TODO parse and create string values. - return null; - } - - if (desc.length == 1) { - return getObjectFromString(data, desc[0].getEventValueType()); - } else if (data.startsWith("[") && data.endsWith("]")) { - data = data.substring(1, data.length() - 1); - - // get each individual values as String - String[] values = data.split(","); - - if (tagValue == GcEventContainer.GC_EVENT_TAG) { - // special case for the GC event! - Object[] objects = new Object[2]; - - objects[0] = getObjectFromString(values[0], EventValueType.LONG); - objects[1] = getObjectFromString(values[1], EventValueType.LONG); - - return objects; - } else { - // must be the same number as the number of descriptors. - if (values.length != desc.length) { - return null; - } - - Object[] objects = new Object[values.length]; - - for (int i = 0 ; i < desc.length ; i++) { - Object obj = getObjectFromString(values[i], desc[i].getEventValueType()); - if (obj == null) { - return null; - } - objects[i] = obj; - } - - return objects; - } - } - - return null; - } - - - private Object getObjectFromString(String value, EventValueType type) { - try { - switch (type) { - case INT: - return Integer.valueOf(value); - case LONG: - return Long.valueOf(value); - case STRING: - return value; - } - } catch (NumberFormatException e) { - // do nothing, we'll return null. - } - - return null; - } - - /** - * Recreates the event-log-tags at the specified file path. - * @param filePath the file path to write the file. - * @throws IOException - */ - public void saveTags(String filePath) throws IOException { - File destFile = new File(filePath); - destFile.createNewFile(); - FileOutputStream fos = null; - - try { - - fos = new FileOutputStream(destFile); - - for (Integer key : mTagMap.keySet()) { - // get the tag name - String tagName = mTagMap.get(key); - - // get the value descriptions - EventValueDescription[] descriptors = mValueDescriptionMap.get(key); - - String line = null; - if (descriptors != null) { - StringBuilder sb = new StringBuilder(); - sb.append(String.format("%1$d %2$s", key, tagName)); //$NON-NLS-1$ - boolean first = true; - for (EventValueDescription evd : descriptors) { - if (first) { - sb.append(" ("); //$NON-NLS-1$ - first = false; - } else { - sb.append(",("); //$NON-NLS-1$ - } - sb.append(evd.getName()); - sb.append("|"); //$NON-NLS-1$ - sb.append(evd.getEventValueType().getValue()); - sb.append("|"); //$NON-NLS-1$ - sb.append(evd.getValueType().getValue()); - sb.append("|)"); //$NON-NLS-1$ - } - sb.append("\n"); //$NON-NLS-1$ - - line = sb.toString(); - } else { - line = String.format("%1$d %2$s\n", key, tagName); //$NON-NLS-1$ - } - - byte[] buffer = line.getBytes(); - fos.write(buffer); - } - } finally { - if (fos != null) { - fos.close(); - } - } - } - - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventValueDescription.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventValueDescription.java deleted file mode 100644 index 58d147c..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/EventValueDescription.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import com.android.ddmlib.log.EventContainer.EventValueType; - -import java.util.Locale; - - -/** - * Describes an {@link EventContainer} value. - * <p/> - * This is a stand-alone object, not linked to a particular Event. It describes the value, by - * name, type ({@link EventValueType}), and (if needed) value unit ({@link ValueType}). - * <p/> - * The index of the value is not contained within this class, and is instead dependent on the - * index of this particular object in the array of {@link EventValueDescription} returned by - * {@link EventLogParser#getEventInfoMap()} when queried for a particular event tag. - * - */ -public final class EventValueDescription { - - /** - * Represents the type of a numerical value. This is used to display values of vastly different - * type/range in graphs. - */ - public static enum ValueType { - NOT_APPLICABLE(0), - OBJECTS(1), - BYTES(2), - MILLISECONDS(3), - ALLOCATIONS(4), - ID(5), - PERCENT(6); - - private int mValue; - - /** - * Checks that the {@link EventValueType} is compatible with the {@link ValueType}. - * @param type the {@link EventValueType} to check. - * @throws InvalidValueTypeException if the types are not compatible. - */ - public void checkType(EventValueType type) throws InvalidValueTypeException { - if ((type != EventValueType.INT && type != EventValueType.LONG) - && this != NOT_APPLICABLE) { - throw new InvalidValueTypeException( - String.format("%1$s doesn't support type %2$s", type, this)); - } - } - - /** - * Returns a {@link ValueType} from an integer value, or <code>null</code> if no match - * were found. - * @param value the integer value. - */ - public static ValueType getValueType(int value) { - for (ValueType type : values()) { - if (type.mValue == value) { - return type; - } - } - return null; - } - - /** - * Returns the integer value of the enum. - */ - public int getValue() { - return mValue; - } - - @Override - public String toString() { - return super.toString().toLowerCase(Locale.US); - } - - private ValueType(int value) { - mValue = value; - } - } - - private String mName; - private EventValueType mEventValueType; - private ValueType mValueType; - - /** - * Builds a {@link EventValueDescription} with a name and a type. - * <p/> - * If the type is {@link EventValueType#INT} or {@link EventValueType#LONG}, the - * {@link #mValueType} is set to {@link ValueType#BYTES} by default. It set to - * {@link ValueType#NOT_APPLICABLE} for all other {@link EventValueType} values. - * @param name - * @param type - */ - EventValueDescription(String name, EventValueType type) { - mName = name; - mEventValueType = type; - if (mEventValueType == EventValueType.INT || mEventValueType == EventValueType.LONG) { - mValueType = ValueType.BYTES; - } else { - mValueType = ValueType.NOT_APPLICABLE; - } - } - - /** - * Builds a {@link EventValueDescription} with a name and a type, and a {@link ValueType}. - * <p/> - * @param name - * @param type - * @param valueType - * @throws InvalidValueTypeException if type and valuetype are not compatible. - * - */ - EventValueDescription(String name, EventValueType type, ValueType valueType) - throws InvalidValueTypeException { - mName = name; - mEventValueType = type; - mValueType = valueType; - mValueType.checkType(mEventValueType); - } - - /** - * @return the Name. - */ - public String getName() { - return mName; - } - - /** - * @return the {@link EventValueType}. - */ - public EventValueType getEventValueType() { - return mEventValueType; - } - - /** - * @return the {@link ValueType}. - */ - public ValueType getValueType() { - return mValueType; - } - - @Override - public String toString() { - if (mValueType != ValueType.NOT_APPLICABLE) { - return String.format("%1$s (%2$s, %3$s)", mName, mEventValueType.toString(), - mValueType.toString()); - } - - return String.format("%1$s (%2$s)", mName, mEventValueType.toString()); - } - - /** - * Checks if the value is of the proper type for this receiver. - * @param value the value to check. - * @return true if the value is of the proper type for this receiver. - */ - public boolean checkForType(Object value) { - switch (mEventValueType) { - case INT: - return value instanceof Integer; - case LONG: - return value instanceof Long; - case STRING: - return value instanceof String; - case LIST: - return value instanceof Object[]; - } - - return false; - } - - /** - * Returns an object of a valid type (based on the value returned by - * {@link #getEventValueType()}) from a String value. - * <p/> - * IMPORTANT {@link EventValueType#LIST} and {@link EventValueType#TREE} are not - * supported. - * @param value the value of the object expressed as a string. - * @return an object or null if the conversion could not be done. - */ - public Object getObjectFromString(String value) { - switch (mEventValueType) { - case INT: - try { - return Integer.valueOf(value); - } catch (NumberFormatException e) { - return null; - } - case LONG: - try { - return Long.valueOf(value); - } catch (NumberFormatException e) { - return null; - } - case STRING: - return value; - } - - return null; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java deleted file mode 100644 index 7bae202..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import com.android.ddmlib.log.EventValueDescription.ValueType; -import com.android.ddmlib.log.LogReceiver.LogEntry; - -/** - * Custom Event Container for the Gc event since this event doesn't simply output data in - * int or long format, but encodes several values on 4 longs. - * <p/> - * The array of {@link EventValueDescription}s parsed from the "event-log-tags" file must - * be ignored, and instead, the array returned from {@link #getValueDescriptions()} must be used. - */ -final class GcEventContainer extends EventContainer { - - public final static int GC_EVENT_TAG = 20001; - - private String processId; - private long gcTime; - private long bytesFreed; - private long objectsFreed; - private long actualSize; - private long allowedSize; - private long softLimit; - private long objectsAllocated; - private long bytesAllocated; - private long zActualSize; - private long zAllowedSize; - private long zObjectsAllocated; - private long zBytesAllocated; - private long dlmallocFootprint; - private long mallinfoTotalAllocatedSpace; - private long externalLimit; - private long externalBytesAllocated; - - GcEventContainer(LogEntry entry, int tag, Object data) { - super(entry, tag, data); - init(data); - } - - GcEventContainer(int tag, int pid, int tid, int sec, int nsec, Object data) { - super(tag, pid, tid, sec, nsec, data); - init(data); - } - - /** - * @param data - */ - private void init(Object data) { - if (data instanceof Object[]) { - Object[] values = (Object[])data; - for (int i = 0; i < values.length; i++) { - if (values[i] instanceof Long) { - parseDvmHeapInfo((Long)values[i], i); - } - } - } - } - - @Override - public EventValueType getType() { - return EventValueType.LIST; - } - - @Override - public boolean testValue(int index, Object value, CompareMethod compareMethod) - throws InvalidTypeException { - // do a quick easy check on the type. - if (index == 0) { - if ((value instanceof String) == false) { - throw new InvalidTypeException(); - } - } else if ((value instanceof Long) == false) { - throw new InvalidTypeException(); - } - - switch (compareMethod) { - case EQUAL_TO: - if (index == 0) { - return processId.equals(value); - } else { - return getValueAsLong(index) == ((Long)value).longValue(); - } - case LESSER_THAN: - return getValueAsLong(index) <= ((Long)value).longValue(); - case LESSER_THAN_STRICT: - return getValueAsLong(index) < ((Long)value).longValue(); - case GREATER_THAN: - return getValueAsLong(index) >= ((Long)value).longValue(); - case GREATER_THAN_STRICT: - return getValueAsLong(index) > ((Long)value).longValue(); - case BIT_CHECK: - return (getValueAsLong(index) & ((Long)value).longValue()) != 0; - } - - throw new ArrayIndexOutOfBoundsException(); - } - - @Override - public Object getValue(int valueIndex) { - if (valueIndex == 0) { - return processId; - } - - try { - return new Long(getValueAsLong(valueIndex)); - } catch (InvalidTypeException e) { - // this would only happened if valueIndex was 0, which we test above. - } - - return null; - } - - @Override - public double getValueAsDouble(int valueIndex) throws InvalidTypeException { - return (double)getValueAsLong(valueIndex); - } - - @Override - public String getValueAsString(int valueIndex) { - switch (valueIndex) { - case 0: - return processId; - default: - try { - return Long.toString(getValueAsLong(valueIndex)); - } catch (InvalidTypeException e) { - // we shouldn't stop there since we test, in this method first. - } - } - - throw new ArrayIndexOutOfBoundsException(); - } - - /** - * Returns a custom array of {@link EventValueDescription} since the actual content of this - * event (list of (long, long) does not match the values encoded into those longs. - */ - static EventValueDescription[] getValueDescriptions() { - try { - return new EventValueDescription[] { - new EventValueDescription("Process Name", EventValueType.STRING), - new EventValueDescription("GC Time", EventValueType.LONG, - ValueType.MILLISECONDS), - new EventValueDescription("Freed Objects", EventValueType.LONG, - ValueType.OBJECTS), - new EventValueDescription("Freed Bytes", EventValueType.LONG, ValueType.BYTES), - new EventValueDescription("Soft Limit", EventValueType.LONG, ValueType.BYTES), - new EventValueDescription("Actual Size (aggregate)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Allowed Size (aggregate)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Allocated Objects (aggregate)", - EventValueType.LONG, ValueType.OBJECTS), - new EventValueDescription("Allocated Bytes (aggregate)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Actual Size", EventValueType.LONG, ValueType.BYTES), - new EventValueDescription("Allowed Size", EventValueType.LONG, ValueType.BYTES), - new EventValueDescription("Allocated Objects", EventValueType.LONG, - ValueType.OBJECTS), - new EventValueDescription("Allocated Bytes", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Actual Size (zygote)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Allowed Size (zygote)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Allocated Objects (zygote)", EventValueType.LONG, - ValueType.OBJECTS), - new EventValueDescription("Allocated Bytes (zygote)", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("External Allocation Limit", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("External Bytes Allocated", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("dlmalloc Footprint", EventValueType.LONG, - ValueType.BYTES), - new EventValueDescription("Malloc Info: Total Allocated Space", - EventValueType.LONG, ValueType.BYTES), - }; - } catch (InvalidValueTypeException e) { - // this shouldn't happen since we control manual the EventValueType and the ValueType - // values. For development purpose, we assert if this happens. - assert false; - } - - // this shouldn't happen, but the compiler complains otherwise. - return null; - } - - private void parseDvmHeapInfo(long data, int index) { - switch (index) { - case 0: - // [63 ] Must be zero - // [62-24] ASCII process identifier - // [23-12] GC time in ms - // [11- 0] Bytes freed - - gcTime = float12ToInt((int)((data >> 12) & 0xFFFL)); - bytesFreed = float12ToInt((int)(data & 0xFFFL)); - - // convert the long into an array, in the proper order so that we can convert the - // first 5 char into a string. - byte[] dataArray = new byte[8]; - put64bitsToArray(data, dataArray, 0); - - // get the name from the string - processId = new String(dataArray, 0, 5); - break; - case 1: - // [63-62] 10 - // [61-60] Reserved; must be zero - // [59-48] Objects freed - // [47-36] Actual size (current footprint) - // [35-24] Allowed size (current hard max) - // [23-12] Objects allocated - // [11- 0] Bytes allocated - objectsFreed = float12ToInt((int)((data >> 48) & 0xFFFL)); - actualSize = float12ToInt((int)((data >> 36) & 0xFFFL)); - allowedSize = float12ToInt((int)((data >> 24) & 0xFFFL)); - objectsAllocated = float12ToInt((int)((data >> 12) & 0xFFFL)); - bytesAllocated = float12ToInt((int)(data & 0xFFFL)); - break; - case 2: - // [63-62] 11 - // [61-60] Reserved; must be zero - // [59-48] Soft limit (current soft max) - // [47-36] Actual size (current footprint) - // [35-24] Allowed size (current hard max) - // [23-12] Objects allocated - // [11- 0] Bytes allocated - softLimit = float12ToInt((int)((data >> 48) & 0xFFFL)); - zActualSize = float12ToInt((int)((data >> 36) & 0xFFFL)); - zAllowedSize = float12ToInt((int)((data >> 24) & 0xFFFL)); - zObjectsAllocated = float12ToInt((int)((data >> 12) & 0xFFFL)); - zBytesAllocated = float12ToInt((int)(data & 0xFFFL)); - break; - case 3: - // [63-48] Reserved; must be zero - // [47-36] dlmallocFootprint - // [35-24] mallinfo: total allocated space - // [23-12] External byte limit - // [11- 0] External bytes allocated - dlmallocFootprint = float12ToInt((int)((data >> 36) & 0xFFFL)); - mallinfoTotalAllocatedSpace = float12ToInt((int)((data >> 24) & 0xFFFL)); - externalLimit = float12ToInt((int)((data >> 12) & 0xFFFL)); - externalBytesAllocated = float12ToInt((int)(data & 0xFFFL)); - break; - default: - break; - } - } - - /** - * Converts a 12 bit float representation into an unsigned int (returned as a long) - * @param f12 - */ - private static long float12ToInt(int f12) { - return (f12 & 0x1FF) << ((f12 >>> 9) * 4); - } - - /** - * puts an unsigned value in an array. - * @param value The value to put. - * @param dest the destination array - * @param offset the offset in the array where to put the value. - * Array length must be at least offset + 8 - */ - private static void put64bitsToArray(long value, byte[] dest, int offset) { - dest[offset + 7] = (byte)(value & 0x00000000000000FFL); - dest[offset + 6] = (byte)((value & 0x000000000000FF00L) >> 8); - dest[offset + 5] = (byte)((value & 0x0000000000FF0000L) >> 16); - dest[offset + 4] = (byte)((value & 0x00000000FF000000L) >> 24); - dest[offset + 3] = (byte)((value & 0x000000FF00000000L) >> 32); - dest[offset + 2] = (byte)((value & 0x0000FF0000000000L) >> 40); - dest[offset + 1] = (byte)((value & 0x00FF000000000000L) >> 48); - dest[offset + 0] = (byte)((value & 0xFF00000000000000L) >> 56); - } - - /** - * Returns the long value of the <code>valueIndex</code>-th value. - * @param valueIndex the index of the value. - * @throws InvalidTypeException if index is 0 as it is a string value. - */ - private final long getValueAsLong(int valueIndex) throws InvalidTypeException { - switch (valueIndex) { - case 0: - throw new InvalidTypeException(); - case 1: - return gcTime; - case 2: - return objectsFreed; - case 3: - return bytesFreed; - case 4: - return softLimit; - case 5: - return actualSize; - case 6: - return allowedSize; - case 7: - return objectsAllocated; - case 8: - return bytesAllocated; - case 9: - return actualSize - zActualSize; - case 10: - return allowedSize - zAllowedSize; - case 11: - return objectsAllocated - zObjectsAllocated; - case 12: - return bytesAllocated - zBytesAllocated; - case 13: - return zActualSize; - case 14: - return zAllowedSize; - case 15: - return zObjectsAllocated; - case 16: - return zBytesAllocated; - case 17: - return externalLimit; - case 18: - return externalBytesAllocated; - case 19: - return dlmallocFootprint; - case 20: - return mallinfoTotalAllocatedSpace; - } - - throw new ArrayIndexOutOfBoundsException(); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidTypeException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidTypeException.java deleted file mode 100644 index 016f8aa..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidTypeException.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import java.io.Serializable; - -/** - * Exception thrown when accessing an {@link EventContainer} value with the wrong type. - */ -public final class InvalidTypeException extends Exception { - - /** - * Needed by {@link Serializable}. - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with the default detail message. - * @see java.lang.Exception - */ - public InvalidTypeException() { - super("Invalid Type"); - } - - /** - * Constructs a new exception with the specified detail message. - * @param message the detail message. The detail message is saved for later retrieval - * by the {@link Throwable#getMessage()} method. - * @see java.lang.Exception - */ - public InvalidTypeException(String message) { - super(message); - } - - /** - * Constructs a new exception with the specified cause and a detail message of - * <code>(cause==null ? null : cause.toString())</code> (which typically contains - * the class and detail message of cause). - * @param cause the cause (which is saved for later retrieval by the - * {@link Throwable#getCause()} method). (A <code>null</code> value is permitted, - * and indicates that the cause is nonexistent or unknown.) - * @see java.lang.Exception - */ - public InvalidTypeException(Throwable cause) { - super(cause); - } - - /** - * Constructs a new exception with the specified detail message and cause. - * @param message the detail message. The detail message is saved for later retrieval - * by the {@link Throwable#getMessage()} method. - * @param cause the cause (which is saved for later retrieval by the - * {@link Throwable#getCause()} method). (A <code>null</code> value is permitted, - * and indicates that the cause is nonexistent or unknown.) - * @see java.lang.Exception - */ - public InvalidTypeException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidValueTypeException.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidValueTypeException.java deleted file mode 100644 index a3050c8..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/InvalidValueTypeException.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - -import com.android.ddmlib.log.EventContainer.EventValueType; -import com.android.ddmlib.log.EventValueDescription.ValueType; - -import java.io.Serializable; - -/** - * Exception thrown when associating an {@link EventValueType} with an incompatible - * {@link ValueType}. - */ -public final class InvalidValueTypeException extends Exception { - - /** - * Needed by {@link Serializable}. - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with the default detail message. - * @see java.lang.Exception - */ - public InvalidValueTypeException() { - super("Invalid Type"); - } - - /** - * Constructs a new exception with the specified detail message. - * @param message the detail message. The detail message is saved for later retrieval - * by the {@link Throwable#getMessage()} method. - * @see java.lang.Exception - */ - public InvalidValueTypeException(String message) { - super(message); - } - - /** - * Constructs a new exception with the specified cause and a detail message of - * <code>(cause==null ? null : cause.toString())</code> (which typically contains - * the class and detail message of cause). - * @param cause the cause (which is saved for later retrieval by the - * {@link Throwable#getCause()} method). (A <code>null</code> value is permitted, - * and indicates that the cause is nonexistent or unknown.) - * @see java.lang.Exception - */ - public InvalidValueTypeException(Throwable cause) { - super(cause); - } - - /** - * Constructs a new exception with the specified detail message and cause. - * @param message the detail message. The detail message is saved for later retrieval - * by the {@link Throwable#getMessage()} method. - * @param cause the cause (which is saved for later retrieval by the - * {@link Throwable#getCause()} method). (A <code>null</code> value is permitted, - * and indicates that the cause is nonexistent or unknown.) - * @see java.lang.Exception - */ - public InvalidValueTypeException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/LogReceiver.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/LogReceiver.java deleted file mode 100644 index b49f025..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/log/LogReceiver.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2008 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.log; - - -import com.android.ddmlib.utils.ArrayHelper; - -import java.security.InvalidParameterException; - -/** - * Receiver able to provide low level parsing for device-side log services. - */ -public final class LogReceiver { - - private final static int ENTRY_HEADER_SIZE = 20; // 2*2 + 4*4; see LogEntry. - - /** - * Represents a log entry and its raw data. - */ - public final static class LogEntry { - /* - * See //device/include/utils/logger.h - */ - /** 16bit unsigned: length of the payload. */ - public int len; /* This is normally followed by a 16 bit padding */ - /** pid of the process that generated this {@link LogEntry} */ - public int pid; - /** tid of the process that generated this {@link LogEntry} */ - public int tid; - /** Seconds since epoch. */ - public int sec; - /** nanoseconds. */ - public int nsec; - /** The entry's raw data. */ - public byte[] data; - }; - - /** - * Classes which implement this interface provide a method that deals - * with {@link LogEntry} objects coming from log service through a {@link LogReceiver}. - * <p/>This interface provides two methods. - * <ul> - * <li>{@link #newEntry(com.android.ddmlib.log.LogReceiver.LogEntry)} provides a - * first level of parsing, extracting {@link LogEntry} objects out of the log service output.</li> - * <li>{@link #newData(byte[], int, int)} provides a way to receive the raw information - * coming directly from the log service.</li> - * </ul> - */ - public interface ILogListener { - /** - * Sent when a new {@link LogEntry} has been parsed by the {@link LogReceiver}. - * @param entry the new log entry. - */ - public void newEntry(LogEntry entry); - - /** - * Sent when new raw data is coming from the log service. - * @param data the raw data buffer. - * @param offset the offset into the buffer signaling the beginning of the new data. - * @param length the length of the new data. - */ - public void newData(byte[] data, int offset, int length); - } - - /** Current {@link LogEntry} being read, before sending it to the listener. */ - private LogEntry mCurrentEntry; - - /** Temp buffer to store partial entry headers. */ - private byte[] mEntryHeaderBuffer = new byte[ENTRY_HEADER_SIZE]; - /** Offset in the partial header buffer */ - private int mEntryHeaderOffset = 0; - /** Offset in the partial entry data */ - private int mEntryDataOffset = 0; - - /** Listener waiting for receive fully read {@link LogEntry} objects */ - private ILogListener mListener; - - private boolean mIsCancelled = false; - - /** - * Creates a {@link LogReceiver} with an {@link ILogListener}. - * <p/> - * The {@link ILogListener} will receive new log entries as they are parsed, in the form - * of {@link LogEntry} objects. - * @param listener the listener to receive new log entries. - */ - public LogReceiver(ILogListener listener) { - mListener = listener; - } - - - /** - * Parses new data coming from the log service. - * @param data the data buffer - * @param offset the offset into the buffer signaling the beginning of the new data. - * @param length the length of the new data. - */ - public void parseNewData(byte[] data, int offset, int length) { - // notify the listener of new raw data - if (mListener != null) { - mListener.newData(data, offset, length); - } - - // loop while there is still data to be read and the receiver has not be cancelled. - while (length > 0 && mIsCancelled == false) { - // first check if we have no current entry. - if (mCurrentEntry == null) { - if (mEntryHeaderOffset + length < ENTRY_HEADER_SIZE) { - // if we don't have enough data to finish the header, save - // the data we have and return - System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, length); - mEntryHeaderOffset += length; - return; - } else { - // we have enough to fill the header, let's do it. - // did we store some part at the beginning of the header? - if (mEntryHeaderOffset != 0) { - // copy the rest of the entry header into the header buffer - int size = ENTRY_HEADER_SIZE - mEntryHeaderOffset; - System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, - size); - - // create the entry from the header buffer - mCurrentEntry = createEntry(mEntryHeaderBuffer, 0); - - // since we used the whole entry header buffer, we reset the offset - mEntryHeaderOffset = 0; - - // adjust current offset and remaining length to the beginning - // of the entry data - offset += size; - length -= size; - } else { - // create the entry directly from the data array - mCurrentEntry = createEntry(data, offset); - - // adjust current offset and remaining length to the beginning - // of the entry data - offset += ENTRY_HEADER_SIZE; - length -= ENTRY_HEADER_SIZE; - } - } - } - - // at this point, we have an entry, and offset/length have been updated to skip - // the entry header. - - // if we have enough data for this entry or more, we'll need to end this entry - if (length >= mCurrentEntry.len - mEntryDataOffset) { - // compute and save the size of the data that we have to read for this entry, - // based on how much we may already have read. - int dataSize = mCurrentEntry.len - mEntryDataOffset; - - // we only read what we need, and put it in the entry buffer. - System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, dataSize); - - // notify the listener of a new entry - if (mListener != null) { - mListener.newEntry(mCurrentEntry); - } - - // reset some flags: we have read 0 data of the current entry. - // and we have no current entry being read. - mEntryDataOffset = 0; - mCurrentEntry = null; - - // and update the data buffer info to the end of the current entry / start - // of the next one. - offset += dataSize; - length -= dataSize; - } else { - // we don't have enough data to fill this entry, so we store what we have - // in the entry itself. - System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, length); - - // save the amount read for the data. - mEntryDataOffset += length; - return; - } - } - } - - /** - * Returns whether this receiver is canceling the remote service. - */ - public boolean isCancelled() { - return mIsCancelled; - } - - /** - * Cancels the current remote service. - */ - public void cancel() { - mIsCancelled = true; - } - - /** - * Creates a {@link LogEntry} from the array of bytes. This expects the data buffer size - * to be at least <code>offset + {@link #ENTRY_HEADER_SIZE}</code>. - * @param data the data buffer the entry is read from. - * @param offset the offset of the first byte from the buffer representing the entry. - * @return a new {@link LogEntry} or <code>null</code> if some error happened. - */ - private LogEntry createEntry(byte[] data, int offset) { - if (data.length < offset + ENTRY_HEADER_SIZE) { - throw new InvalidParameterException( - "Buffer not big enough to hold full LoggerEntry header"); - } - - // create the new entry and fill it. - LogEntry entry = new LogEntry(); - entry.len = ArrayHelper.swapU16bitFromArray(data, offset); - - // we've read only 16 bits, but since there's also a 16 bit padding, - // we can skip right over both. - offset += 4; - - entry.pid = ArrayHelper.swap32bitFromArray(data, offset); - offset += 4; - entry.tid = ArrayHelper.swap32bitFromArray(data, offset); - offset += 4; - entry.sec = ArrayHelper.swap32bitFromArray(data, offset); - offset += 4; - entry.nsec = ArrayHelper.swap32bitFromArray(data, offset); - offset += 4; - - // allocate the data - entry.data = new byte[entry.len]; - - return entry; - } - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java deleted file mode 100644 index 7d3d6bf..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/IRemoteAndroidTestRunner.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.testrunner; - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.ShellCommandUnresponsiveException; -import com.android.ddmlib.TimeoutException; - -import java.io.IOException; -import java.util.Collection; - -/** - * Interface for running a Android test command remotely and reporting result to a listener. - */ -public interface IRemoteAndroidTestRunner { - - public static enum TestSize { - /** Run tests annotated with SmallTest */ - SMALL("small"), - /** Run tests annotated with MediumTest */ - MEDIUM("medium"), - /** Run tests annotated with LargeTest */ - LARGE("large"); - - private String mRunnerValue; - - /** - * Create a {@link TestSize}. - * - * @param runnerValue the {@link String} value that represents the size that is passed to - * device. Defined on device in android.test.InstrumentationTestRunner. - */ - TestSize(String runnerValue) { - mRunnerValue = runnerValue; - } - - String getRunnerValue() { - return mRunnerValue; - } - - /** - * Return the {@link TestSize} corresponding to the given Android platform defined value. - * - * @throws IllegalArgumentException if {@link TestSize} cannot be found. - */ - public static TestSize getTestSize(String value) { - // build the error message in the success case too, to avoid two for loops - StringBuilder msgBuilder = new StringBuilder("Unknown TestSize "); - msgBuilder.append(value); - msgBuilder.append(", Must be one of "); - for (TestSize size : values()) { - if (size.getRunnerValue().equals(value)) { - return size; - } - msgBuilder.append(size.getRunnerValue()); - msgBuilder.append(", "); - } - throw new IllegalArgumentException(msgBuilder.toString()); - } - } - - /** - * Returns the application package name. - */ - public String getPackageName(); - - /** - * Returns the runnerName. - */ - public String getRunnerName(); - - /** - * Sets to run only tests in this class - * Must be called before 'run'. - * - * @param className fully qualified class name (eg x.y.z) - */ - public void setClassName(String className); - - /** - * Sets to run only tests in the provided classes - * Must be called before 'run'. - * <p> - * If providing more than one class, requires a InstrumentationTestRunner that supports - * the multiple class argument syntax. - * - * @param classNames array of fully qualified class names (eg x.y.z) - */ - public void setClassNames(String[] classNames); - - /** - * Sets to run only specified test method - * Must be called before 'run'. - * - * @param className fully qualified class name (eg x.y.z) - * @param testName method name - */ - public void setMethodName(String className, String testName); - - /** - * Sets to run all tests in specified package - * Must be called before 'run'. - * - * @param packageName fully qualified package name (eg x.y.z) - */ - public void setTestPackageName(String packageName); - - /** - * Sets to run only tests of given size. - * Must be called before 'run'. - * - * @param size the {@link TestSize} to run. - */ - public void setTestSize(TestSize size); - - /** - * Adds a argument to include in instrumentation command. - * <p/> - * Must be called before 'run'. If an argument with given name has already been provided, it's - * value will be overridden. - * - * @param name the name of the instrumentation bundle argument - * @param value the value of the argument - */ - public void addInstrumentationArg(String name, String value); - - /** - * Removes a previously added argument. - * - * @param name the name of the instrumentation bundle argument to remove - */ - public void removeInstrumentationArg(String name); - - /** - * Adds a boolean argument to include in instrumentation command. - * <p/> - * @see RemoteAndroidTestRunner#addInstrumentationArg - * - * @param name the name of the instrumentation bundle argument - * @param value the value of the argument - */ - public void addBooleanArg(String name, boolean value); - - /** - * Sets this test run to log only mode - skips test execution. - */ - public void setLogOnly(boolean logOnly); - - /** - * Sets this debug mode of this test run. If true, the Android test runner will wait for a - * debugger to attach before proceeding with test execution. - */ - public void setDebug(boolean debug); - - /** - * Sets this code coverage mode of this test run. - */ - 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 IDevice#executeShellCommand(String, com.android.ddmlib.IShellOutputReceiver, int) - */ - public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse); - - /** - * Set a custom run name to be reported to the {@link ITestRunListener} on {@link #run} - * <p/> - * If unspecified, will use package name - * - * @param runName - */ - public void setRunName(String runName); - - /** - * 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 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 TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException; - - /** - * Requests cancellation of this test run. - */ - public void cancel(); - -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/ITestRunListener.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/ITestRunListener.java deleted file mode 100644 index 7e20c9f..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/ITestRunListener.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - -import java.util.Map; - -/** - * Receives event notifications during instrumentation test runs. - * <p/> - * Patterned after junit.runner.TestRunListener. - * <p/> - * The sequence of calls will be: - * <ul> - * <li> testRunStarted - * <li> testStarted - * <li> [testFailed] - * <li> testEnded - * <li> .... - * <li> [testRunFailed] - * <li> testRunEnded - * </ul> - */ -public interface ITestRunListener { - - /** - * Types of test failures. - */ - enum TestFailure { - /** Test failed due to unanticipated uncaught exception. */ - ERROR, - /** Test failed due to a false assertion. */ - FAILURE - } - - /** - * Reports the start of a test run. - * - * @param runName the test run name - * @param testCount total number of tests in test run - */ - public void testRunStarted(String runName, int testCount); - - /** - * Reports the start of an individual test case. - * - * @param test identifies the test - */ - public void testStarted(TestIdentifier test); - - /** - * Reports the failure of a individual test case. - * <p/> - * Will be called between testStarted and testEnded. - * - * @param status failure type - * @param test identifies the test - * @param trace stack trace of failure - */ - public void testFailed(TestFailure status, TestIdentifier test, String trace); - - /** - * Reports the execution end of an individual test case. - * <p/> - * If {@link #testFailed} was not invoked, this test passed. Also returns any key/value - * metrics which may have been emitted during the test case's execution. - * - * @param test identifies the test - * @param testMetrics a {@link Map} of the metrics emitted - */ - public void testEnded(TestIdentifier test, Map<String, String> testMetrics); - - /** - * Reports test run failed to complete due to a fatal error. - * - * @param errorMessage {@link String} describing reason for run failure. - */ - public void testRunFailed(String errorMessage); - - /** - * Reports test run stopped before completion due to a user request. - * <p/> - * TODO: currently unused, consider removing - * - * @param elapsedTime device reported elapsed time, in milliseconds - */ - public void testRunStopped(long elapsedTime); - - /** - * Reports end of test run. - * - * @param elapsedTime device reported elapsed time, in milliseconds - * @param runMetrics key-value pairs reported at the end of a test run - */ - public void testRunEnded(long elapsedTime, Map<String, String> runMetrics); -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java deleted file mode 100644 index 71f329a..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java +++ /dev/null @@ -1,609 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - -import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.Log; -import com.android.ddmlib.MultiLineReceiver; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Parses the 'raw output mode' results of an instrumentation test run from shell and informs a - * ITestRunListener of the results. - * - * <p>Expects the following output: - * - * <p>If fatal error occurred when attempted to run the tests: - * <pre> - * INSTRUMENTATION_STATUS: Error=error Message - * INSTRUMENTATION_FAILED: - * </pre> - * <p>or - * <pre> - * INSTRUMENTATION_RESULT: shortMsg=error Message - * </pre> - * - * <p>Otherwise, expect a series of test results, each one containing a set of status key/value - * pairs, delimited by a start(1)/pass(0)/fail(-2)/error(-1) status code result. At end of test - * run, expects that the elapsed test time in seconds will be displayed - * - * <p>For example: - * <pre> - * INSTRUMENTATION_STATUS_CODE: 1 - * INSTRUMENTATION_STATUS: class=com.foo.FooTest - * INSTRUMENTATION_STATUS: test=testFoo - * INSTRUMENTATION_STATUS: numtests=2 - * INSTRUMENTATION_STATUS: stack=com.foo.FooTest#testFoo:312 - * com.foo.X - * INSTRUMENTATION_STATUS_CODE: -2 - * ... - * - * Time: X - * </pre> - * <p>Note that the "value" portion of the key-value pair may wrap over several text lines - */ -public class InstrumentationResultParser extends MultiLineReceiver { - - /** Relevant test status keys. */ - private static class StatusKeys { - private static final String TEST = "test"; - private static final String CLASS = "class"; - private static final String STACK = "stack"; - private static final String NUMTESTS = "numtests"; - private static final String ERROR = "Error"; - private static final String SHORTMSG = "shortMsg"; - } - - /** The set of expected status keys. Used to filter which keys should be stored as metrics */ - private static final Set<String> KNOWN_KEYS = new HashSet<String>(); - static { - KNOWN_KEYS.add(StatusKeys.TEST); - KNOWN_KEYS.add(StatusKeys.CLASS); - KNOWN_KEYS.add(StatusKeys.STACK); - KNOWN_KEYS.add(StatusKeys.NUMTESTS); - KNOWN_KEYS.add(StatusKeys.ERROR); - KNOWN_KEYS.add(StatusKeys.SHORTMSG); - // unused, but regularly occurring status keys. - KNOWN_KEYS.add("stream"); - KNOWN_KEYS.add("id"); - KNOWN_KEYS.add("current"); - } - - /** Test result status codes. */ - private static class StatusCodes { - private static final int FAILURE = -2; - private static final int START = 1; - private static final int ERROR = -1; - private static final int OK = 0; - private static final int IN_PROGRESS = 2; - } - - /** Prefixes used to identify output. */ - private static class Prefixes { - private static final String STATUS = "INSTRUMENTATION_STATUS: "; - private static final String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: "; - private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: "; - private static final String CODE = "INSTRUMENTATION_CODE: "; - private static final String RESULT = "INSTRUMENTATION_RESULT: "; - private static final String TIME_REPORT = "Time: "; - } - - private final Collection<ITestRunListener> mTestListeners; - - /** - * Test result data - */ - private static class TestResult { - private Integer mCode = null; - private String mTestName = null; - private String mTestClass = null; - private String mStackTrace = null; - private Integer mNumTests = null; - - /** Returns true if all expected values have been parsed */ - boolean isComplete() { - return mCode != null && mTestName != null && mTestClass != null; - } - - /** Provides a more user readable string for TestResult, if possible */ - @Override - public String toString() { - StringBuilder output = new StringBuilder(); - if (mTestClass != null ) { - output.append(mTestClass); - output.append('#'); - } - if (mTestName != null) { - output.append(mTestName); - } - if (output.length() > 0) { - return output.toString(); - } - return "unknown result"; - } - } - - /** the name to provide to {@link ITestRunListener#testRunStarted(String, int)} */ - private final String mTestRunName; - - /** Stores the status values for the test result currently being parsed */ - private TestResult mCurrentTestResult = null; - - /** Stores the status values for the test result last parsed */ - private TestResult mLastTestResult = null; - - /** Stores the current "key" portion of the status key-value being parsed. */ - private String mCurrentKey = null; - - /** Stores the current "value" portion of the status key-value being parsed. */ - private StringBuilder mCurrentValue = null; - - /** True if start of test has already been reported to listener. */ - private boolean mTestStartReported = false; - - /** True if the completion of the test run has been detected. */ - private boolean mTestRunFinished = false; - - /** True if test run failure has already been reported to listener. */ - private boolean mTestRunFailReported = false; - - /** The elapsed time of the test run, in milliseconds. */ - private long mTestTime = 0; - - /** True if current test run has been canceled by user. */ - private boolean mIsCancelled = false; - - /** The number of tests currently run */ - private int mNumTestsRun = 0; - - /** The number of tests expected to run */ - private int mNumTestsExpected = 0; - - /** True if the parser is parsing a line beginning with "INSTRUMENTATION_RESULT" */ - private boolean mInInstrumentationResultKey = false; - - /** - * Stores key-value pairs under INSTRUMENTATION_RESULT header, these are printed at the - * end of a test run, if applicable - */ - private Map<String, String> mInstrumentationResultBundle = new HashMap<String, String>(); - - /** - * Stores key-value pairs of metrics emitted during the execution of each test case. Note that - * standard keys that are stored in the TestResults class are filtered out of this Map. - */ - private Map<String, String> mTestMetrics = new HashMap<String, String>(); - - private static final String LOG_TAG = "InstrumentationResultParser"; - - /** Error message supplied when no parseable test results are received from test run. */ - static final String NO_TEST_RESULTS_MSG = "No test results"; - - /** Error message supplied when a test start bundle is parsed, but not the test end bundle. */ - static final String INCOMPLETE_TEST_ERR_MSG_PREFIX = "Test failed to run to completion"; - static final String INCOMPLETE_TEST_ERR_MSG_POSTFIX = "Check device logcat for details"; - - /** Error message supplied when the test run is incomplete. */ - static final String INCOMPLETE_RUN_ERR_MSG_PREFIX = "Test run failed to complete"; - - /** - * Creates the InstrumentationResultParser. - * - * @param runName the test run name to provide to - * {@link ITestRunListener#testRunStarted(String, int)} - * @param listeners informed of test results as the tests are executing - */ - public InstrumentationResultParser(String runName, Collection<ITestRunListener> listeners) { - mTestRunName = runName; - mTestListeners = new ArrayList<ITestRunListener>(listeners); - } - - /** - * Creates the InstrumentationResultParser for a single listener. - * - * @param runName the test run name to provide to - * {@link ITestRunListener#testRunStarted(String, int)} - * @param listener informed of test results as the tests are executing - */ - public InstrumentationResultParser(String runName, ITestRunListener listener) { - this(runName, Collections.singletonList(listener)); - } - - /** - * Processes the instrumentation test output from shell. - * - * @see MultiLineReceiver#processNewLines - */ - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - parse(line); - // in verbose mode, dump all adb output to log - Log.v(LOG_TAG, line); - } - } - - /** - * Parse an individual output line. Expects a line that is one of: - * <ul> - * <li> - * The start of a new status line (starts with Prefixes.STATUS or Prefixes.STATUS_CODE), - * and thus there is a new key=value pair to parse, and the previous key-value pair is - * finished. - * </li> - * <li> - * A continuation of the previous status (the "value" portion of the key has wrapped - * to the next line). - * </li> - * <li> A line reporting a fatal error in the test run (Prefixes.STATUS_FAILED) </li> - * <li> A line reporting the total elapsed time of the test run. (Prefixes.TIME_REPORT) </li> - * </ul> - * - * @param line Text output line - */ - private void parse(String line) { - if (line.startsWith(Prefixes.STATUS_CODE)) { - // Previous status key-value has been collected. Store it. - submitCurrentKeyValue(); - mInInstrumentationResultKey = false; - parseStatusCode(line); - } else if (line.startsWith(Prefixes.STATUS)) { - // Previous status key-value has been collected. Store it. - submitCurrentKeyValue(); - mInInstrumentationResultKey = false; - parseKey(line, Prefixes.STATUS.length()); - } else if (line.startsWith(Prefixes.RESULT)) { - // Previous status key-value has been collected. Store it. - submitCurrentKeyValue(); - mInInstrumentationResultKey = true; - parseKey(line, Prefixes.RESULT.length()); - } else if (line.startsWith(Prefixes.STATUS_FAILED) || - line.startsWith(Prefixes.CODE)) { - // Previous status key-value has been collected. Store it. - submitCurrentKeyValue(); - mInInstrumentationResultKey = false; - // these codes signal the end of the instrumentation run - mTestRunFinished = true; - // just ignore the remaining data on this line - } else if (line.startsWith(Prefixes.TIME_REPORT)) { - parseTime(line); - } else { - if (mCurrentValue != null) { - // this is a value that has wrapped to next line. - mCurrentValue.append("\r\n"); - mCurrentValue.append(line); - } else if (line.trim().length() > 0){ - Log.d(LOG_TAG, "unrecognized line " + line); - } - } - } - - /** - * Stores the currently parsed key-value pair in the appropriate place. - */ - private void submitCurrentKeyValue() { - if (mCurrentKey != null && mCurrentValue != null) { - String statusValue = mCurrentValue.toString(); - if (mInInstrumentationResultKey) { - if (!KNOWN_KEYS.contains(mCurrentKey)) { - mInstrumentationResultBundle.put(mCurrentKey, statusValue); - } else if (mCurrentKey.equals(StatusKeys.SHORTMSG)) { - // test run must have failed - handleTestRunFailed(String.format("Instrumentation run failed due to '%1$s'", - statusValue)); - } - } else { - TestResult testInfo = getCurrentTestInfo(); - - if (mCurrentKey.equals(StatusKeys.CLASS)) { - testInfo.mTestClass = statusValue.trim(); - } else if (mCurrentKey.equals(StatusKeys.TEST)) { - testInfo.mTestName = statusValue.trim(); - } else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) { - try { - testInfo.mNumTests = Integer.parseInt(statusValue); - } catch (NumberFormatException e) { - Log.w(LOG_TAG, "Unexpected integer number of tests, received " - + statusValue); - } - } else if (mCurrentKey.equals(StatusKeys.ERROR)) { - // test run must have failed - handleTestRunFailed(statusValue); - } else if (mCurrentKey.equals(StatusKeys.STACK)) { - testInfo.mStackTrace = statusValue; - } else if (!KNOWN_KEYS.contains(mCurrentKey)) { - // Not one of the recognized key/value pairs, so dump it in mTestMetrics - mTestMetrics.put(mCurrentKey, statusValue); - } - } - - mCurrentKey = null; - mCurrentValue = null; - } - } - - /** - * A utility method to return the test metrics from the current test case execution and get - * ready for the next one. - */ - private Map<String, String> getAndResetTestMetrics() { - Map<String, String> retVal = mTestMetrics; - mTestMetrics = new HashMap<String, String>(); - return retVal; - } - - private TestResult getCurrentTestInfo() { - if (mCurrentTestResult == null) { - mCurrentTestResult = new TestResult(); - } - return mCurrentTestResult; - } - - private void clearCurrentTestInfo() { - mLastTestResult = mCurrentTestResult; - mCurrentTestResult = null; - } - - /** - * Parses the key from the current line. - * Expects format of "key=value". - * - * @param line full line of text to parse - * @param keyStartPos the starting position of the key in the given line - */ - private void parseKey(String line, int keyStartPos) { - int endKeyPos = line.indexOf('=', keyStartPos); - if (endKeyPos != -1) { - mCurrentKey = line.substring(keyStartPos, endKeyPos).trim(); - parseValue(line, endKeyPos + 1); - } - } - - /** - * Parses the start of a key=value pair. - * - * @param line - full line of text to parse - * @param valueStartPos - the starting position of the value in the given line - */ - private void parseValue(String line, int valueStartPos) { - mCurrentValue = new StringBuilder(); - mCurrentValue.append(line.substring(valueStartPos)); - } - - /** - * Parses out a status code result. - */ - private void parseStatusCode(String line) { - String value = line.substring(Prefixes.STATUS_CODE.length()).trim(); - TestResult testInfo = getCurrentTestInfo(); - testInfo.mCode = StatusCodes.ERROR; - try { - testInfo.mCode = Integer.parseInt(value); - } catch (NumberFormatException e) { - Log.w(LOG_TAG, "Expected integer status code, received: " + value); - testInfo.mCode = StatusCodes.ERROR; - } - if (testInfo.mCode != StatusCodes.IN_PROGRESS) { - // this means we're done with current test result bundle - reportResult(testInfo); - clearCurrentTestInfo(); - } - } - - /** - * Returns true if test run canceled. - * - * @see IShellOutputReceiver#isCancelled() - */ - @Override - public boolean isCancelled() { - return mIsCancelled; - } - - /** - * Requests cancellation of test run. - */ - public void cancel() { - mIsCancelled = true; - } - - /** - * Reports a test result to the test run listener. Must be called when a individual test - * result has been fully parsed. - * - * @param statusMap key-value status pairs of test result - */ - private void reportResult(TestResult testInfo) { - if (!testInfo.isComplete()) { - Log.w(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString()); - return; - } - reportTestRunStarted(testInfo); - TestIdentifier testId = new TestIdentifier(testInfo.mTestClass, testInfo.mTestName); - Map<String, String> metrics; - - switch (testInfo.mCode) { - case StatusCodes.START: - for (ITestRunListener listener : mTestListeners) { - listener.testStarted(testId); - } - break; - case StatusCodes.FAILURE: - metrics = getAndResetTestMetrics(); - for (ITestRunListener listener : mTestListeners) { - listener.testFailed(ITestRunListener.TestFailure.FAILURE, testId, - getTrace(testInfo)); - - listener.testEnded(testId, metrics); - } - mNumTestsRun++; - break; - case StatusCodes.ERROR: - metrics = getAndResetTestMetrics(); - for (ITestRunListener listener : mTestListeners) { - listener.testFailed(ITestRunListener.TestFailure.ERROR, testId, - getTrace(testInfo)); - listener.testEnded(testId, metrics); - } - mNumTestsRun++; - break; - case StatusCodes.OK: - metrics = getAndResetTestMetrics(); - for (ITestRunListener listener : mTestListeners) { - listener.testEnded(testId, metrics); - } - mNumTestsRun++; - break; - default: - metrics = getAndResetTestMetrics(); - Log.e(LOG_TAG, "Unknown status code received: " + testInfo.mCode); - for (ITestRunListener listener : mTestListeners) { - listener.testEnded(testId, metrics); - } - mNumTestsRun++; - break; - } - - } - - /** - * Reports the start of a test run, and the total test count, if it has not been previously - * reported. - * - * @param testInfo current test status values - */ - private void reportTestRunStarted(TestResult testInfo) { - // if start test run not reported yet - if (!mTestStartReported && testInfo.mNumTests != null) { - for (ITestRunListener listener : mTestListeners) { - listener.testRunStarted(mTestRunName, testInfo.mNumTests); - } - mNumTestsExpected = testInfo.mNumTests; - mTestStartReported = true; - } - } - - /** - * Returns the stack trace of the current failed test, from the provided testInfo. - */ - private String getTrace(TestResult testInfo) { - if (testInfo.mStackTrace != null) { - return testInfo.mStackTrace; - } else { - Log.e(LOG_TAG, "Could not find stack trace for failed test "); - return new Throwable("Unknown failure").toString(); - } - } - - /** - * Parses out and store the elapsed time. - */ - private void parseTime(String line) { - final Pattern timePattern = Pattern.compile(String.format("%s\\s*([\\d\\.]+)", - Prefixes.TIME_REPORT)); - Matcher timeMatcher = timePattern.matcher(line); - if (timeMatcher.find()) { - String timeString = timeMatcher.group(1); - try { - float timeSeconds = Float.parseFloat(timeString); - mTestTime = (long) (timeSeconds * 1000); - } catch (NumberFormatException e) { - Log.w(LOG_TAG, String.format("Unexpected time format %1$s", line)); - } - } else { - Log.w(LOG_TAG, String.format("Unexpected time format %1$s", line)); - } - } - - /** - * Inform the parser of a instrumentation run failure. Should be called when the adb command - * used to run the test fails. - */ - public void handleTestRunFailed(String errorMsg) { - errorMsg = (errorMsg == null ? "Unknown error" : errorMsg); - Log.i(LOG_TAG, String.format("test run failed: '%1$s'", errorMsg)); - if (mLastTestResult != null && - mLastTestResult.isComplete() && - StatusCodes.START == mLastTestResult.mCode) { - - // received test start msg, but not test complete - // assume test caused this, report as test failure - TestIdentifier testId = new TestIdentifier(mLastTestResult.mTestClass, - mLastTestResult.mTestName); - for (ITestRunListener listener : mTestListeners) { - listener.testFailed(ITestRunListener.TestFailure.ERROR, testId, - String.format("%1$s. Reason: '%2$s'. %3$s", INCOMPLETE_TEST_ERR_MSG_PREFIX, - errorMsg, INCOMPLETE_TEST_ERR_MSG_POSTFIX)); - listener.testEnded(testId, getAndResetTestMetrics()); - } - } - for (ITestRunListener listener : mTestListeners) { - if (!mTestStartReported) { - // test run wasn't started - must have crashed before it started - listener.testRunStarted(mTestRunName, 0); - } - listener.testRunFailed(errorMsg); - listener.testRunEnded(mTestTime, mInstrumentationResultBundle); - } - mTestStartReported = true; - mTestRunFailReported = true; - } - - /** - * Called by parent when adb session is complete. - */ - @Override - public void done() { - super.done(); - if (!mTestRunFailReported) { - handleOutputDone(); - } - } - - /** - * Handles the end of the adb session when a test run failure has not been reported yet - */ - private void handleOutputDone() { - if (!mTestStartReported && !mTestRunFinished) { - // no results - handleTestRunFailed(NO_TEST_RESULTS_MSG); - } else if (mNumTestsExpected > mNumTestsRun) { - final String message = - String.format("%1$s. Expected %2$d tests, received %3$d", - INCOMPLETE_RUN_ERR_MSG_PREFIX, mNumTestsExpected, mNumTestsRun); - handleTestRunFailed(message); - } else { - for (ITestRunListener listener : mTestListeners) { - if (!mTestStartReported) { - // test run wasn't started, but it finished successfully. Must be a run with - // no tests - listener.testRunStarted(mTestRunName, 0); - } - listener.testRunEnded(mTestTime, mInstrumentationResultBundle); - } - } - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java deleted file mode 100644 index 124df7d..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.Log; -import com.android.ddmlib.ShellCommandUnresponsiveException; -import com.android.ddmlib.TimeoutException; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Hashtable; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Runs a Android test command remotely and reports results. - */ -public class RemoteAndroidTestRunner implements IRemoteAndroidTestRunner { - - private final String mPackageName; - private final String mRunnerName; - private IDevice mRemoteDevice; - // default to no timeout - private int mMaxTimeToOutputResponse = 0; - private String mRunName = null; - - /** map of name-value instrumentation argument pairs */ - private Map<String, String> mArgMap; - private InstrumentationResultParser mParser; - - private static final String LOG_TAG = "RemoteAndroidTest"; - private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner"; - - private static final char CLASS_SEPARATOR = ','; - private static final char METHOD_SEPARATOR = '#'; - private static final char RUNNER_SEPARATOR = '/'; - - // defined instrumentation argument names - private static final String CLASS_ARG_NAME = "class"; - private static final String LOG_ARG_NAME = "log"; - private static final String DEBUG_ARG_NAME = "debug"; - private static final String COVERAGE_ARG_NAME = "coverage"; - private static final String PACKAGE_ARG_NAME = "package"; - private static final String SIZE_ARG_NAME = "size"; - - /** - * Creates a remote Android test runner. - * - * @param packageName the Android application package that contains the tests to run - * @param runnerName the instrumentation test runner to execute. If null, will use default - * runner - * @param remoteDevice the Android device to execute tests on - */ - public RemoteAndroidTestRunner(String packageName, - String runnerName, - IDevice remoteDevice) { - - mPackageName = packageName; - mRunnerName = runnerName; - mRemoteDevice = remoteDevice; - mArgMap = new Hashtable<String, String>(); - } - - /** - * Alternate constructor. Uses default instrumentation runner. - * - * @param packageName the Android application package that contains the tests to run - * @param remoteDevice the Android device to execute tests on - */ - public RemoteAndroidTestRunner(String packageName, - IDevice remoteDevice) { - this(packageName, null, remoteDevice); - } - - /** - * {@inheritDoc} - */ - @Override - public String getPackageName() { - return mPackageName; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRunnerName() { - if (mRunnerName == null) { - return DEFAULT_RUNNER_NAME; - } - return mRunnerName; - } - - /** - * Returns the complete instrumentation component path. - */ - private String getRunnerPath() { - return getPackageName() + RUNNER_SEPARATOR + getRunnerName(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setClassName(String className) { - addInstrumentationArg(CLASS_ARG_NAME, className); - } - - /** - * {@inheritDoc} - */ - @Override - public void setClassNames(String[] classNames) { - StringBuilder classArgBuilder = new StringBuilder(); - - for (int i = 0; i < classNames.length; i++) { - if (i != 0) { - classArgBuilder.append(CLASS_SEPARATOR); - } - classArgBuilder.append(classNames[i]); - } - setClassName(classArgBuilder.toString()); - } - - /** - * {@inheritDoc} - */ - @Override - public void setMethodName(String className, String testName) { - setClassName(className + METHOD_SEPARATOR + testName); - } - - /** - * {@inheritDoc} - */ - @Override - public void setTestPackageName(String packageName) { - addInstrumentationArg(PACKAGE_ARG_NAME, packageName); - } - - /** - * {@inheritDoc} - */ - @Override - public void addInstrumentationArg(String name, String value) { - if (name == null || value == null) { - throw new IllegalArgumentException("name or value arguments cannot be null"); - } - mArgMap.put(name, value); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeInstrumentationArg(String name) { - if (name == null) { - throw new IllegalArgumentException("name argument cannot be null"); - } - mArgMap.remove(name); - } - - /** - * {@inheritDoc} - */ - @Override - public void addBooleanArg(String name, boolean value) { - addInstrumentationArg(name, Boolean.toString(value)); - } - - /** - * {@inheritDoc} - */ - @Override - public void setLogOnly(boolean logOnly) { - addBooleanArg(LOG_ARG_NAME, logOnly); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDebug(boolean debug) { - addBooleanArg(DEBUG_ARG_NAME, debug); - } - - /** - * {@inheritDoc} - */ - @Override - public void setCoverage(boolean coverage) { - addBooleanArg(COVERAGE_ARG_NAME, coverage); - } - - /** - * {@inheritDoc} - */ - @Override - public void setTestSize(TestSize size) { - addInstrumentationArg(SIZE_ARG_NAME, size.getRunnerValue()); - } - - /** - * {@inheritDoc} - */ - @Override - public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse) { - mMaxTimeToOutputResponse = maxTimeToOutputResponse; - } - - /** - * {@inheritDoc} - */ - @Override - public void setRunName(String runName) { - mRunName = runName; - } - - /** - * {@inheritDoc} - */ - @Override - public void run(ITestRunListener... listeners) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - run(Arrays.asList(listeners)); - } - - /** - * {@inheritDoc} - */ - @Override - public void run(Collection<ITestRunListener> listeners) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - final String runCaseCommandStr = String.format("am instrument -w -r %1$s %2$s", - getArgsCommand(), getRunnerPath()); - Log.i(LOG_TAG, String.format("Running %1$s on %2$s", runCaseCommandStr, - mRemoteDevice.getSerialNumber())); - String runName = mRunName == null ? mPackageName : mRunName; - mParser = new InstrumentationResultParser(runName, listeners); - - try { - mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse); - } catch (IOException e) { - Log.w(LOG_TAG, String.format("IOException %1$s when running tests %2$s on %3$s", - e.toString(), getPackageName(), mRemoteDevice.getSerialNumber())); - // rely on parser to communicate results to listeners - mParser.handleTestRunFailed(e.toString()); - throw e; - } catch (ShellCommandUnresponsiveException e) { - Log.w(LOG_TAG, String.format( - "ShellCommandUnresponsiveException %1$s when running tests %2$s on %3$s", - e.toString(), getPackageName(), mRemoteDevice.getSerialNumber())); - mParser.handleTestRunFailed(String.format( - "Failed to receive adb shell test output within %1$d ms. " + - "Test may have timed out, or adb connection to device became unresponsive", - mMaxTimeToOutputResponse)); - throw e; - } catch (TimeoutException e) { - Log.w(LOG_TAG, String.format( - "TimeoutException when running tests %1$s on %2$s", getPackageName(), - mRemoteDevice.getSerialNumber())); - mParser.handleTestRunFailed(e.toString()); - throw e; - } catch (AdbCommandRejectedException e) { - Log.w(LOG_TAG, String.format( - "AdbCommandRejectedException %1$s when running tests %2$s on %3$s", - e.toString(), getPackageName(), mRemoteDevice.getSerialNumber())); - mParser.handleTestRunFailed(e.toString()); - throw e; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void cancel() { - if (mParser != null) { - mParser.cancel(); - } - } - - /** - * Returns the full instrumentation command line syntax for the provided instrumentation - * arguments. - * Returns an empty string if no arguments were specified. - */ - private String getArgsCommand() { - StringBuilder commandBuilder = new StringBuilder(); - for (Entry<String, String> argPair : mArgMap.entrySet()) { - final String argCmd = String.format(" -e %1$s %2$s", argPair.getKey(), - argPair.getValue()); - commandBuilder.append(argCmd); - } - return commandBuilder.toString(); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestIdentifier.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestIdentifier.java deleted file mode 100644 index 7de5736..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestIdentifier.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - -/** - * Identifies a parsed instrumentation test. - */ -public class TestIdentifier { - - private final String mClassName; - private final String mTestName; - - /** - * Creates a test identifier. - * - * @param className fully qualified class name of the test. Cannot be null. - * @param testName name of the test. Cannot be null. - */ - public TestIdentifier(String className, String testName) { - if (className == null || testName == null) { - throw new IllegalArgumentException("className and testName must " + - "be non-null"); - } - mClassName = className; - mTestName = testName; - } - - /** - * Returns the fully qualified class name of the test. - */ - public String getClassName() { - return mClassName; - } - - /** - * Returns the name of the test. - */ - public String getTestName() { - return mTestName; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mClassName == null) ? 0 : mClassName.hashCode()); - result = prime * result + ((mTestName == null) ? 0 : mTestName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - TestIdentifier other = (TestIdentifier) obj; - if (mClassName == null) { - if (other.mClassName != null) - return false; - } else if (!mClassName.equals(other.mClassName)) - return false; - if (mTestName == null) { - if (other.mTestName != null) - return false; - } else if (!mTestName.equals(other.mTestName)) - return false; - return true; - } - - @Override - public String toString() { - return String.format("%s#%s", getClassName(), getTestName()); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestResult.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestResult.java deleted file mode 100644 index 57e91f7..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestResult.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.testrunner; - -import java.util.Arrays; -import java.util.Map; - -/** - * Container for a result of a single test. - */ -public class TestResult { - - public enum TestStatus { - /** Test error */ - ERROR, - /** Test failed. */ - FAILURE, - /** Test passed */ - PASSED, - /** Test started but not ended */ - INCOMPLETE - } - - private TestStatus mStatus; - private String mStackTrace; - private Map<String, String> mMetrics; - // the start and end time of the test, measured via {@link System#currentTimeMillis()} - private long mStartTime = 0; - private long mEndTime = 0; - - public TestResult() { - mStatus = TestStatus.INCOMPLETE; - mStartTime = System.currentTimeMillis(); - } - - /** - * Get the {@link TestStatus} result of the test. - */ - public TestStatus getStatus() { - return mStatus; - } - - /** - * Get the associated {@link String} stack trace. Should be <code>null</code> if - * {@link #getStatus()} is {@link TestStatus.PASSED}. - */ - public String getStackTrace() { - return mStackTrace; - } - - /** - * Get the associated test metrics. - */ - public Map<String, String> getMetrics() { - return mMetrics; - } - - /** - * Set the test metrics, overriding any previous values. - */ - public void setMetrics(Map<String, String> metrics) { - mMetrics = metrics; - } - - /** - * Return the {@link System#currentTimeMillis()} time that the - * {@link ITestInvocationListener#testStarted(TestIdentifier)} event was received. - */ - public long getStartTime() { - return mStartTime; - } - - /** - * Return the {@link System#currentTimeMillis()} time that the - * {@link ITestInvocationListener#testEnded(TestIdentifier)} event was received. - */ - public long getEndTime() { - return mEndTime; - } - - /** - * Set the {@link TestStatus}. - */ - public TestResult setStatus(TestStatus status) { - mStatus = status; - return this; - } - - /** - * Set the stack trace. - */ - public void setStackTrace(String trace) { - mStackTrace = trace; - } - - /** - * Sets the end time - */ - public void setEndTime(long currentTimeMillis) { - mEndTime = currentTimeMillis; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Arrays.hashCode(new Object[] {mMetrics, mStackTrace, mStatus}); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - TestResult other = (TestResult) obj; - return equal(mMetrics, other.mMetrics) && - equal(mStackTrace, other.mStackTrace) && - equal(mStatus, other.mStatus); - } - - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java deleted file mode 100644 index 14bb477..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * 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.testrunner; - -import com.android.ddmlib.Log; -import com.android.ddmlib.testrunner.TestResult.TestStatus; - -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -/** - * Holds results from a single test run. - * <p/> - * Maintains an accurate count of tests during execution, and tracks incomplete tests. - */ -public class TestRunResult { - private static final String LOG_TAG = TestRunResult.class.getSimpleName(); - private final String mTestRunName; - // Uses a synchronized map to make thread safe. - // Uses a LinkedHashmap to have predictable iteration order - private Map<TestIdentifier, TestResult> mTestResults = - Collections.synchronizedMap(new LinkedHashMap<TestIdentifier, TestResult>()); - private Map<String, String> mRunMetrics = new HashMap<String, String>(); - private boolean mIsRunComplete = false; - private long mElapsedTime = 0; - private int mNumFailedTests = 0; - private int mNumErrorTests = 0; - private int mNumPassedTests = 0; - private int mNumInCompleteTests = 0; - private String mRunFailureError = null; - - /** - * Create a {@link TestRunResult}. - * - * @param runName - */ - public TestRunResult(String runName) { - mTestRunName = runName; - } - - /** - * Create an empty{@link TestRunResult}. - */ - public TestRunResult() { - this("not started"); - } - - /** - * @return the test run name - */ - public String getName() { - return mTestRunName; - } - - /** - * Gets a map of the test results. - * @return - */ - public Map<TestIdentifier, TestResult> getTestResults() { - return mTestResults; - } - - /** - * Adds test run metrics. - * <p/> - * @param runMetrics the run metrics - * @param aggregateMetrics if <code>true</code>, attempt to add given metrics values to any - * currently stored values. If <code>false</code>, replace any currently stored metrics with - * the same key. - */ - public void addMetrics(Map<String, String> runMetrics, boolean aggregateMetrics) { - if (aggregateMetrics) { - for (Map.Entry<String, String> entry : runMetrics.entrySet()) { - String existingValue = mRunMetrics.get(entry.getKey()); - String combinedValue = combineValues(existingValue, entry.getValue()); - mRunMetrics.put(entry.getKey(), combinedValue); - } - } else { - mRunMetrics.putAll(runMetrics); - } - } - - /** - * Combine old and new metrics value - * - * @param existingValue - * @param value - * @return - */ - private String combineValues(String existingValue, String newValue) { - if (existingValue != null) { - try { - Long existingLong = Long.parseLong(existingValue); - Long newLong = Long.parseLong(newValue); - return Long.toString(existingLong + newLong); - } catch (NumberFormatException e) { - // not a long, skip to next - } - try { - Double existingDouble = Double.parseDouble(existingValue); - Double newDouble = Double.parseDouble(newValue); - return Double.toString(existingDouble + newDouble); - } catch (NumberFormatException e) { - // not a double either, fall through - } - } - // default to overriding existingValue - return newValue; - } - - /** - * @return a {@link Map} of the test test run metrics. - */ - public Map<String, String> getRunMetrics() { - return mRunMetrics; - } - - /** - * Gets the set of completed tests. - */ - public Set<TestIdentifier> getCompletedTests() { - Set<TestIdentifier> completedTests = new LinkedHashSet<TestIdentifier>(); - for (Map.Entry<TestIdentifier, TestResult> testEntry : getTestResults().entrySet()) { - if (!testEntry.getValue().getStatus().equals(TestStatus.INCOMPLETE)) { - completedTests.add(testEntry.getKey()); - } - } - return completedTests; - } - - /** - * @return <code>true</code> if test run failed. - */ - public boolean isRunFailure() { - return mRunFailureError != null; - } - - /** - * @return <code>true</code> if test run finished. - */ - public boolean isRunComplete() { - return mIsRunComplete; - } - - void setRunComplete(boolean runComplete) { - mIsRunComplete = runComplete; - } - - void addElapsedTime(long elapsedTime) { - mElapsedTime+= elapsedTime; - } - - void setRunFailureError(String errorMessage) { - mRunFailureError = errorMessage; - } - - /** - * Gets the number of passed tests for this run. - */ - public int getNumPassedTests() { - return mNumPassedTests; - } - - /** - * Gets the number of tests in this run. - */ - public int getNumTests() { - return mTestResults.size(); - } - - /** - * Gets the number of complete tests in this run ie with status != incomplete. - */ - public int getNumCompleteTests() { - return getNumTests() - getNumIncompleteTests(); - } - - /** - * Gets the number of failed tests in this run. - */ - public int getNumFailedTests() { - return mNumFailedTests; - } - - /** - * Gets the number of error tests in this run. - */ - public int getNumErrorTests() { - return mNumErrorTests; - } - - /** - * Gets the number of incomplete tests in this run. - */ - public int getNumIncompleteTests() { - return mNumInCompleteTests; - } - - /** - * @return <code>true</code> if test run had any failed or error tests. - */ - public boolean hasFailedTests() { - return getNumErrorTests() > 0 || getNumFailedTests() > 0; - } - - /** - * @return - */ - public long getElapsedTime() { - return mElapsedTime; - } - - /** - * Return the run failure error message, <code>null</code> if run did not fail. - */ - public String getRunFailureMessage() { - return mRunFailureError; - } - - /** - * Report the start of a test. - * @param test - */ - void reportTestStarted(TestIdentifier test) { - TestResult result = mTestResults.get(test); - - if (result != null) { - Log.d(LOG_TAG, String.format("Replacing result for %s", test)); - switch (result.getStatus()) { - case ERROR: - mNumErrorTests--; - break; - case FAILURE: - mNumFailedTests--; - break; - case PASSED: - mNumPassedTests--; - break; - case INCOMPLETE: - // ignore - break; - } - } else { - mNumInCompleteTests++; - } - mTestResults.put(test, new TestResult()); - } - - /** - * Report a test failure. - * - * @param test - * @param status - * @param trace - */ - void reportTestFailure(TestIdentifier test, TestStatus status, String trace) { - TestResult result = mTestResults.get(test); - if (result == null) { - Log.d(LOG_TAG, String.format("Received test failure for %s without testStarted", test)); - result = new TestResult(); - mTestResults.put(test, result); - } else if (result.getStatus().equals(TestStatus.PASSED)) { - // this should never happen... - Log.d(LOG_TAG, String.format("Replacing passed result for %s", test)); - mNumPassedTests--; - } - - result.setStackTrace(trace); - switch (status) { - case ERROR: - mNumErrorTests++; - result.setStatus(TestStatus.ERROR); - break; - case FAILURE: - result.setStatus(TestStatus.FAILURE); - mNumFailedTests++; - break; - } - } - - /** - * Report the end of the test - * - * @param test - * @param testMetrics - * @return <code>true</code> if test was recorded as passed, false otherwise - */ - boolean reportTestEnded(TestIdentifier test, Map<String, String> testMetrics) { - TestResult result = mTestResults.get(test); - if (result == null) { - Log.d(LOG_TAG, String.format("Received test ended for %s without testStarted", test)); - result = new TestResult(); - mTestResults.put(test, result); - } else { - mNumInCompleteTests--; - } - - result.setEndTime(System.currentTimeMillis()); - result.setMetrics(testMetrics); - if (result.getStatus().equals(TestStatus.INCOMPLETE)) { - result.setStatus(TestStatus.PASSED); - mNumPassedTests++; - return true; - } - return false; - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java deleted file mode 100644 index 27ba476..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2009 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.testrunner; - -import com.android.ddmlib.Log; -import com.android.ddmlib.Log.LogLevel; -import com.android.ddmlib.testrunner.TestResult.TestStatus; - -import org.kxml2.io.KXmlSerializer; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; -import java.util.TimeZone; - -/** - * Writes JUnit results to an XML files in a format consistent with - * Ant's XMLJUnitResultFormatter. - * <p/> - * Creates a separate XML file per test run. - * <p/> - */ -public class XmlTestRunListener implements ITestRunListener { - - private static final String LOG_TAG = "XmlResultReporter"; - - private static final String TEST_RESULT_FILE_SUFFIX = ".xml"; - private static final String TEST_RESULT_FILE_PREFIX = "test_result_"; - - private static final String TESTSUITE = "testsuite"; - private static final String TESTCASE = "testcase"; - private static final String ERROR = "error"; - private static final String FAILURE = "failure"; - private static final String ATTR_NAME = "name"; - private static final String ATTR_TIME = "time"; - private static final String ATTR_ERRORS = "errors"; - private static final String ATTR_FAILURES = "failures"; - private static final String ATTR_TESTS = "tests"; - //private static final String ATTR_TYPE = "type"; - //private static final String ATTR_MESSAGE = "message"; - private static final String PROPERTIES = "properties"; - private static final String ATTR_CLASSNAME = "classname"; - private static final String TIMESTAMP = "timestamp"; - private static final String HOSTNAME = "hostname"; - - /** the XML namespace */ - private static final String ns = null; - - private File mReportDir = new File(System.getProperty("java.io.tmpdir")); - - private String mReportPath = ""; - - private TestRunResult mRunResult = new TestRunResult(); - - /** - * Sets the report file to use. - */ - public void setReportDir(File file) { - mReportDir = file; - } - - @Override - public void testRunStarted(String runName, int numTests) { - mRunResult = new TestRunResult(runName); - } - - @Override - public void testStarted(TestIdentifier test) { - mRunResult.reportTestStarted(test); - } - - @Override - public void testFailed(TestFailure status, TestIdentifier test, String trace) { - if (status.equals(TestFailure.ERROR)) { - mRunResult.reportTestFailure(test, TestStatus.ERROR, trace); - } else { - mRunResult.reportTestFailure(test, TestStatus.FAILURE, trace); - } - Log.d(LOG_TAG, String.format("%s %s: %s", test, status, trace)); - } - - @Override - public void testEnded(TestIdentifier test, Map<String, String> testMetrics) { - mRunResult.reportTestEnded(test, testMetrics); - } - - @Override - public void testRunFailed(String errorMessage) { - mRunResult.setRunFailureError(errorMessage); - } - - @Override - public void testRunStopped(long arg0) { - // ignore - } - - @Override - public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) { - mRunResult.setRunComplete(true); - generateDocument(mReportDir, elapsedTime); - } - - /** - * Creates a report file and populates it with the report data from the completed tests. - */ - private void generateDocument(File reportDir, long elapsedTime) { - String timestamp = getTimestamp(); - - OutputStream stream = null; - try { - stream = createOutputResultStream(reportDir); - KXmlSerializer serializer = new KXmlSerializer(); - serializer.setOutput(stream, "UTF-8"); - serializer.startDocument("UTF-8", null); - serializer.setFeature( - "http://xmlpull.org/v1/doc/features.html#indent-output", true); - // TODO: insert build info - printTestResults(serializer, timestamp, elapsedTime); - serializer.endDocument(); - String msg = String.format("XML test result file generated at %s. Total tests %d, " + - "Failed %d, Error %d", getAbsoluteReportPath(), mRunResult.getNumTests(), - mRunResult.getNumFailedTests(), mRunResult.getNumErrorTests()); - Log.logAndDisplay(LogLevel.INFO, LOG_TAG, msg); - } catch (IOException e) { - Log.e(LOG_TAG, "Failed to generate report data"); - // TODO: consider throwing exception - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException ignored) { - } - } - } - } - - private String getAbsoluteReportPath() { - return mReportPath ; - } - - /** - * Return the current timestamp as a {@link String}. - */ - String getTimestamp() { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - TimeZone gmt = TimeZone.getTimeZone("UTC"); - dateFormat.setTimeZone(gmt); - dateFormat.setLenient(true); - String timestamp = dateFormat.format(new Date()); - return timestamp; - } - - /** - * Creates the output stream to use for test results. Exposed for mocking. - */ - OutputStream createOutputResultStream(File reportDir) throws IOException { - File reportFile = File.createTempFile(TEST_RESULT_FILE_PREFIX, TEST_RESULT_FILE_SUFFIX, - reportDir); - Log.i(LOG_TAG, String.format("Created xml report file at %s", - reportFile.getAbsolutePath())); - mReportPath = reportFile.getAbsolutePath(); - return new BufferedOutputStream(new FileOutputStream(reportFile)); - } - - void printTestResults(KXmlSerializer serializer, String timestamp, long elapsedTime) - throws IOException { - serializer.startTag(ns, TESTSUITE); - serializer.attribute(ns, ATTR_NAME, mRunResult.getName()); - serializer.attribute(ns, ATTR_TESTS, Integer.toString(mRunResult.getNumTests())); - serializer.attribute(ns, ATTR_FAILURES, Integer.toString(mRunResult.getNumFailedTests())); - serializer.attribute(ns, ATTR_ERRORS, Integer.toString(mRunResult.getNumErrorTests())); - serializer.attribute(ns, ATTR_TIME, Long.toString(elapsedTime)); - serializer.attribute(ns, TIMESTAMP, timestamp); - serializer.attribute(ns, HOSTNAME, "localhost"); - serializer.startTag(ns, PROPERTIES); - serializer.endTag(ns, PROPERTIES); - - Map<TestIdentifier, TestResult> testResults = mRunResult.getTestResults(); - for (Map.Entry<TestIdentifier, TestResult> testEntry : testResults.entrySet()) { - print(serializer, testEntry.getKey(), testEntry.getValue()); - } - - serializer.endTag(ns, TESTSUITE); - } - - void print(KXmlSerializer serializer, TestIdentifier testId, TestResult testResult) - throws IOException { - - serializer.startTag(ns, TESTCASE); - serializer.attribute(ns, ATTR_NAME, testId.getTestName()); - serializer.attribute(ns, ATTR_CLASSNAME, testId.getClassName()); - long elapsedTimeMs = testResult.getEndTime() - testResult.getStartTime(); - serializer.attribute(ns, ATTR_TIME, Long.toString(elapsedTimeMs)); - - if (!TestStatus.PASSED.equals(testResult.getStatus())) { - String result = testResult.getStatus().equals(TestStatus.FAILURE) ? FAILURE : ERROR; - serializer.startTag(ns, result); - // TODO: get message of stack trace ? -// String msg = testResult.getStackTrace(); -// if (msg != null && msg.length() > 0) { -// serializer.attribute(ns, ATTR_MESSAGE, msg); -// } - // TODO: get class name of stackTrace exception - //serializer.attribute(ns, ATTR_TYPE, testId.getClassName()); - String stackText = sanitize(testResult.getStackTrace()); - serializer.text(stackText); - serializer.endTag(ns, result); - } - - serializer.endTag(ns, TESTCASE); - } - - /** - * Returns the text in a format that is safe for use in an XML document. - */ - private String sanitize(String text) { - return text.replace("\0", "<\\0>"); - } -} diff --git a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/utils/ArrayHelper.java b/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/utils/ArrayHelper.java deleted file mode 100644 index 8167e5d..0000000 --- a/ddms/libs/ddmlib/src/main/java/com/android/ddmlib/utils/ArrayHelper.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2007 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.utils; - -/** - * Utility class providing array to int/long conversion for data received from devices through adb. - */ -public final class ArrayHelper { - - /** - * Swaps an unsigned value around, and puts the result in an array that can be sent to a device. - * @param value The value to swap. - * @param dest the destination array - * @param offset the offset in the array where to put the swapped value. - * Array length must be at least offset + 4 - */ - public static void swap32bitsToArray(int value, byte[] dest, int offset) { - dest[offset] = (byte)(value & 0x000000FF); - dest[offset + 1] = (byte)((value & 0x0000FF00) >> 8); - dest[offset + 2] = (byte)((value & 0x00FF0000) >> 16); - dest[offset + 3] = (byte)((value & 0xFF000000) >> 24); - } - - /** - * Reads a signed 32 bit integer from an array coming from a device. - * @param value the array containing the int - * @param offset the offset in the array at which the int starts - * @return the integer read from the array - */ - public static int swap32bitFromArray(byte[] value, int offset) { - int v = 0; - v |= ((int)value[offset]) & 0x000000FF; - v |= (((int)value[offset + 1]) & 0x000000FF) << 8; - v |= (((int)value[offset + 2]) & 0x000000FF) << 16; - v |= (((int)value[offset + 3]) & 0x000000FF) << 24; - - return v; - } - - /** - * Reads an unsigned 16 bit integer from an array coming from a device, - * and returns it as an 'int' - * @param value the array containing the 16 bit int (2 byte). - * @param offset the offset in the array at which the int starts - * Array length must be at least offset + 2 - * @return the integer read from the array. - */ - public static int swapU16bitFromArray(byte[] value, int offset) { - int v = 0; - v |= ((int)value[offset]) & 0x000000FF; - v |= (((int)value[offset + 1]) & 0x000000FF) << 8; - - return v; - } - - /** - * Reads a signed 64 bit integer from an array coming from a device. - * @param value the array containing the int - * @param offset the offset in the array at which the int starts - * Array length must be at least offset + 8 - * @return the integer read from the array - */ - public static long swap64bitFromArray(byte[] value, int offset) { - long v = 0; - v |= ((long)value[offset]) & 0x00000000000000FFL; - v |= (((long)value[offset + 1]) & 0x00000000000000FFL) << 8; - v |= (((long)value[offset + 2]) & 0x00000000000000FFL) << 16; - v |= (((long)value[offset + 3]) & 0x00000000000000FFL) << 24; - v |= (((long)value[offset + 4]) & 0x00000000000000FFL) << 32; - v |= (((long)value[offset + 5]) & 0x00000000000000FFL) << 40; - v |= (((long)value[offset + 6]) & 0x00000000000000FFL) << 48; - v |= (((long)value[offset + 7]) & 0x00000000000000FFL) << 56; - - return v; - } -} diff --git a/ddms/libs/ddmlib/src/test/.classpath b/ddms/libs/ddmlib/src/test/.classpath deleted file mode 100644 index 3fcac6c..0000000 --- a/ddms/libs/ddmlib/src/test/.classpath +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> - <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/easymock.jar"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/ddms/libs/ddmlib/src/test/.project b/ddms/libs/ddmlib/src/test/.project deleted file mode 100644 index 81a7f69..0000000 --- a/ddms/libs/ddmlib/src/test/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>ddmlib-tests</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/EmulatorConsoleTest.java b/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/EmulatorConsoleTest.java deleted file mode 100644 index 1697acd..0000000 --- a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/EmulatorConsoleTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ddmlib; - -import junit.framework.TestCase; - -/** - * Unit tests for {@link EmulatorConsole}. - */ -public class EmulatorConsoleTest extends TestCase { - - /** - * Test success case for {@link EmulatorConsole#getEmulatorPort(String)}. - */ - public void testGetEmulatorPort() { - assertEquals(Integer.valueOf(5554), EmulatorConsole.getEmulatorPort("emulator-5554")); - } - - /** - * Test {@link EmulatorConsole#getEmulatorPort(String)} when input serial has invalid format. - */ - public void testGetEmulatorPort_invalid() { - assertNull(EmulatorConsole.getEmulatorPort("invalidserial")); - } - - /** - * Test {@link EmulatorConsole#getEmulatorPort(String)} when port is not a number. - */ - public void testGetEmulatorPort_nan() { - assertNull(EmulatorConsole.getEmulatorPort("emulator-NaN")); - } -} diff --git a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java deleted file mode 100644 index 478e09e..0000000 --- a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - -import com.android.ddmlib.testrunner.ITestRunListener.TestFailure; - -import junit.framework.TestCase; - -import org.easymock.Capture; -import org.easymock.EasyMock; - -import java.util.Collections; -import java.util.Map; - -/** - * Unit tests for {@link @InstrumentationResultParser}. - */ -@SuppressWarnings("unchecked") -public class InstrumentationResultParserTest extends TestCase { - - private InstrumentationResultParser mParser; - private ITestRunListener mMockListener; - - // static dummy test names to use for validation - private static final String RUN_NAME = "foo"; - private static final String CLASS_NAME = "com.test.FooTest"; - private static final String TEST_NAME = "testFoo"; - private static final String STACK_TRACE = "java.lang.AssertionFailedException"; - private static final TestIdentifier TEST_ID = new TestIdentifier(CLASS_NAME, TEST_NAME); - - /** - * @param name - test name - */ - public InstrumentationResultParserTest(String name) { - super(name); - } - - /** - * @see junit.framework.TestCase#setUp() - */ - @Override - protected void setUp() throws Exception { - super.setUp(); - // use a strict mock to verify order of method calls - mMockListener = EasyMock.createStrictMock(ITestRunListener.class); - mParser = new InstrumentationResultParser(RUN_NAME, mMockListener); - } - - /** - * Tests parsing empty output. - */ - public void testParse_empty() { - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunFailed(InstrumentationResultParser.NO_TEST_RESULTS_MSG); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(""); - } - - /** - * Tests parsing output for a successful test run with no tests. - */ - public void testParse_noTests() { - StringBuilder output = new StringBuilder(); - addLine(output, "INSTRUMENTATION_RESULT: stream="); - addLine(output, "Test results for InstrumentationTestRunner="); - addLine(output, "Time: 0.001"); - addLine(output, "OK (0 tests)"); - addLine(output, "INSTRUMENTATION_CODE: -1"); - - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunEnded(1, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Tests parsing output for a single successful test execution. - */ - public void testParse_singleTest() { - StringBuilder output = createSuccessTest(); - - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Tests parsing output for a successful test execution with metrics. - */ - public void testParse_testMetrics() { - StringBuilder output = buildCommonResult(); - - addStatusKey(output, "randomKey", "randomValue"); - addSuccessCode(output); - - final Capture<Map<String, String>> captureMetrics = new Capture<Map<String, String>>(); - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testEnded(EasyMock.eq(TEST_ID), EasyMock.capture(captureMetrics)); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - - assertEquals("randomValue", captureMetrics.getValue().get("randomKey")); - } - - /** - * Test parsing output for a test that produces repeated metrics values - * <p/> - * This mimics launch performance test output. - */ - public void testParse_repeatedTestMetrics() { - StringBuilder output = new StringBuilder(); - // add test start output - addCommonStatus(output); - addStartCode(output); - - addStatusKey(output, "currentiterations", "1"); - addStatusCode(output, "2"); - addStatusKey(output, "currentiterations", "2"); - addStatusCode(output, "2"); - addStatusKey(output, "currentiterations", "3"); - addStatusCode(output, "2"); - - // add test end - addCommonStatus(output); - addStatusKey(output, "numiterations", "3"); - addSuccessCode(output); - - final Capture<Map<String, String>> captureMetrics = new Capture<Map<String, String>>(); - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testEnded(EasyMock.eq(TEST_ID), EasyMock.capture(captureMetrics)); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - - assertEquals("3", captureMetrics.getValue().get("currentiterations")); - assertEquals("3", captureMetrics.getValue().get("numiterations")); - } - - /** - * Test parsing output for a test failure. - */ - public void testParse_testFailed() { - StringBuilder output = buildCommonResult(); - addStackTrace(output); - addFailureCode(output); - - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testFailed(TestFailure.FAILURE, TEST_ID, STACK_TRACE); - mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing and conversion of time output that contains extra chars. - */ - public void testParse_timeBracket() { - StringBuilder output = createSuccessTest(); - output.append("Time: 0.001)"); - - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP); - mMockListener.testRunEnded(1, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output for a test run failure. - */ - public void testParse_runFailed() { - StringBuilder output = new StringBuilder(); - final String errorMessage = "Unable to find instrumentation info"; - addStatusKey(output, "Error", errorMessage); - addStatusCode(output, "-1"); - output.append("INSTRUMENTATION_FAILED: com.dummy/android.test.InstrumentationTestRunner"); - addLineBreak(output); - - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunFailed(errorMessage); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output when a status code cannot be parsed - */ - public void testParse_invalidCode() { - StringBuilder output = new StringBuilder(); - addLine(output, "android.util.AndroidException: INSTRUMENTATION_FAILED: foo/foo"); - addLine(output, "INSTRUMENTATION_STATUS: id=ActivityManagerService"); - addLine(output, "INSTRUMENTATION_STATUS: Error=Unable to find instrumentation target package: foo"); - addLine(output, "INSTRUMENTATION_STATUS_CODE: -1at com.android.commands.am.Am.runInstrument(Am.java:532)"); - addLine(output, ""); - addLine(output, " at com.android.commands.am.Am.run(Am.java:111)"); - addLineBreak(output); - - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunFailed((String)EasyMock.anyObject()); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output for a test run failure, where an instrumentation component failed to - * load. - * <p/> - * Parsing input takes the from of INSTRUMENTATION_RESULT: fff - */ - public void testParse_failedResult() { - StringBuilder output = new StringBuilder(); - final String errorMessage = "Unable to instantiate instrumentation"; - output.append("INSTRUMENTATION_RESULT: shortMsg="); - output.append(errorMessage); - addLineBreak(output); - output.append("INSTRUMENTATION_CODE: 0"); - addLineBreak(output); - - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunFailed(EasyMock.contains(errorMessage)); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output for a test run that did not complete. - * <p/> - * This can occur if device spontaneously reboots, or if test method could not be found. - */ - public void testParse_incomplete() { - StringBuilder output = new StringBuilder(); - // add a start test sequence, but without an end test sequence - addCommonStatus(output); - addStartCode(output); - - mMockListener.testRunStarted(RUN_NAME, 1); - mMockListener.testStarted(TEST_ID); - mMockListener.testFailed(EasyMock.eq(TestFailure.ERROR), EasyMock.eq(TEST_ID), - EasyMock.startsWith(InstrumentationResultParser.INCOMPLETE_TEST_ERR_MSG_PREFIX)); - mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP); - mMockListener.testRunFailed(EasyMock.startsWith( - InstrumentationResultParser.INCOMPLETE_RUN_ERR_MSG_PREFIX)); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output for a test run that did not start due to incorrect syntax supplied to am. - */ - public void testParse_amFailed() { - StringBuilder output = new StringBuilder(); - addLine(output, "usage: am [subcommand] [options]"); - addLine(output, "start an Activity: am start [-D] [-W] <INTENT>"); - addLine(output, "-D: enable debugging"); - addLine(output, "-W: wait for launch to complete"); - addLine(output, "start a Service: am startservice <INTENT>"); - addLine(output, "Error: Bad component name: wfsdafddfasasdf"); - - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunFailed(InstrumentationResultParser.NO_TEST_RESULTS_MSG); - mMockListener.testRunEnded(0, Collections.EMPTY_MAP); - - injectAndVerifyTestString(output.toString()); - } - - /** - * Test parsing output for a test run that produces INSTRUMENTATION_RESULT output. - * <p/> - * This mimics launch performance test output. - */ - public void testParse_instrumentationResults() { - StringBuilder output = new StringBuilder(); - addResultKey(output, "other_pss", "2390"); - addResultKey(output, "java_allocated", "2539"); - addResultKey(output, "foo", "bar"); - addResultKey(output, "stream", "should not be captured"); - addLine(output, "INSTRUMENTATION_CODE: -1"); - - Capture<Map<String, String>> captureMetrics = new Capture<Map<String, String>>(); - mMockListener.testRunStarted(RUN_NAME, 0); - mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.capture(captureMetrics)); - - injectAndVerifyTestString(output.toString()); - - assertEquals("2390", captureMetrics.getValue().get("other_pss")); - assertEquals("2539", captureMetrics.getValue().get("java_allocated")); - assertEquals("bar", captureMetrics.getValue().get("foo")); - assertEquals(3, captureMetrics.getValue().size()); - } - - /** - * Builds a common test result using TEST_NAME and TEST_CLASS. - */ - private StringBuilder buildCommonResult() { - StringBuilder output = new StringBuilder(); - // add test start bundle - addCommonStatus(output); - addStartCode(output); - // add end test bundle, without status - addCommonStatus(output); - return output; - } - - /** - * Create instrumentation output for a successful single test case execution. - */ - private StringBuilder createSuccessTest() { - StringBuilder output = buildCommonResult(); - addSuccessCode(output); - return output; - } - - /** - * Adds common status results to the provided output. - */ - private void addCommonStatus(StringBuilder output) { - addStatusKey(output, "stream", "\r\n" + CLASS_NAME); - addStatusKey(output, "test", TEST_NAME); - addStatusKey(output, "class", CLASS_NAME); - addStatusKey(output, "current", "1"); - addStatusKey(output, "numtests", "1"); - addStatusKey(output, "id", "InstrumentationTestRunner"); - } - - /** - * Adds a stack trace status bundle to output. - */ - private void addStackTrace(StringBuilder output) { - addStatusKey(output, "stack", STACK_TRACE); - } - - /** - * Helper method to add a status key-value bundle. - */ - private void addStatusKey(StringBuilder outputBuilder, String key, - String value) { - outputBuilder.append("INSTRUMENTATION_STATUS: "); - outputBuilder.append(key); - outputBuilder.append('='); - outputBuilder.append(value); - addLineBreak(outputBuilder); - } - - /** - * Helper method to add a result key value bundle. - */ - private void addResultKey(StringBuilder outputBuilder, String key, - String value) { - outputBuilder.append("INSTRUMENTATION_RESULT: "); - outputBuilder.append(key); - outputBuilder.append('='); - outputBuilder.append(value); - addLineBreak(outputBuilder); - } - - /** - * Append a line to output. - */ - private void addLine(StringBuilder outputBuilder, String lineContent) { - outputBuilder.append(lineContent); - addLineBreak(outputBuilder); - } - - /** - * Append line break characters to output - */ - private void addLineBreak(StringBuilder outputBuilder) { - outputBuilder.append("\r\n"); - } - - private void addStartCode(StringBuilder outputBuilder) { - addStatusCode(outputBuilder, "1"); - } - - private void addSuccessCode(StringBuilder outputBuilder) { - addStatusCode(outputBuilder, "0"); - } - - private void addFailureCode(StringBuilder outputBuilder) { - addStatusCode(outputBuilder, "-2"); - } - - private void addStatusCode(StringBuilder outputBuilder, String value) { - outputBuilder.append("INSTRUMENTATION_STATUS_CODE: "); - outputBuilder.append(value); - addLineBreak(outputBuilder); - } - - /** - * Inject a test string into the result parser, and verify the mock listener. - * - * @param result the string to inject into parser under test. - */ - private void injectAndVerifyTestString(String result) { - EasyMock.replay(mMockListener); - byte[] data = result.getBytes(); - mParser.addOutput(data, 0, data.length); - mParser.flush(); - EasyMock.verify(mMockListener); - } -} diff --git a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java deleted file mode 100644 index 8bde492..0000000 --- a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2008 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.testrunner; - -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; - -import org.easymock.EasyMock; - -import java.io.IOException; -import java.util.Collections; - -import junit.framework.TestCase; - -/** - * Unit tests for {@link RemoteAndroidTestRunner}. - */ -public class RemoteAndroidTestRunnerTest extends TestCase { - - private RemoteAndroidTestRunner mRunner; - private IDevice mMockDevice; - private ITestRunListener mMockListener; - - private static final String TEST_PACKAGE = "com.test"; - private static final String TEST_RUNNER = "com.test.InstrumentationTestRunner"; - - /** - * @see junit.framework.TestCase#setUp() - */ - @Override - protected void setUp() throws Exception { - mMockDevice = EasyMock.createMock(IDevice.class); - EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("serial"); - mMockListener = EasyMock.createNiceMock(ITestRunListener.class); - mRunner = new RemoteAndroidTestRunner(TEST_PACKAGE, TEST_RUNNER, mMockDevice); - } - - /** - * Test the basic case building of the instrumentation runner command with no arguments. - */ - public void testRun() throws Exception { - String expectedCmd = EasyMock.eq(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, - TEST_RUNNER)); - runAndVerify(expectedCmd); - } - - /** - * Test the building of the instrumentation runner command with log set. - */ - public void testRun_withLog() throws Exception { - mRunner.setLogOnly(true); - String expectedCmd = EasyMock.contains("-e log true"); - runAndVerify(expectedCmd); - } - - /** - * Test the building of the instrumentation runner command with method set. - */ - public void testRun_withMethod() throws Exception { - final String className = "FooTest"; - final String testName = "fooTest"; - mRunner.setMethodName(className, testName); - String expectedCmd = EasyMock.contains(String.format("-e class %s#%s", className, - testName)); - runAndVerify(expectedCmd); - } - - /** - * Test the building of the instrumentation runner command with test package set. - */ - public void testRun_withPackage() throws Exception { - final String packageName = "foo.test"; - mRunner.setTestPackageName(packageName); - String expectedCmd = EasyMock.contains(String.format("-e package %s", packageName)); - runAndVerify(expectedCmd); - } - - /** - * Test the building of the instrumentation runner command with extra argument added. - */ - public void testRun_withAddInstrumentationArg() throws Exception { - final String extraArgName = "blah"; - final String extraArgValue = "blahValue"; - mRunner.addInstrumentationArg(extraArgName, extraArgValue); - String expectedCmd = EasyMock.contains(String.format("-e %s %s", extraArgName, - extraArgValue)); - runAndVerify(expectedCmd); - } - - /** - * Test run when the device throws a IOException - */ - @SuppressWarnings("unchecked") - public void testRun_ioException() throws Exception { - mMockDevice.executeShellCommand((String)EasyMock.anyObject(), (IShellOutputReceiver) - EasyMock.anyObject(), EasyMock.eq(0)); - EasyMock.expectLastCall().andThrow(new IOException()); - // verify that the listeners run started, run failure, and run ended methods are called - mMockListener.testRunStarted(TEST_PACKAGE, 0); - mMockListener.testRunFailed((String)EasyMock.anyObject()); - mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.eq(Collections.EMPTY_MAP)); - - EasyMock.replay(mMockDevice, mMockListener); - try { - mRunner.run(mMockListener); - fail("IOException not thrown"); - } catch (IOException e) { - // expected - } - EasyMock.verify(mMockDevice, mMockListener); - } - - /** - * Calls {@link RemoteAndroidTestRunner#run(ITestRunListener...)} and verifies the given - * <var>expectedCmd</var> pattern was received by the mock device. - */ - private void runAndVerify(String expectedCmd) throws Exception { - mMockDevice.executeShellCommand(expectedCmd, (IShellOutputReceiver) - EasyMock.anyObject(), EasyMock.eq(0)); - EasyMock.replay(mMockDevice); - mRunner.run(mMockListener); - EasyMock.verify(mMockDevice); - } -} diff --git a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/XmlTestRunListenerTest.java b/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/XmlTestRunListenerTest.java deleted file mode 100644 index f5672fa..0000000 --- a/ddms/libs/ddmlib/src/test/java/com/android/ddmlib/testrunner/XmlTestRunListenerTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2012 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.testrunner; - -import com.android.ddmlib.testrunner.ITestRunListener.TestFailure; - -import junit.framework.TestCase; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Collections; -import java.util.Map; - -/** - * Unit tests for {@link XmlTestRunListener}. - */ -public class XmlTestRunListenerTest extends TestCase { - - private XmlTestRunListener mResultReporter; - private ByteArrayOutputStream mOutputStream; - private File mReportDir; - - /** - * {@inheritDoc} - */ - @Override - protected void setUp() throws Exception { - super.setUp(); - - mOutputStream = new ByteArrayOutputStream(); - mResultReporter = new XmlTestRunListener() { - @Override - OutputStream createOutputResultStream(File reportDir) throws IOException { - return mOutputStream; - } - - @Override - String getTimestamp() { - return "ignore"; - } - }; - // TODO: use mock file dir instead - mReportDir = createTmpDir(); - mResultReporter.setReportDir(mReportDir); - } - - private File createTmpDir() throws IOException { - // create a temp file with unique name, then make it a directory - File tmpDir = File.createTempFile("foo", "dir"); - tmpDir.delete(); - if (!tmpDir.mkdirs()) { - throw new IOException("unable to create directory"); - } - return tmpDir; - } - - /** - * Recursively delete given file and all its contents - */ - private static void recursiveDelete(File rootDir) { - if (rootDir.isDirectory()) { - File[] childFiles = rootDir.listFiles(); - if (childFiles != null) { - for (File child : childFiles) { - recursiveDelete(child); - } - } - } - rootDir.delete(); - } - - @Override - protected void tearDown() throws Exception { - if (mReportDir != null) { - recursiveDelete(mReportDir); - } - super.tearDown(); - } - - /** - * A simple test to ensure expected output is generated for test run with no tests. - */ - public void testEmptyGeneration() { - final String expectedOutput = "<?xml version='1.0' encoding='UTF-8' ?>" + - "<testsuite name=\"test\" tests=\"0\" failures=\"0\" errors=\"0\" time=\"1\" " + - "timestamp=\"ignore\" hostname=\"localhost\"> " + - "<properties />" + - "</testsuite>"; - mResultReporter.testRunStarted("test", 1); - mResultReporter.testRunEnded(1, Collections.<String, String> emptyMap()); - assertEquals(expectedOutput, getOutput()); - } - - /** - * A simple test to ensure expected output is generated for test run with a single passed test. - */ - public void testSinglePass() { - Map<String, String> emptyMap = Collections.emptyMap(); - final TestIdentifier testId = new TestIdentifier("FooTest", "testFoo"); - mResultReporter.testRunStarted("run", 1); - mResultReporter.testStarted(testId); - mResultReporter.testEnded(testId, emptyMap); - mResultReporter.testRunEnded(3, emptyMap); - String output = getOutput(); - // TODO: consider doing xml based compare - assertTrue(output.contains("tests=\"1\" failures=\"0\" errors=\"0\"")); - final String testCaseTag = String.format("<testcase name=\"%s\" classname=\"%s\"", - testId.getTestName(), testId.getClassName()); - assertTrue(output.contains(testCaseTag)); - } - - /** - * A simple test to ensure expected output is generated for test run with a single failed test. - */ - public void testSingleFail() { - Map<String, String> emptyMap = Collections.emptyMap(); - final TestIdentifier testId = new TestIdentifier("FooTest", "testFoo"); - final String trace = "this is a trace"; - mResultReporter.testRunStarted("run", 1); - mResultReporter.testStarted(testId); - mResultReporter.testFailed(TestFailure.FAILURE, testId, trace); - mResultReporter.testEnded(testId, emptyMap); - mResultReporter.testRunEnded(3, emptyMap); - String output = getOutput(); - // TODO: consider doing xml based compare - assertTrue(output.contains("tests=\"1\" failures=\"1\" errors=\"0\"")); - final String testCaseTag = String.format("<testcase name=\"%s\" classname=\"%s\"", - testId.getTestName(), testId.getClassName()); - assertTrue(output.contains(testCaseTag)); - final String failureTag = String.format("<failure>%s</failure>", trace); - assertTrue(output.contains(failureTag)); - } - - /** - * Gets the output produced, stripping it of extraneous whitespace characters. - */ - private String getOutput() { - String output = mOutputStream.toString(); - // ignore newlines and tabs whitespace - output = output.replaceAll("[\\r\\n\\t]", ""); - // replace two ws chars with one - return output.replaceAll(" ", " "); - } -} |