From ad6e78ec9cbd6db136ae4d5eda72cc9aa837a364 Mon Sep 17 00:00:00 2001 From: "hyunsik.noh" Date: Tue, 25 Oct 2011 21:28:02 +0900 Subject: [PATCH] [Title] adapt sdblib [Type] Enhancement [Module] common-plugin [Priority] [CQ#] [Redmine#]2969 --- .../META-INF/MANIFEST.MF | 1 + com.samsung.slp.common.connection/build.properties | 2 + .../slp/common/connection/ConnectionActivator.java | 9 +- .../ddmlib/AdbCommandRejectedException.java | 56 - .../slp/common/connection/ddmlib/AdbHelper.java | 775 -------------- .../common/connection/ddmlib/AllocationInfo.java | 71 -- .../connection/ddmlib/AndroidDebugBridge.java | 1122 -------------------- .../connection/ddmlib/BadPacketException.java | 35 - .../slp/common/connection/ddmlib/ChunkHandler.java | 222 ---- .../slp/common/connection/ddmlib/Client.java | 837 --------------- .../slp/common/connection/ddmlib/ClientData.java | 694 ------------ .../slp/common/connection/ddmlib/DdmConstants.java | 59 - .../common/connection/ddmlib/DdmPreferences.java | 166 --- .../common/connection/ddmlib/DebugPortManager.java | 72 -- .../slp/common/connection/ddmlib/Debugger.java | 353 ------ .../slp/common/connection/ddmlib/Device.java | 508 --------- .../common/connection/ddmlib/DeviceMonitor.java | 951 ----------------- .../common/connection/ddmlib/EmulatorConsole.java | 727 ------------- .../connection/ddmlib/FileListingService.java | 857 --------------- .../common/connection/ddmlib/GetPropReceiver.java | 74 -- .../common/connection/ddmlib/HandleAppName.java | 94 -- .../slp/common/connection/ddmlib/HandleExit.java | 76 -- .../slp/common/connection/ddmlib/HandleHeap.java | 597 ----------- .../slp/common/connection/ddmlib/HandleHello.java | 177 --- .../common/connection/ddmlib/HandleNativeHeap.java | 305 ------ .../common/connection/ddmlib/HandleProfiling.java | 304 ------ .../slp/common/connection/ddmlib/HandleTest.java | 86 -- .../slp/common/connection/ddmlib/HandleThread.java | 379 ------- .../slp/common/connection/ddmlib/HandleWait.java | 91 -- .../slp/common/connection/ddmlib/HeapSegment.java | 446 -------- .../slp/common/connection/ddmlib/IDevice.java | 401 ------- .../connection/ddmlib/IShellOutputReceiver.java | 44 - .../common/connection/ddmlib/IStackTraceInfo.java | 29 - .../slp/common/connection/ddmlib/JdwpPacket.java | 371 ------- .../samsung/slp/common/connection/ddmlib/Log.java | 358 ------- .../common/connection/ddmlib/MonitorThread.java | 780 -------------- .../connection/ddmlib/MultiLineReceiver.java | 130 --- .../connection/ddmlib/NativeAllocationInfo.java | 278 ----- .../connection/ddmlib/NativeLibraryMapInfo.java | 73 -- .../connection/ddmlib/NativeStackCallInfo.java | 95 -- .../connection/ddmlib/NullOutputReceiver.java | 50 - .../slp/common/connection/ddmlib/RawImage.java | 222 ---- .../ddmlib/ShellCommandUnresponsiveException.java | 28 - .../slp/common/connection/ddmlib/SyncService.java | 1003 ----------------- .../slp/common/connection/ddmlib/ThreadInfo.java | 139 --- .../common/connection/ddmlib/TimeoutException.java | 27 - .../connection/ddmlib/log/EventContainer.java | 461 -------- .../connection/ddmlib/log/EventLogParser.java | 578 ---------- .../ddmlib/log/EventValueDescription.java | 214 ---- .../connection/ddmlib/log/GcEventContainer.java | 347 ------ .../ddmlib/log/InvalidTypeException.java | 74 -- .../ddmlib/log/InvalidValueTypeException.java | 78 -- .../common/connection/ddmlib/log/LogReceiver.java | 247 ----- .../connection/ddmlib/utils/ArrayHelper.java | 90 -- .../slp/common/connection/ddmuilib/Addr2Line.java | 281 ----- .../connection/ddmuilib/AllocationPanel.java | 486 --------- .../connection/ddmuilib/BackgroundThread.java | 50 - .../common/connection/ddmuilib/BaseHeapPanel.java | 193 ---- .../connection/ddmuilib/ClientDisplayPanel.java | 33 - .../common/connection/ddmuilib/DevicePanel.java | 759 ------------- .../common/connection/ddmuilib/ImageLoader.java | 4 +- .../connection/ddmuilib/ScreenShotDialog.java | 309 ------ .../ddmuilib/SelectionDependentPanel.java | 78 -- .../connection/ddmuilib/StackTracePanel.java | 258 ----- .../connection/ddmuilib/SyncProgressMonitor.java | 4 +- .../slp/common/connection/ddmuilib/TablePanel.java | 128 --- .../ddmuilib/explorer/DeviceContentProvider.java | 172 --- .../ddmuilib/explorer/DeviceExplorer.java | 888 ---------------- .../ddmuilib/explorer/FileLabelProvider.java | 156 --- .../ConnectionExplorerInfoPropertyPages.java | 2 +- .../ConnectionExplorerPermissionPropertyPages.java | 2 +- .../sdblib/dnd/FileEntryDropAdapter.java | 15 +- .../connection/sdblib/dnd/FileEntryTransfer.java | 3 +- .../common/connection/ui/ConnectionExplorer.java | 21 +- .../ui/ConnectionExplorerContentProvider.java | 12 +- .../ui/ConnectionExplorerLabelProvider.java | 6 +- .../connection/ui/ConnectionExplorerPanel.java | 69 +- com.samsung.slp.common/.classpath | 1 + com.samsung.slp.common/META-INF/MANIFEST.MF | 7 +- com.samsung.slp.common/build.properties | 5 +- com.samsung.slp.common/sdblib.jar | Bin 0 -> 112256 bytes .../src/com/samsung/slp/common/Activator.java | 6 + 82 files changed, 93 insertions(+), 20118 deletions(-) delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbCommandRejectedException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbHelper.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AllocationInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AndroidDebugBridge.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/BadPacketException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ChunkHandler.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Client.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ClientData.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmConstants.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmPreferences.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DebugPortManager.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Debugger.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Device.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DeviceMonitor.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/EmulatorConsole.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/FileListingService.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/GetPropReceiver.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleAppName.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleExit.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHeap.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHello.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleNativeHeap.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleProfiling.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleTest.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleThread.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleWait.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HeapSegment.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IDevice.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IShellOutputReceiver.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IStackTraceInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/JdwpPacket.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Log.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MonitorThread.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MultiLineReceiver.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeAllocationInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeLibraryMapInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeStackCallInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NullOutputReceiver.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/RawImage.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ShellCommandUnresponsiveException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/SyncService.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ThreadInfo.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/TimeoutException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventContainer.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventLogParser.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventValueDescription.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/GcEventContainer.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidTypeException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidValueTypeException.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/LogReceiver.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/utils/ArrayHelper.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/Addr2Line.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/AllocationPanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BackgroundThread.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BaseHeapPanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ClientDisplayPanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/DevicePanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ScreenShotDialog.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SelectionDependentPanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/StackTracePanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/TablePanel.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceContentProvider.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceExplorer.java delete mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/FileLabelProvider.java create mode 100644 com.samsung.slp.common/sdblib.jar diff --git a/com.samsung.slp.common.connection/META-INF/MANIFEST.MF b/com.samsung.slp.common.connection/META-INF/MANIFEST.MF index 57c29d0..ab24ce0 100644 --- a/com.samsung.slp.common.connection/META-INF/MANIFEST.MF +++ b/com.samsung.slp.common.connection/META-INF/MANIFEST.MF @@ -13,3 +13,4 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Import-Package: com.samsung.slp.common.conn.device, com.samsung.slp.common.util +Bundle-ClassPath: . diff --git a/com.samsung.slp.common.connection/build.properties b/com.samsung.slp.common.connection/build.properties index 34d2e4d..a146d0c 100644 --- a/com.samsung.slp.common.connection/build.properties +++ b/com.samsung.slp.common.connection/build.properties @@ -2,3 +2,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ . +jars.compile.order = .,\ + sdblib.jar diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java index afd29dd..e04bffb 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java @@ -1,19 +1,14 @@ package com.samsung.slp.common.connection; -import java.util.ArrayList; import java.util.Properties; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; -import com.samsung.slp.common.connection.ddmlib.Client; - import com.samsung.slp.common.connection.ddmuilib.DdmUiPreferences; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge; +import com.samsung.slp.sdblib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.IDevice; //import com.samsung.slp.common.connection.device.DeviceMonitor; //import com.samsung.slp.common.connection.sdb.SdbHelper; diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbCommandRejectedException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbCommandRejectedException.java deleted file mode 100644 index 37965af..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbCommandRejectedException.java +++ /dev/null @@ -1,56 +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.samsung.slp.common.connection.ddmlib; - -import java.io.IOException; - -/** - * Exception thrown when adb refuses a command. - */ -public class AdbCommandRejectedException extends IOException { - private static final long serialVersionUID = 1L; - private final boolean mIsDeviceOffline; - private final boolean mErrorDuringDeviceSelection; - - AdbCommandRejectedException(String message) { - super(message); - mIsDeviceOffline = "device offline".equals(message); - mErrorDuringDeviceSelection = false; - } - - AdbCommandRejectedException(String message, boolean errorDuringDeviceSelection) { - super(message); - mErrorDuringDeviceSelection = errorDuringDeviceSelection; - mIsDeviceOffline = "device offline".equals(message); - } - - /** - * Returns true if the error is due to the device being offline. - */ - public boolean isDeviceOffline() { - return mIsDeviceOffline; - } - - /** - * Returns whether adb refused to target a given device for the command. - *

If false, adb refused the command itself, if true, it refused to target the given - * device. - */ - public boolean wasErrorDuringDeviceSelection() { - return mErrorDuringDeviceSelection; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbHelper.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbHelper.java deleted file mode 100644 index 30b68e8..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AdbHelper.java +++ /dev/null @@ -1,775 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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. - *

{@link DebugBridgeServer} is the public API to connection to adb, while {@link AdbHelper} - * does the low level stuff. - *

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 rcvr 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 maxTimeToOutputResponse. - * @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}. - *

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}. - *

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 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 static void createForward(InetSocketAddress adbSockAddr, Device device, int localPort, - int remotePort) 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:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$ - device.getSerialNumber(), localPort, remotePort)); - - 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 localPort the local port of the 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 static void removeForward(InetSocketAddress adbSockAddr, Device device, int localPort, - int remotePort) 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:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$ - device.getSerialNumber(), localPort, remotePort)); - - 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). - *

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. - *

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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AllocationInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AllocationInfo.java deleted file mode 100644 index 58554fb..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AllocationInfo.java +++ /dev/null @@ -1,71 +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.samsung.slp.common.connection.ddmlib; - -/** - * Holds an Allocation information. - */ -public class AllocationInfo implements Comparable, IStackTraceInfo { - private String mAllocatedClass; - private int mAllocationSize; - private short mThreadId; - private StackTraceElement[] mStackTrace; - - /* - * Simple constructor. - */ - AllocationInfo(String allocatedClass, int allocationSize, - short threadId, StackTraceElement[] stackTrace) { - mAllocatedClass = allocatedClass; - mAllocationSize = allocationSize; - mThreadId = threadId; - mStackTrace = stackTrace; - } - - /** - * 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() - */ - public StackTraceElement[] getStackTrace() { - return mStackTrace; - } - - public int compareTo(AllocationInfo otherAlloc) { - return otherAlloc.mAllocationSize - mAllocationSize; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AndroidDebugBridge.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AndroidDebugBridge.java deleted file mode 100644 index a0d3d5b..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/AndroidDebugBridge.java +++ /dev/null @@ -1,1122 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.Log.LogLevel; - -import java.io.BufferedReader; -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.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A connection to the host-side android debug bridge (adb) - *

This is the central point to communicate with any devices, emulators, or the applications - * running on them. - *

{@link #init(boolean)} must be called before anything is done. - */ -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_MIN = 0; - 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; - final static int ADB_PORT = 7037; - - private static InetAddress sHostAddr; - private static InetSocketAddress sSocketAddr; - - private static AndroidDebugBridge sThis; - 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 sBridgeListeners = - new ArrayList(); - private final static ArrayList sDeviceListeners = - new ArrayList(); - private final static ArrayList sClientListeners = - new ArrayList(); - - // 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. - *

- * 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}. - *

- * 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}. - *

- * 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. - *

- * 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. - *

- * 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 ddm library. - *

This must be called once before any call to - * {@link #createBridge(String, boolean)}. - *

The library can be initialized in 2 ways: - *

- *

Only one tool can run in mode 1 at the same time. - *

Note that mode 1 does not prevent debugging of applications running on devices. Mode 1 - * lets debuggers connect to ddmlib which acts as a proxy between the debuggers and - * the applications to debug. See {@link Client#getDebuggerListenPort()}. - *

The preferences of ddmlib should also be initialized with whatever default - * values were changed from the default values. - *

When the application quits, {@link #terminate()} should be called. - * @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 void init(boolean clientSupport) { - 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); - } - - /** - * Terminates the ddm library. This must be called upon application termination. - */ - public static 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(); - } - } - - /** - * 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. - *

This bridge will expect adb to be running. It will not be able to start/stop/restart - * adb. - *

If a bridge has already been started, it is directly returned with no changes (similar - * to calling {@link #getBridge()}). - * @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. - *

- * Any existing server will be disconnected, unless the location is the same and - * forceNewBridge 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 null if none were created. - */ - public static AndroidDebugBridge getBridge() { - return sThis; - } - - /** - * Disconnects the current debug bridge, and destroy the object. - *

This also stops the current adb host server. - *

- * 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. - *

Calling {@link #getDevices()} right after {@link #createBridge(String, boolean)} will - * generally result in an empty list. This is due to the internal asynchronous communication - * mechanism with adb that does not guarantee that the {@link IDevice} list has been - * built before the call to {@link #getDevices()}. - *

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; - } - - try { - String[] command = new String[2]; - command[0] = mAdbOsLocation; - command[1] = "version"; //$NON-NLS-1$ - Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); //$NON-NLS-1$ - Process process = Runtime.getRuntime().exec(command); - - ArrayList errorOutput = new ArrayList(); - ArrayList stdOutput = new ArrayList(); - int status = grabProcessOutput(process, errorOutput, stdOutput, - true /* waitForReaders */); - - if (status != 0) { - StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$ - for (String error : errorOutput) { - builder.append('\n'); - builder.append(error); - } - Log.logAndDisplay(LogLevel.ERROR, "adb", builder.toString()); - } - - // check both stdout and stderr - boolean versionFound = false; - for (String line : stdOutput) { - versionFound = scanVersionLine(line); - if (versionFound) { - break; - } - } - if (!versionFound) { - for (String line : errorOutput) { - versionFound = scanVersionLine(line); - if (versionFound) { - break; - } - } - } - - if (!versionFound) { - // if we get here, we failed to parse the output. - Log.logAndDisplay(LogLevel.ERROR, ADB, - "Failed to parse the output of 'adb version'"); //$NON-NLS-1$ - } - - } catch (IOException e) { - Log.logAndDisplay(LogLevel.ERROR, ADB, - "Failed to get the adb version: " + e.getMessage()); //$NON-NLS-1$ - } catch (InterruptedException e) { - } finally { - - } - } - - /** - * Scans a line resulting from 'adb version' for a potential version number. - *

- * If a version number is found, it checks the version number against what is expected - * by this version of ddms. - *

- * Returns true when a version number has been found so that we can stop scanning, - * whether the version number is in the acceptable range or not. - * - * @param line The line to scan. - * @return True if a version number was found (whether it is acceptable or not). - */ - 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}. - *

- * 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. - *

- * 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 IDevice. - * @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}. - *

- * 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. - *

- * 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 IDevice. - * @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}. - *

- * 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. - *

- * 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 IDevice. - * @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}. - *

- * 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. - *

- * 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 Client. - * @param changeMask the mask indicating what changed in the Client - * @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])); - proc = Runtime.getRuntime().exec(command); - - ArrayList errorOutput = new ArrayList(); - ArrayList stdOutput = new ArrayList(); - 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 must 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 errorOutput, - final ArrayList 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. - *

- * 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. - *

- * The value for the environment variable ANDROID_ADB_SERVER_PORT is validated, - * IllegalArgumentException is thrown on illegal values. - *

- * @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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/BadPacketException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/BadPacketException.java deleted file mode 100644 index 3ceee2c..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ChunkHandler.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ChunkHandler.java deleted file mode 100644 index 82ce925..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Client.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Client.java deleted file mode 100644 index 12e1014..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Client.java +++ /dev/null @@ -1,837 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.samsung.slp.common.connection.ddmlib.ClientData.MethodProfilingStatus; -import com.samsung.slp.common.connection.ddmlib.DebugPortManager.IDebugPortProvider; - -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. - *

This class gives access to basic client information, as well as methods to perform actions - * on the client. - *

More detailed information, usually updated in real time, can be access through the - * {@link ClientData} class. Each Client object has its own ClientData - * 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 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(); - - 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 true 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 true 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 { - if (canStream) { - HandleProfiling.sendMPSS(this, 8*1024*1024, 0 /*flags*/); - } else { - String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") + - ".trace"; - HandleProfiling.sendMPRS(this, file, 8*1024*1024, 0 /*flags*/); - } - } - } catch (IOException e) { - Log.w("ddms", "Toggle method profiling failed"); - // ignore - } - } - - /** - * Sends a request to the VM to send the enable status of the method profiling. - * This is asynchronous. - *

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 changeMask - * 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. - *

If true 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. - *

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 changeMask - * containing the mask {@link #CHANGE_THREAD_DATA}. - */ - public void requestThreadUpdate() { - HandleThread.requestThreadUpdate(this); - } - - /** - * Sends a thread stack trace update request. This is asynchronous. - *

The thread info can be accessed by {@link ClientData#getThreads()} and - * {@link ThreadInfo#getStackTrace()}. - *

The notification that the new data is available - * will be received through {@link IClientChangeListener#clientChanged(Client, int)} - * with a changeMask containing the mask {@link #CHANGE_THREAD_STACKTRACE}. - */ - public void requestThreadStackTrace(int threadId) { - HandleThread.requestThreadStackCallRefresh(this, threadId); - } - - /** - * Enables or disables the heap update. - *

If true, any GC will cause the client to send its heap information. - *

The heap information can be accessed by {@link ClientData#getVmHeapData()}. - *

The notification that the new data is available - * will be received through {@link IClientChangeListener#clientChanged(Client, int)} - * with a changeMask 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. - *

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 changeMask - * 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. - *

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. - *

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 changeMask - * 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 enable - * set to null. This is asynchronous. - *

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 changeMask - * 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ClientData.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ClientData.java deleted file mode 100644 index 4870680..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ClientData.java +++ /dev/null @@ -1,694 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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 = ""; //$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. - *

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. - *

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 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; - - // how interested are we in a debugger? - private DebuggerStatus mDebuggerInterest; - - // List of supported features by the client. - private final HashSet mFeatures = new HashSet(); - - // Thread tracking (THCR, THDE). - private TreeMap mThreadMap; - - /** VM Heap data */ - private final HeapData mHeapData = new HeapData(); - /** Native Heap data */ - private final HeapData mNativeHeapData = new HeapData(); - - private HashMap> mHeapInfoMap = - new HashMap>(); - - - /** library map info. Stored here since the backtrace data - * is computed on a need to display basis. - */ - private ArrayList mNativeLibMapInfo = - new ArrayList(); - - /** Native Alloc info list */ - private ArrayList mNativeAllocationList = - new ArrayList(); - private int mNativeTotalMemory; - - private AllocationInfo[] mAllocations; - private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN; - - private String mPendingHprofDump; - - private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN; - private String mPendingMethodProfiling; - - /** - * Heap Information. - *

The heap is composed of several {@link HeapSegment} objects. - *

A call to {@link #isHeapDataComplete()} will indicate if the segments (available through - * {@link #getHeapSegments()}) represent the full heap. - */ - public static class HeapData { - private TreeSet mHeapSegments = new TreeSet(); - private boolean mHeapDataComplete = false; - private byte[] mProcessedHeapData; - private Map> 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(); - 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 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> heapMap) { - mProcessedHeapMap = heapMap; - } - - public Map> 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 (null 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 (null 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 (null 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(); - } - - /** - * 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. - *

This is generally the name of the package defined in the - * AndroidManifest.xml. - * - * @return the client description or null if not the description was not yet - * sent by the client. - */ - public String getClientDescription() { - return mClientDescription; - } - - /** - * 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 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; - } - } - } - - /** - * 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 heapInfo = new HashMap(); - 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. - *

- * The caller must synchronize on the {@link ClientData} object while iterating. - * - * @return an iterator over the list of heap ids - */ - public synchronized Iterator 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 null if the heap ID is unknown. - */ - public synchronized Map 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. - *

The list is empty until a thread update was requested with - * {@link Client#requestThreadUpdate()}. - */ - public synchronized ThreadInfo[] getThreads() { - Collection 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 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 an {@link Iterator} on {@link NativeLibraryMapInfo} objects. - *

- * The caller must synchronize on the {@link ClientData} object while iterating. - */ - public synchronized Iterator getNativeLibraryMapInfo() { - return mNativeLibMapInfo.iterator(); - } - - 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 feature - * @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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmConstants.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmConstants.java deleted file mode 100644 index e2b47dc..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmConstants.java +++ /dev/null @@ -1,59 +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.samsung.slp.common.connection.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(); - - /** 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmPreferences.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmPreferences.java deleted file mode 100644 index c5eddc6..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DdmPreferences.java +++ /dev/null @@ -1,166 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.Log.LogLevel; - -/** - * Preferences for the ddm library. - *

This class does not handle storing the preferences. It is merely a central point for - * applications using the ddmlib to override the default values. - *

Various components of the ddmlib query this class to get their values. - *

Calls to some set##() 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. - *

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 - - 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; - - /** - * 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. - *

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. - *

If true, the {@link ClientData} will automatically be updated with - * the VM heap information whenever a GC happens. - *

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}. - *

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}. - *

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. - *

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. - *

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. - *

This change takes effect for newly created connections only. - * @param timeOut the timeout value (milliseconds). - */ - public static void setTimeOut(int timeOut) { - sTimeOut = timeOut; - } - - /** - * Non accessible constructor. - */ - private DdmPreferences() { - // pass, only static methods in the class. - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DebugPortManager.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DebugPortManager.java deleted file mode 100644 index 9a0168c..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.Device; - -/** - * Centralized point to provide a {@link IDebugPortProvider} to ddmlib. - * - *

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 Client. - * - *

This {@link DebugPortManager} allows applications using ddmlib to provide a custom - * port provider on a per-Client 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 AndroidManifest.xml - * package attribute of the manifest 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 IDebugPortProvider to use. - */ - public static void setProvider(IDebugPortProvider provider) { - sProvider = provider; - } - - /** - * Returns the - * @return - */ - static IDebugPortProvider getProvider() { - return sProvider; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Debugger.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Debugger.java deleted file mode 100644 index 8195df8..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Device.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Device.java deleted file mode 100644 index da89fca..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Device.java +++ /dev/null @@ -1,508 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.log.LogReceiver; -import com.samsung.slp.common.connection.ddmlib.SyncService.SyncResult; - -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 final static int INSTALL_TIMEOUT = 2*60*1000; //2min - - /** 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 mProperties = new HashMap(); - private final Map mMountPoints = new HashMap(); - - private final ArrayList mClients = new ArrayList(); - private DeviceMonitor mMonitor; - - private static final String LOG_TAG = "Device"; - - /** - * Socket for the connection monitoring client connection/disconnection. - */ - private SocketChannel mSocketChannel; - - /** - * 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); - } - } - } - } - } - - public boolean isCancelled() { - return false; - } - - public String getErrorMessage() { - return mErrorMessage; - } - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getSerialNumber() - */ - public String getSerialNumber() { - return mSerialNumber; - } - - /** {@inheritDoc} */ - 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; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getState() - */ - 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() - */ - public Map getProperties() { - return Collections.unmodifiableMap(mProperties); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getPropertyCount() - */ - public int getPropertyCount() { - return mProperties.size(); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getProperty(java.lang.String) - */ - public String getProperty(String name) { - return mProperties.get(name); - } - - public String getMountPoint(String name) { - return mMountPoints.get(name); - } - - - @Override - public String toString() { - return mSerialNumber; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isOnline() - */ - public boolean isOnline() { - return mState == DeviceState.ONLINE; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isEmulator() - */ - public boolean isEmulator() { - return mSerialNumber.matches(RE_EMULATOR_SN); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isOffline() - */ - public boolean isOffline() { - return mState == DeviceState.OFFLINE; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#isBootLoader() - */ - public boolean isBootLoader() { - return mState == DeviceState.BOOTLOADER; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#hasClients() - */ - public boolean hasClients() { - return mClients.size() > 0; - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClients() - */ - public Client[] getClients() { - synchronized (mClients) { - return mClients.toArray(new Client[mClients.size()]); - } - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClient(java.lang.String) - */ - 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() - */ - 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() - */ - public FileListingService getFileListingService() { - return new FileListingService(this); - } - - public RawImage getScreenshot() - throws TimeoutException, AdbCommandRejectedException, IOException { - return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this); - } - - public void executeShellCommand(String command, IShellOutputReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, - receiver, DdmPreferences.getTimeOut()); - } - - public void executeShellCommand(String command, IShellOutputReceiver receiver, - int maxTimeToOutputResponse) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, - receiver, maxTimeToOutputResponse); - } - - public void runEventLogService(LogReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver); - } - - public void runLogService(String logname, LogReceiver receiver) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver); - } - - public void createForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort); - } - - public void removeForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#getClientName(int) - */ - 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 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) { - 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); - } - - public String installPackage(String packageFilePath, boolean reinstall) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - String remoteFilePath = syncPackageToDevice(packageFilePath); - String result = installRemotePackage(remoteFilePath, reinstall); - removeRemotePackage(remoteFilePath); - return result; - } - - public String syncPackageToDevice(String localFilePath) - throws IOException, AdbCommandRejectedException, TimeoutException { - 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())); - - SyncService sync = getSyncService(); - if (sync != null) { - String message = String.format("Uploading file onto device '%1$s'", - getSerialNumber()); - Log.d(LOG_TAG, message); - SyncResult result = sync.pushFile(localFilePath, remoteFilePath, - SyncService.getNullProgressMonitor()); - - if (result.getCode() != SyncService.RESULT_OK) { - throw new IOException(String.format("Unable to upload file: %1$s", - result.getMessage())); - } - } else { - throw new IOException("Unable to open sync connection!"); - } - return remoteFilePath; - } catch (TimeoutException e) { - Log.e(LOG_TAG, "Unable to open sync connection! Timeout."); - throw e; - } catch (IOException e) { - Log.e(LOG_TAG, String.format("Unable to open sync connection! reason: %1$s", - e.getMessage())); - throw e; - } - } - - /** - * 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(); - } - - public String installRemotePackage(String remoteFilePath, boolean reinstall) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - InstallReceiver receiver = new InstallReceiver(); - String cmd = String.format(reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"", - remoteFilePath); - executeShellCommand(cmd, receiver, INSTALL_TIMEOUT); - return receiver.getErrorMessage(); - } - - /** - * {@inheritDoc} - */ - public void removeRemotePackage(String remoteFilePath) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - // now we delete the app we sync'ed - try { - executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver(), INSTALL_TIMEOUT); - } catch (IOException e) { - Log.e(LOG_TAG, String.format("Failed to delete temporary package: %1$s", - e.getMessage())); - throw e; - } - } - - /** - * {@inheritDoc} - */ - public String uninstallPackage(String packageName) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException { - InstallReceiver receiver = new InstallReceiver(); - executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT); - return receiver.getErrorMessage(); - } - - /* - * (non-Javadoc) - * @see com.android.ddmlib.IDevice#reboot() - */ - public void reboot(String into) - throws TimeoutException, AdbCommandRejectedException, IOException { - AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this); - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DeviceMonitor.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DeviceMonitor.java deleted file mode 100644 index 4753d2e..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/DeviceMonitor.java +++ /dev/null @@ -1,951 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.AdbHelper.AdbResponse; -import com.samsung.slp.common.connection.ddmlib.ClientData.DebuggerStatus; -import com.samsung.slp.common.connection.ddmlib.DebugPortManager.IDebugPortProvider; -import com.samsung.slp.common.connection.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.Iterator; -import java.util.List; -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 mDevices = new ArrayList(); - - private final ArrayList mDebuggerPorts = new ArrayList(); - - private final HashMap mClientsToReopen = new HashMap(); - - /** - * 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; - } - } - } - - /** - * 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 list = new ArrayList(); - - 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 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 devicesToQuery = new ArrayList(); - 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). - // FIXME : sdb - if (device.isEmulator()) { - EmulatorConsole console = EmulatorConsole.getConsole(device); - if (console != null) { - device.setAvdName("test"); - } - } - } 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$ - 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 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 keys = mSelector.selectedKeys(); - Iterator 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 { - if (length >= 0) { - // array for the current pids. - ArrayList pidList = new ArrayList(); - - // 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 { - pidList.add(Integer.valueOf(pid)); - } catch (NumberFormatException nfe) { - // looks like this pid is not really a number. Lets ignore it. - continue; - } - } - } - - MonitorThread monitorThread = MonitorThread.getInstance(); - - // Now we merge the current list with the old one. - // this is the same mechanism as the merging of the device list. - - // For each client in the current list, we look for a matching the pid in the new list. - // * if we find it, we do nothing, except removing the pid from its list, - // to mark it as "processed" - // * if we do not find any match, we remove the client from the current list. - // Once this is done, the new list contains pids for which we don't have clients yet, - // so we create clients for them, add them to the list, and start monitoring them. - - List clients = device.getClientList(); - - boolean changed = false; - - // because MonitorThread#dropClient acquires first the monitorThread lock and then the - // Device client list lock (when removing the Client from the list), we have to make - // sure we acquire the locks in the same order, since another thread (MonitorThread), - // could call dropClient itself. - synchronized (monitorThread) { - synchronized (clients) { - for (int c = 0 ; c < clients.size() ;) { - Client client = clients.get(c); - int pid = client.getClientData().getPid(); - - // look for a matching pid - Integer match = null; - for (Integer matchingPid : pidList) { - if (pid == matchingPid.intValue()) { - match = matchingPid; - break; - } - } - - if (match != null) { - pidList.remove(match); - c++; // move on to the next client. - } else { - // we need to drop the client. the client will remove itself from the - // list of its device which is 'clients', so there's no need to - // increment c. - // We ask the monitor thread to not send notification, as we'll do - // it once at the end. - monitorThread.dropClient(client, false /* notify */); - changed = true; - } - } - } - } - - // at this point whatever pid is left in the list needs to be converted into Clients. - for (int newPid : pidList) { - openClient(device, newPid, getNextDebuggerPort(), monitorThread); - changed = true; - } - - if (changed) { - 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/EmulatorConsole.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/EmulatorConsole.java deleted file mode 100644 index 98e5885..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/EmulatorConsole.java +++ /dev/null @@ -1,727 +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.samsung.slp.common.connection.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. - *

This is basically a wrapper around the command line console normally used with telnet. - *

- * Regarding line termination handling:
- * One of the issues is that the telnet protocol requires usage of \r\n. 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 \r and waits for \n. - *

However this means you may receive \r\n when reading from the console. - *

- * This API will change in the near future. - */ -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 sEmulators = - new HashMap(); - - /** 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 EmulatorConsole object or null if the connection failed. - */ - public static synchronized EmulatorConsole getConsole(IDevice d) { - // we need to make sure that the device is an emulator - Matcher m = sEmulatorRegexp.matcher(d.getSerialNumber()); - 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 null; - } - } 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; - } - - 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 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 - * null 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 null - * 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()); - - // 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()); - - // 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); - formatter.format(COMMAND_GPS, longitude, latitude, elevation); - - return processCommand(formatter.toString()); - } - - /** - * Sends a command to the emulator console. - * @param command The command string. MUST BE TERMINATED BY \n. - * @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: - *

- * @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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/FileListingService.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/FileListingService.java deleted file mode 100644 index 03fbbc4..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/FileListingService.java +++ /dev/null @@ -1,857 +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.samsung.slp.common.connection.ddmlib; - -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. - *

- * 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.
- * The output format looks like:
- * /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$ - - private final static String[] sRootLevelApprovedItems = { DIRECTORY_DATA, - DIRECTORY_SDCARD, DIRECTORY_SYSTEM, DIRECTORY_TEMP, DIRECTORY_MNT, }; - - 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; - /** Entry type: root */ - public static final int TYPE_ROOT_EMULATOR = 9; - public static final int TYPE_ROOT_DEVICE = 10; - - /** 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. - * Do not think the same format between Emulator and Device such as "ls -l --time-style=long-iso" - */ - - private static String patternPermisions = "([bcdlsp-][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xstST])"; - private static String patternFilecount = "(\\d{1,})"; - private static String patternOwner = "(\\S+)"; - private static String patternGroup = "(\\S+)"; - private static String patternSize = "([\\d\\s,]*)"; - private static String patternDate = "([a-zA-Z]{3}\\s+\\d{1,2})"; - private static String patternTime = "(\\d{1,2}:\\d{1,2})"; - private static String patternName = "(.*)"; - private static String patternLs = "^" + patternPermisions + "\\s+" - + patternFilecount + "\\s+" + patternOwner + "\\s+" + patternGroup - + "\\s+" + patternSize + "\\s+" + patternDate + "\\s+" - + patternTime + "\\s+" + patternName + "$"; - - // private static Pattern sLsPattern = 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 Pattern sLsPattern = Pattern.compile(patternLs); - private Device mDevice; - private FileEntry mRoot; - - private ArrayList mThreadList = new ArrayList(); - - /** - * 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. */ - private final static Pattern sEscapePattern = Pattern - .compile("([\\\\()*+?\"'#/\\s])"); //$NON-NLS-1$ - - /** - * Comparator object for FileEntry - */ - private static Comparator sEntryComparator = new Comparator() { - public int compare(FileEntry o1, FileEntry o2) { - if (o1 instanceof FileEntry && o2 instanceof FileEntry) { - FileEntry fe1 = (FileEntry) o1; - FileEntry fe2 = (FileEntry) 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; - String linkPath; - FileListingService fileListingService; - int type; - boolean isAppPackage; - - boolean isRoot; - - /** - * Indicates whether the entry content has been fetched yet, or not. - */ - long fetchTime = 0; - - final ArrayList mChildren = new ArrayList(); - - /** - * 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}. - */ - public FileEntry(FileEntry parent, String name, int type, - boolean isRoot, FileListingService service) { - this.parent = parent; - this.name = name; - this.type = type; - this.isRoot = isRoot; - this.fileListingService = service; - } - - /** - * Returns the name of the entry - */ - public String getName() { - return name; - } - - /** - * Returns the size string of the entry, as returned by ls. - */ - 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 ls. - */ - public String getDate() { - return date; - } - - /** - * Returns the time string of the entry, as returned by ls. - */ - public String getTime() { - return time; - } - - /** - * Returns the permission string of the entry, as returned by - * ls. - */ - public String getPermissions() { - return permissions; - } - - /** - * Returns the extra info for the entry. - *

- * For a link, it will be a description of the link. - *

- * For an application apk file it will be the application package as - * returned by the Package Manager. - */ - public String getInfo() { - return info; - } - public String getLinkFullPath() { - return linkPath; - } - /** - * 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 list = new ArrayList(); - fillPathSegments(list); - - return list.toArray(new String[list.size()]); - } - - /** - * Returns true if the entry is a directory, false otherwise; - */ - public int getType() { - return type; - } - - public FileListingService getFileListingService() - { - return fileListingService; - } - - /** - * 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 FileListingService.getChildren(). - */ - 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 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 list) { - if (isRoot) { - return; - } - - if (parent != null) { - parent.fillPathSegments(list); - } - - list.add(name); - } - - /** - * Returns an escaped version of the entry name. - * - * @param entryName - */ - private String escape(String entryName) { - return sEscapePattern.matcher(entryName).replaceAll("\\\\$1"); //$NON-NLS-1$ - } - } - - private class LsReceiver extends MultiLineReceiver { - - private ArrayList mEntryList; - private ArrayList mLinkList; - private FileEntry[] mCurrentChildren; - private FileEntry mParentEntry; - int linkType = -1; - /** - * 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 entryList, ArrayList 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 = sLsPattern.matcher(line); - if (m.matches() == false) { - continue; - } - - // get the name - String name = m.group(8); - // get the rest of the groups - String permissions = m.group(1); - String owner = m.group(3); - String group = m.group(4); - String size = m.group(5); - String date = m.group(6); - String time = m.group(7); - String info = null; - String linkTo = null; - int objectType = getFileType(permissions.charAt(0)); - - // 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]; - linkTo = segments[1]; - - // add an arrow in front to specify - // it's a link. - info = "-> " + info; //$NON-NLS-1$; - // now get the path to the link - String[] pathSegments = info.split(FILE_SEPARATOR); - - if (pathSegments.length >= 1) { - // FIXME : not fully impl - String command = "ls -al "; - if (linkTo.charAt(0) != '/') - command = command + "/"; - command = command + linkTo; - try { - LsLinkReceiver receiver = new LsLinkReceiver(); - mDevice.executeShellCommand(command,receiver); - if (receiver.isDirectoryLink == true) - objectType = TYPE_DIRECTORY_LINK; - } catch (Exception e) { - // adb failed somehow, we do nothing. - } - } - } - } - // 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 */, mParentEntry.fileListingService); - } - - // 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 || objectType == TYPE_DIRECTORY_LINK) { - entry.info = info; - entry.linkPath = linkTo; - } - - mEntryList.add(entry); - } - } - final class LsLinkReceiver extends MultiLineReceiver { - boolean isDirectoryLink = false; - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.length() > 0) { - Matcher m = sLsPattern.matcher(line); - if (m.matches()) { - int objectType = getFileType(m.group(1).charAt(0)); - String name = m.group(8); - if (".".equals(name) && objectType == TYPE_DIRECTORY) { - isDirectoryLink = true; - break; - } - } - } - } - - } - - public boolean isCancelled() { - return false; - } - } - /** - * 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; - } - - public boolean isCancelled() { - return false; - } - - public void finishLinks() { - // TODO Handle links in the listing service - } - private int getFileType(char type){ - // and the type - int objectType = TYPE_OTHER; - switch (type) { - 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; - } - return objectType; - } - } - - /** - * Classes which implement this interface provide a method that deals with - * asynchronous result from ls 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 - * null if the device is invalid. - */ - public FileEntry getRoot() { - if (mDevice != null) { - if (mRoot == null) { - mRoot = new FileEntry(null /* parent */, "" /* name */, - TYPE_DIRECTORY, true /* isRoot */, this); - } - - return mRoot; - } - - return null; - } - - /** - * Returns the children of a {@link FileEntry}. - *

- * This method supports a cache mechanism and synchronous and asynchronous - * modes. - *

- * If receiver is null, the device side - * ls command is done synchronously, and the method will return - * upon completion of the command.
- * If receiver is non null, the command is launched - * is a separate thread and upon completion, the receiver will be notified - * of the result. - *

- * The result for each ls command is cached in the parent - * FileEntry. useCache 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 - * ls command is always executed. - *

- * If the cache is valid and useCache == true, 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 null 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 map = new HashMap(); - - 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); - } - } - } - } - } - - 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; - } - - public IDevice getDevice() - { - return mDevice; - } - - private void doLs(FileEntry entry) { - // create a list that will receive the list of the entries - ArrayList entryList = new ArrayList(); - - // create a list that will receive the link to compute post ls; - ArrayList linkList = new ArrayList(); - - try { - // create the command - String command = null; - if (entry.getType() == FileListingService.TYPE_LINK || entry.getType() == FileListingService.TYPE_DIRECTORY_LINK) { - if (entry.getLinkFullPath().charAt(0) == '/') - command = "ls -l " + entry.getLinkFullPath(); //$NON-NLS-1$ - else - command = "ls -l /" + entry.getLinkFullPath(); //$NON-NLS-1$ - }else { - command = "ls -l " + entry.getFullPath(); //$NON-NLS-1$ - } - - // 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(); - } catch (Exception e) { - // catch all and do nothing. - } - - // 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/GetPropReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/GetPropReceiver.java deleted file mode 100644 index 060b233..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/GetPropReceiver.java +++ /dev/null @@ -1,74 +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.samsung.slp.common.connection.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); - } - } - } - } - - public boolean isCancelled() { - return false; - } - - @Override - public void done() { - mDevice.update(Device.CHANGE_BUILD_INFO); - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleAppName.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleAppName.java deleted file mode 100644 index 900b188..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleAppName.java +++ /dev/null @@ -1,94 +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.samsung.slp.common.connection.ddmlib; - -import java.io.IOException; -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); - - Log.d("ddm-appname", "APNM: app='" + appName + "'"); - - ClientData cd = client.getClientData(); - synchronized (cd) { - cd.setClientDescription(appName); - } - - client = checkDebuggerPortForAppName(client, appName); - - if (client != null) { - client.update(Client.CHANGE_NAME); - } - } - } - diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleExit.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleExit.java deleted file mode 100644 index 5c14486..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHeap.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHeap.java deleted file mode 100644 index 9bc5e61..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHeap.java +++ /dev/null @@ -1,597 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.ClientData.AllocationTrackingStatus; -import com.samsung.slp.common.connection.ddmlib.ClientData.IHprofDumpHandler; - -import java.io.IOException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; - -/** - * 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 . - 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 . - 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 - } - - /** - * 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 list = new ArrayList(numEntries); - 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(classNames[classNameIndex], - totalSize, (short) threadId, steArray)); - } - - // sort biggest allocations first. - Collections.sort(list); - - 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHello.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHello.java deleted file mode 100644 index a35cebb..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleHello.java +++ /dev/null @@ -1,177 +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.samsung.slp.common.connection.ddmlib; - -import java.io.IOException; -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); - - 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); - } 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleNativeHeap.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleNativeHeap.java deleted file mode 100644 index dbe2768..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleNativeHeap.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.samsung.slp.common.connection.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_GC); - 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 = ((long)buffer.getInt()) & 0x00000000ffffffffL; - - 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 = (int) buffer.get(); - long startAddress = (long) 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 = (int)buffer.get() & 0x000000ff; - int eLen = ((int)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); - - /* - * only check for library addresses as defined in - * //device/config/prelink-linux-arm.map - */ - if (tmpStart >= 0x0000000080000000L && tmpStart <= 0x00000000BFFFFFFFL) { - - 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleProfiling.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleProfiling.java deleted file mode 100644 index 0d2d7ed..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.ClientData.IMethodProfilingHandler; -import com.samsung.slp.common.connection.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 ".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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleTest.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleTest.java deleted file mode 100644 index a4aa08f..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleThread.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleThread.java deleted file mode 100644 index edbd539..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleWait.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HandleWait.java deleted file mode 100644 index 4b04d20..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HeapSegment.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HeapSegment.java deleted file mode 100644 index c7285c7..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/HeapSegment.java +++ /dev/null @@ -1,446 +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.samsung.slp.common.connection.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 { - - /** - * Describes an object/region encoded in the HPSG data. - */ - public static class HeapSegmentElement implements Comparable { - - /* - * 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 = (int)data.get() & 0x000000ff; - int eLen = ((int)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 = (int)data.get() & 0x000000ff; - if ((nextState & ~PARTIAL_MASK) != (eState & ~PARTIAL_MASK)) { - throw new ParseException("State mismatch", data.position()); - } - eState = nextState; - eLen += ((int)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; - } - - 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 = (int) hpsgData.get(); - mStartAddress = (long) 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 other comes immediately after this segment. - * - * @param other The HeapSegment to check. - * @return true if other 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 other 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, other will be invalid - * when this method returns. - * @return true if other 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(); - } - - 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IDevice.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IDevice.java deleted file mode 100644 index 4d3555f..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IDevice.java +++ /dev/null @@ -1,401 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.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}. */ - 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$ - - private String mState; - - DeviceState(String state) { - mState = state; - } - - /** - * Returns a {@link DeviceState} from the string returned by adb devices. - * - * @param state the device state. - * @return a {@link DeviceState} object or null if the state is unknown. - */ - public static DeviceState getState(String state) { - for (DeviceState deviceState : values()) { - if (deviceState.mState.equals(state)) { - return deviceState; - } - } - return null; - } - } - - /** - * Returns the serial number of the device. - */ - public String getSerialNumber(); - - /** - * Returns the name of the AVD the emulator is running. - *

This is only valid if {@link #isEmulator()} returns true. - *

If the emulator is not running any AVD (for instance it's running from an Android source - * tree build), this method will return "<build>". - * - * @return the name of the AVD or null if there isn't any. - */ - public String getAvdName(); - - /** - * Returns the state of the device. - */ - public DeviceState getState(); - - /** - * Returns the device properties. It contains the whole output of 'getprop' - */ - public Map getProperties(); - - /** - * Returns the number of property for this device. - */ - public int getPropertyCount(); - - /** - * Returns a property value. - * - * @param name the name of the value to return. - * @return the value or null if the property does not exist. - */ - public String getProperty(String name); - - /** - * 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 true if {@link #getState()} returns {@link DeviceState#ONLINE}. - */ - public boolean isOnline(); - - /** - * Returns true if the device is an emulator. - */ - public boolean isEmulator(); - - /** - * Returns if the device is offline. - * - * @return true if {@link #getState()} returns {@link DeviceState#OFFLINE}. - */ - public boolean isOffline(); - - /** - * Returns if the device is in bootloader mode. - * - * @return true 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 Client object or null 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 null 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 RawImage or null 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 receiver - *

This is similar to calling - * executeShellCommand(command, receiver, DdmPreferences.getTimeOut()). - * - * @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 receiver. - *

maxTimeToOutputResponse is used as a maximum waiting time when expecting the - * command output from the device.
- * At any time, if the shell command does not output anything for a period longer than - * maxTimeToOutputResponse, then the method will throw - * {@link ShellCommandUnresponsiveException}. - *

For commands like log output, a maxTimeToOutputResponse value of 0, meaning - * that the method will never throw and will block until the receiver's - * {@link IShellOutputReceiver#isCancelled()} returns true, 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 receiver 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 maxTimeToOutputResponse. - * @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}. - *

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}. - *

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. - * @return true if success. - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws IOException in case of I/O error on the connection. - */ - public void createForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Removes a port forwarding between a local and a remote port. - * - * @param localPort the local port to forward - * @param remotePort the remote port. - * @return true if success. - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws IOException in case of I/O error on the connection. - */ - public void removeForward(int localPort, int remotePort) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Returns the name of the client by pid or null if pid is unknown - * @param pid the pid of the client. - */ - public String getClientName(int pid); - - /** - * 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 true if re-install of app should be performed - * @return a {@link String} with an error code, or null if success. - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when - * performing the action. - * @throws IOException in case of I/O error on the connection. - */ - public String installPackage(String packageFilePath, boolean reinstall) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException; - - /** - * Pushes a file to device - * - * @param localFilePath the absolute path to file on local host - * @return {@link String} destination path on device for file - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws IOException in case of I/O error on the connection. - */ - public String syncPackageToDevice(String localFilePath) - throws TimeoutException, AdbCommandRejectedException, IOException; - - /** - * Installs the application package that was pushed to a temporary location on the device. - * - * @param remoteFilePath absolute file path to package file on device - * @param reinstall set to true if re-install of app should be performed - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when - * performing the action. - * @throws IOException if installation failed - */ - public String installRemotePackage(String remoteFilePath, boolean reinstall) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException; - - /** - * Removes a file from device. - * - * @param remoteFilePath path on device of file to remove - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when - * performing the action. - * @throws IOException if file removal failed - */ - public void removeRemotePackage(String remoteFilePath) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException; - - /** - * Uninstalls an package from the device. - * - * @param packageName the Android application package name to uninstall - * @return a {@link String} with an error code, or null if success. - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws ShellCommandUnresponsiveException if the device didn't respond for long time when - * performing the action. - * @throws IOException - */ - public String uninstallPackage(String packageName) - throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, - IOException; - - /** - * Reboot the device. - * - * @param into the bootloader name to reboot into, or null to just reboot the device. - * @throws TimeoutException in case of timeout on the connection. - * @throws AdbCommandRejectedException if adb rejects the command - * @throws IOException - */ - public void reboot(String into) - throws TimeoutException, AdbCommandRejectedException, IOException; -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IShellOutputReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IShellOutputReceiver.java deleted file mode 100644 index d2a1c98..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IStackTraceInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/IStackTraceInfo.java deleted file mode 100644 index a839566..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -/** - * Classes which implement this interface provide a method that returns a stack trace. - */ -public interface IStackTraceInfo { - - /** - * Returns the stack trace. This can be null. - */ - public StackTraceElement[] getStackTrace(); - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/JdwpPacket.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/JdwpPacket.java deleted file mode 100644 index ce5fe00..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Log.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/Log.java deleted file mode 100644 index 37dd965..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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. - *

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 LogLevel enum - * @return a LogLevel object or null 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. - *

- * The letter is passed as a {@link String} argument, but only the first character - * is used. - * @param letter the letter matching a LogLevel enum - * @return a LogLevel object or null 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. - *

- * Local addition. Output looks like: - * 1230- 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef - *

- * 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MonitorThread.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MonitorThread.java deleted file mode 100644 index fbfd79c..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MonitorThread.java +++ /dev/null @@ -1,780 +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.samsung.slp.common.connection.ddmlib; - - -import com.samsung.slp.common.connection.ddmlib.DebugPortManager.IDebugPortProvider; -import com.samsung.slp.common.connection.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 mClientList; - - // The almighty mux - private Selector mSelector; - - // Map chunk types to handlers - private HashMap 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(); - mHandlerMap = new HashMap(); - - 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 keys = mSelector.selectedKeys(); - Iterator 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. - *

This will lock the {@link Client} list of the {@link Device} running client. - * @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(); - } - - /* - * 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 set; - synchronized (mHandlerMap) { - Collection values = mHandlerMap.values(); - set = new HashSet(values); - } - - Iterator 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MultiLineReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MultiLineReceiver.java deleted file mode 100644 index 3d87208..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/MultiLineReceiver.java +++ /dev/null @@ -1,130 +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.samsung.slp.common.connection.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. - *

Additionally, it splits the string by lines. - *

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 mArray = new ArrayList(); - - /** - * 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) - */ - public final void addOutput(byte[] data, int offset, int length) { - if (isCancelled() == false) { - String s = null; - try { - s = new String(data, offset, length, "ISO-8859-1"); //$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() - */ - 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. - *

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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeAllocationInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeAllocationInfo.java deleted file mode 100644 index 110029d..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeAllocationInfo.java +++ /dev/null @@ -1,278 +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.samsung.slp.common.connection.ddmlib; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Stores native allocation information. - *

Contains number of allocations, their size and the stack trace. - *

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 { - /* constants for flag bits */ - private static final int FLAG_ZYGOTE_CHILD = (1<<31); - private static final int FLAG_MASK = (FLAG_ZYGOTE_CHILD); - - /** - * list of alloc functions that are filtered out when attempting to display - * a relevant method responsible for an allocation - */ - private static ArrayList sAllocFunctionFilter; - static { - sAllocFunctionFilter = new ArrayList(); - sAllocFunctionFilter.add("malloc"); //$NON-NLS-1$ - sAllocFunctionFilter.add("calloc"); //$NON-NLS-1$ - sAllocFunctionFilter.add("realloc"); //$NON-NLS-1$ - sAllocFunctionFilter.add("get_backtrace"); //$NON-NLS-1$ - sAllocFunctionFilter.add("get_hash"); //$NON-NLS-1$ - sAllocFunctionFilter.add("??"); //$NON-NLS-1$ - sAllocFunctionFilter.add("internal_free"); //$NON-NLS-1$ - sAllocFunctionFilter.add("operator new"); //$NON-NLS-1$ - sAllocFunctionFilter.add("leak_free"); //$NON-NLS-1$ - sAllocFunctionFilter.add("chk_free"); //$NON-NLS-1$ - sAllocFunctionFilter.add("chk_memalign"); //$NON-NLS-1$ - sAllocFunctionFilter.add("Malloc"); //$NON-NLS-1$ - sAllocFunctionFilter.add("leak_memalign"); //$NON-NLS-1$ - } - - private final int mSize; - - private final boolean mIsZygoteChild; - - private final int mAllocations; - - private final ArrayList mStackCallAddresses = new ArrayList(); - - private ArrayList mResolvedStackCall = null; - - private boolean mIsStackCallResolved = false; - - /** - * Constructs a new {@link NativeAllocationInfo}. - * @param size The size of the allocations. - * @param allocations the allocation count - */ - 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. - */ - 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 Long[] getStackCallAddresses() { - return mStackCallAddresses.toArray(new Long[mStackCallAddresses.size()]); - } - - /** - * Sets the resolved stack call for this allocation. - *

- * If resolvedStackCall is non null then - * {@link #isStackCallResolved()} will return true after this call. - * @param resolvedStackCall The list of {@link NativeStackCallInfo}. - */ - public synchronized void setResolvedStackCall(List resolvedStackCall) { - if (mResolvedStackCall == null) { - mResolvedStackCall = new ArrayList(); - } else { - mResolvedStackCall.clear(); - } - mResolvedStackCall.addAll(resolvedStackCall); - mIsStackCallResolved = mResolvedStackCall.size() != 0; - } - - /** - * Returns the resolved stack call. - * @return An array of {@link NativeStackCallInfo} or null if the stack call - * was not resolved. - * @see #setResolvedStackCall(ArrayList) - * @see #isStackCallResolved() - */ - public synchronized NativeStackCallInfo[] getResolvedStackCall() { - if (mIsStackCallResolved) { - return mResolvedStackCall.toArray(new NativeStackCallInfo[mResolvedStackCall.size()]); - } - - return null; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - * @return true if this object is equal to the obj argument; - * false 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; - } - - /** - * Returns a string representation of the object. - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("Allocations: "); - buffer.append(mAllocations); - buffer.append("\n"); //$NON-NLS-1$ - - buffer.append("Size: "); - buffer.append(mSize); - buffer.append("\n"); //$NON-NLS-1$ - - buffer.append("Total Size: "); - buffer.append(mSize * mAllocations); - buffer.append("\n"); //$NON-NLS-1$ - - Iterator addrIterator = mStackCallAddresses.iterator(); - Iterator sourceIterator = mResolvedStackCall.iterator(); - - while (sourceIterator.hasNext()) { - long addr = addrIterator.next(); - NativeStackCallInfo source = sourceIterator.next(); - 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())); - } - } - - return buffer.toString(); - } - - /** - * Returns the first {@link NativeStackCallInfo} that is relevant. - *

- * A relevant NativeStackCallInfo 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 NativeStackCallInfo or null if the stack call has not - * been processed from the raw addresses. - * @see #setResolvedStackCall(ArrayList) - * @see #isStackCallResolved() - */ - public synchronized NativeStackCallInfo getRelevantStackCallInfo() { - if (mIsStackCallResolved && mResolvedStackCall != null) { - Iterator sourceIterator = mResolvedStackCall.iterator(); - Iterator addrIterator = mStackCallAddresses.iterator(); - - while (sourceIterator.hasNext() && addrIterator.hasNext()) { - long addr = addrIterator.next(); - NativeStackCallInfo info = sourceIterator.next(); - if (addr != 0 && info != null) { - if (isRelevant(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; - } - - /** - * Returns true if the method name is relevant. - * @param methodName the method name to test. - */ - private boolean isRelevant(String methodName) { - for (String filter : sAllocFunctionFilter) { - if (methodName.contains(filter)) { - return false; - } - } - - return true; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeLibraryMapInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeLibraryMapInfo.java deleted file mode 100644 index 91683ce..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib; - -/** - * Memory address to library mapping for native libraries. - *

- * 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 true 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeStackCallInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeStackCallInfo.java deleted file mode 100644 index ed510c6..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NativeStackCallInfo.java +++ /dev/null @@ -1,95 +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.samsung.slp.common.connection.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+)$"); - - /** name of the library */ - private String mLibrary; - - /** name of the method */ - private String mMethod; - - /** - * name of the source file + line number in the format
- * <sourcefile>:<linenumber> - */ - private String mSourceFile; - - private int mLineNumber = -1; - - /** - * Basic constructor with library, method, and sourcefile information - * - * @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(String lib, String method, String sourceFile) { - 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 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; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NullOutputReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NullOutputReceiver.java deleted file mode 100644 index a08f4d7..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/NullOutputReceiver.java +++ /dev/null @@ -1,50 +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.samsung.slp.common.connection.ddmlib; - -/** - * Implementation of {@link IShellOutputReceiver} that does nothing. - *

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) - */ - public void addOutput(byte[] data, int offset, int length) { - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#flush() - */ - public void flush() { - } - - /* (non-Javadoc) - * @see com.android.ddmlib.adb.IShellOutputReceiver#isCancelled() - */ - public boolean isCancelled() { - return false; - } - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/RawImage.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/RawImage.java deleted file mode 100644 index cb69191..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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}. - *

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. - *

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. - *

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. - *

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 index 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. - *

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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ShellCommandUnresponsiveException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ShellCommandUnresponsiveException.java deleted file mode 100644 index b5fa4fc..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ShellCommandUnresponsiveException.java +++ /dev/null @@ -1,28 +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.samsung.slp.common.connection.ddmlib; - -import java.io.IOException; - -/** - * Exception thrown when a shell command executed on a device takes too long to send its output. - *

The command may not actually be unresponsive, it just has spent too much time not outputting - * any thing to the console. - */ -public class ShellCommandUnresponsiveException extends IOException { - private static final long serialVersionUID = 1L; -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/SyncService.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/SyncService.java deleted file mode 100644 index 9643d3a..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/SyncService.java +++ /dev/null @@ -1,1003 +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.samsung.slp.common.connection.ddmlib; - -import com.samsung.slp.common.connection.ddmlib.utils.ArrayHelper; -import com.samsung.slp.common.connection.ddmlib.AdbHelper.AdbResponse; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -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. - *

- * 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; - - /** Result code for transfer success. */ - public static final int RESULT_OK = 0; - /** Result code for canceled transfer */ - public static final int RESULT_CANCELED = 1; - /** Result code for unknown error */ - public static final int RESULT_UNKNOWN_ERROR = 2; - /** Result code for network connection error */ - public static final int RESULT_CONNECTION_ERROR = 3; - /** Result code for unknown remote object during a pull */ - public static final int RESULT_NO_REMOTE_OBJECT = 4; - /** Result code when attempting to pull multiple files into a file */ - public static final int RESULT_TARGET_IS_FILE = 5; - /** Result code when attempting to pull multiple into a directory that does not exist. */ - public static final int RESULT_NO_DIR_TARGET = 6; - /** Result code for wrong encoding on the remote path. */ - public static final int RESULT_REMOTE_PATH_ENCODING = 7; - /** Result code for remote path that is too long. */ - public static final int RESULT_REMOTE_PATH_LENGTH = 8; - /** Result code for error while writing local file. */ - public static final int RESULT_FILE_WRITE_ERROR = 9; - /** Result code for error while reading local file. */ - public static final int RESULT_FILE_READ_ERROR = 10; - /** Result code for attempting to push a file that does not exist. */ - public static final int RESULT_NO_LOCAL_FILE = 11; - /** Result code for attempting to push a directory. */ - public static final int RESULT_LOCAL_IS_DIRECTORY = 12; - /** Result code for when the target path of a multi file push is a file. */ - public static final int RESULT_REMOTE_IS_FILE = 13; - /** Result code for receiving too much data from the remove device at once */ - public static final int RESULT_BUFFER_OVERRUN = 14; - /** Result code for network connection timeout */ - public static final int RESULT_CONNECTION_TIMEOUT = 15; - - /** - * A file transfer result. - *

- * This contains a code, and an optional string - */ - public static class SyncResult { - private int mCode; - private String mMessage; - SyncResult(int code, String message) { - mCode = code; - mMessage = message; - } - - SyncResult(int code, Exception e) { - this(code, e.getMessage()); - } - - SyncResult(int code) { - this(code, errorCodeToString(code)); - } - - public int getCode() { - return mCode; - } - - public String getMessage() { - return mMessage; - } - } - - /** - * 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 { - public void advance(int work) { - } - public boolean isCanceled() { - return false; - } - - public void start(int totalWork) { - } - public void startSubTask(String name) { - } - 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}. - *

This object can be reused multiple times and can be used by concurrent threads. - */ - public static ISyncProgressMonitor getNullProgressMonitor() { - return sNullSyncProgressMonitor; - } - - /** - * Converts an error code into a non-localized string - * @param code the error code; - */ - private static String errorCodeToString(int code) { - switch (code) { - case RESULT_OK: - return "Success."; - case RESULT_CANCELED: - return "Tranfert canceled by the user."; - case RESULT_UNKNOWN_ERROR: - return "Unknown Error."; - case RESULT_CONNECTION_ERROR: - return "Adb Connection Error."; - case RESULT_NO_REMOTE_OBJECT: - return "Remote object doesn't exist!"; - case RESULT_TARGET_IS_FILE: - return "Target object is a file."; - case RESULT_NO_DIR_TARGET: - return "Target directory doesn't exist."; - case RESULT_REMOTE_PATH_ENCODING: - return "Remote Path encoding is not supported."; - case RESULT_REMOTE_PATH_LENGTH: - return "Remove path is too long."; - case RESULT_FILE_WRITE_ERROR: - return "Writing local file failed!"; - case RESULT_FILE_READ_ERROR: - return "Reading local file failed!"; - case RESULT_NO_LOCAL_FILE: - return "Local file doesn't exist."; - case RESULT_LOCAL_IS_DIRECTORY: - return "Local path is a directory."; - case RESULT_REMOTE_IS_FILE: - return "Remote path is a file."; - case RESULT_BUFFER_OVERRUN: - return "Receiving too much data."; - case RESULT_CONNECTION_TIMEOUT: - return "timeout"; - } - - throw new RuntimeException(); - } - - /** - * 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. - * @return a {@link SyncResult} object with a code and an optional message. - * - * @see FileListingService.FileEntry - * @see #getNullProgressMonitor() - */ - public SyncResult pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor) { - - // first we check the destination is a directory and exists - File f = new File(localPath); - if (f.exists() == false) { - return new SyncResult(RESULT_NO_DIR_TARGET); - } - if (f.isDirectory() == false) { - return new SyncResult(RESULT_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); - - SyncResult result = doPull(entries, localPath, fls, monitor); - - monitor.stop(); - - return result; - } - - /** - * Pulls a single file. - * @param remote the remote file - * @param localFilename The local destination. - * @param monitor The progress monitor. Cannot be null. - * @return a {@link SyncResult} object with a code and an optional message. - * - * @see FileListingService.FileEntry - * @see #getNullProgressMonitor() - */ - public SyncResult pullFile(FileEntry remote, String localFilename, - ISyncProgressMonitor monitor) { - int total = remote.getSizeValue(); - monitor.start(total); - - SyncResult result = doPullFile(remote.getFullPath(), localFilename, monitor); - - monitor.stop(); - return result; - } - - /** - * Pulls a single file. - *

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. - * @return a {@link SyncResult} object with a code and an optional message. - * - * @see #getNullProgressMonitor() - */ - public SyncResult pullFile(String remoteFilepath, String localFilename, - ISyncProgressMonitor monitor) { - monitor.start(0); - //TODO: use the {@link FileListingService} to get the file size. - - SyncResult result = doPullFile(remoteFilepath, localFilename, monitor); - - monitor.stop(); - return result; - } - - /** - * 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. - * @return a {@link SyncResult} object with a code and an optional message. - */ - public SyncResult push(String[] local, FileEntry remote, ISyncProgressMonitor monitor) { - if (remote.isDirectory() == false) { - return new SyncResult(RESULT_REMOTE_IS_FILE); - } - - // make a list of File from the list of String - ArrayList files = new ArrayList(); - 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); - - SyncResult result = doPush(fileArray, remote.getFullPath(), monitor); - - monitor.stop(); - - return result; - } - - /** - * Push a single file. - * @param local the local filepath. - * @param remote The remote filepath. - * @param monitor The progress monitor. Cannot be null. - * @return a {@link SyncResult} object with a code and an optional message. - */ - public SyncResult pushFile(String local, String remote, ISyncProgressMonitor monitor) { - File f = new File(local); - if (f.exists() == false) { - return new SyncResult(RESULT_NO_LOCAL_FILE); - } - - if (f.isDirectory()) { - return new SyncResult(RESULT_LOCAL_IS_DIRECTORY); - } - - monitor.start((int)f.length()); - - SyncResult result = doPushFile(local, remote, monitor); - - monitor.stop(); - - return result; - } - - /** - * 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. - * @return a {@link SyncResult} object with a code and an optional message. - */ - private SyncResult doPull(FileEntry[] entries, String localPath, - FileListingService fileListingService, - ISyncProgressMonitor monitor) { - - for (FileEntry e : entries) { - // check if we're cancelled - if (monitor.isCanceled() == true) { - return new SyncResult(RESULT_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); - SyncResult result = doPull(children, dest, fileListingService, monitor); - if (result.mCode != RESULT_OK) { - return result; - } - monitor.advance(1); - } else if (type == FileListingService.TYPE_FILE) { - monitor.startSubTask(e.getFullPath()); - String dest = localPath + File.separator + e.getName(); - SyncResult result = doPullFile(e.getFullPath(), dest, monitor); - if (result.mCode != RESULT_OK) { - return result; - } - } - } - - return new SyncResult(RESULT_OK); - } - - /** - * 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. - * @return a {@link SyncResult} object with a code and an optional message. - */ - private SyncResult doPullFile(String remotePath, String localPath, - ISyncProgressMonitor monitor) { - 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) { - return new SyncResult(RESULT_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) { - return new SyncResult(RESULT_CONNECTION_ERROR); - } - } catch (UnsupportedEncodingException e) { - return new SyncResult(RESULT_REMOTE_PATH_ENCODING, e); - } catch (TimeoutException e) { - return new SyncResult(RESULT_CONNECTION_TIMEOUT, e); - } catch (IOException e) { - return new SyncResult(RESULT_CONNECTION_ERROR, e); - } - - // 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); - } catch (FileNotFoundException e) { - return new SyncResult(RESULT_FILE_WRITE_ERROR, e); - } - - // 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) { - return new SyncResult(RESULT_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 - return new SyncResult(RESULT_CONNECTION_ERROR); - } - int length = ArrayHelper.swap32bitFromArray(pullResult, 4); - if (length > SYNC_DATA_MAX) { - // buffer overrun! - // error and exit - return new SyncResult(RESULT_BUFFER_OVERRUN); - } - - try { - // 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); - } catch (TimeoutException e) { - return new SyncResult(RESULT_CONNECTION_TIMEOUT, e); - } catch (IOException e) { - return new SyncResult(RESULT_CONNECTION_ERROR, e); - } - - // write the content in the file - try { - fos.write(data, 0, length); - } catch (IOException e) { - return new SyncResult(RESULT_FILE_WRITE_ERROR, e); - } - - monitor.advance(length); - } - - try { - fos.flush(); - } catch (IOException e) { - return new SyncResult(RESULT_FILE_WRITE_ERROR, e); - } - return new SyncResult(RESULT_OK); - } - - - /** - * Push multiple files - * @param fileArray - * @param remotePath - * @param monitor - * @return a {@link SyncResult} object with a code and an optional message. - */ - private SyncResult doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor) { - for (File f : fileArray) { - // check if we're canceled - if (monitor.isCanceled() == true) { - return new SyncResult(RESULT_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); - SyncResult result = doPush(f.listFiles(), dest, monitor); - - if (result.mCode != RESULT_OK) { - return result; - } - - 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); - SyncResult result = doPushFile(f.getAbsolutePath(), remoteFile, monitor); - if (result.mCode != RESULT_OK) { - return result; - } - } - } - } - - return new SyncResult(RESULT_OK); - } - - /** - * 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. - * @return a {@link SyncResult} object with a code and an optional message. - */ - private SyncResult doPushFile(String localPath, String remotePath, - ISyncProgressMonitor monitor) { - 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) { - return new SyncResult(RESULT_REMOTE_PATH_LENGTH); - } - - File f = new File(localPath); - - // this shouldn't happen but still... - if (f.exists() == false) { - return new SyncResult(RESULT_NO_LOCAL_FILE); - } - - // create the stream to read the file - fis = new FileInputStream(f); - - // create the header for the action - msg = createSendFileReq(ID_SEND, remotePathContent, 0644); - } catch (UnsupportedEncodingException e) { - return new SyncResult(RESULT_REMOTE_PATH_ENCODING, e); - } catch (FileNotFoundException e) { - return new SyncResult(RESULT_FILE_READ_ERROR, e); - } - - // and send it. We use a custom try/catch block to make the difference between - // file and network IO exceptions. - try { - AdbHelper.write(mChannel, msg, -1, timeOut); - } catch (TimeoutException e) { - return new SyncResult(RESULT_CONNECTION_TIMEOUT, e); - } catch (IOException e) { - return new SyncResult(RESULT_CONNECTION_ERROR, e); - } - - // 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) { - return new SyncResult(RESULT_CANCELED); - } - - // read up to SYNC_DATA_MAX - int readCount = 0; - try { - readCount = fis.read(mBuffer, 8, SYNC_DATA_MAX); - } catch (IOException e) { - return new SyncResult(RESULT_FILE_READ_ERROR, e); - } - - 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 - try { - AdbHelper.write(mChannel, mBuffer, readCount+8, timeOut); - } catch (TimeoutException e) { - return new SyncResult(RESULT_CONNECTION_TIMEOUT, e); - } catch (IOException e) { - return new SyncResult(RESULT_CONNECTION_ERROR, e); - } - - // and advance the monitor - monitor.advance(readCount); - } - // close the local file - try { - fis.close(); - } catch (IOException e) { - return new SyncResult(RESULT_FILE_READ_ERROR, e); - } - - try { - // 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) { - if (checkResult(result, ID_FAIL)) { - // read some error message... - int len = ArrayHelper.swap32bitFromArray(result, 4); - - AdbHelper.read(mChannel, mBuffer, len, timeOut); - - // output the result? - String message = new String(mBuffer, 0, len); - Log.e("ddms", "transfer error: " + message); - return new SyncResult(RESULT_UNKNOWN_ERROR, message); - } - - return new SyncResult(RESULT_UNKNOWN_ERROR); - } - } catch (TimeoutException e) { - return new SyncResult(RESULT_CONNECTION_TIMEOUT, e); - } catch (IOException e) { - return new SyncResult(RESULT_CONNECTION_ERROR, e); - } - - return new SyncResult(RESULT_OK); - } - - /** - * Returns the mode of the remote file. - * @param path the remote file - * @return and Integer containing the mode if all went well or null - * otherwise - * @throws IOException - * @throws TimeoutException - */ - private Integer readMode(String path) 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ThreadInfo.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ThreadInfo.java deleted file mode 100644 index 658d692..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/ThreadInfo.java +++ /dev/null @@ -1,139 +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.samsung.slp.common.connection.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() - */ - public StackTraceElement[] getStackTrace() { - return mTrace; - } - - /** - * Returns the approximate time of the stacktrace data. - * @see #getStackTrace() - */ - public long getStackCallTime() { - return mTraceTime; - } -} - diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/TimeoutException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/TimeoutException.java deleted file mode 100644 index 72b2a4b..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/TimeoutException.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.samsung.slp.common.connection.ddmlib; - -import java.io.IOException; - -/** - * Exception thrown when a connection to Adb failed with a timeout. - * - */ -public class TimeoutException extends IOException { - private static final long serialVersionUID = 1L; -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventContainer.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventContainer.java deleted file mode 100644 index 3c7bb6a..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventContainer.java +++ /dev/null @@ -1,461 +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.samsung.slp.common.connection.ddmlib.log; - -import com.samsung.slp.common.connection.ddmlib.log.LogReceiver.LogEntry; - -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.samsung.slp.common.connection.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 null 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}. - *

- * Strings created by this method can be reloaded with - * {@link #getObjectFromStorageString(String)}. - *

- * 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(); - } - - 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 valueIndex 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 valueIndex 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 index-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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventLogParser.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventLogParser.java deleted file mode 100644 index a47eb1b..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventLogParser.java +++ /dev/null @@ -1,578 +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.samsung.slp.common.connection.ddmlib.log; - -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.Log; -import com.samsung.slp.common.connection.ddmlib.MultiLineReceiver; -import com.samsung.slp.common.connection.ddmlib.log.EventContainer.EventValueType; -import com.samsung.slp.common.connection.ddmlib.log.EventValueDescription.ValueType; -import com.samsung.slp.common.connection.ddmlib.log.LogReceiver.LogEntry; -import com.samsung.slp.common.connection.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.Set; -import java.util.TreeMap; -import java.util.Map.Entry; -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 mTagMap = new TreeMap(); - - private final TreeMap mValueDescriptionMap = - new TreeMap(); - - public EventLogParser() { - } - - /** - * Inits the parser for a specific Device. - *

- * 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 true if success, false 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); - } - } - 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 true if success, false 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 true if success, false if failure. - */ - public boolean init(String filePath) { - try { - BufferedReader 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; - } - } - - /** - * 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 list = new ArrayList(); - - 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 list = new ArrayList(); - 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): - // where 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> tagSet = mTagMap.entrySet(); - for (Entry 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 getTagMap() { - return mTagMap; - } - - public Map 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 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 subList = new ArrayList(); - 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventValueDescription.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventValueDescription.java deleted file mode 100644 index a75966a..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/EventValueDescription.java +++ /dev/null @@ -1,214 +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.samsung.slp.common.connection.ddmlib.log; - -import com.samsung.slp.common.connection.ddmlib.log.EventContainer.EventValueType; - - -/** - * Describes an {@link EventContainer} value. - *

- * 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}). - *

- * 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 null 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(); - } - - 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. - *

- * 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}. - *

- * @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. - *

- * 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/GcEventContainer.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/GcEventContainer.java deleted file mode 100644 index 27f4d78..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib.log; - -import com.samsung.slp.common.connection.ddmlib.log.EventValueDescription.ValueType; -import com.samsung.slp.common.connection.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. - *

- * 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 valueIndex-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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidTypeException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidTypeException.java deleted file mode 100644 index 99e4204..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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 - * (cause==null ? null : cause.toString()) (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 null 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 null 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidValueTypeException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/InvalidValueTypeException.java deleted file mode 100644 index 055909d..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib.log; - -import com.samsung.slp.common.connection.ddmlib.log.EventContainer.EventValueType; -import com.samsung.slp.common.connection.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 - * (cause==null ? null : cause.toString()) (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 null 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 null 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/LogReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/log/LogReceiver.java deleted file mode 100644 index ee07959..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.ddmlib.log; - - -import com.samsung.slp.common.connection.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}. - *

This interface provides two methods. - *

    - *
  • {@link #newEntry(com.android.ddmlib.log.LogReceiver.LogEntry)} provides a - * first level of parsing, extracting {@link LogEntry} objects out of the log service output.
  • - *
  • {@link #newData(byte[], int, int)} provides a way to receive the raw information - * coming directly from the log service.
  • - *
- */ - 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}. - *

- * 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 offset + {@link #ENTRY_HEADER_SIZE}. - * @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 null 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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/utils/ArrayHelper.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmlib/utils/ArrayHelper.java deleted file mode 100644 index 2f3765d..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/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.samsung.slp.common.connection.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/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/Addr2Line.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/Addr2Line.java deleted file mode 100644 index 3cff589..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/Addr2Line.java +++ /dev/null @@ -1,281 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.*; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Collection; -import java.util.HashMap; - -/** - * Represents an addr2line process to get filename/method information from a - * memory address.
- * Each process can only handle one library, which should be provided when - * creating a new process.
- *
- * The processes take some time to load as they need to parse the library files. - * For this reason, processes cannot be manually started. Instead the class - * keeps an internal list of processes and one asks for a process for a specific - * library, using getProcess(String library).

- * Internally, the processes are started in pipe mode to be able to query them - * with multiple addresses. - */ -public class Addr2Line { - - /** - * Loaded processes list. This is also used as a locking object for any - * methods dealing with starting/stopping/creating processes/querying for - * method. - */ - private static final HashMap sProcessCache = - new HashMap(); - - /** - * byte array representing a carriage return. Used to push addresses in the - * process pipes. - */ - private static final byte[] sCrLf = { - '\n' - }; - - /** Path to the library */ - private String mLibrary; - - /** the command line process */ - private Process mProcess; - - /** buffer to read the result of the command line process from */ - private BufferedReader mResultReader; - - /** - * output stream to provide new addresses to decode to the command line - * process - */ - private BufferedOutputStream mAddressWriter; - - /** - * Returns the instance of a Addr2Line process for the specified library. - *
The library should be in a format that makes
- * $ANDROID_PRODUCT_OUT + "/symbols" + library a valid file. - * - * @param library the library in which to look for addresses. - * @return a new Addr2Line object representing a started process, ready to - * be queried for addresses. If any error happened when launching a - * new process, null will be returned. - */ - public static Addr2Line getProcess(final String library) { - // synchronize around the hashmap object - if (library != null) { - synchronized (sProcessCache) { - // look for an existing process - Addr2Line process = sProcessCache.get(library); - - // if we don't find one, we create it - if (process == null) { - process = new Addr2Line(library); - - // then we start it - boolean status = process.start(); - - if (status) { - // if starting the process worked, then we add it to the - // list. - sProcessCache.put(library, process); - } else { - // otherwise we just drop the object, to return null - process = null; - } - } - // return the process - return process; - } - } - return null; - } - - /** - * Construct the object with a library name. - *
The library should be in a format that makes
- * $ANDROID_PRODUCT_OUT + "/symbols" + library a valid file. - * - * @param library the library in which to look for address. - */ - private Addr2Line(final String library) { - mLibrary = library; - } - - /** - * Starts the command line process. - * - * @return true if the process was started, false if it failed to start, or - * if there was any other errors. - */ - private boolean start() { - // because this is only called from getProcess() we know we don't need - // to synchronize this code. - - // get the output directory. - String symbols = System.getenv("ANDROID_SYMBOLS"); - if (symbols == null) { - symbols = DdmUiPreferences.getSymbolDirectory(); - } - - // build the command line - String[] command = new String[5]; - command[0] = DdmUiPreferences.getAddr2Line(); - command[1] = "-C"; - command[2] = "-f"; - command[3] = "-e"; - command[4] = symbols + mLibrary; - - try { - // attempt to start the process - mProcess = Runtime.getRuntime().exec(command); - - if (mProcess != null) { - // get the result reader - InputStreamReader is = new InputStreamReader(mProcess - .getInputStream()); - mResultReader = new BufferedReader(is); - - // get the outstream to write the addresses - mAddressWriter = new BufferedOutputStream(mProcess - .getOutputStream()); - - // check our streams are here - if (mResultReader == null || mAddressWriter == null) { - // not here? stop the process and return false; - mProcess.destroy(); - mProcess = null; - return false; - } - - // return a success - return true; - } - - } catch (IOException e) { - // log the error - String msg = String.format( - "Error while trying to start %1$s process for library %2$s", - DdmUiPreferences.getAddr2Line(), mLibrary); - Log.e("ddm-Addr2Line", msg); - - // drop the process just in case - if (mProcess != null) { - mProcess.destroy(); - mProcess = null; - } - } - - // we can be here either cause the allocation of mProcess failed, or we - // caught an exception - return false; - } - - /** - * Stops the command line process. - */ - public void stop() { - synchronized (sProcessCache) { - if (mProcess != null) { - // remove the process from the list - sProcessCache.remove(mLibrary); - - // then stops the process - mProcess.destroy(); - - // set the reference to null. - // this allows to make sure another thread calling getAddress() - // will not query a stopped thread - mProcess = null; - } - } - } - - /** - * Stops all current running processes. - */ - public static void stopAll() { - // because of concurrent access (and our use of HashMap.values()), we - // can't rely on the synchronized inside stop(). We need to put one - // around the whole loop. - synchronized (sProcessCache) { - // just a basic loop on all the values in the hashmap and call to - // stop(); - Collection col = sProcessCache.values(); - for (Addr2Line a2l : col) { - a2l.stop(); - } - } - } - - /** - * Looks up an address and returns method name, source file name, and line - * number. - * - * @param addr the address to look up - * @return a BacktraceInfo object containing the method/filename/linenumber - * or null if the process we stopped before the query could be - * processed, or if an IO exception happened. - */ - public NativeStackCallInfo getAddress(long addr) { - // even though we don't access the hashmap object, we need to - // synchronized on it to prevent - // another thread from stopping the process we're going to query. - synchronized (sProcessCache) { - // check the process is still alive/allocated - if (mProcess != null) { - // prepare to the write the address to the output buffer. - - // first, conversion to a string containing the hex value. - String tmp = Long.toString(addr, 16); - - try { - // write the address to the buffer - mAddressWriter.write(tmp.getBytes()); - - // add CR-LF - mAddressWriter.write(sCrLf); - - // flush it all. - mAddressWriter.flush(); - - // read the result. We need to read 2 lines - String method = mResultReader.readLine(); - String source = mResultReader.readLine(); - - // make the backtrace object and return it - if (method != null && source != null) { - return new NativeStackCallInfo(mLibrary, method, source); - } - } catch (IOException e) { - // log the error - Log.e("ddms", - "Error while trying to get information for addr: " - + tmp + " in library: " + mLibrary); - // we'll return null later - } - } - } - return null; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/AllocationPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/AllocationPanel.java deleted file mode 100644 index d1be6a7..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/AllocationPanel.java +++ /dev/null @@ -1,486 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.AllocationInfo; -import com.samsung.slp.common.connection.ddmlib.Client; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.samsung.slp.common.connection.ddmlib.ClientData.AllocationTrackingStatus; - -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Sash; -import org.eclipse.swt.widgets.Table; - -/** - * Base class for our information panels. - */ -public class AllocationPanel extends TablePanel { - - private final static String PREFS_ALLOC_COL_SIZE = "allocPanel.Col0"; //$NON-NLS-1$ - private final static String PREFS_ALLOC_COL_CLASS = "allocPanel.Col1"; //$NON-NLS-1$ - private final static String PREFS_ALLOC_COL_THREAD = "allocPanel.Col2"; //$NON-NLS-1$ - private final static String PREFS_ALLOC_COL_TRACE_CLASS = "allocPanel.Col3"; //$NON-NLS-1$ - private final static String PREFS_ALLOC_COL_TRACE_METHOD = "allocPanel.Col4"; //$NON-NLS-1$ - - private final static String PREFS_ALLOC_SASH = "allocPanel.sash"; //$NON-NLS-1$ - - private static final String PREFS_STACK_COL_CLASS = "allocPanel.stack.col0"; //$NON-NLS-1$ - private static final String PREFS_STACK_COL_METHOD = "allocPanel.stack.col1"; //$NON-NLS-1$ - private static final String PREFS_STACK_COL_FILE = "allocPanel.stack.col2"; //$NON-NLS-1$ - private static final String PREFS_STACK_COL_LINE = "allocPanel.stack.col3"; //$NON-NLS-1$ - private static final String PREFS_STACK_COL_NATIVE = "allocPanel.stack.col4"; //$NON-NLS-1$ - - private Composite mAllocationBase; - private Table mAllocationTable; - private TableViewer mAllocationViewer; - - private StackTracePanel mStackTracePanel; - private Table mStackTraceTable; - private Button mEnableButton; - private Button mRequestButton; - - /** - * Content Provider to display the allocations of a client. - * Expected input is a {@link Client} object, elements used in the table are of type - * {@link AllocationInfo}. - */ - private static class AllocationContentProvider implements IStructuredContentProvider { - public Object[] getElements(Object inputElement) { - if (inputElement instanceof Client) { - AllocationInfo[] allocs = ((Client)inputElement).getClientData().getAllocations(); - if (allocs != null) { - return allocs; - } - } - - return new Object[0]; - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - } - - /** - * A Label Provider to use with {@link AllocationContentProvider}. It expects the elements to be - * of type {@link AllocationInfo}. - */ - private static class AllocationLabelProvider implements ITableLabelProvider { - - public Image getColumnImage(Object element, int columnIndex) { - return null; - } - - public String getColumnText(Object element, int columnIndex) { - if (element instanceof AllocationInfo) { - AllocationInfo alloc = (AllocationInfo)element; - switch (columnIndex) { - case 0: - return Integer.toString(alloc.getSize()); - case 1: - return alloc.getAllocatedClass(); - case 2: - return Short.toString(alloc.getThreadId()); - case 3: - StackTraceElement[] traces = alloc.getStackTrace(); - if (traces.length > 0) { - return traces[0].getClassName(); - } - break; - case 4: - traces = alloc.getStackTrace(); - if (traces.length > 0) { - return traces[0].getMethodName(); - } - break; - } - } - - return null; - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public void dispose() { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - /** - * Create our control(s). - */ - @Override - protected Control createControl(Composite parent) { - final IPreferenceStore store = DdmUiPreferences.getStore(); - - // base composite for selected client with enabled thread update. - mAllocationBase = new Composite(parent, SWT.NONE); - mAllocationBase.setLayout(new FormLayout()); - - // table above the sash - Composite topParent = new Composite(mAllocationBase, SWT.NONE); - topParent.setLayout(new GridLayout(2, false)); - - mEnableButton = new Button(topParent, SWT.PUSH); - mEnableButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - Client current = getCurrentClient(); - AllocationTrackingStatus status = current.getClientData().getAllocationStatus(); - if (status == AllocationTrackingStatus.ON) { - current.enableAllocationTracker(false); - } else { - current.enableAllocationTracker(true); - } - current.requestAllocationStatus(); - } - }); - - mRequestButton = new Button(topParent, SWT.PUSH); - mRequestButton.setText("Get Allocations"); - mRequestButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - getCurrentClient().requestAllocationDetails(); - } - }); - - setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF); - - mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION); - GridData gridData; - mAllocationTable.setLayoutData(gridData = new GridData(GridData.FILL_BOTH)); - gridData.horizontalSpan = 2; - mAllocationTable.setHeaderVisible(true); - mAllocationTable.setLinesVisible(true); - - TableHelper.createTableColumn( - mAllocationTable, - "Allocation Size", - SWT.RIGHT, - "888", //$NON-NLS-1$ - PREFS_ALLOC_COL_SIZE, store); - - TableHelper.createTableColumn( - mAllocationTable, - "Allocated Class", - SWT.LEFT, - "Allocated Class", //$NON-NLS-1$ - PREFS_ALLOC_COL_CLASS, store); - - TableHelper.createTableColumn( - mAllocationTable, - "Thread Id", - SWT.LEFT, - "999", //$NON-NLS-1$ - PREFS_ALLOC_COL_THREAD, store); - - TableHelper.createTableColumn( - mAllocationTable, - "Allocated in", - SWT.LEFT, - "utime", //$NON-NLS-1$ - PREFS_ALLOC_COL_TRACE_CLASS, store); - - TableHelper.createTableColumn( - mAllocationTable, - "Allocated in", - SWT.LEFT, - "utime", //$NON-NLS-1$ - PREFS_ALLOC_COL_TRACE_METHOD, store); - - mAllocationViewer = new TableViewer(mAllocationTable); - mAllocationViewer.setContentProvider(new AllocationContentProvider()); - mAllocationViewer.setLabelProvider(new AllocationLabelProvider()); - - mAllocationViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - AllocationInfo selectedAlloc = getAllocationSelection(event.getSelection()); - updateAllocationStackTrace(selectedAlloc); - } - }); - - // the separating sash - final Sash sash = new Sash(mAllocationBase, SWT.HORIZONTAL); - Color darkGray = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); - sash.setBackground(darkGray); - - // the UI below the sash - mStackTracePanel = new StackTracePanel(); - mStackTraceTable = mStackTracePanel.createPanel(mAllocationBase, - PREFS_STACK_COL_CLASS, - PREFS_STACK_COL_METHOD, - PREFS_STACK_COL_FILE, - PREFS_STACK_COL_LINE, - PREFS_STACK_COL_NATIVE, - store); - - // now setup the sash. - // form layout data - FormData data = new FormData(); - data.top = new FormAttachment(0, 0); - data.bottom = new FormAttachment(sash, 0); - data.left = new FormAttachment(0, 0); - data.right = new FormAttachment(100, 0); - topParent.setLayoutData(data); - - final FormData sashData = new FormData(); - if (store != null && store.contains(PREFS_ALLOC_SASH)) { - sashData.top = new FormAttachment(0, store.getInt(PREFS_ALLOC_SASH)); - } else { - sashData.top = new FormAttachment(50,0); // 50% across - } - sashData.left = new FormAttachment(0, 0); - sashData.right = new FormAttachment(100, 0); - sash.setLayoutData(sashData); - - data = new FormData(); - data.top = new FormAttachment(sash, 0); - data.bottom = new FormAttachment(100, 0); - data.left = new FormAttachment(0, 0); - data.right = new FormAttachment(100, 0); - mStackTraceTable.setLayoutData(data); - - // allow resizes, but cap at minPanelWidth - sash.addListener(SWT.Selection, new Listener() { - public void handleEvent(Event e) { - Rectangle sashRect = sash.getBounds(); - Rectangle panelRect = mAllocationBase.getClientArea(); - int bottom = panelRect.height - sashRect.height - 100; - e.y = Math.max(Math.min(e.y, bottom), 100); - if (e.y != sashRect.y) { - sashData.top = new FormAttachment(0, e.y); - store.setValue(PREFS_ALLOC_SASH, e.y); - mAllocationBase.layout(); - } - } - }); - - return mAllocationBase; - } - - /** - * Sets the focus to the proper control inside the panel. - */ - @Override - public void setFocus() { - mAllocationTable.setFocus(); - } - - /** - * Sent when an existing client information changed. - *

- * 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_NAME} - * {@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} - * - * @see IClientChangeListener#clientChanged(Client, int) - */ - public void clientChanged(final Client client, int changeMask) { - if (client == getCurrentClient()) { - if ((changeMask & Client.CHANGE_HEAP_ALLOCATIONS) != 0) { - try { - mAllocationTable.getDisplay().asyncExec(new Runnable() { - public void run() { - mAllocationViewer.refresh(); - updateAllocationStackCall(); - } - }); - } catch (SWTException e) { - // widget is disposed, we do nothing - } - } else if ((changeMask & Client.CHANGE_HEAP_ALLOCATION_STATUS) != 0) { - try { - mAllocationTable.getDisplay().asyncExec(new Runnable() { - public void run() { - setUpButtons(true, client.getClientData().getAllocationStatus()); - } - }); - } catch (SWTException e) { - // widget is disposed, we do nothing - } - } - } - } - - /** - * Sent when a new device is selected. The new device can be accessed - * with {@link #getCurrentDevice()}. - */ - @Override - public void deviceSelected() { - // pass - } - - /** - * Sent when a new client is selected. The new client can be accessed - * with {@link #getCurrentClient()}. - */ - @Override - public void clientSelected() { - if (mAllocationTable.isDisposed()) { - return; - } - - Client client = getCurrentClient(); - - mStackTracePanel.setCurrentClient(client); - mStackTracePanel.setViewerInput(null); // always empty on client selection change. - - if (client != null) { - setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus()); - } else { - setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF); - } - - mAllocationViewer.setInput(client); - } - - /** - * Updates the stack call of the currently selected thread. - *

- * This must be called from the UI thread. - */ - private void updateAllocationStackCall() { - Client client = getCurrentClient(); - if (client != null) { - // get the current selection in the ThreadTable - AllocationInfo selectedAlloc = getAllocationSelection(null); - - if (selectedAlloc != null) { - updateAllocationStackTrace(selectedAlloc); - } else { - updateAllocationStackTrace(null); - } - } - } - - /** - * updates the stackcall of the specified allocation. If null the UI is emptied - * of current data. - * @param thread - */ - private void updateAllocationStackTrace(AllocationInfo alloc) { - mStackTracePanel.setViewerInput(alloc); - } - - @Override - protected void setTableFocusListener() { - addTableToFocusListener(mAllocationTable); - addTableToFocusListener(mStackTraceTable); - } - - /** - * Returns the current allocation selection or null if none is found. - * If a {@link ISelection} object is specified, the first {@link AllocationInfo} from this - * selection is returned, otherwise, the ISelection returned by - * {@link TableViewer#getSelection()} is used. - * @param selection the {@link ISelection} to use, or null - */ - private AllocationInfo getAllocationSelection(ISelection selection) { - if (selection == null) { - selection = mAllocationViewer.getSelection(); - } - - if (selection instanceof IStructuredSelection) { - IStructuredSelection structuredSelection = (IStructuredSelection)selection; - Object object = structuredSelection.getFirstElement(); - if (object instanceof AllocationInfo) { - return (AllocationInfo)object; - } - } - - return null; - } - - /** - * - * @param enabled - * @param trackingStatus - */ - private void setUpButtons(boolean enabled, AllocationTrackingStatus trackingStatus) { - if (enabled) { - switch (trackingStatus) { - case UNKNOWN: - mEnableButton.setText("?"); - mEnableButton.setEnabled(false); - mRequestButton.setEnabled(false); - break; - case OFF: - mEnableButton.setText("Start Tracking"); - mEnableButton.setEnabled(true); - mRequestButton.setEnabled(false); - break; - case ON: - mEnableButton.setText("Stop Tracking"); - mEnableButton.setEnabled(true); - mRequestButton.setEnabled(true); - break; - } - } else { - mEnableButton.setEnabled(false); - mRequestButton.setEnabled(false); - mEnableButton.setText("Start Tracking"); - } - } -} - diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BackgroundThread.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BackgroundThread.java deleted file mode 100644 index 61e6b85..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BackgroundThread.java +++ /dev/null @@ -1,50 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.Log; - -/** - * base background thread class. The class provides a synchronous quit method - * which sets a quitting flag to true. Inheriting classes should regularly test - * this flag with isQuitting() and should finish if the flag is - * true. - */ -public abstract class BackgroundThread extends Thread { - private boolean mQuit = false; - - /** - * Tell the thread to exit. This is usually called from the UI thread. The - * call is synchronous and will only return once the thread has terminated - * itself. - */ - public final void quit() { - mQuit = true; - Log.d("ddms", "Waiting for BackgroundThread to quit"); - try { - this.join(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - - /** returns if the thread was asked to quit. */ - protected final boolean isQuitting() { - return mQuit; - } - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BaseHeapPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BaseHeapPanel.java deleted file mode 100644 index 9ae883c..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/BaseHeapPanel.java +++ /dev/null @@ -1,193 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.HeapSegment; -import com.samsung.slp.common.connection.ddmlib.ClientData.HeapData; -import com.samsung.slp.common.connection.ddmlib.HeapSegment.HeapSegmentElement; - -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.PaletteData; - -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeMap; - - -/** - * Base Panel for heap panels. - */ -public abstract class BaseHeapPanel extends TablePanel { - - /** store the processed heap segment, so that we don't recompute Image for nothing */ - protected byte[] mProcessedHeapData; - private Map> mHeapMap; - - /** - * Serialize the heap data into an array. The resulting array is available through - * getSerializedData(). - * @param heapData The heap data to serialize - * @return true if the data changed. - */ - protected boolean serializeHeapData(HeapData heapData) { - Collection heapSegments; - - // Atomically get and clear the heap data. - synchronized (heapData) { - // get the segments - heapSegments = heapData.getHeapSegments(); - - - if (heapSegments != null) { - // if they are not null, we never processed them. - // Before we process then, we drop them from the HeapData - heapData.clearHeapData(); - - // process them into a linear byte[] - doSerializeHeapData(heapSegments); - heapData.setProcessedHeapData(mProcessedHeapData); - heapData.setProcessedHeapMap(mHeapMap); - - } else { - // the heap segments are null. Let see if the heapData contains a - // list that is already processed. - - byte[] pixData = heapData.getProcessedHeapData(); - - // and compare it to the one we currently have in the panel. - if (pixData == mProcessedHeapData) { - // looks like its the same - return false; - } else { - mProcessedHeapData = pixData; - } - - Map> heapMap = - heapData.getProcessedHeapMap(); - mHeapMap = heapMap; - } - } - - return true; - } - - /** - * Returns the serialized heap data - */ - protected byte[] getSerializedData() { - return mProcessedHeapData; - } - - /** - * Processes and serialize the heapData. - *

- * The resulting serialized array is {@link #mProcessedHeapData}. - *

- * the resulting map is {@link #mHeapMap}. - * @param heapData the collection of {@link HeapSegment} that forms the heap data. - */ - private void doSerializeHeapData(Collection heapData) { - mHeapMap = new TreeMap>(); - - Iterator iterator; - ByteArrayOutputStream out; - - out = new ByteArrayOutputStream(4 * 1024); - - iterator = heapData.iterator(); - while (iterator.hasNext()) { - HeapSegment hs = iterator.next(); - - HeapSegmentElement e = null; - while (true) { - int v; - - e = hs.getNextElement(null); - if (e == null) { - break; - } - - if (e.getSolidity() == HeapSegmentElement.SOLIDITY_FREE) { - v = 1; - } else { - v = e.getKind() + 2; - } - - // put the element in the map - ArrayList elementList = mHeapMap.get(v); - if (elementList == null) { - elementList = new ArrayList(); - mHeapMap.put(v, elementList); - } - elementList.add(e); - - - int len = e.getLength() / 8; - while (len > 0) { - out.write(v); - --len; - } - } - } - mProcessedHeapData = out.toByteArray(); - - // sort the segment element in the heap info. - Collection> elementLists = mHeapMap.values(); - for (ArrayList elementList : elementLists) { - Collections.sort(elementList); - } - } - - /** - * Creates a linear image of the heap data. - * @param pixData - * @param h - * @param palette - * @return - */ - protected ImageData createLinearHeapImage(byte[] pixData, int h, PaletteData palette) { - int w = pixData.length / h; - if (pixData.length % h != 0) { - w++; - } - - // Create the heap image. - ImageData id = new ImageData(w, h, 8, palette); - - int x = 0; - int y = 0; - for (byte b : pixData) { - if (b >= 0) { - id.setPixel(x, y, b); - } - - y++; - if (y >= h) { - y = 0; - x++; - } - } - - return id; - } - - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ClientDisplayPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ClientDisplayPanel.java deleted file mode 100644 index c2b126e..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ClientDisplayPanel.java +++ /dev/null @@ -1,33 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IClientChangeListener; - -public abstract class ClientDisplayPanel extends SelectionDependentPanel - implements IClientChangeListener { - - @Override - protected void postCreation() { - AndroidDebugBridge.addClientChangeListener(this); - } - - public void dispose() { - AndroidDebugBridge.removeClientChangeListener(this); - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/DevicePanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/DevicePanel.java deleted file mode 100644 index a7319f1..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/DevicePanel.java +++ /dev/null @@ -1,759 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge; -import com.samsung.slp.common.connection.ddmlib.Client; -import com.samsung.slp.common.connection.ddmlib.ClientData; -import com.samsung.slp.common.connection.ddmlib.DdmPreferences; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IDeviceChangeListener; -import com.samsung.slp.common.connection.ddmlib.ClientData.DebuggerStatus; -import com.samsung.slp.common.connection.ddmlib.IDevice.DeviceState; - -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.TreeSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; -import org.eclipse.swt.widgets.TreeItem; - -import java.util.ArrayList; - -/** - * A display of both the devices and their clients. - */ -public final class DevicePanel extends Panel implements IDebugBridgeChangeListener, - IDeviceChangeListener, IClientChangeListener { - - private final static String PREFS_COL_NAME_SERIAL = "devicePanel.Col0"; //$NON-NLS-1$ - private final static String PREFS_COL_PID_STATE = "devicePanel.Col1"; //$NON-NLS-1$ - private final static String PREFS_COL_PORT_BUILD = "devicePanel.Col4"; //$NON-NLS-1$ - - private final static int DEVICE_COL_SERIAL = 0; - private final static int DEVICE_COL_STATE = 1; - // col 2, 3 not used. - private final static int DEVICE_COL_BUILD = 4; - - private final static int CLIENT_COL_NAME = 0; - private final static int CLIENT_COL_PID = 1; - private final static int CLIENT_COL_THREAD = 2; - private final static int CLIENT_COL_HEAP = 3; - private final static int CLIENT_COL_PORT = 4; - - public final static int ICON_WIDTH = 16; - public final static String ICON_THREAD = "thread.png"; //$NON-NLS-1$ - public final static String ICON_HEAP = "heap.png"; //$NON-NLS-1$ - public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$ - public final static String ICON_GC = "gc.png"; //$NON-NLS-1$ - public final static String ICON_HPROF = "hprof.png"; //$NON-NLS-1$ - public final static String ICON_TRACING_START = "tracing_start.png"; //$NON-NLS-1$ - public final static String ICON_TRACING_STOP = "tracing_stop.png"; //$NON-NLS-1$ - - private IDevice mCurrentDevice; - private Client mCurrentClient; - - private Tree mTree; - private TreeViewer mTreeViewer; - - private Image mDeviceImage; - private Image mEmulatorImage; - - private Image mThreadImage; - private Image mHeapImage; - private Image mWaitingImage; - private Image mDebuggerImage; - private Image mDebugErrorImage; - - private final ArrayList mListeners = new ArrayList(); - - private final ArrayList mDevicesToExpand = new ArrayList(); - - private boolean mAdvancedPortSupport; - - /** - * A Content provider for the {@link TreeViewer}. - *

- * The input is a {@link AndroidDebugBridge}. First level elements are {@link IDevice} objects, - * and second level elements are {@link Client} object. - */ - private class ContentProvider implements ITreeContentProvider { - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof IDevice) { - return ((IDevice)parentElement).getClients(); - } - return new Object[0]; - } - - public Object getParent(Object element) { - if (element instanceof Client) { - return ((Client)element).getDevice(); - } - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof IDevice) { - return ((IDevice)element).hasClients(); - } - - // Clients never have children. - return false; - } - - public Object[] getElements(Object inputElement) { - if (inputElement instanceof AndroidDebugBridge) { - return ((AndroidDebugBridge)inputElement).getDevices(); - } - return new Object[0]; - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - } - - /** - * A Label Provider for the {@link TreeViewer} in {@link DevicePanel}. It provides - * labels and images for {@link IDevice} and {@link Client} objects. - */ - private class LabelProvider implements ITableLabelProvider { - - public Image getColumnImage(Object element, int columnIndex) { - if (columnIndex == DEVICE_COL_SERIAL && element instanceof IDevice) { - IDevice device = (IDevice)element; - if (device.isEmulator()) { - return mEmulatorImage; - } - - return mDeviceImage; - } else if (element instanceof Client) { - Client client = (Client)element; - ClientData cd = client.getClientData(); - - switch (columnIndex) { - case CLIENT_COL_NAME: - switch (cd.getDebuggerConnectionStatus()) { - case DEFAULT: - return null; - case WAITING: - return mWaitingImage; - case ATTACHED: - return mDebuggerImage; - case ERROR: - return mDebugErrorImage; - } - return null; - case CLIENT_COL_THREAD: - if (client.isThreadUpdateEnabled()) { - return mThreadImage; - } - return null; - case CLIENT_COL_HEAP: - if (client.isHeapUpdateEnabled()) { - return mHeapImage; - } - return null; - } - } - return null; - } - - public String getColumnText(Object element, int columnIndex) { - if (element instanceof IDevice) { - IDevice device = (IDevice)element; - switch (columnIndex) { - case DEVICE_COL_SERIAL: - return device.getSerialNumber(); - case DEVICE_COL_STATE: - return getStateString(device); - case DEVICE_COL_BUILD: { - String version = device.getProperty(IDevice.PROP_BUILD_VERSION); - if (version != null) { - String debuggable = device.getProperty(IDevice.PROP_DEBUGGABLE); - if (device.isEmulator()) { - String avdName = device.getAvdName(); - if (avdName == null) { - avdName = "?"; // the device is probably not online yet, so - // we don't know its AVD name just yet. - } - if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$ - return String.format("%1$s [%2$s, debug]", avdName, - version); - } else { - return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$ - } - } else { - if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$ - return String.format("%1$s, debug", version); - } else { - return String.format("%1$s", version); //$NON-NLS-1$ - } - } - } else { - return "unknown"; - } - } - } - } else if (element instanceof Client) { - Client client = (Client)element; - ClientData cd = client.getClientData(); - - switch (columnIndex) { - case CLIENT_COL_NAME: - String name = cd.getClientDescription(); - if (name != null) { - return name; - } - return "?"; - case CLIENT_COL_PID: - return Integer.toString(cd.getPid()); - case CLIENT_COL_PORT: - if (mAdvancedPortSupport) { - int port = client.getDebuggerListenPort(); - String portString = "?"; - if (port != 0) { - portString = Integer.toString(port); - } - if (client.isSelectedClient()) { - return String.format("%1$s / %2$d", portString, //$NON-NLS-1$ - DdmPreferences.getSelectedDebugPort()); - } - - return portString; - } - } - } - return null; - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public void dispose() { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - /** - * Classes which implement this interface provide methods that deals - * with {@link IDevice} and {@link Client} selection changes coming from the ui. - */ - public interface IUiSelectionListener { - /** - * Sent when a new {@link IDevice} and {@link Client} are selected. - * @param selectedDevice the selected device. If null, no devices are selected. - * @param selectedClient The selected client. If null, no clients are selected. - */ - public void selectionChanged(IDevice selectedDevice, Client selectedClient); - } - - /** - * Creates the {@link DevicePanel} object. - * @param loader - * @param advancedPortSupport if true the device panel will add support for selected client port - * and display the ports in the ui. - */ - public DevicePanel(boolean advancedPortSupport) { - mAdvancedPortSupport = advancedPortSupport; - } - - public void addSelectionListener(IUiSelectionListener listener) { - mListeners.add(listener); - } - - public void removeSelectionListener(IUiSelectionListener listener) { - mListeners.remove(listener); - } - - @Override - protected Control createControl(Composite parent) { - loadImages(parent.getDisplay()); - - parent.setLayout(new FillLayout()); - - // create the tree and its column - mTree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION); - mTree.setHeaderVisible(true); - mTree.setLinesVisible(true); - - IPreferenceStore store = DdmUiPreferences.getStore(); - - TableHelper.createTreeColumn(mTree, "Name", SWT.LEFT, - "com.android.home", //$NON-NLS-1$ - PREFS_COL_NAME_SERIAL, store); - TableHelper.createTreeColumn(mTree, "", SWT.LEFT, //$NON-NLS-1$ - "Offline", //$NON-NLS-1$ - PREFS_COL_PID_STATE, store); - - TreeColumn col = new TreeColumn(mTree, SWT.NONE); - col.setWidth(ICON_WIDTH + 8); - col.setResizable(false); - col = new TreeColumn(mTree, SWT.NONE); - col.setWidth(ICON_WIDTH + 8); - col.setResizable(false); - - TableHelper.createTreeColumn(mTree, "", SWT.LEFT, //$NON-NLS-1$ - "9999-9999", //$NON-NLS-1$ - PREFS_COL_PORT_BUILD, store); - - // create the tree viewer - mTreeViewer = new TreeViewer(mTree); - - // make the device auto expanded. - mTreeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); - - // set up the content and label providers. - mTreeViewer.setContentProvider(new ContentProvider()); - mTreeViewer.setLabelProvider(new LabelProvider()); - - mTree.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - notifyListeners(); - } - }); - - return mTree; - } - - /** - * Sets the focus to the proper control inside the panel. - */ - @Override - public void setFocus() { - mTree.setFocus(); - } - - @Override - protected void postCreation() { - // ask for notification of changes in AndroidDebugBridge (a new one is created when - // adb is restarted from a different location), IDevice and Client objects. - AndroidDebugBridge.addDebugBridgeChangeListener(this); - AndroidDebugBridge.addDeviceChangeListener(this); - AndroidDebugBridge.addClientChangeListener(this); - } - - public void dispose() { - AndroidDebugBridge.removeDebugBridgeChangeListener(this); - AndroidDebugBridge.removeDeviceChangeListener(this); - AndroidDebugBridge.removeClientChangeListener(this); - } - - /** - * Returns the selected {@link Client}. May be null. - */ - public Client getSelectedClient() { - return mCurrentClient; - } - - /** - * Returns the selected {@link IDevice}. If a {@link Client} is selected, it returns the - * IDevice object containing the client. - */ - public IDevice getSelectedDevice() { - return mCurrentDevice; - } - - /** - * Kills the selected {@link Client} by sending its VM a halt command. - */ - public void killSelectedClient() { - if (mCurrentClient != null) { - Client client = mCurrentClient; - - // reset the selection to the device. - TreePath treePath = new TreePath(new Object[] { mCurrentDevice }); - TreeSelection treeSelection = new TreeSelection(treePath); - mTreeViewer.setSelection(treeSelection); - - client.kill(); - } - } - - /** - * Forces a GC on the selected {@link Client}. - */ - public void forceGcOnSelectedClient() { - if (mCurrentClient != null) { - mCurrentClient.executeGarbageCollector(); - } - } - - public void dumpHprof() { - if (mCurrentClient != null) { - mCurrentClient.dumpHprof(); - } - } - - public void toggleMethodProfiling() { - if (mCurrentClient != null) { - mCurrentClient.toggleMethodProfiling(); - } - } - - public void setEnabledHeapOnSelectedClient(boolean enable) { - if (mCurrentClient != null) { - mCurrentClient.setHeapUpdateEnabled(enable); - } - } - - public void setEnabledThreadOnSelectedClient(boolean enable) { - if (mCurrentClient != null) { - mCurrentClient.setThreadUpdateEnabled(enable); - } - } - - /** - * Sent when a new {@link AndroidDebugBridge} is started. - *

- * This is sent from a non UI thread. - * @param bridge the new {@link AndroidDebugBridge} object. - * - * @see IDebugBridgeChangeListener#serverChanged(AndroidDebugBridge) - */ - public void bridgeChanged(final AndroidDebugBridge bridge) { - if (mTree.isDisposed() == false) { - exec(new Runnable() { - public void run() { - if (mTree.isDisposed() == false) { - // set up the data source. - mTreeViewer.setInput(bridge); - - // notify the listener of a possible selection change. - notifyListeners(); - } else { - // tree is disposed, we need to do something. - // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); - AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); - AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); - } - } - }); - } - - // all current devices are obsolete - synchronized (mDevicesToExpand) { - mDevicesToExpand.clear(); - } - } - - /** - * Sent when the a device is connected to the {@link AndroidDebugBridge}. - *

- * This is sent from a non UI thread. - * @param device the new device. - * - * @see IDeviceChangeListener#deviceConnected(IDevice) - */ - public void deviceConnected(IDevice device) { - exec(new Runnable() { - public void run() { - if (mTree.isDisposed() == false) { - // refresh all - mTreeViewer.refresh(); - - // notify the listener of a possible selection change. - notifyListeners(); - } else { - // tree is disposed, we need to do something. - // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); - AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); - AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); - } - } - }); - - // if it doesn't have clients yet, it'll need to be manually expanded when it gets them. - if (device.hasClients() == false) { - synchronized (mDevicesToExpand) { - mDevicesToExpand.add(device); - } - } - } - - /** - * Sent when the a device is connected to the {@link AndroidDebugBridge}. - *

- * This is sent from a non UI thread. - * @param device the new device. - * - * @see IDeviceChangeListener#deviceDisconnected(IDevice) - */ - public void deviceDisconnected(IDevice device) { - deviceConnected(device); - - // just in case, we remove it from the list of devices to expand. - synchronized (mDevicesToExpand) { - mDevicesToExpand.remove(device); - } - } - - /** - * Sent when a device data changed, or when clients are started/terminated on the device. - *

- * This is sent from a non UI thread. - * @param device the device that was updated. - * @param changeMask the mask indicating what changed. - * - * @see IDeviceChangeListener#deviceChanged(IDevice) - */ - public void deviceChanged(final IDevice device, int changeMask) { - boolean expand = false; - synchronized (mDevicesToExpand) { - int index = mDevicesToExpand.indexOf(device); - if (device.hasClients() && index != -1) { - mDevicesToExpand.remove(index); - expand = true; - } - } - - final boolean finalExpand = expand; - - exec(new Runnable() { - public void run() { - if (mTree.isDisposed() == false) { - // look if the current device is selected. This is done in case the current - // client of this particular device was killed. In this case, we'll need to - // manually reselect the device. - - IDevice selectedDevice = getSelectedDevice(); - - // refresh the device - mTreeViewer.refresh(device); - - // if the selected device was the changed device and the new selection is - // empty, we reselect the device. - if (selectedDevice == device && mTreeViewer.getSelection().isEmpty()) { - mTreeViewer.setSelection(new TreeSelection(new TreePath( - new Object[] { device }))); - } - - // notify the listener of a possible selection change. - notifyListeners(); - - if (finalExpand) { - mTreeViewer.setExpandedState(device, true); - } - } else { - // tree is disposed, we need to do something. - // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); - AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); - AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); - } - } - }); - } - - /** - * Sent when an existing client information changed. - *

- * 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} - * - * @see IClientChangeListener#clientChanged(Client, int) - */ - public void clientChanged(final Client client, final int changeMask) { - exec(new Runnable() { - public void run() { - if (mTree.isDisposed() == false) { - // refresh the client - mTreeViewer.refresh(client); - - if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == - Client.CHANGE_DEBUGGER_STATUS && - client.getClientData().getDebuggerConnectionStatus() == - DebuggerStatus.WAITING) { - // make sure the device is expanded. Normally the setSelection below - // will auto expand, but the children of device may not already exist - // at this time. Forcing an expand will make the TreeViewer create them. - IDevice device = client.getDevice(); - if (mTreeViewer.getExpandedState(device) == false) { - mTreeViewer.setExpandedState(device, true); - } - - // create and set the selection - TreePath treePath = new TreePath(new Object[] { device, client}); - TreeSelection treeSelection = new TreeSelection(treePath); - mTreeViewer.setSelection(treeSelection); - - if (mAdvancedPortSupport) { - client.setAsSelectedClient(); - } - - // notify the listener of a possible selection change. - notifyListeners(device, client); - } - } else { - // tree is disposed, we need to do something. - // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); - AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); - AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); - } - } - }); - } - - private void loadImages(Display display) { - ImageLoader loader = ImageLoader.getDdmUiLibLoader(); - - if (mDeviceImage == null) { - mDeviceImage = loader.loadImage(display, "device.png", //$NON-NLS-1$ - ICON_WIDTH, ICON_WIDTH, - display.getSystemColor(SWT.COLOR_RED)); - } - if (mEmulatorImage == null) { - mEmulatorImage = loader.loadImage(display, - "emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ - display.getSystemColor(SWT.COLOR_BLUE)); - } - if (mThreadImage == null) { - mThreadImage = loader.loadImage(display, ICON_THREAD, - ICON_WIDTH, ICON_WIDTH, - display.getSystemColor(SWT.COLOR_YELLOW)); - } - if (mHeapImage == null) { - mHeapImage = loader.loadImage(display, ICON_HEAP, - ICON_WIDTH, ICON_WIDTH, - display.getSystemColor(SWT.COLOR_BLUE)); - } - if (mWaitingImage == null) { - mWaitingImage = loader.loadImage(display, - "debug-wait.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ - display.getSystemColor(SWT.COLOR_RED)); - } - if (mDebuggerImage == null) { - mDebuggerImage = loader.loadImage(display, - "debug-attach.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ - display.getSystemColor(SWT.COLOR_GREEN)); - } - if (mDebugErrorImage == null) { - mDebugErrorImage = loader.loadImage(display, - "debug-error.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ - display.getSystemColor(SWT.COLOR_RED)); - } - } - - /** - * Returns a display string representing the state of the device. - * @param d the device - */ - private static String getStateString(IDevice d) { - DeviceState deviceState = d.getState(); - if (deviceState == DeviceState.ONLINE) { - return "Online"; - } else if (deviceState == DeviceState.OFFLINE) { - return "Offline"; - } else if (deviceState == DeviceState.BOOTLOADER) { - return "Bootloader"; - } - - return "??"; - } - - /** - * Executes the {@link Runnable} in the UI thread. - * @param runnable the runnable to execute. - */ - private void exec(Runnable runnable) { - try { - Display display = mTree.getDisplay(); - display.asyncExec(runnable); - } catch (SWTException e) { - // tree is disposed, we need to do something. lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(this); - AndroidDebugBridge.removeDeviceChangeListener(this); - AndroidDebugBridge.removeClientChangeListener(this); - } - } - - private void notifyListeners() { - // get the selection - TreeItem[] items = mTree.getSelection(); - - Client client = null; - IDevice device = null; - - if (items.length == 1) { - Object object = items[0].getData(); - if (object instanceof Client) { - client = (Client)object; - device = client.getDevice(); - } else if (object instanceof IDevice) { - device = (IDevice)object; - } - } - - notifyListeners(device, client); - } - - private void notifyListeners(IDevice selectedDevice, Client selectedClient) { - if (selectedDevice != mCurrentDevice || selectedClient != mCurrentClient) { - mCurrentDevice = selectedDevice; - mCurrentClient = selectedClient; - - for (IUiSelectionListener listener : mListeners) { - // notify the listener with a try/catch-all to make sure this thread won't die - // because of an uncaught exception before all the listeners were notified. - try { - listener.selectionChanged(selectedDevice, selectedClient); - } catch (Exception e) { - } - } - } - } - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ImageLoader.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ImageLoader.java index 3c5e49d..d2606b2 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ImageLoader.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ImageLoader.java @@ -16,7 +16,7 @@ package com.samsung.slp.common.connection.ddmuilib; -import com.samsung.slp.common.connection.ddmlib.Log; +//import com.samsung.slp.common.connection.ddmlib.Log; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; @@ -177,7 +177,7 @@ public class ImageLoader { Image img = loadImage(fileName, display); if (img == null) { - Log.w("ddms", "Couldn't load " + fileName); +// Log.w("ddms", "Couldn't load " + fileName); // if we had the extra parameter to create replacement image then we // create and return it. if (width != -1 && height != -1) { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ScreenShotDialog.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ScreenShotDialog.java deleted file mode 100644 index 472bdea..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/ScreenShotDialog.java +++ /dev/null @@ -1,309 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.Log; -import com.samsung.slp.common.connection.ddmlib.RawImage; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.ImageTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.PaletteData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Dialog; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -import java.io.IOException; - - -/** - * Gather a screen shot from the device and save it to a file. - */ -public class ScreenShotDialog extends Dialog { - - private Label mBusyLabel; - private Label mImageLabel; - private Button mSave; - private IDevice mDevice; - private RawImage mRawImage; - private Clipboard mClipboard; - - - /** - * Create with default style. - */ - public ScreenShotDialog(Shell parent) { - this(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); - mClipboard = new Clipboard(parent.getDisplay()); - } - - /** - * Create with app-defined style. - */ - public ScreenShotDialog(Shell parent, int style) { - super(parent, style); - } - - /** - * Prepare and display the dialog. - * @param device The {@link IDevice} from which to get the screenshot. - */ - public void open(IDevice device) { - mDevice = device; - - Shell parent = getParent(); - Shell shell = new Shell(parent, getStyle()); - shell.setText("Device Screen Capture"); - - createContents(shell); - shell.pack(); - shell.open(); - - updateDeviceImage(shell); - - Display display = parent.getDisplay(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - - } - - /* - * Create the screen capture dialog contents. - */ - private void createContents(final Shell shell) { - GridData data; - - final int colCount = 5; - - shell.setLayout(new GridLayout(colCount, true)); - - // "refresh" button - Button refresh = new Button(shell, SWT.PUSH); - refresh.setText("Refresh"); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.widthHint = 80; - refresh.setLayoutData(data); - refresh.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - updateDeviceImage(shell); - } - }); - - // "rotate" button - Button rotate = new Button(shell, SWT.PUSH); - rotate.setText("Rotate"); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.widthHint = 80; - rotate.setLayoutData(data); - rotate.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - if (mRawImage != null) { - mRawImage = mRawImage.getRotated(); - updateImageDisplay(shell); - } - } - }); - - // "save" button - mSave = new Button(shell, SWT.PUSH); - mSave.setText("Save"); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.widthHint = 80; - mSave.setLayoutData(data); - mSave.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - saveImage(shell); - } - }); - - Button copy = new Button(shell, SWT.PUSH); - copy.setText("Copy"); - copy.setToolTipText("Copy the screenshot to the clipboard"); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.widthHint = 80; - copy.setLayoutData(data); - copy.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - copy(); - } - }); - - - // "done" button - Button done = new Button(shell, SWT.PUSH); - done.setText("Done"); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.widthHint = 80; - done.setLayoutData(data); - done.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - shell.close(); - } - }); - - // title/"capturing" label - mBusyLabel = new Label(shell, SWT.NONE); - mBusyLabel.setText("Preparing..."); - data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); - data.horizontalSpan = colCount; - mBusyLabel.setLayoutData(data); - - // space for the image - mImageLabel = new Label(shell, SWT.BORDER); - data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); - data.horizontalSpan = colCount; - mImageLabel.setLayoutData(data); - Display display = shell.getDisplay(); - mImageLabel.setImage(ImageLoader.createPlaceHolderArt( - display, 50, 50, display.getSystemColor(SWT.COLOR_BLUE))); - - - shell.setDefaultButton(done); - } - - /** - * Copies the content of {@link #mImageLabel} to the clipboard. - */ - private void copy() { - mClipboard.setContents( - new Object[] { - mImageLabel.getImage().getImageData() - }, new Transfer[] { - ImageTransfer.getInstance() - }); - } - - /** - * Captures a new image from the device, and display it. - */ - private void updateDeviceImage(Shell shell) { - mBusyLabel.setText("Capturing..."); // no effect - - shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT)); - - mRawImage = getDeviceImage(); - - updateImageDisplay(shell); - } - - /** - * Updates the display with {@link #mRawImage}. - * @param shell - */ - private void updateImageDisplay(Shell shell) { - Image image; - if (mRawImage == null) { - Display display = shell.getDisplay(); - image = ImageLoader.createPlaceHolderArt( - display, 320, 240, display.getSystemColor(SWT.COLOR_BLUE)); - - mSave.setEnabled(false); - mBusyLabel.setText("Screen not available"); - } else { - // convert raw data to an Image. - PaletteData palette = new PaletteData( - mRawImage.getRedMask(), - mRawImage.getGreenMask(), - mRawImage.getBlueMask()); - - ImageData imageData = new ImageData(mRawImage.width, mRawImage.height, - mRawImage.bpp, palette, 1, mRawImage.data); - image = new Image(getParent().getDisplay(), imageData); - - mSave.setEnabled(true); - mBusyLabel.setText("Captured image:"); - } - - mImageLabel.setImage(image); - mImageLabel.pack(); - shell.pack(); - - // there's no way to restore old cursor; assume it's ARROW - shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); - } - - /** - * Grabs an image from an ADB-connected device and returns it as a {@link RawImage}. - */ - private RawImage getDeviceImage() { - try { - return mDevice.getScreenshot(); - } - catch (IOException ioe) { - Log.w("ddms", "Unable to get frame buffer: " + ioe.getMessage()); - return null; - } - } - - /* - * Prompt the user to save the image to disk. - */ - private void saveImage(Shell shell) { - FileDialog dlg = new FileDialog(shell, SWT.SAVE); - String fileName; - - dlg.setText("Save image..."); - dlg.setFileName("device.png"); - dlg.setFilterPath(DdmUiPreferences.getStore().getString("lastImageSaveDir")); - dlg.setFilterNames(new String[] { - "PNG Files (*.png)" - }); - dlg.setFilterExtensions(new String[] { - "*.png" //$NON-NLS-1$ - }); - - fileName = dlg.open(); - if (fileName != null) { - DdmUiPreferences.getStore().setValue("lastImageSaveDir", dlg.getFilterPath()); - - Log.d("ddms", "Saving image to " + fileName); - ImageData imageData = mImageLabel.getImage().getImageData(); - - try { - org.eclipse.swt.graphics.ImageLoader loader = - new org.eclipse.swt.graphics.ImageLoader(); - - loader.data = new ImageData[] { imageData }; - loader.save(fileName, SWT.IMAGE_PNG); - } - catch (SWTException e) { - Log.w("ddms", "Unable to save " + fileName + ": " + e.getMessage()); - } - } - } - -} - diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SelectionDependentPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SelectionDependentPanel.java deleted file mode 100644 index 58590a0..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SelectionDependentPanel.java +++ /dev/null @@ -1,78 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.Client; -import com.samsung.slp.common.connection.ddmlib.IDevice; - -/** - * A Panel that requires {@link Device}/{@link Client} selection notifications. - */ -public abstract class SelectionDependentPanel extends Panel { - private IDevice mCurrentDevice = null; - private Client mCurrentClient = null; - - /** - * Returns the current {@link Device}. - * @return the current device or null if none are selected. - */ - protected final IDevice getCurrentDevice() { - return mCurrentDevice; - } - - /** - * Returns the current {@link Client}. - * @return the current client or null if none are selected. - */ - protected final Client getCurrentClient() { - return mCurrentClient; - } - - /** - * Sent when a new device is selected. - * @param selectedDevice the selected device. - */ - public final void deviceSelected(IDevice selectedDevice) { - if (selectedDevice != mCurrentDevice) { - mCurrentDevice = selectedDevice; - deviceSelected(); - } - } - - /** - * Sent when a new client is selected. - * @param selectedClient the selected client. - */ - public final void clientSelected(Client selectedClient) { - if (selectedClient != mCurrentClient) { - mCurrentClient = selectedClient; - clientSelected(); - } - } - - /** - * Sent when a new device is selected. The new device can be accessed - * with {@link #getCurrentDevice()}. - */ - public abstract void deviceSelected(); - - /** - * Sent when a new client is selected. The new client can be accessed - * with {@link #getCurrentClient()}. - */ - public abstract void clientSelected(); -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/StackTracePanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/StackTracePanel.java deleted file mode 100644 index 7f1ae87..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/StackTracePanel.java +++ /dev/null @@ -1,258 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmlib.Client; -import com.samsung.slp.common.connection.ddmlib.IStackTraceInfo; - -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Table; - -/** - * Stack Trace Panel. - *

This is not a panel in the regular sense. Instead this is just an object around the creation - * and management of a Stack Trace display. - *

UI creation is done through - * {@link #createPanel(Composite, String, String, String, String, String, IPreferenceStore)}. - * - */ -public final class StackTracePanel { - - private static ISourceRevealer sSourceRevealer; - - private Table mStackTraceTable; - private TableViewer mStackTraceViewer; - - private Client mCurrentClient; - - - /** - * Content Provider to display the stack trace of a thread. - * Expected input is a {@link IStackTraceInfo} object. - */ - private static class StackTraceContentProvider implements IStructuredContentProvider { - public Object[] getElements(Object inputElement) { - if (inputElement instanceof IStackTraceInfo) { - // getElement cannot return null, so we return an empty array - // if there's no stack trace - StackTraceElement trace[] = ((IStackTraceInfo)inputElement).getStackTrace(); - if (trace != null) { - return trace; - } - } - - return new Object[0]; - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - } - - - /** - * A Label Provider to use with {@link StackTraceContentProvider}. It expects the elements to be - * of type {@link StackTraceElement}. - */ - private static class StackTraceLabelProvider implements ITableLabelProvider { - - public Image getColumnImage(Object element, int columnIndex) { - return null; - } - - public String getColumnText(Object element, int columnIndex) { - if (element instanceof StackTraceElement) { - StackTraceElement traceElement = (StackTraceElement)element; - switch (columnIndex) { - case 0: - return traceElement.getClassName(); - case 1: - return traceElement.getMethodName(); - case 2: - return traceElement.getFileName(); - case 3: - return Integer.toString(traceElement.getLineNumber()); - case 4: - return Boolean.toString(traceElement.isNativeMethod()); - } - } - - return null; - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public void dispose() { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - /** - * Classes which implement this interface provide a method that is able to reveal a method - * in a source editor - */ - public interface ISourceRevealer { - /** - * Sent to reveal a particular line in a source editor - * @param applicationName the name of the application running the source. - * @param className the fully qualified class name - * @param line the line to reveal - */ - public void reveal(String applicationName, String className, int line); - } - - - /** - * Sets the {@link ISourceRevealer} object able to reveal source code in a source editor. - * @param revealer - */ - public static void setSourceRevealer(ISourceRevealer revealer) { - sSourceRevealer = revealer; - } - - /** - * Creates the controls for the StrackTrace display. - *

This method will set the parent {@link Composite} to use a {@link GridLayout} with - * 2 columns. - * @param parent the parent composite. - * @param prefs_stack_col_class - * @param prefs_stack_col_method - * @param prefs_stack_col_file - * @param prefs_stack_col_line - * @param prefs_stack_col_native - * @param store - */ - public Table createPanel(Composite parent, String prefs_stack_col_class, - String prefs_stack_col_method, String prefs_stack_col_file, String prefs_stack_col_line, - String prefs_stack_col_native, IPreferenceStore store) { - - mStackTraceTable = new Table(parent, SWT.MULTI | SWT.FULL_SELECTION); - mStackTraceTable.setHeaderVisible(true); - mStackTraceTable.setLinesVisible(true); - - TableHelper.createTableColumn( - mStackTraceTable, - "Class", - SWT.LEFT, - "SomeLongClassName", //$NON-NLS-1$ - prefs_stack_col_class, store); - - TableHelper.createTableColumn( - mStackTraceTable, - "Method", - SWT.LEFT, - "someLongMethod", //$NON-NLS-1$ - prefs_stack_col_method, store); - - TableHelper.createTableColumn( - mStackTraceTable, - "File", - SWT.LEFT, - "android/somepackage/someotherpackage/somefile.class", //$NON-NLS-1$ - prefs_stack_col_file, store); - - TableHelper.createTableColumn( - mStackTraceTable, - "Line", - SWT.RIGHT, - "99999", //$NON-NLS-1$ - prefs_stack_col_line, store); - - TableHelper.createTableColumn( - mStackTraceTable, - "Native", - SWT.LEFT, - "Native", //$NON-NLS-1$ - prefs_stack_col_native, store); - - mStackTraceViewer = new TableViewer(mStackTraceTable); - mStackTraceViewer.setContentProvider(new StackTraceContentProvider()); - mStackTraceViewer.setLabelProvider(new StackTraceLabelProvider()); - - mStackTraceViewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - if (sSourceRevealer != null && mCurrentClient != null) { - // get the selected stack trace element - ISelection selection = mStackTraceViewer.getSelection(); - - if (selection instanceof IStructuredSelection) { - IStructuredSelection structuredSelection = (IStructuredSelection)selection; - Object object = structuredSelection.getFirstElement(); - if (object instanceof StackTraceElement) { - StackTraceElement traceElement = (StackTraceElement)object; - - if (traceElement.isNativeMethod() == false) { - sSourceRevealer.reveal( - mCurrentClient.getClientData().getClientDescription(), - traceElement.getClassName(), - traceElement.getLineNumber()); - } - } - } - } - } - }); - - return mStackTraceTable; - } - - /** - * Sets the input for the {@link TableViewer}. - * @param input the {@link IStackTraceInfo} that will provide the viewer with the list of - * {@link StackTraceElement} - */ - public void setViewerInput(IStackTraceInfo input) { - mStackTraceViewer.setInput(input); - mStackTraceViewer.refresh(); - } - - /** - * Sets the current client running the stack trace. - * @param currentClient the {@link Client}. - */ - public void setCurrentClient(Client currentClient) { - mCurrentClient = currentClient; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SyncProgressMonitor.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SyncProgressMonitor.java index dfb029e..19eb4c0 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SyncProgressMonitor.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/SyncProgressMonitor.java @@ -16,10 +16,10 @@ package com.samsung.slp.common.connection.ddmuilib; -import com.samsung.slp.common.connection.ddmlib.SyncService.ISyncProgressMonitor; - import org.eclipse.core.runtime.IProgressMonitor; +import com.samsung.slp.sdblib.SyncService.ISyncProgressMonitor; + /** * Implementation of the {@link ISyncProgressMonitor} wrapping an Eclipse {@link IProgressMonitor}. */ diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/TablePanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/TablePanel.java deleted file mode 100644 index e1768fc..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/TablePanel.java +++ /dev/null @@ -1,128 +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.samsung.slp.common.connection.ddmuilib; - -import com.samsung.slp.common.connection.ddmuilib.ITableFocusListener.IFocusedTableActivator; - -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableItem; - -import java.util.Arrays; - -/** - * Base class for panel containing Table that need to support copy-paste-selectAll - */ -public abstract class TablePanel extends ClientDisplayPanel { - private ITableFocusListener mGlobalListener; - - /** - * Sets a TableFocusListener which will be notified when one of the tables - * gets or loses focus. - * - * @param listener - */ - public final void setTableFocusListener(ITableFocusListener listener) { - // record the global listener, to make sure table created after - // this call will still be setup. - mGlobalListener = listener; - - setTableFocusListener(); - } - - /** - * Sets up the Table of object of the panel to work with the global listener.
- * Default implementation does nothing. - */ - protected void setTableFocusListener() { - - } - - /** - * Sets up a Table object to notify the global Table Focus listener when it - * gets or loses the focus. - * - * @param table the Table object. - * @param colStart - * @param colEnd - */ - protected final void addTableToFocusListener(final Table table, - final int colStart, final int colEnd) { - // create the activator for this table - final IFocusedTableActivator activator = new IFocusedTableActivator() { - public void copy(Clipboard clipboard) { - int[] selection = table.getSelectionIndices(); - - // we need to sort the items to be sure. - Arrays.sort(selection); - - // all lines must be concatenated. - StringBuilder sb = new StringBuilder(); - - // loop on the selection and output the file. - for (int i : selection) { - TableItem item = table.getItem(i); - for (int c = colStart ; c <= colEnd ; c++) { - sb.append(item.getText(c)); - sb.append('\t'); - } - sb.append('\n'); - } - - // now add that to the clipboard if the string has content - String data = sb.toString(); - if (data != null || data.length() > 0) { - clipboard.setContents( - new Object[] { data }, - new Transfer[] { TextTransfer.getInstance() }); - } - } - - public void selectAll() { - table.selectAll(); - } - }; - - // add the focus listener on the table to notify the global listener - table.addFocusListener(new FocusListener() { - public void focusGained(FocusEvent e) { - mGlobalListener.focusGained(activator); - } - - public void focusLost(FocusEvent e) { - mGlobalListener.focusLost(activator); - } - }); - } - - /** - * Sets up a Table object to notify the global Table Focus listener when it - * gets or loses the focus.
- * When the copy method is invoked, all columns are put in the clipboard, separated - * by tabs - * - * @param table the Table object. - */ - protected final void addTableToFocusListener(final Table table) { - addTableToFocusListener(table, 0, table.getColumnCount()-1); - } - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceContentProvider.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceContentProvider.java deleted file mode 100644 index f7453ee..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceContentProvider.java +++ /dev/null @@ -1,172 +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.samsung.slp.common.connection.ddmuilib.explorer; - -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.FileListingService.IListingReceiver; - -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Tree; - -/** - * Content provider class for device Explorer. - */ -public class DeviceContentProvider implements ITreeContentProvider { - - private TreeViewer mViewer; - private FileListingService mFileListingService; - private FileEntry mRootEntry; - - private IListingReceiver sListingReceiver = new IListingReceiver() { - public void setChildren(final FileEntry entry, FileEntry[] children) { - final Tree t = mViewer.getTree(); - if (t != null && t.isDisposed() == false) { - Display display = t.getDisplay(); - if (display.isDisposed() == false) { - display.asyncExec(new Runnable() { - public void run() { - if (t.isDisposed() == false) { - // refresh the entry. - mViewer.refresh(entry); - - // force it open, since on linux and windows - // when getChildren() returns null, the node is - // not considered expanded. - mViewer.setExpandedState(entry, true); - } - } - }); - } - } - } - - public void refreshEntry(final FileEntry entry) { - final Tree t = mViewer.getTree(); - if (t != null && t.isDisposed() == false) { - Display display = t.getDisplay(); - if (display.isDisposed() == false) { - display.asyncExec(new Runnable() { - public void run() { - if (t.isDisposed() == false) { - // refresh the entry. - mViewer.refresh(entry); - } - } - }); - } - } - } - }; - - /** - * - */ - public DeviceContentProvider() { - } - - public void setListingService(FileListingService fls) { - mFileListingService = fls; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) - */ - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof FileEntry) { - FileEntry parentEntry = (FileEntry)parentElement; - - Object[] oldEntries = parentEntry.getCachedChildren(); - Object[] newEntries = mFileListingService.getChildren(parentEntry, - true, sListingReceiver); - - if (newEntries != null) { - return newEntries; - } else { - // if null was returned, this means the cache was not valid, - // and a thread was launched for ls. sListingReceiver will be - // notified with the new entries. - return oldEntries; - } - } - return new Object[0]; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) - */ - public Object getParent(Object element) { - if (element instanceof FileEntry) { - FileEntry entry = (FileEntry)element; - - return entry.getParent(); - } - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) - */ - public boolean hasChildren(Object element) { - if (element instanceof FileEntry) { - FileEntry entry = (FileEntry)element; - if (entry.getType() == FileListingService.TYPE_LINK) - return true; - else - return (entry.getType() == FileListingService.TYPE_DIRECTORY); - } - return false; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) - */ - public Object[] getElements(Object inputElement) { - if (inputElement instanceof FileEntry) { - FileEntry entry = (FileEntry)inputElement; - if (entry.isRoot()) { - return getChildren(mRootEntry); - } - } - - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IContentProvider#dispose() - */ - public void dispose() { - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) - */ - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - if (viewer instanceof TreeViewer) { - mViewer = (TreeViewer)viewer; - } - if (newInput instanceof FileEntry) { - mRootEntry = (FileEntry)newInput; - } - } - public void setRootEntry(FileEntry fe) { - mRootEntry = fe; - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceExplorer.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceExplorer.java deleted file mode 100644 index 3a8a461..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/DeviceExplorer.java +++ /dev/null @@ -1,888 +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.samsung.slp.common.connection.ddmuilib.explorer; - -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.IShellOutputReceiver; -import com.samsung.slp.common.connection.ddmlib.SyncService; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.SyncService.ISyncProgressMonitor; -import com.samsung.slp.common.connection.ddmlib.SyncService.SyncResult; -import com.samsung.slp.common.connection.ddmuilib.DdmUiPreferences; -import com.samsung.slp.common.connection.ddmuilib.ImageLoader; -import com.samsung.slp.common.connection.ddmuilib.Panel; -import com.samsung.slp.common.connection.ddmuilib.SyncProgressMonitor; -import com.samsung.slp.common.connection.ddmuilib.TableHelper; -import com.samsung.slp.common.connection.ddmuilib.actions.ICommonAction; -import com.samsung.slp.common.connection.ddmuilib.console.DdmConsole; -import com.samsung.slp.common.connection.ui.ConnectionExplorerContentProvider; -import com.samsung.slp.common.connection.ui.ConnectionExplorerLabelProvider; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jface.dialogs.ProgressMonitorDialog; -import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.FileTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.DirectoryDialog; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Device filesystem explorer class. - */ -public class DeviceExplorer extends Panel { - - private final static String TRACE_KEY_EXT = ".key"; // $NON-NLS-1S - private final static String TRACE_DATA_EXT = ".data"; // $NON-NLS-1S - - private static Pattern mKeyFilePattern = Pattern.compile("(.+)\\" - + TRACE_KEY_EXT); // $NON-NLS-1S - private static Pattern mDataFilePattern = Pattern.compile("(.+)\\" - + TRACE_DATA_EXT); // $NON-NLS-1S - - public static String COLUMN_NAME = "android.explorer.name"; //$NON-NLS-1S - public static String COLUMN_SIZE = "android.explorer.size"; //$NON-NLS-1S - public static String COLUMN_DATE = "android.explorer.data"; //$NON-NLS-1S - public static String COLUMN_TIME = "android.explorer.time"; //$NON-NLS-1S - public static String COLUMN_PERMISSIONS = "android.explorer.permissions"; // $NON-NLS-1S - public static String COLUMN_INFO = "android.explorer.info"; // $NON-NLS-1S - - private Composite mParent; - private TreeViewer mTreeViewer; - private Tree mTree; - private ConnectionExplorerContentProvider mContentProvider; - - private ICommonAction mPushAction; - private ICommonAction mPullAction; - private ICommonAction mDeleteAction; - - private Image mFileImage; - private Image mFolderImage; - private Image mPackageImage; - private Image mOtherImage; - - private IDevice mCurrentDevice; - - private String mDefaultSave; - - public DeviceExplorer() { - } - - /** - * Sets custom images for the device explorer. If none are set then defaults - * are used. This can be useful to set platform-specific explorer icons. - * - * This should be called before {@link #createControl(Composite)}. - * - * @param fileImage - * the icon to represent a file. - * @param folderImage - * the icon to represent a folder. - * @param packageImage - * the icon to represent an apk. - * @param otherImage - * the icon to represent other types of files. - */ - public void setCustomImages(Image fileImage, Image folderImage, - Image packageImage, Image otherImage) { - mFileImage = fileImage; - mFolderImage = folderImage; - mPackageImage = packageImage; - mOtherImage = otherImage; - } - - /** - * Sets the actions so that the device explorer can enable/disable them - * based on the current selection - * - * @param pushAction - * @param pullAction - * @param deleteAction - */ - public void setActions(ICommonAction pushAction, ICommonAction pullAction, - ICommonAction deleteAction) { - mPushAction = pushAction; - mPullAction = pullAction; - mDeleteAction = deleteAction; - } - - /** - * Creates a control capable of displaying some information. This is called - * once, when the application is initializing, from the UI thread. - */ - @Override - protected Control createControl(Composite parent) { - mParent = parent; - parent.setLayout(new FillLayout()); - - ImageLoader loader = ImageLoader.getDdmUiLibLoader(); - if (mFileImage == null) { - mFileImage = loader.loadImage("file.gif", mParent.getDisplay()); - } - if (mFolderImage == null) { - mFolderImage = loader.loadImage("folder.gif", mParent.getDisplay()); - } - if (mPackageImage == null) { - mPackageImage = loader.loadImage("android.png", - mParent.getDisplay()); - } - if (mOtherImage == null) { - // TODO: find a default image for other. - } - - mTree = new Tree(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL); - mTree.setHeaderVisible(true); - - IPreferenceStore store = DdmUiPreferences.getStore(); - - // create columns - TableHelper.createTreeColumn(mTree, "Name", SWT.LEFT, - "0000drwxrwxrwx", COLUMN_NAME, store); //$NON-NLS-1$ - TableHelper.createTreeColumn(mTree, "Size", SWT.RIGHT, - "000000", COLUMN_SIZE, store); //$NON-NLS-1$ - TableHelper.createTreeColumn(mTree, "Date", SWT.LEFT, - "2007-08-14", COLUMN_DATE, store); //$NON-NLS-1$ - TableHelper.createTreeColumn(mTree, "Time", SWT.LEFT, - "20:54", COLUMN_TIME, store); //$NON-NLS-1$ - TableHelper.createTreeColumn(mTree, "Permissions", SWT.LEFT, - "drwxrwxrwx", COLUMN_PERMISSIONS, store); //$NON-NLS-1$ - TableHelper.createTreeColumn(mTree, "Info", SWT.LEFT, - "drwxrwxrwx", COLUMN_INFO, store); //$NON-NLS-1$ - - // create the jface wrapper - mTreeViewer = new TreeViewer(mTree); - - // setup data provider - mContentProvider = new ConnectionExplorerContentProvider(); - mTreeViewer.setContentProvider(mContentProvider); -// mTreeViewer.setLabelProvider(new ConnectionExplorerLabelProvider(mFileImage, -// mFolderImage, mPackageImage, mOtherImage)); - - // setup a listener for selection - mTreeViewer - .addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - ISelection sel = event.getSelection(); - if (sel.isEmpty()) { - mPullAction.setEnabled(false); - mPushAction.setEnabled(false); - mDeleteAction.setEnabled(false); - return; - } - if (sel instanceof IStructuredSelection) { - IStructuredSelection selection = (IStructuredSelection) sel; - Object element = selection.getFirstElement(); - if (element == null) - return; - if (element instanceof FileEntry) { - mPullAction.setEnabled(true); - mPushAction.setEnabled(selection.size() == 1); - if (selection.size() == 1) { - setDeleteEnabledState((FileEntry) element); - } else { - mDeleteAction.setEnabled(false); - } - } - } - } - }); - - // add support for double click - mTreeViewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - ISelection sel = event.getSelection(); - - if (sel instanceof IStructuredSelection) { - IStructuredSelection selection = (IStructuredSelection) sel; - - if (selection.size() == 1) { - FileEntry entry = (FileEntry) selection - .getFirstElement(); - String name = entry.getName(); - - FileEntry parentEntry = entry.getParent(); - - // can't really do anything with no parent - if (parentEntry == null) { - return; - } - - // check this is a file like we want. - Matcher m = mKeyFilePattern.matcher(name); - if (m.matches()) { - // get the name w/o the extension - String baseName = m.group(1); - - // add the data extension - String dataName = baseName + TRACE_DATA_EXT; - - FileEntry dataEntry = parentEntry - .findChild(dataName); - - handleTraceDoubleClick(baseName, entry, dataEntry); - - } else { - m = mDataFilePattern.matcher(name); - if (m.matches()) { - // get the name w/o the extension - String baseName = m.group(1); - - // add the key extension - String keyName = baseName + TRACE_KEY_EXT; - - FileEntry keyEntry = parentEntry - .findChild(keyName); - - handleTraceDoubleClick(baseName, keyEntry, - entry); - } - } - } - } - } - }); - - // setup drop listener - mTreeViewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, - new Transfer[] { FileTransfer.getInstance() }, - new ViewerDropAdapter(mTreeViewer) { - @Override - public boolean performDrop(Object data) { - // get the item on which we dropped the item(s) - FileEntry target = (FileEntry) getCurrentTarget(); - - // in case we drop at the same level as root - if (target == null) { - return false; - } - - // if the target is not a directory, we get the parent - // directory - if (target.isDirectory() == false) { - target = target.getParent(); - } - - if (target == null) { - return false; - } - - // get the list of files to drop - String[] files = (String[]) data; - - // do the drop - pushFiles(files, target); - - // we need to finish with a refresh - refresh(target); - - return true; - } - - @Override - public boolean validateDrop(Object target, int operation, - TransferData transferType) { - if (target == null) { - return false; - } - - // convert to the real item - FileEntry targetEntry = (FileEntry) target; - - // if the target is not a directory, we get the parent - // directory - if (targetEntry.isDirectory() == false) { - target = targetEntry.getParent(); - } - - if (target == null) { - return false; - } - - return true; - } - }); - - // create and start the refresh thread - new Thread("Device Ls refresher") { - @Override - public void run() { - while (true) { - try { - sleep(FileListingService.REFRESH_RATE); - } catch (InterruptedException e) { - return; - } - - if (mTree != null && mTree.isDisposed() == false) { - Display display = mTree.getDisplay(); - if (display.isDisposed() == false) { - display.asyncExec(new Runnable() { - public void run() { - if (mTree.isDisposed() == false) { - //TODO : SDB fixed - //mTreeViewer.refresh(true); - } - } - }); - } else { - return; - } - } else { - return; - } - } - - } - }.start(); - - return mTree; - } - - @Override - protected void postCreation() { - - } - - /** - * Sets the focus to the proper control inside the panel. - */ - @Override - public void setFocus() { - mTree.setFocus(); - } - - /** - * Processes a double click on a trace file - * - * @param baseName - * the base name of the 2 files. - * @param keyEntry - * The FileEntry for the .key file. - * @param dataEntry - * The FileEntry for the .data file. - */ - private void handleTraceDoubleClick(String baseName, FileEntry keyEntry, - FileEntry dataEntry) { - // first we need to download the files. - File keyFile; - File dataFile; - String path; - try { - // create a temp file for keyFile - File f = File.createTempFile(baseName, ".trace"); - f.delete(); - f.mkdir(); - - path = f.getAbsolutePath(); - - keyFile = new File(path + File.separator + keyEntry.getName()); - dataFile = new File(path + File.separator + dataEntry.getName()); - } catch (IOException e) { - return; - } - - // download the files - try { - SyncService sync = mCurrentDevice.getSyncService(); - if (sync != null) { - ISyncProgressMonitor monitor = SyncService - .getNullProgressMonitor(); - SyncResult result = sync.pullFile(keyEntry, - keyFile.getAbsolutePath(), monitor); - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String.format( - "Failed to pull %1$s: %2$s", keyEntry.getName(), - result.getMessage())); - return; - } - - result = sync.pullFile(dataEntry, dataFile.getAbsolutePath(), - monitor); - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String.format( - "Failed to pull %1$s: %2$s", dataEntry.getName(), - result.getMessage())); - return; - } - - // now that we have the file, we need to launch traceview - String[] command = new String[2]; - command[0] = DdmUiPreferences.getTraceview(); - command[1] = path + File.separator + baseName; - - try { - final Process p = Runtime.getRuntime().exec(command); - - // create a thread for the output - new Thread("Traceview output") { - @Override - public void run() { - // create a buffer to read the stderr output - InputStreamReader is = new InputStreamReader( - p.getErrorStream()); - BufferedReader resultReader = new BufferedReader(is); - - // read the lines as they come. if null is returned, - // it's - // because the process finished - try { - while (true) { - String line = resultReader.readLine(); - if (line != null) { - DdmConsole - .printErrorToConsole("Traceview: " - + line); - } else { - break; - } - } - // get the return code from the process - p.waitFor(); - } catch (IOException e) { - } catch (InterruptedException e) { - - } - } - }.start(); - - } catch (IOException e) { - } - } - } catch (IOException e) { - DdmConsole.printErrorToConsole(String.format( - "Failed to pull %1$s: %2$s", keyEntry.getName(), - e.getMessage())); - return; - } - } - - /** - * Pull the current selection on the local drive. This method displays a - * dialog box to let the user select where to store the file(s) and - * folder(s). - */ - public void pullSelection() { - // get the selection - TreeItem[] items = mTree.getSelection(); - - // name of the single file pull, or null if we're pulling a directory - // or more than one object. - String filePullName = null; - FileEntry singleEntry = null; - - // are we pulling a single file? - if (items.length == 1) { - singleEntry = (FileEntry) items[0].getData(); - if (singleEntry.getType() == FileListingService.TYPE_FILE) { - filePullName = singleEntry.getName(); - } - } - - // where do we save by default? - String defaultPath = mDefaultSave; - if (defaultPath == null) { - defaultPath = System.getProperty("user.home"); //$NON-NLS-1$ - } - - if (filePullName != null) { - FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.SAVE); - - fileDialog.setText("Get Device File"); - fileDialog.setFileName(filePullName); - fileDialog.setFilterPath(defaultPath); - - String fileName = fileDialog.open(); - if (fileName != null) { - mDefaultSave = fileDialog.getFilterPath(); - - pullFile(singleEntry, fileName); - } - } else { - DirectoryDialog directoryDialog = new DirectoryDialog( - mParent.getShell(), SWT.SAVE); - - directoryDialog.setText("Get Device Files/Folders"); - directoryDialog.setFilterPath(defaultPath); - - String directoryName = directoryDialog.open(); - if (directoryName != null) { - pullSelection(items, directoryName); - } - } - } - - /** - * Push new file(s) and folder(s) into the current selection. Current - * selection must be single item. If the current selection is not a - * directory, the parent directory is used. This method displays a dialog to - * let the user choose file to push to the device. - */ - public void pushIntoSelection() { - // get the name of the object we're going to pull - TreeItem[] items = mTree.getSelection(); - - if (items.length == 0) { - return; - } - - FileDialog dlg = new FileDialog(mParent.getShell(), SWT.OPEN); - String fileName; - - dlg.setText("Put File on Device"); - - // There should be only one. - FileEntry entry = (FileEntry) items[0].getData(); - dlg.setFileName(entry.getName()); - - String defaultPath = mDefaultSave; - if (defaultPath == null) { - defaultPath = System.getProperty("user.home"); //$NON-NLS-1$ - } - dlg.setFilterPath(defaultPath); - - fileName = dlg.open(); - if (fileName != null) { - mDefaultSave = dlg.getFilterPath(); - - // we need to figure out the remote path based on the current - // selection type. - String remotePath; - FileEntry toRefresh = entry; - if (entry.isDirectory()) { - remotePath = entry.getFullPath(); - } else { - toRefresh = entry.getParent(); - remotePath = toRefresh.getFullPath(); - } - - pushFile(fileName, remotePath); - mTreeViewer.refresh(toRefresh); - } - } - - public void deleteSelection() { - // get the name of the object we're going to pull - TreeItem[] items = mTree.getSelection(); - - if (items.length != 1) { - return; - } - - FileEntry entry = (FileEntry) items[0].getData(); - final FileEntry parentEntry = entry.getParent(); - - // create the delete command - String command = "rm " + entry.getFullEscapedPath(); //$NON-NLS-1$ - - try { - mCurrentDevice.executeShellCommand(command, - new IShellOutputReceiver() { - public void addOutput(byte[] data, int offset, - int length) { - // pass - // TODO get output to display errors if any. - } - - public void flush() { - mTreeViewer.refresh(parentEntry); - } - - public boolean isCancelled() { - return false; - } - }); - } catch (IOException e) { - // adb failed somehow, we do nothing. We should be displaying the - // error from the output - // of the shell command. - } - - } - - /** - * Force a full refresh of the explorer. - */ - public void refresh() { - mTreeViewer.refresh(true); - } - -// /** -// * Sets the new device to explorer -// */ -// public void switchDevice(final IDevice device) { -// if (device != mCurrentDevice) { -// mCurrentDevice = device; -// // now we change the input. but we need to do that in the -// // ui thread. -// if (mTree.isDisposed() == false) { -// Display d = mTree.getDisplay(); -// d.asyncExec(new Runnable() { -// public void run() { -// if (mTree.isDisposed() == false) { -// // new service -// if (mCurrentDevice != null) { -// FileListingService fls = mCurrentDevice -// .getFileListingService(); -// mContentProvider.setListingService(fls); -// mTreeViewer.setInput(fls.getRoot()); -// } -// } -// } -// }); -// } -// } -// } - - /** - * Refresh an entry from a non ui thread. - * - * @param entry - * the entry to refresh. - */ - private void refresh(final FileEntry entry) { - Display d = mTreeViewer.getTree().getDisplay(); - d.asyncExec(new Runnable() { - public void run() { - mTreeViewer.refresh(entry); - } - }); - } - - /** - * Pulls the selection from a device. - * - * @param items - * the tree selection the remote file on the device - * @param localDirector - * the local directory in which to save the files. - */ - private void pullSelection(TreeItem[] items, final String localDirectory) { - try { - final SyncService sync = mCurrentDevice.getSyncService(); - if (sync != null) { - // make a list of the FileEntry. - ArrayList entries = new ArrayList(); - for (TreeItem item : items) { - Object data = item.getData(); - if (data instanceof FileEntry) { - entries.add((FileEntry) data); - } - } - final FileEntry[] entryArray = entries - .toArray(new FileEntry[entries.size()]); - - // get a progressdialog - new ProgressMonitorDialog(mParent.getShell()).run(true, true, - new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) - throws InvocationTargetException, - InterruptedException { - // create a monitor wrapper around the jface - // monitor - SyncResult result = sync - .pull(entryArray, - localDirectory, - (ISyncProgressMonitor) new SyncProgressMonitor( - monitor, - "Pulling file(s) from the device")); - - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String - .format("Failed to pull selection: %1$s", - result.getMessage())); - } - sync.close(); - } - }); - } - } catch (Exception e) { - DdmConsole.printErrorToConsole("Failed to pull selection"); - DdmConsole.printErrorToConsole(e.getMessage()); - } - } - - /** - * Pulls a file from a device. - * - * @param remote - * the remote file on the device - * @param local - * the destination filepath - */ - private void pullFile(final FileEntry remote, final String local) { - try { - final SyncService sync = mCurrentDevice.getSyncService(); - if (sync != null) { - new ProgressMonitorDialog(mParent.getShell()).run(true, true, - new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) - throws InvocationTargetException, - InterruptedException { - SyncResult result = sync - .pullFile( - remote, - local, - (ISyncProgressMonitor) new SyncProgressMonitor( - monitor, - String.format( - "Pulling %1$s from the device", - remote.getName()))); - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String - .format("Failed to pull %1$s: %2$s", - remote, result.getMessage())); - } - - sync.close(); - } - }); - } - } catch (Exception e) { - DdmConsole.printErrorToConsole("Failed to pull selection"); - DdmConsole.printErrorToConsole(e.getMessage()); - } - } - - /** - * Pushes several files and directory into a remote directory. - * - * @param localFiles - * @param remoteDirectory - */ - private void pushFiles(final String[] localFiles, - final FileEntry remoteDirectory) { - try { - final SyncService sync = mCurrentDevice.getSyncService(); - if (sync != null) { - new ProgressMonitorDialog(mParent.getShell()).run(true, true, - new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) - throws InvocationTargetException, - InterruptedException { - SyncResult result = sync - .push(localFiles, - remoteDirectory, - (ISyncProgressMonitor) new SyncProgressMonitor( - monitor, - "Pushing file(s) to the device")); - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String - .format("Failed to push the items: %1$s", - result.getMessage())); - } - - sync.close(); - } - }); - } - } catch (Exception e) { - DdmConsole.printErrorToConsole("Failed to push the items"); - DdmConsole.printErrorToConsole(e.getMessage()); - } - } - - /** - * Pushes a file on a device. - * - * @param local - * the local filepath of the file to push - * @param remoteDirectory - * the remote destination directory on the device - */ - private void pushFile(final String local, final String remoteDirectory) { - try { - final SyncService sync = mCurrentDevice.getSyncService(); - if (sync != null) { - new ProgressMonitorDialog(mParent.getShell()).run(true, true, - new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) - throws InvocationTargetException, - InterruptedException { - // get the file name - String[] segs = local.split(Pattern - .quote(File.separator)); - String name = segs[segs.length - 1]; - String remoteFile = remoteDirectory - + FileListingService.FILE_SEPARATOR - + name; - - SyncResult result = sync - .pushFile( - local, - remoteFile, - (ISyncProgressMonitor) new SyncProgressMonitor( - monitor, - String.format( - "Pushing %1$s to the device.", - name))); - if (result.getCode() != SyncService.RESULT_OK) { - DdmConsole.printErrorToConsole(String - .format("Failed to push %1$s on %2$s: %3$s", - name, mCurrentDevice - .getSerialNumber(), - result.getMessage())); - } - - sync.close(); - } - }); - } - } catch (Exception e) { - DdmConsole.printErrorToConsole("Failed to push the item(s)."); - DdmConsole.printErrorToConsole(e.getMessage()); - } - } - - /** - * Sets the enabled state based on a FileEntry properties - * - * @param element - * The selected FileEntry - */ - protected void setDeleteEnabledState(FileEntry element) { - mDeleteAction - .setEnabled(element.getType() == FileListingService.TYPE_FILE); - } -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/FileLabelProvider.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/FileLabelProvider.java deleted file mode 100644 index b3a333f..0000000 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ddmuilib/explorer/FileLabelProvider.java +++ /dev/null @@ -1,156 +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.samsung.slp.common.connection.ddmuilib.explorer; - -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; - -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** - * Label provider for the FileEntry. - */ -class FileLabelProvider implements ILabelProvider, ITableLabelProvider { - - private Image mFileImage; - private Image mFolderImage; - private Image mPackageImage; - private Image mOtherImage; - - /** - * Creates Label provider with custom images. - * @param fileImage the Image to represent a file - * @param folderImage the Image to represent a folder - * @param packageImage the Image to represent a .apk file. If null, - * fileImage is used instead. - * @param otherImage the Image to represent all other entry type. - */ - public FileLabelProvider(Image fileImage, Image folderImage, - Image packageImage, Image otherImage) { - mFileImage = fileImage; - mFolderImage = folderImage; - mOtherImage = otherImage; - if (packageImage != null) { - mPackageImage = packageImage; - } else { - mPackageImage = fileImage; - } - } - - /** - * Creates a label provider with default images. - * - */ - public FileLabelProvider() { - - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) - */ - public Image getImage(Object element) { - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) - */ - public String getText(Object element) { - return null; - } - - public Image getColumnImage(Object element, int columnIndex) { - if (columnIndex == 0) { - if (element instanceof FileEntry) { - FileEntry entry = (FileEntry)element; - switch (entry.getType()) { - case FileListingService.TYPE_FILE: - case FileListingService.TYPE_LINK: - // get the name and extension - if (entry.isApplicationPackage()) { - return mPackageImage; - } - return mFileImage; - case FileListingService.TYPE_DIRECTORY: - case FileListingService.TYPE_DIRECTORY_LINK: - return mFolderImage; - } - } - - // default case return a different image. - return mOtherImage; - } - return null; - } - - public String getColumnText(Object element, int columnIndex) { - if (element instanceof FileEntry) { - FileEntry entry = (FileEntry)element; - - switch (columnIndex) { - case 0: - return entry.getName(); - case 1: - return entry.getSize(); - case 2: - return entry.getDate(); - case 3: - return entry.getTime(); - case 4: - return entry.getPermissions(); - case 5: - return entry.getInfo(); - } - }else if (element instanceof IDevice) { - IDevice device = (IDevice)element; - return device.getSerialNumber(); - } - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) - */ - public void addListener(ILabelProviderListener listener) { - // we don't need listeners. - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() - */ - public void dispose() { - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) - */ - public boolean isLabelProperty(Object element, String property) { - return false; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) - */ - public void removeListener(ILabelProviderListener listener) { - // we don't need listeners - } - -} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerInfoPropertyPages.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerInfoPropertyPages.java index f2e0e94..f98ade1 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerInfoPropertyPages.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerInfoPropertyPages.java @@ -9,7 +9,7 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.dialogs.PropertyPage; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.FileListingService.FileEntry; public class ConnectionExplorerInfoPropertyPages extends PropertyPage { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerPermissionPropertyPages.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerPermissionPropertyPages.java index 38781f5..43ca579 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerPermissionPropertyPages.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/properties/ConnectionExplorerPermissionPropertyPages.java @@ -13,7 +13,7 @@ import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.dialogs.PropertyPage; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.FileListingService.FileEntry; public class ConnectionExplorerPermissionPropertyPages extends PropertyPage { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryDropAdapter.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryDropAdapter.java index 8d1cbb8..645f65f 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryDropAdapter.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryDropAdapter.java @@ -3,10 +3,7 @@ package com.samsung.slp.common.connection.sdblib.dnd; import java.io.File; import java.io.IOException; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IProjectNature; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; @@ -15,13 +12,13 @@ import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.ui.part.IDropActionDelegate; import com.samsung.slp.common.connection.ConnectionActivator; -import com.samsung.slp.common.connection.ddmlib.AdbCommandRejectedException; -import com.samsung.slp.common.connection.ddmlib.SyncService; -import com.samsung.slp.common.connection.ddmlib.SyncService.ISyncProgressMonitor; -import com.samsung.slp.common.connection.ddmlib.SyncService.SyncResult; -import com.samsung.slp.common.connection.ddmlib.TimeoutException; import com.samsung.slp.common.connection.ddmuilib.FileDialogUtils; import com.samsung.slp.common.connection.ddmuilib.console.DdmConsole; +import com.samsung.slp.sdblib.SdbCommandRejectedException; +import com.samsung.slp.sdblib.SyncService; +import com.samsung.slp.sdblib.SyncService.ISyncProgressMonitor; +import com.samsung.slp.sdblib.SyncService.SyncResult; +import com.samsung.slp.sdblib.TimeoutException; public class FileEntryDropAdapter implements IDropActionDelegate { @@ -54,7 +51,7 @@ public class FileEntryDropAdapter implements IDropActionDelegate { } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); - } catch (AdbCommandRejectedException e) { + } catch (SdbCommandRejectedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryTransfer.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryTransfer.java index f20564f..762db8c 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryTransfer.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdblib/dnd/FileEntryTransfer.java @@ -26,8 +26,7 @@ import java.io.IOException; import org.eclipse.swt.dnd.ByteArrayTransfer; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -//Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html +import com.samsung.slp.sdblib.FileListingService.FileEntry; public class FileEntryTransfer extends ByteArrayTransfer { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorer.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorer.java index e7552a4..7aa3e22 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorer.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorer.java @@ -34,14 +34,13 @@ import org.eclipse.ui.part.ViewPart; import com.samsung.slp.common.connection.ConnectionActivator; import com.samsung.slp.common.connection.ConnectionActivator.ISelectionListener; -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.IDevice; import com.samsung.slp.common.connection.ddmuilib.FileDialogUtils; import com.samsung.slp.common.connection.ddmuilib.ImageLoader; -import com.samsung.slp.common.connection.ddmuilib.explorer.DeviceExplorer; import com.samsung.slp.common.properties.InstallPathConfig; import com.samsung.slp.common.util.HostUtil; +import com.samsung.slp.sdblib.FileListingService; +import com.samsung.slp.sdblib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.IDevice; public class ConnectionExplorer extends ViewPart implements ISelectionListener { @@ -57,7 +56,7 @@ public class ConnectionExplorer extends ViewPart implements ISelectionListener { + ".explorer.permissions"; //$NON-NLS-1S private final static String COLUMN_INFO = ConnectionActivator.PLUGIN_ID + ".explorer.info"; //$NON-NLS-1$ - + private final static boolean USE_SELECTED_DEBUG_PORT = true; private ConnectionExplorerPanel mPanel; private ImageLoader mloader; @@ -135,12 +134,12 @@ public class ConnectionExplorer extends ViewPart implements ISelectionListener { private void createFileExplorerView(Composite parent) { - DeviceExplorer.COLUMN_NAME = COLUMN_NAME; - DeviceExplorer.COLUMN_SIZE = COLUMN_SIZE; - DeviceExplorer.COLUMN_DATE = COLUMN_DATE; - DeviceExplorer.COLUMN_TIME = COLUMN_TIME; - DeviceExplorer.COLUMN_PERMISSIONS = COLUMN_PERMISSIONS; - DeviceExplorer.COLUMN_INFO = COLUMN_INFO; +// DeviceExplorer.COLUMN_NAME = COLUMN_NAME; +// DeviceExplorer.COLUMN_SIZE = COLUMN_SIZE; +// DeviceExplorer.COLUMN_DATE = COLUMN_DATE; +// DeviceExplorer.COLUMN_TIME = COLUMN_TIME; +// DeviceExplorer.COLUMN_PERMISSIONS = COLUMN_PERMISSIONS; +// DeviceExplorer.COLUMN_INFO = COLUMN_INFO; // device explorer mPanel = new ConnectionExplorerPanel(); diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerContentProvider.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerContentProvider.java index 7d1e8db..8cad523 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerContentProvider.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerContentProvider.java @@ -6,11 +6,11 @@ import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Tree; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge; -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.FileListingService.IListingReceiver; +import com.samsung.slp.sdblib.FileListingService; +import com.samsung.slp.sdblib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.FileListingService.IListingReceiver; +import com.samsung.slp.sdblib.IDevice; +import com.samsung.slp.sdblib.SmartDevelopmentBridge; public class ConnectionExplorerContentProvider implements ITreeContentProvider { @@ -102,7 +102,7 @@ public class ConnectionExplorerContentProvider implements ITreeContentProvider @Override public Object[] getElements(Object arg0) { - IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); + IDevice[] devices = SmartDevelopmentBridge.getBridge().getDevices(); FileEntry[] entries = new FileEntry[ devices.length ]; for( int i = 0; i < devices.length ; i++) { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerLabelProvider.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerLabelProvider.java index 6da87ae..abd2ad6 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerLabelProvider.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerLabelProvider.java @@ -5,10 +5,10 @@ import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.IDevice; import com.samsung.slp.common.connection.ddmuilib.ImageLoader; +import com.samsung.slp.sdblib.FileListingService; +import com.samsung.slp.sdblib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.IDevice; public class ConnectionExplorerLabelProvider implements ITableLabelProvider { diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerPanel.java index dcbe158..9407d5f 100644 --- a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerPanel.java +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionExplorerPanel.java @@ -44,24 +44,24 @@ import org.eclipse.ui.part.PluginTransferData; import com.samsung.slp.common.connection.ConnectionActivator; import com.samsung.slp.common.connection.ConnectionActivator.ISelectionListener; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; -import com.samsung.slp.common.connection.ddmlib.AndroidDebugBridge.IDeviceChangeListener; -import com.samsung.slp.common.connection.ddmlib.FileListingService; -import com.samsung.slp.common.connection.ddmlib.FileListingService.FileEntry; -import com.samsung.slp.common.connection.ddmlib.IDevice; -import com.samsung.slp.common.connection.ddmlib.IShellOutputReceiver; -import com.samsung.slp.common.connection.ddmlib.SyncService; -import com.samsung.slp.common.connection.ddmlib.SyncService.ISyncProgressMonitor; -import com.samsung.slp.common.connection.ddmlib.SyncService.SyncResult; import com.samsung.slp.common.connection.ddmuilib.DdmUiPreferences; import com.samsung.slp.common.connection.ddmuilib.Panel; import com.samsung.slp.common.connection.ddmuilib.SyncProgressMonitor; import com.samsung.slp.common.connection.ddmuilib.TableHelper; import com.samsung.slp.common.connection.ddmuilib.console.DdmConsole; import com.samsung.slp.common.connection.sdblib.dnd.FileEntryTransfer; - -public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListener, IDebugBridgeChangeListener{ +import com.samsung.slp.sdblib.FileListingService; +import com.samsung.slp.sdblib.FileListingService.FileEntry; +import com.samsung.slp.sdblib.IDevice; +import com.samsung.slp.sdblib.IShellOutputReceiver; +import com.samsung.slp.sdblib.SmartDevelopmentBridge; +import com.samsung.slp.sdblib.SmartDevelopmentBridge.IDebugBridgeChangeListener; +import com.samsung.slp.sdblib.SmartDevelopmentBridge.IDeviceChangeListener; +import com.samsung.slp.sdblib.SyncService; +import com.samsung.slp.sdblib.SyncService.ISyncProgressMonitor; +import com.samsung.slp.sdblib.SyncService.SyncResult; + +public class ConnectionExplorerPanel extends Panel implements SmartDevelopmentBridge.IDeviceChangeListener, SmartDevelopmentBridge.IDebugBridgeChangeListener{ private final static String TRACE_KEY_EXT = ".key"; // $NON-NLS-1S private final static String TRACE_DATA_EXT = ".data"; // $NON-NLS-1S @@ -102,8 +102,6 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe public ConnectionExplorerPanel() { - AndroidDebugBridge.addDebugBridgeChangeListener(this); - AndroidDebugBridge.addDeviceChangeListener(this); } /** @@ -391,8 +389,21 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe @Override protected void postCreation() { - AndroidDebugBridge.init(false); - AndroidDebugBridge.createBridge("/home/kenoz/sdb/sdb",true); + SmartDevelopmentBridge sdbBridge = SmartDevelopmentBridge.getBridge(); + IDevice[] devices = null; + if ( sdbBridge != null ) + { + devices = sdbBridge.getDevices(); + } + if( devices.length != 0 ) + { + for( IDevice device : devices ) + { + this.deviceConnected(device); + } + } + SmartDevelopmentBridge.addDebugBridgeChangeListener(this); + SmartDevelopmentBridge.addDeviceChangeListener(this); } /** @@ -832,14 +843,14 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe } /** - * Sent when a new {@link AndroidDebugBridge} is started. + * Sent when a new {@link SmartDevelopmentBridge} is started. *

* This is sent from a non UI thread. - * @param bridge the new {@link AndroidDebugBridge} object. + * @param bridge the new {@link SmartDevelopmentBridge} object. * - * @see IDebugBridgeChangeListener#serverChanged(AndroidDebugBridge) + * @see IDebugBridgeChangeListener#serverChanged(SmartDevelopmentBridge) */ - public void bridgeChanged(final AndroidDebugBridge bridge) { + public void bridgeChanged(final SmartDevelopmentBridge bridge) { if (mTree.isDisposed() == false) { exec(new Runnable() { public void run() { @@ -852,8 +863,8 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe } else { // tree is disposed, we need to do something. // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); - AndroidDebugBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); } } }); @@ -1114,7 +1125,7 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe // } /** - * Sent when the a device is connected to the {@link AndroidDebugBridge}. + * Sent when the a device is connected to the {@link SmartDevelopmentBridge}. *

* This is sent from a non UI thread. * @param device the new device. @@ -1134,8 +1145,8 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe } else { // tree is disposed, we need to do something. // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); - AndroidDebugBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); } } }); @@ -1162,8 +1173,8 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe } else { // tree is disposed, we need to do something. // lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); - AndroidDebugBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); } } }); @@ -1195,8 +1206,8 @@ public class ConnectionExplorerPanel extends Panel implements IDeviceChangeListe display.asyncExec(runnable); } catch (SWTException e) { // tree is disposed, we need to do something. lets remove ourselves from the listener. - AndroidDebugBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); - AndroidDebugBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDebugBridgeChangeListener(ConnectionExplorerPanel.this); + SmartDevelopmentBridge.removeDeviceChangeListener(ConnectionExplorerPanel.this); } } diff --git a/com.samsung.slp.common/.classpath b/com.samsung.slp.common/.classpath index ad32c83..c619f68 100644 --- a/com.samsung.slp.common/.classpath +++ b/com.samsung.slp.common/.classpath @@ -1,5 +1,6 @@ + diff --git a/com.samsung.slp.common/META-INF/MANIFEST.MF b/com.samsung.slp.common/META-INF/MANIFEST.MF index 81e5a0f..e91e3d3 100644 --- a/com.samsung.slp.common/META-INF/MANIFEST.MF +++ b/com.samsung.slp.common/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: SLP SDK Common -Bundle-SymbolicName: com.samsung.slp.common +Bundle-SymbolicName: com.samsung.slp.common;singleton:=true Bundle-Version: 1.3.20.qualifier Bundle-Vendor: Samsung Electronics Require-Bundle: org.eclipse.ui, @@ -16,9 +16,12 @@ Export-Package: com.samsung.slp.common.model, com.samsung.slp.common.properties, com.samsung.slp.common.swt, - com.samsung.slp.common.util + com.samsung.slp.common.util, + com.samsung.slp.sdblib Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: org.eclipse.jface.text, org.eclipse.ui.console Bundle-Activator: com.samsung.slp.common.Activator Bundle-ActivationPolicy: lazy +Bundle-ClassPath: sdblib.jar, + . diff --git a/com.samsung.slp.common/build.properties b/com.samsung.slp.common/build.properties index 0135b71..c8c2a34 100644 --- a/com.samsung.slp.common/build.properties +++ b/com.samsung.slp.common/build.properties @@ -1,5 +1,8 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . javacSource = 1.6 + .,\ + plugin.xml,\ + sdblib.jar +javacSource = 1.6 javacTarget = 1.6 diff --git a/com.samsung.slp.common/sdblib.jar b/com.samsung.slp.common/sdblib.jar new file mode 100644 index 0000000000000000000000000000000000000000..761461cf1d4838f180286b4530280c589ef740b1 GIT binary patch literal 112256 zcmbTcRcs~QvaM-mW@cDsW-8ldW@ct)X114^nVDUtGBYz&nVFdx``I z{xtytaxXVEDJ#RkIFBg9Ksz%v+oa6=hi&I@?+^?`Q3e{OF#zle|DUCB|9KSu?+sks z{`U>Y|M_a-V8`TQWar{$Z^7hZ>&WC{YHVv|%=FXD-O9v_(Ztrs#idjO+86&X;nz%> zU!$*lAE_zoJ_Pk`j8wr7W9vQGqB|0I0tA{P`dq@G67=$9bU2#gIack0AB8~+$`kTy zP|^h0>6^-Mt|HGx*;m3IG&kKI7FubPqf|oF9Uj+T_wVt~0kgfgC;K2vbY8S~81%?l z^KYR!lBrLYHhR2M}a)~5y!IboTRb~g5^&dK;X+KmPmXCt+ zI-ZUdLv_d#p4jpJjV7<;K7490QfN7ioTxgz0^Brb zkQT$hrCDVl$L0h%PB~_HKlVqG8Zw7?mnf98_jG0x`S@@b?wn*!V+g{^5U?*Tg!1f= z*6@lC8gb_+qtVbjrn;ai|1B>d*>&``TfEJlbS$F7DlCV)qpO~w6+t{x{X<)k*KLbv z6J`~rmkSzTf!`F^*?-UX&<3B#lw;|FJ{>|pY!K(~<&Jwu0%J`M zQF&Dp2FCnBA*oSQgL_DLtVrkD->s{PeCog+W-QZ{TmsEp{3~|e@Ui7qHp`()W*d=V z8n~r{oijuUL^iAP3N>~b&3qX#%ZA|?@kdR|Cen$&Fr~LG08NrygzU$!x@P1Dlf!W4Fj_LDfQ^yFCVXm}zhhjLS5Vsm(_KR*N!$ZL!{gYPz+<=M!b1{WNOW-JukL04^m z%B@S1I*(69ariXrUL1#(8T^z+lwa9v*+7;p;~7_)bFK(VEu#mX@j*^?9}Q{X{%~T{ z8rNKhiRLquH~+}WiIS{pPC@N&X8^k z(k_mC|yuB z&T!ML_jTZo0Bg$aL%8c4mTy=#`W3kaTWqOQpwJ8V3>R}2A$^6%=L)G;II$zi8?&3z z1gBb=S?=m)IJxAu?@(Vm*?r+W&c~jLf4{SD z#6!azC0IErPwJ;zK(k&0RuOKD|dd z2aJgYqa#!=R2tYlHmy8y^7jv^D0FrnY;%$1^5+^1M8V~s_WCfA!y~1_rOr#FYG1m- zC0OcqBt@1pV}5opxM-?c(z4;3bGWI$49h?@5i}7k-f-vb^rI_KwMOp%+U(Vly6tM4 z8Z~OX9w^8;YklEnD?Gi!Pub{-!?N6*qzDT~%>*9lUNMssQN03ut)IXwbW zds1V+G-G+UnjjdWsF;FXV(=~2bqO-L@L80h8+8@A$-ZOky!WCT79uomf?r-s!wsSo zqh5G>JR_9#=^f=sEaqHv(t+E~UE?tZF02HwxM|qE$5KXOlYX9RV*!`}3pW-YVPEG? z1B7UUpSE~;boIiKFWKQ7nl*dZ?W4X*iz=hc#QKAOi!?rD201hIKO(#q>lRxS+b>X1 z2hFi?=G&}Jm%HVvS6}S&LB}@VyPYE!nmEZ~ zoiQOXcH1w;U3r0gxO~XJ@rI{@QqC9w@P|j*^vCr_CZV5end4o8@N9}f zYK$)AmG=;CZqxoXzJTG&xJDHAa1AE_SR|cg4@t$&H`Zw|03q zdq%3;(O|P%AKe?{3dsvORAzZ}rTPm1_6V8P}5eN!ZGvHg`b1;5WsR9?2} z)FKR$^&9(TaAG-BqMZG=hE%7i=2TEOXTqdVf}{=pTA(e@Od@tSNmhXTPZN1W-lW+zdiApt<%2GZj(~>wS$m{6 zV_qqdWB6-e_NYicj0LXCOO>^mP#Q2lwJ1POM~_bbyv((cwJdn&uHhW>m(KJcWlf39 zY71lW)8ryr3B8#xuHa8RvAR0)Rxf8a0lvJ32Pj`|nbA(pro6hTHE)L@IYyW4Yz4|- zFAOdXCj4Q$On{yo=yJ)Ss`sX^Zc)h&dK()9)O3t+v0?$g!fe>JPn8YQPBLMshYyjc ze}KQ;EY8(s4TE1ppWo%PYP@m%3gMtdo3ClHMbxp@i;M(ecvL+;yfii=4aqc`g4`y~ zi1=+-Y?ut3#O;B74UvRPz-c-G)URi=ekYJwAyOe@{KkDV>G2g%R2y-@4WS-rOCQ&+B*4N=EyGw$o+=65GHqMc=EuYI4u03V8r z^@GH2YRGpYUyYIGgvwWeOgssasB4#01-{lj=s#zLkfZw%8+0V!a33&2RG%Cv4`}`( zaoMYF1_KK1XqRs|o?Cs4nCFMROGR8X0t#UR8n=rl8n7aHZQ&hJP2+L|b_DegY|TQ% z!%VEn4guOTmNfeuAKG~JJO418L_~Ks+RrqH3Zl{K&(dDLK%pDMw|&4DZ`|@5@(yun zkJ87zAU!(ihesNUAO)i4Ok-YTu1c_i^D#})ExYv%Li|~|6IeYD_3mmjRRgKn8B`R_ zb(Ttla-s+0uIAA4w{-k>#4>4FwJ#h)-!~USKfa}zE3l$UZMW+Y>tEy@vEj~50}lcs zMDXv(o9zFWyseGgjSe;E?e|;J{GK%$MzHA0^?9-*uq;swH)|I0+%Ec@?Uf3CkjOMo z*Zr)bFmq@a>-7jPv6W6sUf;U13pyKa;)CcJlv1 z)UpNO-l@W+C20xsME#^em%b`_Nu|Ow-yMRL0cfyOQYutZ;89~bfeG1{D13>Q8V4sw z-{e35=G;U#wOoE6U^j zIaq%#<<5CJ&YwsPB0WOU%OOI$PPBo>`XzpNp7jINskmj zwCRiFB^cjVkksKyO&7q@Q?oNQX~J9OYOx@JNnwE(t4A269;Mibja)63%R?vECfMTJ z>@3MDkEwv+7L9x2F@z_N4;uUO4Fu{ZMmN>Z&Ti%9=NlyT?~qw+Sc7z#9)SE9qW~9J zWHkVrO{ybpCJh!D&x(anhTY>J7KpLGrXC!&{X&uJm;UX1BzH5(bgI$KsO`g%E4M96 zJ)ix%9!Wfx!QQSt$+?R=I@M!F)4rVBRfCyhl2ITG&&&omP)0SGmpXN+Ag7>rrS2Hs zd~j7tdQj2O#T}Ep`ivaC`#cu1YV@E0u z|9)4FqXn0!1OSwo^P7C&a*q8?oi*UjmLFN1zvr!{^+MIo7{y!i!~9cS{0w&R_YB#% zr=16KO(##*n&1nnyW83KK^&M%2A`&)boTkSOs&+>h2@20bCa{zDy!l!r$Zyq(4%zN zEgCeRD}?vci=LxRgiUFu@rjUPFe^M#m4di}sEo!QAYAXjX(vG-Xe$P$-~lKI`Y|m$ zTRvSpgDz-UxpH@Kf#*0yafN}t!c9skAQIo(8iu%OpZK6#wa)riOlSe3^~6%NX-bU5 zu_bY(x33pBH?;%sm_wCfH*IZa@C5{UU%KWQDhk}hP9gqjEiQi`+VK%&-cTGdQ_eFT zR_0Uw^-i(E7x(9{oK8pW@kk|~l!mJE$qB}p$Y>B?EPi&>lMwZ)$Lj+Xk?AMLAd2Mo zbRmz}_;|xuQhckSFWFNnczr(QrU6+cMuHNm#YvtvGz;F#?<&&=|! zFVe9cH99+<-TQidhw!1B+NbtlHqn-io<-?t)q>l~WA-St+aEb?n&sc#SlWRa?t$RX zYin(rZzMTj=+}*szrsewsLH^5WB*2QDv0=~uI!PLHfPvQ=0T;|IC$I8on(Xxp3LMW zVGlU<8W@vku9R;KdoO0`>bG_!XiT#|5G>sNnlIvczq*NoDQi##d8`{82oNURG|L9d zHcmyD`n^4vnlKjyy$PyFh#XTCG!xyQ?qE=9-)r@I*;~1mNE3XJ-BR|Hi(wNxGg4(J zlrwTiTUBCh*+9QbxxRB$rzA>E<$C@VApmgs}++0yNMEAI(8-z1$e-jreT3SqO}u^EQwN|)sA=y;+51*>$8B=)iEl*{>m zUHaP?8bu+ff{$jYt?Tl<3l|8~i-suIi^foZ=xeTb5CmRPM@Ov2+x^)$nV2TP?*hVS zvFse_qhv72vC9H_C^;(?-oj=#x|Sw*u`jH%{Dsk$Mmwc7@$1kWk$`ga@(nKFfN2i%$G zya4_Au{3_1F{2d<8(%AzL~wErq3wF%Fp^Mw-R)qkVsV%FTB7IS%iCpD7tdfKDwSRu zL##8?9!(JS1-6nC1;-)5UhG8`1f$O?x-~-GlQ`cyINrP&HJf}Kj9{Oi{eD%lLM{l% zPyd*2CuhIW2hZ1sxLd>;^|x-xyfekPEKVPH=WLs~4(d-6B>xc5-2_}d+XB`qyNvy! zKBkQDdaJjo@J)B+tBD-fagAxnrG~g_i_fVT2IGC}j_2j2B{>8XSy~Mu<*xB~IlL?s zzqk(Xvk#dH3CCt^sdVkDVAM&qpLG>|v7sQ!f`viD6I^VoO<;Jdhf{aJBYEH2vaW8VH zD+Uev>Wd)a)l; z!{92AxAur|t<-}C&uOCX4B;&=jEcM~%;YM{kokfMy6llLm4QGSQ!HF?eKz}#Vcg&{ z&fcN;VpH2(q5D~@yRu<%rmibP`EGL8P>8mP+^}2tEfJN@;Iw0ir(AlHuA|=7A>rb) zShz9a5c_!i66B~Q=J*3*B0B7^;=rMT6-Cn0PYU^yZRpsmOX@2yil320Q!x&EKg^BM zqoxk1px6v{cLb7TxMgR#l_H0!{o=`oVC@!y;>tx{Ve;%(O_UlHwr*x#9&I1`Pa}Pw z7}Xz6PF$^w;fwS4K9)Vx+FaG=QyFmbcT|sLC|oHU!`d0TUQpn*)HHg->Z&`?D0iu5 z7Gs6=KB1z~1k+Ye91of{gP z%}Ge6R7c`?_p90BisRmPMx(z2cMEmF1VRB6-cJ&L~FYO!(Ix|?OTzn?xyc*Pk;tS1{vloiW#3$n^ z?CL0SKI|79gLTF=FA8hjSXAY#%oB9#1g7V{ZnA_~{?Ppy3#YYR(LEl)_>J2k!@Ov4 zLdT?v9$FW@!oT3DIe+#tpP;i?Ski#H z(Xr?f{;6r^$d$IptsQVig@9U}(SF#*07B12#=_;9!w58EW6qL{DOR5HdgSbILUosD zrlIo$xuR!b*qX1Xhl660hSF%GZ(PnlP$)c5AhH>`6dC!boLFjQRH61`K{6oZ9v-Ix z0RY**NT51_rm20$0)Z3j;(2SFnCmwLgVuwqx#Jn(b?xG7cnc3IYY@-QX0pQ2EGMNZ zOB_=t{?k%OmYE=Hzw}H$yp76L#IQT8M2B1rme?cMxun#yMTWfq7#>wCVc6sTROdj& zji$4?7mDT0y1hijTSdwzMNo6)SgOlLmSl-b_SPJX*KQ3lrZ+6~)>u+?>49*fW_PCN zBg~6(T82W&kXq1-LD2qH;c%Byh|ZS2(QZ)BArpo`Py%JD^3Rb(?g;42zIERMPp1QSzu)n=2fbOqMxs@?ipb4K}3 z{Qiny4VVwsMcNU^6h^A-@&%QT9u*(K!;#Qf4K;#MSl$f6ecK+Yx8a5|V*=hsn!_;f zZ&p~^I!NOiiX}Ex?mF{MxTJf}uCQ(k*F01XiOywg`QW%a>#V+#W`tD(Djk?Wi~Og= zTcFDPaq-wjDYp36qcGm1%ys$c)CZwk&5H2zx2x7QMK4{rUX>Z^S-T2z<<3LWv(i>j z@F}G(+SS7;e_x*?ug$q`hMA_yg=6$w5ncQ8!gRCfKKO=Q89MFONc#s(t0#Sc>ees* zC&@=Gd$5YnwBbu46&C{e`pBvR8|$G#sESI=S-kpE3B+m12Q|@B zH#$kY5`V~+$K9iIOxME&Os=$vP+_5jgPpxgS40?f_C@7~ zoKLag!N=9i9c+Lzz>llCgedwPUh9vC~9fXy_GlFs%<}3rIx8wdl6>0(bBZW#41Sp|&C)M|j=t z5$TLFXPQ$d`_r>u|Azf9^+vozX)%BV0kJ^*ck0deAF21h_)yH=)!FNRpfFcO#|B*q zM8J9u^vvY&V-U@V#h z(-+0>Q6j2|TBaL9IL&NkY9{B^yoc@B_xtu0K2UB$Ezz*ep#yyI7(C$M0dboM&%fSA zM{oa^S#;|mblT!I%=I5uF&5WL-k|3$O^rh41Nj#kE?fD3yb!t(6sT3uz^+o&oP3*uzkOad^HxC7yJfnB71IKlDUmE4#GA(_jnLqSZh&eVbzlq>)8 z)`-2cWxHD5Ue;ty#lE!PQ9=|c9e07y;~s=&;=8gZ5 zPG%F$PA;JG8u|duiZ*8{mkg! zCIy|87w#Qa#iR~ecI^R?QUgv)t9)I8U77MHT(`J$`1-y}Toanx0F!C8p`12lN@-mI zZ8vW%2U@N#ZxG%CAmaj22=rgc%PscJpb7>8@&xhkl9%WIEP4M6L2@;{Tvau2zc)?8 zxiY(IDE>K*R^iR02rY%do4{%{1Y0q*BwZ1tia+SL&E*0wS<@D)sx7rHZJpDuP^-jC zWgx{^-*Z;9saChyJOjSheXspf6yEL4^{J4NLtQd_jnUXGe(!876mYyc>l zC{jaNw&dm(djfLq?5n08*OA(MSxcHqP1B8w>Rsi7vX{Te%o&Jl|Tq*k#b2+l+Uhg>pS3l1thl?$1H@<@ZJ;WO=g8$6Ch@=!Ve*wmWg`i-ZLDLdJi)(Cf$j~XIZFjWH?TU za|IIJ=VESWq5$NxDl^R1)yeD^3+*<=AbRY{r)AcRodb*=HYkW+e3YYPl}S)q_BzLP zx`(`u0gw=9_4+zX3b>|?n?Ig(rc%XaMspPeJWrC){ zwh2SGdUN&%x#H#BJ5Y7v#DsLHWBT0pVi$%!D&bJwyYh!oMP0J0gOqDF=mtO$b#3V{ zW)=K0Q?}dwqnzH;QwPbJVm&E6^DW$u#rZ&S(AYmxNd!S7b0^$?^-#;iP%IaAN4XL2 zVaQAxs@(NaTz>r+m8+oCU`iZe+5UYphk^pIY1%sqm$i+^T(iB1lm<|ei8n7wYJKPt zULnSgmlxOCZF(clRh}ahp}=cyQ}UJ>bwB~9JmV9fotp0L!{Tr=#yhbHhv&S0YfHdN zd;L})HI?1ftB$Lz*xg)rlO3WEx2hL=EnFQWS@xcKfXdWUE99;J*n=iak()ZiBE$qx z239x8lV`C|0<}XNwfC)nZqlQ61q3jPxDC_lj1YhIQLNlTYm0KsjMm*$ z8<_?7eRrGqTw{)#lzG=vWITI-CjesqEosW2S@$!*eiJJ%In0HAa+EmF?(56~OVBm& z)c%D(5MXAO9R9+*p$AttrI?phNn)&tLILG*CK=?bCEg;cTn!DTBB4`$mG#@X*<|cw zB_#GB$#tOp80^TLs=XF_z?_x}DQ|J&PK%d4)rr(ZE#9(hp1*8|f9ckG zKty;w-2I0soM9YQ4$fUG z*(=A&GtjwD)F9X~g?&e!lPsk2h1TPbm&T-0P8zMFLz-J>R_@6>1Rf1tT`3}l&J!)2 z7!`%GA1fQoix?}GJp4bd@I#!vA8v2K_^0>umF}qK6bm6dA7Z?c7Xdy47Yp5$r7*`x z(^`BOljspFVg68Z3Jw+rG)H6T2CALf@$@ivxPp}@@J8rOeRT1KeHQVk$JO6V1qR+& zNU(aJJFhU!Xe*PY$X#C#FZ@^?>(PtQpl>iVLw|6Kr2S29DNB{3Bf{^Or!C^pco{mc zd?RbvP<)#NLE12y|{JItj({3l8ewnl5 z-=6tf0pI+6+^CFJJwA<{j=9uhETS0yg8c~3)ITw}rYy^skoF)g4|tVG!D+b1oI*gA zPSKw^`hyDWA1fw9$_lO@s>ZXZqV{Mk_2WXL%;5$nFO>enEV5M7p3mR86Z{&^dHLn}{IW>(~N?l-JzdZY7`bzH0WFYJ{21sEWmh zQVi*nypE^nlf@w{hmf-jCCHM288!!!yiTso$IOJWthG*WN^@u&w%LR_DbvVPAQ>_W>muOqmtKEXNt;Cb*rX8Fq zO()%|w5@nkHmu$z&sd`E!P|&hT%Eyl>5JKDs-2%uM9Nu?`H!~TvF;U2cBP~mBxw(d zXMM&RI-@*=`~tYJCUsDGvH<$y} zb}PX((d(WN4fQ-513=DQlFVf|6cW42EuYyl{llm5NmQVuPMcE1L@ft+X#6V4;q4}B zT}ng3Hhx9JAH?OV06Z?R@;6$N_dgz-!8vKCvn|b}16~l+bH}RKg?N798#|wo;is)D(8F?G|=NC(^~LSLziwuG@iIg7|Rj44kOG;`a%#tlIZ7S zi#vr--;Ulikm<|Rzt#aM-{XoxHNKTz;NSf|wPXnO5gGN>fayRbN#8VEp6~P-?1k6(BQ_g%mg*T+z8G@gD)~cJv|91t$t~h2OX)2^9Gq^T zr9%O&*ssqiqZfSnx!YriINctKJ4L#sBJG+%gn6J=b3L}5@(F?#GJca=lajg&o`uzA zc@x-aHZ6uORmCeiOssT|xC(<)X@2!8Oj~XXqtFu}(F|BmBoobEDqMqbYrO31U79XB z%w6nCFqu;|d{#ewItd3%#J$yqjEB4hANlK6-HST6KfdBc1ceXR+iziXJplH)9vN*T z8SPCf>WPQE`S_-b7P2Ye=fxy0MQQuV9O{eTr>Lj@tLvA&`YwJ%-a+vI0a3Z*ESg7x z6%(Wl6PWOn~D-U21aj?G_l`jnFUHuDsgBiwXwkzTD{5) zE%AUy+b!Z=DyQ>Oyt8kP;svSRbH@XnMi(&Ze6~Vi4rL@@Q&CqTq4pwmSU%`6_rSCR zCO7>JkI^f&6Z-Zo?Ja8%fY`Lr^z!Ir83$G0NyZUt8_xYBu_NC4@8>}+bj6ck zR#b$prG8HAhOru<*E3(01yWY!J@f=G#}H$aGuk3M2KF3v$ogo&{218j^8yfjf{RT; zTZz7QXYH)wd}rwx?y9)t%B19KzvK!%0}!VSM&!cdV6Of*(~EM6ho@rTQdBrF0w=2~ z)kFi3DlKn>RCyzPNDOsb{xNVDBcm#%t_u4#!J|)9V89Tda$#L6iRKCif1iEwb>XWK z*gH8Jpr~;~G|F*B`4N3o)t-d+4gN1)t#`2xk%9vO>BISVy!zum;#H~t?!+sbnV4C* zn>qhCwk8V8fU}^4ZL4m!+10jS-(L0WBb&Bw;8@!xlZvluH@0G?LMMFoB0;%l@Evy7d>74V*E4KMDaDeUEv9Q&Tj z7_jHW4RQ=rjm=mqVN z;QtvA{(qlrNc~6gm38@_$Wq9<$XeOk{Lkxu2U=B01ziK<>uQ=Ro4y)?UK&^dFy#OGUvI~=9w6qw%0k#uF4WnGBhBY-@m~AvjzeNQ&i~xY#L>~oqwwk% zQ++awrtPQhfe9Akn-sG zhGxq%`$rkoEx?y~O(?qZQ|lVGpD!3-x@jbpFEoaAQu#_;c1C28jWn#FmnoD`rG47q8~HQ zBw)0Plw@%wwYdLs6&am=ZzK(vu8(9+>EUuP1k)nK#*rsLGae9$6LtX3ZK*08I=dq` zEtK8Z+$l+Tx8`gcly&$-O~@K)j}C)RxQfaiu-q5QqTCv2ESt;ej$np+4m!B4A&9Ov zwiswrUgSjCyaZD;!~UsF=#VX>1c$_MQ%tXWSg=LsYPeC757s=nrA)7JMl)D2l%s1f z$OPs<$F?Pw5P^y;pY7vZ%q{^tmW)qO z;Ppk-qbD{1E@EL|{*Xm8*C|<;3CyS!WgE_x+_1dj8AdJ#RRCaUd)n$(*zt=XI&p5T zydC*Hg2&b#yVfy$l&mp+vVs%CGoCqBq20c+ExyL~-dj$dR7r9(69u$``B>W{2DoU) z6aJesUcWcey;Mvrl2z|@mnWu^5=yz6mp>l(2SWHO&d7_F<_7`LmmvCs|LPvL<{thf z%Gm!1n;Z{b8lAm^J*6d*l8iMFu4jbspV?-ziI?y_plN{2G4C)!vGf*2+UX2LGvNYx zVaRD4JeJ=rm--8m)lBE8bV+IjvGg^Kc4{F1(_P3AtlNOa)7WHZ8+T81&$D8&TS5)k zXGraWv`NUjc3AebobzHQ^3zposo;;AmmgJRw8)TEmcmua zc4!a?kSTBg?zoVboOxV}oJ1jlQ6(I@TaPPyqUOqlm4^+NnP={8kDlXI>@8nkj30FR zONFp$!zxi&v(ldJrUB8%WjbA3M9ZgE?Q2e#NTZXy<6n$?P1{lHkAz$5eD-|rmoae# z1-tw?+0OP$r;bTa+$|(YD+yEP_NvFQReVsjpXxG<=WPr&Y`Bxq^%xi)JfBi@*^?{5 zai^B*Ze4zQm}BYf&WQ1GRL1SHBl6}Z;Bo7dVTP|)xVco!Xgc(6DZSXbf`cX^tDvLR zy+0dXE{O|P)On<+JH;IvcEMiEOZJ=Xtv49CeL;)9N4?nGDwWA`WFFUsQKncGfsHU= z8UI*X7l@$nkxMueJU|etFyhg91*R@1xD^!R03QVAr#q+lF!aUNIF9!G9Cs5yxubjW z2Md|?BO|yN4D5v3g6k2QfV+A2-+fj@T0Ui`$xVARf)*x_5L#!PK5P;%Ru^0m%G1hQ z9a{zSql@3bP8I>iM*du>VY}J$W@G>%ji3AARhN)n^g?GNu5_^mnI=%iwVC;FW(2Pd zqZYw%S^mia+LyRRq5)yjO_$NqcRiiO5mFsyKwZ~LHK4Mftk6l?*6hOn-Eb0%F1v+o zJQcOsC>2THXU0nsp$6(+NnHKang;pTjD8s3H~x}8xhvu#sw?BRXWSOxCa77;T37O zpBq1zJVzZlQzaS^*kw(AGe;(-F%zt9J)UJT{Fcu&$_K@U9eC2QpV``=)?`Y8I77$(A~E8b4U&>WL+Z!cVEhan{h66w6JTn#h?<;7Az!8c#7ybJLadu;eGc z^j;s8a72l4ZP1+ht`CJ03`B;jy7*_Q^p;L2+vV6&G+3E-&rNnl!Tb&Pd^Z=y>>0E2 zjg}0o_lBTsM>e{o{aO#!2PJ9D`U62;uk?vf9Ry#D2rd5%SbU_TYpn{`%SEWwsR<86 zdyX5YlwUueb44rPrm0gcgebXhz8@wkvs$k^P~z=1X#LmvF1d@~dUB<>^NvfB^3G_E zb7~`nY<66L+$TH{wvaDfL41Bc>Qf&<4cKoei4)w=1b6Jgp8-FV&VDS+0LIeQ82F0R zpK;r^soj(OXdt<*cdFq~+)o+Owt`@YhPYgPb3xgr2mAV1_=mELAD{Bnq()R+(&HTB z?%yI2q4(W+CuNk$xwcEI%4kggNEzWW%Ld$cVdk1j(HT{Ti-rAWIVGntaPDc9=BwXZ z{e*2*fMZ?gQd%BTkB^V=<7T^s*2%h^x z2jt7;Lo`C_ssG^E4*bDhr609%UP2?0a>H*1Nz1c6rIz4gx41{D$`M=8VKt6&#L0j= z(nb1*g9F*9A9fGC;@E?TQaDc}F{VgU{uc^=?CKqY^??>WMbFG~C04W}%T7IBMaWS- z-Z=gb^kpcHanXU$n{tq}8yUOc=n5SUO8#a~3!V9sJ43^1mi9^dtnH4dH}>t2IhOWE z7TuBQR69~O_85=%h8SF6c*L(lIWAFQj)-YR$?0KpNe!1Fgx41|v^SU#cw!Y=JF^A# zEz@xDK}+KPvcvf^%NJ07$v*Ua9Lfa(hs*;MmG%NxKOE%aK|l(U<2^PztRaInT3A;eTUR8ZxwYK4{{tlU)z z--2CJ);|z3Tf=IBxF5)KP9f;ZB@IlIh%b!PR3Yw{NXaT!dWW!fPx7s+)Q|wTc0wb@ z@2AFCLO|Z-l&2JZL7+B)@o=Oi9KhzrMBZhTPbA%#T;Go0I|E$Tv`vKHV9FngdIlad zFw=$Kwp5;eM191}?i<{| zzQffe8QdK{(;C?K?wy}4`=Wh9h1}`%$oNMzf8ohB$ zE=`!$t24G@VQN&PC{(A2YNu{gO((6lShuTW(>;${@sv&97r$ausnW+uuxZ5aaFe%) z*pL2p40Y0Diny`!jY#Ykc~gZn{?MoNQTsNsbx66aQeh_kEm?ksGBru)++xi=B`2d^ zEk9_BlVVmi%&dU!TzN)3wcJcU3|lv06$|;o*fToS9ov@tryw{B(KxbNx-0UWS+;|g zB}0ka6QQXj)uHS9eglqQz9j(3Bqkf>-e45nHRH`Vh82#4PzJt_t)l45#1pM)3rLnS zdH*@90x)(Pf+HMfSu-z_aD2TvPhiumC_cJdV9C0s-{knZZAq@jwJitj4Q_!_Cms}m zveL7&vAB21Xli?<$Rijz*<|*uaODv4)=buJ!4=?rs^aHWTE?2wnNu&6)Ik*0iLSsB zjco;mZ;q{?{y?i;jE^kqDv5eqI(yfxm{sPNBer6H(B&U===&{jzK)AE8O^@HAN>d! ztJb1|sR&Q-O!sTyhnl0MjvvkITLoqbjDI8#|A@7u@AQ zY`DfU|K53=U@r~GG{HHKH}@LP9ziZaH-R27!US5PBk-QPPHm(P{#K>ma<0CVxx%ch8QWvvgVBE|xY+*ag{J6LoJs5j!G)>f3)5@uI*$WMeRGq%agV4W7ksQKxcmNgzL>=Sb zks{N7LW=)|hb@}0p1Nw7U)No-M`m|xCCT*%$i|%aBg*?Es?^X~ zUxTggf2LPvd7g8=b9}D(Zzn>KK{dyhxD8DzVoK162YAs`Oj4R@?o0~ppZWPmY zPYA~IG{+TI)V6n8*)XpVB7_bErO6@G8OrNRYDnSAW$(&Ra_GsQ5jyi1M*>U%Qq&Xs zrgC1|aSxN-Kz04j=>EzUN^#6tTZHC!zul#$nJaOJ6}qBGQYPASR@_{CIF7fj%UCj9 zgja2LvTCJ;bbNbz*yxNw5{jg^6}Q~NhF+~9-yZwkGP)=hJgXYHAu#O<>ID-D?{!N< zRKzhWyo9w+xy!G)qbqlZmD#@56{C+-T?btFcTuBc1J}?(l8x1)YkN3|*Wkdu)sl6? zxT;-UaeKT;N}=DnBN2cZc@s;MhQAh$p{S@m(_8NKOcxHv8(pY&M>RN!U^b3xIy!@!Uq`jVOqqicaATG2iQ)kgeaFYX)R}`GD};By z69w3HhA5l$kZIUnd<6$R# z*=A=t;b!ir-Q0v~nPfxXRmI}&Xb@cJ$B)$1yo4+kYl0PPxMw zYzo}=-X!_cVoni5H9X9qDHE75OA)$qIJGqXs!^A}=bj9?L$|U_WmC*kx*9<<6d;z3 zJBRZ=uWAuhJ~w1RbeAwycgh}CKay=8NK`kUlpS@r)9)vEEsvr#&4o4d`$+O(8SJ%Cu(`h#v zqz2vDY%TgRQSd?44pG=6O+-)=~OysXFowcyfuN8(oErJ4~(O-bx2)im_sJ2SYy7abkBJ0%@qjw`>+WuYksfKDO_zafz&ub$!P>c-kA%5;sAytErH-yk?QWu+oHAkirs?G>lsQ$r}zo=nQRZ-{e=Lex=f`;vfNDMGyT3oFt7 z?;;GL!6o@wvs6MPe)$IfPaBQM7g{@}y43qi0h(Q!ybXSTxlGR-IG22MiyMx(hI%%F z{9rBg9c%9!Uvj|74#!S5T2Gk83kMS(p$0?K^MM5L-9A$zt#82jEbk|x5&n*3)cIGWM5*^jehaq-{zX1Z@K7y z#5eYaqF<0lj{;5u^UyfU4FIEhC)`8&h21-9;rBCMmQpb+9T(|<_Y_HS4G}+S{wox9mBm9Zrp2%C?4GI z9dj{O+EnJ;?V4mP$Q|)H)cH5s($=Q?ES& zQ>AP{b;h>am%uH0e3BG-R*G20_qMT>7B*nk@)LVktND6~EC!wDF#?l9D9z3{el6ok zQ{G!5$on4VBN4q;8wUB@ox3vf(>ZUZnhvP4nk_W4yxHORGw2BP(KRowJGZPY$c2_RbB{S9pM${KB&wO^++g{SY1xK-xu?EPl+i zD_(t@ZXLJf%DgmhLd>|tA{XTRLSxbPhQwj$R+8M#j`imgQ~C*Gv}X&MF&WA0m|s2U z59fgUe^K_1(V2J4+HY*zPCB-2+qRvK*|BZg_8r@{ZJV8Ra{Af(?D4L%#(3A*=iB}N zI%|%*YF<^>uZHATbrcFLkRq$xk(e~TiBM{nh-&u;r&F+p^jE)x@Eq~+hSX}^5e_aE z+gcZ0w;Am@1J;msE`J00$`d7A>L*-Ux8LmOz6#cQtJQk5-TH;|i?q2Sxo7v`hw8Z# zaJ)ES`szSg5s5+t$Ad&($wofWib6Rk(fSLQuZwxykjp+PO_`ugZlhKCJ;-F2gVhS5 zgoX2ipyw5@6EcxK_GUS{uLwLbV0Ce+4RR{G;z;m>-8Uq)0V;o!#U9H#5yy3K%(){* z+Hx4TUQ>u{ZCx3o{K_NtK;ReaFOb3HkC{!URs}`qd#HqTyn>skX#aMaqA&x^nQ|pz zh*K4CM0s`o0>a%BIKt+}R0ko z()^z{ru`u7?!3J+ajf6>cz)tu>`3Z469Lt+;S(8up(QRAJgA{I=TCndofP-zyqnbZZIOV?j zSOa;Hd=iT09&fi=hyr=fj+0DF*jK+|EoPf$*bnD_;xM?WtF6Kvl9N)8TDQL%lp>qV}*gZpv4u;y*hK5pB<(!@^T3hv1lQBm z=L)A_rLokSWH9+&yPBp_YZ@$co5ycFcb?nAq-6vU>shWRTp$p1wF`t*l$0=E<6kqu zkiUv#Qf1@~|4*F{YrPo|_1DHK@gJQp^#8x={C_O1-Mls$xDu&(B?d}GM_@)#5Io~# zHgM4UOeb&<49bbq8w7dc)QY1u_Hr7qvb?`3iy8jf@ z?UN<1})C~!QpyCVLA>6>~|VMv2Si$+|M zr3m}sOnOo!%0@q~^U58-o%G5~P)!#Vh(aB5z?1e;o1760`g47DyzoS>5y|CQY0Vhe zI_z$5i!mAD9S={tP53Uqte@}Szu$Sl)~^)#%_vtSQyvLmN&(aKbYR}&)#Hq6_}k#@ zMsy+F%MlmUOd(%(l29dGnG>n{>Fo@Mh%zRLrilG)p$w8^;T%}_gHUhhMBxP~;>3Lw zk%tgkHV6#z{_OH}ad4XO_PqD-u;=6A#fyfSc5rv_dFJBc<%G~HKh*#3aBy38DgjDt zG-*iiZl%8m12hc=P?5L14ONDq)IE((%$#)knGV}9$`P{yrBW|Sc;h;n@5+=OV^i4!B#xKKwDl3KU&lV)gx|40 zok(TKToWr2*YGNkqT;mt($owQhB)O_@GyA4L5Q;ZL@jt{0r@)L(E zu*#V3gt`H5Ti0%eV#0NZ?^xopmf%{vLC-0RZNWtlo1|U}dq+N3#7{F_lJset^aEr@|k%JIKueb^^Ru1iv^#SUmW_O23XhoHcA6etG&i z^|<=6Z+X3#0nAp2vkBHtIqC$mKYiR@_ODzxx%s|vWw)|s!(oJ*3Q%-bySWvMEhWIb zfuf7w;*X+!_E5%{t^^K*t$>tU&OVbl?ah?GCqK!IwU$ z0htCFR&*!>^ehKj9!4k2aJmTrB%%plzTEQnD6>sW!+P0)xVZS*J)q_bR&(EKf&0L* z4Qz;YqxI+IeRhEg#~vYR4<8PUpZ4FLXDuKFE&QJdI}x!RT|QycJI2g-LQmK8?`$#2 zTd2@mj0?y?n7_mJ?{V{KN#{51QJ}PwT7(6sHXwM;fl}A-T%3b4AsQMah1M{+%6-HR z1o4iKjQzcVZN(tyu~@}<48|Yi1CyM2b&A^*o2{?;Y5UfI&lhF-jU|Uy)H9}zQmJoM z+QS}9Xp}q^EE808vh`ENAK|fQOvwfxemm5SB&kjVc6zs|_r7`FzLRij-_#H!ZaKU= zZOny)*eG1XlDfP(-al>Ybp@^t=t1R=i3iIrKfY_mFdrqJ2q}I^>a*uR-Q?hO{c7}^ zLvEeR_qdD;s`>W&e!nD-_MNEm`tmo6@z>oE=QZf@cw2kajEf=gzraR*Cgi(s4%ku< zL!7@UPs;_Z=ZHG49k3wGm;npv;S0982?D z`n^FNu+_A%uN(A(&uUW8%A_w>CI)lvRTTEGehWq?irWj{-y`Dl3rIQM87XJ+uu_kT zWTwV&AyC{NWoETR7iHzlGI1cG zzs#+fvWgPMJ4|CVoMnk{`EX}jgkn8jYkS|1&Jl0Da1Crh3xuTE-`KnK@FP7k?)=B< z81u!(Hjb5k5c0*OrK z6%+VnUIexm+M#dY3yzPQHFXwy=|h)S@P6q!K0i_I=B0oAr0Y&?+71*5lny0@e@-6# zzz=N1>OYR3Vt1_&VM&X$`ZIqrIm>?UemI3BuN_rRNiwV<@8)orw!rGAYQjpu0V znJG618Pw#=kz&eOSTZn*JtGgjJBHdJY;vq^oNP^UW5(5&^p{g(ik-HFe^KyEnBk7k zZ!Vwiq07sYO>qu(rkKRZ{~G#65J)L(Yl73Sq;oaq0rmAN!3)kJXX1C{`_`aOT5Ama z0D`4A$tbqs00iY(1OY*q*%loaND!o25tFbo5<5gZIn3Ln*ar=Phzj*t$gU`{bK!Fz z;tHG=G#cATzi}9r_*m~Uv^p@nP-DPn1-hTp6u{`ilfQ_=e3nn8SYDK_-Do(*zu>a< zIK#|gYAcd~I*sVW?qP`lD{ut8-@t`aTHv>lpY-$89-Hx)TnblAw7NmeHE!xYja-{g zoZBYA%+Wq9j#+||PPxkp;*5zAZ3evy?-aE?uQF}(sYIM63{u+(oj2N4CrBdaPd9Y@ z2+nT?r;c)YCzyd_aSUWGrMsdyBn}M)Zlmcp3wi>(Wf*huSioUG;U9$&&=7NQ#3I3s zdq?YY;LS@lD( z=q?gs&>30;Z#K{|+0f{Y(Z{%IVqSmFom&%T(FS8i{`>m6m7)XJ?BTJR>rPjgsA}6J zCX!USKTI1gAeE4=erDMYFtnFClM^5<7@S~>6!^t#2@sgh0@K8>A>XZ&uZ)F_aBQHA z$x+kIw_>h(mg0@!Y^4R_(+~#L1*Ovkc^J^*!O+f+IW@ug-LvY1iWw&?3f)SePWWZg zjo3!-t1S{{Ge{pbjiWcO-7lOJ@VK)x2z+6^)IF(Z1+>ZRAY*JWdZSwg_H|?`TRVU^4 z)wJ^gL{Ho5-6f}yOV=N2Rl&Z(QA-&I5f9tu(~Be$L4W9#ipeuwZlS7k zDwXFUkeH<{+tjXdQlIFf@B7(0$)_hP>%tfL2PXV@nb|)y^pcuT?C1-P?2H}iGUwh_ zd$qMGQ+s3AvzzAZZ%~lpRms)+G9j0ejHaLai3`r|p&nS)@!O3x6MN4_{&Q|YU z7DoX|96CO8Y&Q4mp%7+@3{1nrVp#kE_%Gvwt$qQej;0v78!RBFlUsa`7p=Us?4Tpn zvMM@=o8g{UgCDqtuxPYwVWaPf>sLBm!;~R*ooS=<9jeyuVbcE0-#+)YPuS68P3HZM zh?|53Z2H-TW(2>8emMgR7by7K0*RB#Zg;(nEx2<5ExL6q6@fsb!sZ;IOQ5;>F5@)v zW5~Hih3MalIt1x3*mV<}b3$>yw3CD7^OoAfF$2k{v(EtIgEopgfveybTkR5Bp0dJD z6=!D~Ru2LkyY@zN*D5r*DHn4RMEVo&3+D)~;veo3{QQaZgA%2ZN8kgpW_0wU%>*PF z019W5HmZ1W$nyZFNSDg2DVp$GvzL}Xx+dTPqAd{E%=_oMS6yIg@cC!W z>NSJvuq@HA?P}HcKY0B{>9sw-&EkTy&nFa$Qc^29ig=oP8en88S&(!B?x%fmJGdMV zAmOvH;f^}Yn!_BETV2*=YnoJ(dIoFRGGj^`&H0?~_3~RYD=gr5%^%;t{l(Q(Hie1E z<2njlF}0F4DFRFW_G)*h9*!j1JeR!eX(0pzik@33O5e>oGzzVR)W;*SR(Yt8cwAPtOEXHQg>LbC#`vW&o+TvJFIChs-M2B;6bR!AsNWO3svo zsH0MYxL(U=v@PVqnx51{FB6ro5U-IH9yCLb0M1?F)t%tu>VRDpdhz~{y)+zzApPMxx+AvC`( zRoB_CP}n%Rc&Bn9v55XO389>f!U}dvQY_F#n39E0HK<<0xVbTUr*$!J0iT4Zan;t?$ zc)=`{hJQBL+`i&cq489jh%SIGOot~>#UTNNrCZdswDn~_Z{z_=9P&hC(Fd5|m9;13 zpdEae4+$3cz{Ffa)iwd7-;hdQ>DdamKAve}dI2kL`aB#0z1lpZEmsi?Cr?8(lv>;!ztpotQvlY@tVvm$r+Z>E#2<8v&>k#g_DszTAz(EdwQq6&s~rl59H{a-7f`R7MDdm zzt_QSi?I1>Z`Amfm z8QCZ9pu7{P+|F3YET0{4nuAMcZIa0h+pZ_d2E;{U6ganePj;oeNvd8bI!X{9(1_<2 zY?k7}_nL^u9bk3JV84Sf2=%vW7K|*oeqRen98N@zb8fb(8J?G_YXl2>aPu(i&W#}!)Yn3N zq^VNjL3CZPHE7s;l^=2N!4Ib}TTOAl0hXV%au$zf=Zi%o(>^T{Q<}tWCRrl1NusMY zgo^3>%V*Vbk?%Hq=ijx*b(5h=(H zNv|{N>d@ODX}I9}lIDk+Po~H;>xVR%*z6{}sY`ion^TVLP3IUiZt`~{`h{b2Ok5;2 zk(Er0tr`_m%P6R~2h{B)rlpA}qN+!&<}t zcg_yZgu9FtaG#@4R%D=Rm2B*c)74JbYk)*6B*stVK-V}CI|!QT1@!pHuX6*Fg48jA zjh^C5(8MTkQ9_3r_)2mjPTuN#^=h|H3y9HNy9T&LgCN53_tS$vD;?+mX zSD1V{Ydx2IhADD&RY!?R*aX^vr&NbDPUwe-wb50DbAZn0oDWy9VV#@a5Phin?X{wg4wFJm4_;cz{=gf@agmt9WRT+-b zfWdCX^4&>9;X^>xH4!@0BQH@okq|+hDu3ZlTC;S$?Z1?7B*^>!^7#|&FuX$uwtvwe zv!gDE<}(e@F1L*t@o#S3$)-QFz6!3HC|f}Pa{|Lv(Z$tY24SOLsth1buBp3sObiP+ zJkH>qw8|xV{v_{*5AIR12Ij z){ZD^IG+JfH8(;#3ypA%{7VFJ(WczjZO1}e1JkzxUa#;fD0nHxpua(Asr1vlzaH}g zUX`;bnsdE;z5~vnzEh#;P|S1^(-}S<6uB)cm;KF2=?CSigKPAenBOK3T8f_5w?IQ= z_}y%(6vfpe0rsAHVZ6sxPNC?-qAb}H-M`y2N36+`A6wZc(2c?1aA>?6jH7RV$UvqY zQwy3`8Hrcmb=;6*_=bSPTF6VLOsl_=slpIJ?cuo#wPBol`T8W;%h~o~9_`l5g0&pj z5vGm9z&wSB#7G@6hn|AQ2RWIARAa~j^^>LbH&!)&A}W{+=@fI1xQu%lmS{8>PVEN2 z-bb+2d?^tXnNbG8ajAkcVov_IU02b zvboc;5aa=OjDt%;BS0X#6SOdnQ{tgUY(ycu6LHv<0Nj zQW2vnKZF92=>rqKq8p>7FbZFeBNlzR8urjN9iP0U&)3F0DrVuKd)dyB2WFT7v~6BU znOARvtNBy-$|nXtT0;RlA$@ONkNxS0d14(rCHBbct4Fcqa4a2VrW}gy*RSUkbEICs zn#9IO8HmlsB3;vl8ge0G4#o5hw#D}G=4Tm@CSex+(;QQx9pnWPMCZjyT!>a0FQ3Fb zDuJn2#%dnkqQUABd8Jj;V$B;7Lb_uqtOO#+jgo2(2OySRnWeo)!QX>}2@nO>2w&@? zPv9pGvGV9O+<%+IGZa%htp?_f$CMs!EmBbc^SO3g zCb$7ZQ{&I9FYM$Qd1h=Qj1c|tfO7dGV z+MJ!YqfR3B2eU=6*iRO|;T|Gv(^R-xhWwrLVcS()wE{| z=gmXCbRBd;chsdXvh}zI=VFN>8a`fmUac@((1I@(=(IfS)%rs7lSXPmVK1n~YO+&* zJkjHmPy*bqq%>c(+;o1u3v}I{;O$xXLd_bZi9c8yroE)s$g1&^s91rdQFy>^;##!R zCy^t^KI<}P#e2--knB^I&y$T4GRo|F=YF`k>O>!{7Bv$1hkv}b zSXc!lq%KP;KI!{WQ{lu|ZgPZp5gQXy7>q+HCx){Z6eq(U27Vq==^j&pw0dC!AX zDc)VM3tEimuKR~bup~=4M>kY5I+DBiWW(Gt;zuwrvH8E%Q9lN@)D(!B-}o!-Hxz{4 z7Go_2TUsUyz(d$F%X#cbFA7rkSZO#uj{|YCbuDCbcx!bAN*lVz5pruyZgM!P4({si7auljxrsL%?;;ZV5i*g-N z8(Pu9o0(dFXZHH*zZN7=bDhNm|Ltu>>Hg~+TgbmHNMQTF_qSvnEdIydL?-q3;~#IC znf?Em%2E{jK$<|F#QKZ@&GxcyAP1DJFU`Grd_aNVt zMi3$*vXcomR!i&Zu1{A#ApN~v(5=C8&~2`#HypN=y*uZ>$Xh%slZ@Y|uupmD9gL+C zw=}NWR5{6A)XgoL*@QDnY5uMP*uK9^s+!;!&=YtcRqX%x3hU3#59VV}L0&T89dRmW zvlwoj@<`Od7&chi%$K(umVd$4I^^>XfgO0>Z47X8lnZ0zHP+#Jf)ocEfDT+f`e6}{ zo@-b~x30`q5`{ww%;jqg2Z%R1rl@U} zcC{ve(52)V_rkC!)q~Z4ZbP@IPZpgpGC*PUdJ7zUG7FU*$Z-hEk?yL_Sd%!N=uCc{ z_~;0u><7GpVPT&D`aHtzXi)yI?)3i~1a&)G6*DxyomLyb4Z=jvnx9@I&W~R2Ud@qibx;IrcHt0wA`G-qQbN}HgaV;Fo)XE zBWoRRqnmeRdTIoPr~G3%y&!Q}@BnEQFO+=y&tOmyq<1Gzt2-hAUgYhixuH{NWNow+ zPvv=Zxp-_Rl_ne2q8``eLFu9$_X?Vb$?i9#zDyUZgHgGxOl?|~l)dZ8$Y}fU-o~!y z;&|U%+cXtGP`_Qzb%{$&t3v^RZyy;Y2SDMIAJ{^l5R@9dre9uaGrA+|b|DM`sByTK zdrX!O+$lfy2z69M? zh~-(Sh1OMi(iB=O;df7#(>lsGCi?M38In6q8>9~Q1Y1-U0)wDak%!!1;GJfudh^HJ z2$EhO8~E#hj2wF#6`l*_S+KC4lrp#|6@cbU91%UYQhCg7kPg^}p8=TKp%lo2VOWX_ z6|HY2PWe?vESq>-9geHg&7&-n4yZ_%A(Xv9(O)MChtQ3wI*GicJ+;Zjf*OP4hnk4O zjWQkEG`_v7K;eCLk;RlU9JB3soW=0D)H(1Oz*9RF&5Qys)tgod7^4$ zDDWq&3BamVh(c=oSR6D~KoFNR3 z5C$d?8cqnm_CRR*Kc-;A==vcW>#jkRtAEzk4b8I``N7=qb304brg3r{HxW1;@S;_~ zzslG{>cQk!PoP`reE#TIyDhIlmFYm_f%T7;N=A|uqua@a%+a7`m?TT?5+a5U^`oWH z-H}~6n8LkBvab_aVc36T+I}w{3j*x*llu|D5cm}h@rkya4IAcrU4c>0m>4$ye5DPg z&yl!1qZR*+Th!mQ#uJokl_v23Y0juVWFz+Jfm{dMlaNmqYO8Zt@lO9b2N!T*5K6<^ zmABZZUts@B3iTPi^c`>&k6ll#Txy0d8;D~m({>gXL41iG6|-+d`wjU|ei%^NJe>Fo zFcbRgtN%Zq?|-j(e*Ew9!#~43)7l>ncmU)tyaXy^JAqr5(%PKaa!* zJ&HUWy}jKYnGcR-jHMiLwNqyh!D>}RCvZsn8D<3NwcWGLrq9v-NEyo@f#67C4f^%T z!1BOQ%9<9)D-}BqOuzOja7~z-AD&Y^mUzj5nD15?VTbk&Qlk$?YF!Vi`4HTYoqFtRJ#Y&1Tj-0ssB<^Wiv}kr6w4kUs^64JaLNdiUSW`(+?T2`2E}pCU1Ttn)Dml5eg9as&#A94!jZ)isQB z&I)B+dT8GsOdSv%rOd`4y<||Q8)4wuWKIrJF?fH8pl>q|E``9uaze2eORR9L(wgA) zAz#AQ`&As#&GyqZQh;}wfm2Zh6AMZ_|8#?GBd`G&%dx7wg}51qNb}(fmb_ zwdit4eD(ms?p3J$WZ)ksSu1k`7UA35y4w9-H>;wW(lA$h=}x|Th|1!XevrwHCl&oT zf_!*7JBP*_SG)*ev`*uUP{;Hs;6J0LfJOPTljHO$BU<}uZF#U-hM*L^32ME! z83e?0U-wk*d1*ojJ98lQc$d-Nrbp|0!_=T!$XQg!2rvT15dy0)Ge3zVf!o}SHSpXIb zb4rzA6rTmpW;zX~f`&StIB_e0uIpo4?ZgoReVpThz-aIev?yTM&rJ1Gjz~1%P?M@@ zx_F$(t?|ZQC}c@~;EA4TEfmcG^il~;ne-!#XYlKB=j#e=D+i9`1rIdFO4-4qwc&H1 zm1uJv9Eh={5ZmS<5k>MiW-15USsWIhOe>I3Q<=Wu!m=14YgpR;a0#4CTKy6p${rA! z+zsm~oSSwYm8B%QH>d5T*r8{D&XD;E{%hat*AGoQCw!OmsYXqZ^2D<{vNK1I)m*Wl zIdqK;kI+D%M>6o_KjV~{M$hT&A=R=_6Kg1Uu38yKTuY5qR}B40kTX>*tRa64APFl9 zGC|^~%R>0TM1rHxDJUBq;;;D`mv$yi3tJ!j0oIqh5EIF}DgzETtwL6{+b8l zS-37EaM^UAKzzDXJOUTHSuKQ|nPu%M~FQfjI5X8yK8E>y4AA z4c#usAKWEXj_7+Jdb|h2p@433gRH+o(eIAZ=413`DKxFeJp;Fu*Vq6G7czy3G9;XR+*BF2@>@y`OHUhqJB#DNd?f zgiRv}Wo)a)l&W;Jz19^GSSz$zCmOv^TEyqY&@N2u*rGmJ)pdp=9aMQG7MgM2b5dpq3uRdZc0?;%l_LIl?d?nl~2) z25~^XQ+MU;L>}oCi>}Bu(_bn>k~Z@@4p<0r6gy6$%SD*N%r~b#1C3B0%AKVA&b98{ z)+{c11uZp9t4ab=KerP&y29ND%M}YXeTxW)q-tS`$+$Z~Y_>{KjdjDj zql=*hZ(2ZAvLKh<_uYlJv1Ci!uRO3Z3;8wTTGX*j^Vy(rAL{A58HHfz@MC3r5{qD+ zNHug-U#5b6<6E_FX#M`j;rsU(4d{zUcoad~x&7F?_hR1f;k@7Ls#MoN#Wex){TVH3 zDrC=dS0Fs{g!XRAtZRBUn3?IQk355~Nw$rGXkz$KzMbI6Vxu*zLRcPlNW-n; z7Bf`1DR8nkQfyLh<|Fxpi@>5aID_0@*S{J24h&+}it~w%qTc#5Q99P6?PqFv`_3)U z!}LLMqTIdGo~HJ*4)^kFTyslqJNOniiMJ$t5(Wv(Uw3A#G4cyX`?Q`T*2zEKPHI<= zLQVbUSa%AW@sy|ut^U?RK()tQ9SFbA-i_iC*Q9Y?8DZ&!h?eF<(>NCx$%Q@P)M43vur{%V&Otu&7?w zxg*IUi(Pw7?{hS8^{8j_c(M~sYT^xzU;Svb z6mpJkA!pAI2!;p9X^p4sRoqov?P_OH;>6VtW#!45J(w}iai|dW6)n-1`dYIguT}Fs z(2=7dpVO~k&YEx5bVfh885Yuy!_xF7x6JKWq*T?3#FEg-zq8ksv-{y`uN1djv#Ktw zdf@OF374ri(De2+B8(d8Mc#l+qz(xKZSq`}DncZyF*q>c^>Z^XXVmw0D4qrZh*q0@ zJ@Z`3F(Xo?m6SrIWTDc0B|dD>jp8rI^P9!W73|ZP$X=Fgy!n=kyKQF1~H#( zp+igSFp?B{Ge|1%foMP=3Rz!z%|&j3HD9NxB;2yXMz^~$`q(Q7v}RCRi^(Kx$tuFu zs+ZvXQfq<4H?k{pjdY8Aqz-(v^NU;H5@t}fRgc-}mr?L9Q4m=Z_L@?=ZZB?-@rtn7 zx3G&pZ%4!%F%#~_(*WcutrxnX$U1tC7j!z9jA3}0TNfGmdD3!d&PD&W2;x6TD?&ei8hJ{Z**gA@LKu?RZ;zsc7Wxf4 zC1}&4qKzDYW80a}>L$$;15vd=5o1@cCKDlLvH)D_ni)XM;=cl(5+Z}y6GpF_(RK&y z1HUW6`#Qr|5}^e3x^wF|^ZxB@;rI3R1=>e_G6=;uwOk!~VF_Qh$g70kRKopSX{EYc z_}3b(;L=pGX%vi=tWr}|Q7W$Gy{yu`VjfS3C0Fz(o;Y2rnlYHYrh`A0XoP}D$UL1E zTh14-SbpKARh7vt(rmiS+Elm>VZnf9-OG*_8nW=6gN9X_&7n=xtIf6{zmD>YDVSw5 z40kgsg@}>p4=_?OAy#p%xtnC5U6QPx`h;ZvxH()JQnW0;_wvIk{qm*jA2KJtgy zAW_C;6*ORGq2{t7X!>B|J?G+zYdh{)qud@EO{4)U09Pp!Y4)jO&d+qt?R-uD%7eD^ zjQ6t)DzH_Rb<^Ar&sl{6QYUp12XJWuYWfL+5iY+MJMuFKe;(0ERzM_K#zf+k?~z0` z`k1z?CJmIx(;5}&zLv~ik0-B9(|4mAF+qvT9`d{i3WIXdH1fQbfy)Y}-0woF$d1BZ zOR$|X0d?7=voP!}?%PMw3K&Hai@*|M8v03**iJlxpBu+x@htQ-e*KY{cQk(?%$UFG zt2NytUe5Hq#MSc_@q7EYU+&S&x(0wV%jdkGf12*~2srK%zGXwmFV38`>~Eib{g?Lr zQ)Gz3{V(^+;s3(|{`brLAKrogdYS)u2QI7cI^%F4`JL9S-HU+IgZm&IR4mF?m{7>_ z%`vrQ$WkC4$m3Zfo|Bw&vdDeCUR}6gWf6kf85BY~mo2_t13L5Ews`CMxOA&rO2l{p ziruwBbJpE~KO=Jp9RYXyv^Yxdo@t)th|A;;<`cPiW9=0_XZbt zKUT#lp@oXT)wmr`1ag*Z!;MRrHS5tO&4^j>xPk*RBsuTG;>$3CgB9r(L>VLQosCZE zV=b(ZMj>YlN>lfpLjBQp&9fi_QVO>-b+W#;Y)?FQ4CS3f&f7B3EaV>D9DP~vc0YqF z`7ZP+wy@?tohew&dG&y0l+?yb%h`;$DB)>W@OrhxDxK-7#Y)*L6F5y2VMWEsg}#uUP`awFJLTtn95 zEkn^n$6my6Ez9|R;g%>3NU2(YmK02IYjtYP)7}WE&2>gwBW7$4*+h0(R=J_4N@c5D z%#v0TWH)f50Y!((Wo1#cRI3;{x8as)(S7;yqlJ$s7l|q6E5)&4lq!xzYhYx9LWm&i z=7|F?!)gdlE~`)_k>*U(2*l+gpj(f?_kMvCnD-&6!jO~+E$Me-Kk*?HQd+IVf`)i4 zao0+);tV!=XIj@(&PS3y*gO_k;vV^i4qfeuV9$UU=CL}eMce(1a}*2(k0rym(@_%p zYFG)xbg1Rt@RzIn%xO4=*xwSW;tnCrc<={-`Co+}ShRF3x)QFF=+G$iCS4IxaNx4% zKlc#^EoQ$+U3kmS{d^nXwa9|W<Z_{ zh#oA$h4_t~l4Pe@UUapZ%N4dyH`DfV&*P|2i`H5pXpT9hB3zJG*2llbxc}Y!$C+jH ztP|I*HGp{qfn9%+Us7kSklpO+2fL^HjHVj2TDSi|X;}~JHO~jLxf24UG%bMkfRi~TlHuWuJw6snopIA~YjiI-6tjVa2RLCH`^CPQ2+Sc6ET;psOg*Ju%pRQ4f#gD+X-}Rs@ zr(ZeFJ=JiFlzT5>6(=HJ8NmMhcdf>rcrrV<((HX4w&SgXlyPvoE zqVJBEktxfoW?Rbq1!uYBe*mvE^fB72*ei$bsZtqjKm*h@tC4rfe|u`yq$bI4VCf;E zKPV)wQ1HZQWEuDkIM{y+JDw(%oWpFi_(h zvNT5K3~X}>uqAE6w?6mFB5<9mT6X(Y?Q?0AsK698ce|QRUrqOGlTID?>q`Iie*GW{G| zn}}eYN)64 zsoX;LqvSb8^b_U*^YfO+O8L~*U6V?V@gD9ls_^-3VV5W+aA zDBaxLN(T}y%pycBbE{7>7maeYi8N2mB2r7Uhlxm*%x7#JQZCWYG+pW6iU$ z4x_1MqeFHDQ}QXJ)PPlu6K}F?jPW8}S$9PVKmzCC_sd&Kn~H{|ZJ}C3&(a1$HEk#x znUbL{0X*k&c8H60ECSnn_7LT9iS`gD8~yBxA_ud=JSMq>QC1-=TBRFl?J~f$11PQ= z;DH;+dIG9Rq5Th5(pJ{Y5EB*|5sypnJEqhah=$S?#wCT@!Lo?90|b(RKAQ=pGRo3xle(56dmT z=183GU=>jjT8u4=13z%sUmBFz+Ul6{@ApJPbIZy_I4Z9dJ$y{10D+#s6W_ReQ zA8H55WZO*X#X%)KI~(g(R2{Gc%=c(A2PuBdT)N`>OQ z$R_JetY^59jzhIjCBn3JBZD(Vy^QyqxrY8@#dO9sTlnt61hM8v-Fwkfie>vz^XzkU z<-A%gX5)PG>Zz8jD;c`AP+1hrcWJWE$OA3JTSmj&D%e2hQEdcw!6wzu$dewNW_BIU zSTOA^em+_LE3C!Jlj;;G4We>d6GjK4Ldq7~D18Qp@c|ZLhDw?35d?C^;M?L}{7d#$ z_ODiKyrX#2>I|gnV%pzE{Iel{W*_Z=B<>0WibQZ^21uGlhFTJXSc#t^etcAlxTxc4|n&s+2{~{f2!4jBfhDkzEA#sv^+jjXWL#B_ zx}UUrqCdbCNMNuR`;9BEErF=A#;gTBLg$LwFW?s@+T137whM7UUoxFT>k`$eL8b@f z_qe1x`2f{r)4-58x$o*w-Q6y$0qO`wTJ$0Uv(_(biYy6!dC2OFg#IRW#M{GF2!0~< zY=oSM-M%`{2GZuyxNjpVPOR=47O6QoPJ~J-m78yygKtM&23h%it4Hs|)z3H3!#Pp7 zm7|?}r-gvYa$6lryoQa^L@DFGSN#)w8q1mU44b~Pz284jf6O_sK;I+rzOsP-h*}}x zy#fKgm#ifgVMv_8Q1LOg>>efca++F^ggftAh<+U7WFUHHoe#kz+qToOZFFqgwr$&!Z)VouznM8#bzf_pb!wky z*9O6yM3#rq=?0gq?^`!L9!c1sG+fmb%gg=z_NHLR4cCB5Vw_4epxA{%wy?xrT7+$= z%D|N2=ucEg<2O2qYL4gJR6)Rr>8~z*f@3gf&hJ8d_PI9XbQWPZY=DzPH->^0#6%$Z zgG+4pRO`qt>?@Uu;jy}o>`LnuB85afR=!teJ93uGWt>cL$e1C)w1QG+=w^H5A?~fN zFHv-Jc4n@+LjzcQDtfj~vQRualjm47pIdF?LRXc(do9kay1l9WB^Y05!zVg#O>k~^ z!0S^O#W|fQ{4>Mqa4J@aN>#@v7*$ZS9(rAtd&%4^F2P8tmp3t%;?`;>7mH<4j2na3 z*iS7$v$@U7#7|b|sHQpORgyYz2~q2eO#OdB|xh4wW7cb zOUC{3aP4B%T^ivObc?6qc3(`CWIos`ZR9FyV@azMaK^qoNw)D`WW$zTHF}=kJyV~pk*_z@H}n$qcjnwp*>|>)l(G;S1ZX`H2~P$)6r|b-CV+u zXsm;rm<5?6l7HKyNQTHG#=rc{8l*V{e`&YoUOA@Rm-twppP=IH6a?Dn_DJNo2DTr?lIz@JeXr`t?s%+Y z7@`X>7fLb{OciAi73F%Nz>87c&C#@bn9*V4+ zl=Q-=ag3lcYnBVlb z0&41N0+F4i-#R$XhxQl)G4QlMba3^l@(TvOIiIQmbscHy5BGJN{=l*k!IZq5UD%!hm7#kp5!Km^eRHBRlCAv6zzf+XAkpH z44oy?y(1qQm~)cvUr&3z+ksGe9`;&v1WOXI_SHq_ds5`elR?AH5l4s(&8BYOF##^M znmrb;exGkd*|lrDl8QafWcGh4yB%qHox#fXRNBtimU?)S_b!z~xUeT#EjX^b&>NF} z+k-C~k(Ty&HhrKQxA<4RKPo7~E);iE$<43qenU6l*nJWw#XB~NM~V)ATluqjH<;}q zS^K!>73^C{uVHwVI0AkC4Lu%Fo-T1Ql(~bw8ZoNEx!GoR4cmS_Ufy2X8oF*bO3Qof zaX9YQbe8n-9$p!mOlo(3t;IKy$3fuA>K8!vMy&k9>%0pwX`6sYh(P&CudBz*xyaH_ zy3H;*7FenXwmPZ`njwcu5iUl<>?b1bir}DU*@ol5SU>ZT`xSRzmZ&{}_N-qDQ(Qem z)>5?7uB&!G$Ow<&M^OZ8dcku^t!<}f)9b!n3j!Va25tlLhNu@OYazXFPulY6NpuwB z$TYbWD90m&`A7{+yY^S}on$!KJO>>UK~Ww;&C~8X+`~Bb7T}{`P4^bo^?0@`+kHkAR zvJJ5;g(vux2Pd-c?&K2`Yd&nycI0wo8)MB`^@WUv^)k@&{)uI=4w=KwJ2IPNqK$s? zYR<*KXH+`o+*;zcKM-2$y?6h`!pf-hxEIT^rC=MME9zk3AnBN4c^554n*6g^F7TSIgNPjXOGHn>8BZ0>_e%y&B*#0{J+M~@wpJnVh&To z9ci@2nJ_yN7>vp*JtIzR*SI$O+BvHI>$0FAPL0`OKjw4{MnS&Ufz=6-F~^k0iJ1)U z@XIgBsX6k44jWf{MBSVTD;uxT=s~P^`l{Qm1NGGPXzw@W%ypt(^xDRuVd%AvnHw|e zyEc1!Vf*a%KJtf=Oa(kV9!6Wf@(!5Kw?_VBbZ-BAg&dxU>(ffPZj(yr>n+ zG9#hj@vX&)4@7yYaX z<#=2IYKw@<9_Ll7^qGAM^A4~1z_KwefGDTLm?&n{*iAeAwpc4B=l1Ib{@6#518>v` z$vj@myb;+p!+-^OS(^KH6xAhG+wb|n5yyQ+m#=35YmykopgBv9H{bnF^+Be9ronFm zgIDox9S!!BVU)^vl`9xTrIOVGEiQ2Di+YkMSxF38sPrgqi+895-V!cG=F6AL!f7W? zdatz|aE+E1(FMB~hC2t|keUNQ@ELphkV_NXO5pOh#jst;wGeNxY3MGLB!`ETU(dfv z9b;4dj=&4no6P^oBdio#31&B#_ITiHcLT4>cX*zECt<;BUqErsd=oK{sL6|GYA8NJ zPn0tlz8S46vTXIMEo%|S>x5I{9N)t$ku#vjNG3|Hm`fxWZ;mEuV~{PI@Lwf1Kr7x5 zJ^#xv01b7iF^zj_IlonKRw3w^GT&F?3;nHFB|CH3-{=!4rF7mVA$u6lD_(gz6ckYo zSnZtAy!b5NGFQ5_RI;D)$@X35oBNLJ-!rd%I1!59SfFC#Q`|@8VXd&^O&+nX)@MTf zK2R|@;l*+xgKLM`KeQ>H=2aT?-f-tj9#gdvL}%A9(h7rst#X8WWcq=`xJKIJ%qqjr z8l?f?(|GZ+$4OQU2TcqUJIay<{PdfB<)9^jfbaIl}Q8gq0L=~hEKLsR(P za}LJ6?|SAGI;I^w5x@BUdw$F|jrj@M@1Nv$;{Q`^`mYKj#s9C`^q*gjDr{@pb=Ege zA8=FHiupDNPMj-06WlY>Q#5MnCeBX%6;x1~SypuM1tH36wuA2N`y1hWVhI`B61rNN zyWRV*v56c&e93_n%6o!sQ-suX%;+*1**9UcrU;t50(5pMz<0$gHEc}y;l>FLbVE98q5Ik79|p0V)oXM%7?~8|leM0|vb&4e@EVm7MxVL^kRfAtS_k$D$-rD!{;y!T#Pw zQI$W)^0eLLzRn`uyut(fvlEFksUK#D6$=ypRUT6<5f$}IAxC}9+L{lGJh?miQvn3T zbJ_JCH(uVOtL6|tObFZnfp+}wZBnO}ZP8oo`X26CVO?RA|aS>K80tB0ZX)WhJo%II6qi z9heZ*92s`8m;vIvgU)?sF|iBNjKy~Zve{sBhhHJT9>>g5#TQ3 zz{*tTbDwBubq!x=tOvg?hq7AlFugb(%{O{zL2zL&jYi!)cFvaWiA1o@b%)ze(lo@) zNyAS%DPST%O|5(e-$Lsn7-uDsr3X!-wvxX?7UZ0t8JfO1a!M!(Qe{b0w{JAFIHbLl z^O(?75T_O^g4?cbO+)WM*>aZ+;;fmN39m3o+_v{&1!S%*2tgFT0tGuUav@+<{6rt(iCzVPm1U9pnx>kmF}_@P!k9 z)&%CTO5SZPG(<;&%KXi8C?GJI+_JVIbK0Sm9+6%|>UzTzxt(J$Mk}$*?ElxLxD_>uDoU zw7+21nu}pSD8={ddtFj&o^o1+{YGBlah~d+vJ6f0#-+&n{6Oi9=E?IR173J|F%-l0 zTD2;>7%ahO3y`lIj0Jv%LBV##25ZN0?onY_i-I%dIo9BqOru$9A1TXH?&0$gBgyZ- z>H0hCf#Skk6M_rJt7?F}INLMmkC~>|8^R0Jk#T%Yii>`bt+z!4By`?f4f^hxdhY01 z{CJ`M0xL=dGjoY9e$V;1E7!i`Aovhpov{r4O`0MCo-!$h=Iqhr@X%oQP@ z+epvU2S;PU9M+=*$J{7_*bHrkVDu9V9<72HT-3h>s%TG8>X zIi7K76Zi{FnAt?O_X1upHLoxQKA6A=^Eq73bXoq>0zt=x|9N7%k*#!Ise)$tNQ?k% zSExhnUfCfL8Z-)~;BMfEa~baqELsZbE4(!nh1q$Aqxk znpCayFG>MmN;6h9d=*Hf*EIcxN$|?yYV4(ybQh^8R~(gUes))RjC##%2Kh1wJlcR4 zD_|>VGjx$qw<}1W4M(hyYqIBWvKbxl+I53|CyQ}n)AcHp(xmz*z@ah$Z~B_oIwV** ziIUgNRjxjM9<8FF62jo4F_kL;x-rFAiV$X)_B7|o2pFZ%|bTnLdcmZb|mFnk?Zo1QB z0Elda*lY8sExtujsv^)#ke0P4l=9R?FfdO@{X$Xmr6^ zODobb`Aq~Tq-Etsx*!DQlpR-KhYBL3Yl537_~BXkSs({SEl|6R>k-~= zyutc)(Q0gFjVL(&qf{&m?8&H#*rP%cbC;(Ll?kGE$tpYHHUxdlfEqn&y_wTrTw62$ zB#GHS$d{{{hymCSu4~GHK~8lpQ>P|n4amNn*ML|6PHgl5?q)3MBvr%aJyev{P@3$eOCjDTqIO>CEqrmkOcTyBnPp zf)@R-8T!6g`F|e;nb1qJsu@qwNte1*J6qFN?`mnlEkSZwl*_FSQJ5E$|EV#q@3s_X zV`9q1Q{#upl6{XxkK+sE_#=d*mkARQ(BT%w)h?D`0(TLLdq+Xz%x}hc&2tf{l6Mj6 z#uAH;a4=6mJIfx#;S0cqLPLRQHW?Jfym0&X3@m$wx*#S;7pfRXi%r-nQahN0Wb4ZE z2CQWeO+d*W?@Qy_0xAm#edtlX-uCuy<$yVI=813Da%nXMK4ENn(2*|E(jR-v`97+6 zdPaJ;m{e>g20@Ji=F{I?Ld9;M1UK(ON+^Hlj%gDc z6wDA%Ly+sKR^VRMhKqq%BB^DPS}5C|1-uwFq>X51MR_F zg<0*6y1vdYUW7X2q7}q$L$sy}EBR3M0`+S%w$&4FYTtwt?X$UuQ$#?&66!`v(Wu0J zjnG6yc_6E*`Dd@d#seb594Q{i7fR=ej4nXTZy76&A!LcbWvK?v7V^%RooIP!d4w4Z zFb|@#7hHO?ZVo{~!D-;+3|6ydwK5S6py|{z2AKoF9ecv+F0|0jTaH)cL#{R+CSHSOTgCjzbo%&L;J%0DiIX1Cpz(a4mf2cd#eA^# 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 RqLnoC3QSQ6Ss+S7{{xt+e2M@7 literal 0 HcmV?d00001 diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/Activator.java b/com.samsung.slp.common/src/com/samsung/slp/common/Activator.java index 1f12ec3..3fb2c2b 100644 --- a/com.samsung.slp.common/src/com/samsung/slp/common/Activator.java +++ b/com.samsung.slp.common/src/com/samsung/slp/common/Activator.java @@ -22,6 +22,9 @@ package com.samsung.slp.common; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; +import com.samsung.slp.common.properties.InstallPathConfig; +import com.samsung.slp.sdblib.SmartDevelopmentBridge; + /** * Log. * @author Changhyun Lee {@literal } (S-Core) @@ -38,6 +41,9 @@ public class Activator extends AbstractUIPlugin { * The constructor */ public Activator() { + SmartDevelopmentBridge.init(); + String sdbPath = InstallPathConfig.getSDKPath() + "/SDK/sdb/sdb"; + SmartDevelopmentBridge.createBridge(sdbPath,true); } /* -- 2.7.4