--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.samsung.slp.common.connection</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+#Tue Aug 23 10:32:23 KST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Connection
+Bundle-SymbolicName: com.samsung.slp.common.connection;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: com.samsung.slp.common.connection.ConnectionActivator
+Bundle-Vendor: SAMSUNG
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Import-Package: com.samsung.slp.common.conn.device,
+ com.samsung.slp.common.util
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ name="SLP Application"
+ id="com.samsung.slp.nativeide.viewCategory"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ category="com.samsung.slp.nativeide.viewCategory"
+ class="com.samsung.slp.common.connection.ui.ConnectionsView"
+ icon="icons/Connections View.gif"
+ id="com.samsung.slp.common.connection.ui.ConnectionsView"
+ name="Connections2"
+ restorable="true">
+ </view>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="com.samsung.slp.nativeide.perspective">
+ <view
+ ratio="1"
+ relationship="stack"
+ relative="com.samsung.slp.common.conn.ui.DeviceView"
+ id="com.samsung.slp.common.connection.ui.ConnectionsView">
+ </view>
+ <viewShortcut
+ id="com.samsung.slp.common.connection.ui.ConnectionsView">
+ </viewShortcut>
+ </perspectiveExtension>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="toolbar:com.samsung.slp.common.conn.ui.LogView?after=additions">
+ <command
+ commandId="com.samsung.slp.common.connection.ui.command.ImportLog"
+ icon="icons/Import Log.gif"
+ tooltip="ImportLog">
+ </command>
+ <command
+ commandId="com.samsung.slp.common.connection.ui.command.ExportLog"
+ icon="icons/Export Log.gif"
+ tooltip="ExportLog">
+ </command>
+ <command
+ commandId="com.samsung.slp.common.connection.ui.command.ClearLog"
+ icon="icons/Clear Log.gif"
+ tooltip="ClearLog">
+ </command>
+ </menuContribution>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="com.samsung.slp.common.connection.ui.command.ImportLog"
+ name="ImportLog">
+ </command>
+ <command
+ id="com.samsung.slp.common.connection.ui.command.ExportLog"
+ name="ExportLog">
+ </command>
+ <command
+ id="com.samsung.slp.common.connection.ui.command.ClearLog"
+ name="ClearLog">
+ </command>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="com.samsung.slp.common.connection.handler.ImportLogHandler"
+ commandId="com.samsung.slp.common.connection.ui.command.ImportLog">
+ </handler>
+ <handler
+ class="com.samsung.slp.common.conn.handler.ExportLogHandler"
+ commandId="com.samsung.slp.common.connection.ui.command.ExportLog">
+ </handler>
+ <handler
+ class="com.samsung.slp.common.conn.handler.ClearLogHandler"
+ commandId="com.samsung.slp.common.connection.ui.command.ClearLog">
+ </handler>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ name="Logs"
+ icon="icons/Log View.gif"
+ category="com.samsung.slp.nativeide.viewCategory"
+ class="com.samsung.slp.common.connection.ui.LogView"
+ id="com.samsung.slp.common.connection.ui.LogView">
+ </view>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="com.samsung.slp.nativeide.perspective">
+ <view
+ id="com.samsung.slp.common.connection.ui.LogView"
+ ratio="1"
+ relationship="stack"
+ relative="org.eclipse.ui.views.TaskList">
+ </view>
+ <viewShortcut
+ id="com.samsung.slp.common.connection.ui.LogView">
+ </viewShortcut>
+ </perspectiveExtension>
+ </extension>
+
+ </plugin>
\ No newline at end of file
--- /dev/null
+package com.samsung.slp.common.connection;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.samsung.slp.common.connection.device.DeviceMonitor;
+import com.samsung.slp.common.connection.sdb.SdbHelper;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class ConnectionActivator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.samsung.slp.common.connection"; //$NON-NLS-1$
+
+ // The shared instance
+ private static ConnectionActivator plugin;
+
+ /**
+ * The constructor
+ */
+ public ConnectionActivator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ DeviceMonitor monitor = new DeviceMonitor();
+ monitor.processDeviceData();
+
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static ConnectionActivator getDefault() {
+ return plugin;
+ }
+
+}
--- /dev/null
+package com.samsung.slp.common.connection.device;
+
+/*
+ * 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.
+ */
+
+import com.samsung.slp.common.connection.exception.SdbCommandRejectedException;
+import com.samsung.slp.common.connection.exception.ShellCommandUnresponsiveException;
+import com.samsung.slp.common.connection.exception.TimeoutException;
+import com.samsung.slp.common.connection.receiver.IShellOutputReceiver;
+import com.samsung.slp.common.connection.receiver.LogReceiver;
+import com.samsung.slp.common.connection.receiver.MultiLineReceiver;
+import com.samsung.slp.common.connection.sdb.SdbHelper;
+
+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 public 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<String, String> mProperties = new HashMap<String, String>();
+// private final Map<String, String> mMountPoints = new HashMap<String, String>();
+//
+ 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<String, String> 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#getSyncService()
+// */
+// public SyncService getSyncService()
+// throws TimeoutException, SdbCommandRejectedException, 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 void executeShellCommand(String command, IShellOutputReceiver receiver)
+ throws TimeoutException, SdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
+ SdbHelper.executeRemoteCommand( command, this, receiver);
+ }
+//
+// public void executeShellCommand(String command, IShellOutputReceiver receiver,
+// int maxTimeToOutputResponse)
+// throws TimeoutException, SdbCommandRejectedException, ShellCommandUnresponsiveException,
+// IOException {
+// AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
+// receiver, maxTimeToOutputResponse);
+// }
+//
+// public void runEventLogService(LogReceiver receiver)
+// throws TimeoutException, SdbCommandRejectedException, IOException {
+// AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver);
+// }
+//
+// public void runLogService(String logname, LogReceiver receiver)
+// throws TimeoutException, SdbCommandRejectedException, IOException {
+// AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver);
+// }
+//
+// public void createForward(int localPort, int remotePort)
+// throws TimeoutException, SdbCommandRejectedException, IOException {
+// AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
+// }
+//
+// public void removeForward(int localPort, int remotePort)
+// throws TimeoutException, SdbCommandRejectedException, IOException {
+// AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
+// }
+//
+ Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) {
+ mMonitor = monitor;
+ mSerialNumber = serialNumber;
+ mState = deviceState;
+ }
+//
+// DeviceMonitor getMonitor() {
+// return mMonitor;
+// }
+//
+// 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, SdbCommandRejectedException, ShellCommandUnresponsiveException,
+// IOException {
+// String remoteFilePath = syncPackageToDevice(packageFilePath);
+// String result = installRemotePackage(remoteFilePath, reinstall);
+// removeRemotePackage(remoteFilePath);
+// return result;
+// }
+//
+// public String syncPackageToDevice(String localFilePath)
+// throws IOException, SdbCommandRejectedException, 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, SdbCommandRejectedException, 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, SdbCommandRejectedException, 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, SdbCommandRejectedException, 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, SdbCommandRejectedException, IOException {
+// AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this);
+// }
+}
--- /dev/null
+package com.samsung.slp.common.connection.device;
+
+import java.util.ArrayList;
+
+import com.samsung.slp.common.connection.sdb.SdbHelper;
+import com.samsung.slp.common.connection.device.IDevice.DeviceState;
+
+public class DeviceMonitor {
+
+ public static final ArrayList<Device> mDevices = new ArrayList<Device>();
+
+
+ public void processDeviceData()
+ {
+ ArrayList<Device> list = new ArrayList<Device>();
+
+ String result = null;
+ result = SdbHelper.getDeviceList();
+
+ if( result == null )
+ {
+ System.out.println("No device attached");
+ return ;
+ }
+
+ String[] devices = result.split("\n");
+ 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);
+ mDevices.add(device);
+ }
+ }
+ }
+}
--- /dev/null
+package com.samsung.slp.common.connection.device;
+
+/*
+ * 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.
+ */
+
+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;
+
+import com.samsung.slp.common.connection.receiver.MultiLineReceiver;
+
+/**
+ * Provides {@link Device} side file listing service.
+ * <p/>To get an instance for a known {@link Device}, call {@link Device#getFileListingService()}.
+ */
+public final class FileListingService {
+
+ /** Pattern to find filenames that match "*.apk" */
+ private final static Pattern sDebPattern =
+ Pattern.compile(".*\\.deb", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
+
+ private final static String PM_FULL_LISTING = "pm list packages -f"; //$NON-NLS-1$
+
+ /** Pattern to parse the output of the 'pm -lf' command.<br>
+ * The output format looks like:<br>
+ * /data/app/myapp.apk=com.mypackage.myapp */
+ private final static Pattern sPmPattern = Pattern.compile("^package:(.+?)=(.+)$"); //$NON-NLS-1$
+
+ /** Top level data folder. */
+ public final static String DIRECTORY_DATA = "data"; //$NON-NLS-1$
+ /** Top level sdcard folder. */
+ public final static String DIRECTORY_SDCARD = "sdcard"; //$NON-NLS-1$
+ /** Top level mount folder. */
+ public final static String DIRECTORY_MNT = "mnt"; //$NON-NLS-1$
+ /** Top level system folder. */
+ public final static String DIRECTORY_SYSTEM = "sys"; //$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;
+
+ /** Device side file separator. */
+ public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$
+
+ private static final String FILE_ROOT = "/"; //$NON-NLS-1$
+
+
+ /**
+ * Regexp pattern to parse the result from ls.
+ */
+ private static 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 Device mDevice;
+ private FileEntry mRoot;
+
+ private ArrayList<Thread> mThreadList = new ArrayList<Thread>();
+
+ /**
+ * Represents an entry in a directory. This can be a file or a directory.
+ */
+ public final static class FileEntry {
+ /** Pattern to escape filenames for shell command consumption. */
+ private final static Pattern sEscapePattern = Pattern.compile(
+ "([\\\\()*+?\"'#/\\s])"); //$NON-NLS-1$
+
+ /**
+ * Comparator object for FileEntry
+ */
+ private static Comparator<FileEntry> sEntryComparator = new Comparator<FileEntry>() {
+ 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;
+ int type;
+ boolean isAppPackage;
+
+ boolean isRoot;
+
+ /**
+ * Indicates whether the entry content has been fetched yet, or not.
+ */
+ long fetchTime = 0;
+
+ final ArrayList<FileEntry> mChildren = new ArrayList<FileEntry>();
+
+ /**
+ * Creates a new file entry.
+ * @param parent parent entry or null if entry is root
+ * @param name name of the entry.
+ * @param type entry type. Can be one of the following: {@link FileListingService#TYPE_FILE},
+ * {@link FileListingService#TYPE_DIRECTORY}, {@link FileListingService#TYPE_OTHER}.
+ */
+ private FileEntry(FileEntry parent, String name, int type, boolean isRoot) {
+ this.parent = parent;
+ this.name = name;
+ this.type = type;
+ this.isRoot = isRoot;
+
+ checkAppPackageStatus();
+ }
+
+ /**
+ * Returns the name of the entry
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the size string of the entry, as returned by <code>ls</code>.
+ */
+ public String getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the size of the entry.
+ */
+ public int getSizeValue() {
+ return Integer.parseInt(size);
+ }
+
+ /**
+ * Returns the date string of the entry, as returned by <code>ls</code>.
+ */
+ public String getDate() {
+ return date;
+ }
+
+ /**
+ * Returns the time string of the entry, as returned by <code>ls</code>.
+ */
+ public String getTime() {
+ return time;
+ }
+
+ /**
+ * Returns the permission string of the entry, as returned by <code>ls</code>.
+ */
+ public String getPermissions() {
+ return permissions;
+ }
+
+ /**
+ * Returns the extra info for the entry.
+ * <p/>For a link, it will be a description of the link.
+ * <p/>For an application apk file it will be the application package as returned
+ * by the Package Manager.
+ */
+ public String getInfo() {
+ return info;
+ }
+
+ /**
+ * Return the full path of the entry.
+ * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator.
+ */
+ public String getFullPath() {
+ if (isRoot) {
+ return FILE_ROOT;
+ }
+ StringBuilder pathBuilder = new StringBuilder();
+ fillPathBuilder(pathBuilder, false);
+
+ return pathBuilder.toString();
+ }
+
+ /**
+ * Return the fully escaped path of the entry. This path is safe to use in a
+ * shell command line.
+ * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator
+ */
+ public String getFullEscapedPath() {
+ StringBuilder pathBuilder = new StringBuilder();
+ fillPathBuilder(pathBuilder, true);
+
+ return pathBuilder.toString();
+ }
+
+ /**
+ * Returns the path as a list of segments.
+ */
+ public String[] getPathSegments() {
+ ArrayList<String> list = new ArrayList<String>();
+ fillPathSegments(list);
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Returns true if the entry is a directory, false otherwise;
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Returns if the entry is a folder or a link to a folder.
+ */
+ public boolean isDirectory() {
+ return type == TYPE_DIRECTORY || type == TYPE_DIRECTORY_LINK;
+ }
+
+ /**
+ * Returns the parent entry.
+ */
+ public FileEntry getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the cached children of the entry. This returns the cache created from calling
+ * <code>FileListingService.getChildren()</code>.
+ */
+ public FileEntry[] getCachedChildren() {
+ return mChildren.toArray(new FileEntry[mChildren.size()]);
+ }
+
+ /**
+ * Returns the child {@link FileEntry} matching the name.
+ * This uses the cached children list.
+ * @param name the name of the child to return.
+ * @return the FileEntry matching the name or null.
+ */
+ public FileEntry findChild(String name) {
+ for (FileEntry entry : mChildren) {
+ if (entry.name.equals(name)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the entry is the root.
+ */
+ public boolean isRoot() {
+ return isRoot;
+ }
+
+ void addChild(FileEntry child) {
+ mChildren.add(child);
+ }
+
+ void setChildren(ArrayList<FileEntry> newChildren) {
+ mChildren.clear();
+ mChildren.addAll(newChildren);
+ }
+
+ boolean needFetch() {
+ if (fetchTime == 0) {
+ return true;
+ }
+ long current = System.currentTimeMillis();
+ if (current-fetchTime > REFRESH_TEST) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns if the entry is a valid application package.
+ */
+ public boolean isApplicationPackage() {
+ return isAppPackage;
+ }
+
+ /**
+ * Returns if the file name is an application package name.
+ */
+ public boolean isAppFileName() {
+ Matcher m = sDebPattern.matcher(name);
+ return m.matches();
+ }
+
+ /**
+ * Recursively fills the pathBuilder with the full path
+ * @param pathBuilder a StringBuilder used to create the path.
+ * @param escapePath Whether the path need to be escaped for consumption by
+ * a shell command line.
+ */
+ protected void fillPathBuilder(StringBuilder pathBuilder, boolean escapePath) {
+ if (isRoot) {
+ return;
+ }
+
+ if (parent != null) {
+ parent.fillPathBuilder(pathBuilder, escapePath);
+ }
+ pathBuilder.append(FILE_SEPARATOR);
+ pathBuilder.append(escapePath ? escape(name) : name);
+ }
+
+ /**
+ * Recursively fills the segment list with the full path.
+ * @param list The list of segments to fill.
+ */
+ protected void fillPathSegments(ArrayList<String> list) {
+ if (isRoot) {
+ return;
+ }
+
+ if (parent != null) {
+ parent.fillPathSegments(list);
+ }
+
+ list.add(name);
+ }
+
+ /**
+ * Sets the internal app package status flag. This checks whether the entry is in an app
+ * directory like /data/app or /system/app
+ */
+ private void checkAppPackageStatus() {
+ isAppPackage = false;
+
+ String[] segments = getPathSegments();
+ if (type == TYPE_FILE && segments.length == 3 && isAppFileName()) {
+ isAppPackage = DIRECTORY_APP.equals(segments[1]) &&
+ (DIRECTORY_SYSTEM.equals(segments[0]) || DIRECTORY_DATA.equals(segments[0]));
+ }
+ }
+
+ /**
+ * Returns an escaped version of the entry name.
+ * @param entryName
+ */
+ private String escape(String entryName) {
+ return sEscapePattern.matcher(entryName).replaceAll("\\\\$1"); //$NON-NLS-1$
+ }
+ }
+
+ private class LsReceiver extends MultiLineReceiver {
+
+ private ArrayList<FileEntry> mEntryList;
+ private ArrayList<String> mLinkList;
+ private FileEntry[] mCurrentChildren;
+ private FileEntry mParentEntry;
+
+ /**
+ * Create an ls receiver/parser.
+ * @param currentChildren The list of current children. To prevent
+ * collapse during update, reusing the same FileEntry objects for
+ * files that were already there is paramount.
+ * @param entryList the list of new children to be filled by the
+ * receiver.
+ * @param linkList the list of link path to compute post ls, to figure
+ * out if the link pointed to a file or to a directory.
+ */
+ public LsReceiver(FileEntry parentEntry, ArrayList<FileEntry> entryList,
+ ArrayList<String> linkList) {
+ mParentEntry = parentEntry;
+ mCurrentChildren = parentEntry.getCachedChildren();
+ mEntryList = entryList;
+ mLinkList = linkList;
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ // no need to handle empty lines.
+ if (line.length() == 0) {
+ continue;
+ }
+
+ // run the line through the regexp
+ Matcher m = sLsPattern.matcher(line);
+ if (m.matches() == false) {
+ continue;
+ }
+
+ // get the name
+ String name = m.group(7);
+
+ // if the parent is root, we only accept selected items
+ if (mParentEntry.isRoot()) {
+ boolean found = false;
+ for (String approved : sRootLevelApprovedItems) {
+ if (approved.equals(name)) {
+ found = true;
+ break;
+ }
+ }
+
+ // if it's not in the approved list we skip this entry.
+ if (found == false) {
+ continue;
+ }
+ }
+
+ // get the rest of the groups
+ String permissions = m.group(1);
+ String owner = m.group(2);
+ String group = m.group(3);
+ String size = m.group(4);
+ String date = m.group(5);
+ String time = m.group(6);
+ String info = null;
+
+ // and the type
+ int objectType = TYPE_OTHER;
+ switch (permissions.charAt(0)) {
+ case '-' :
+ objectType = TYPE_FILE;
+ break;
+ case 'b' :
+ objectType = TYPE_BLOCK;
+ break;
+ case 'c' :
+ objectType = TYPE_CHARACTER;
+ break;
+ case 'd' :
+ objectType = TYPE_DIRECTORY;
+ break;
+ case 'l' :
+ objectType = TYPE_LINK;
+ break;
+ case 's' :
+ objectType = TYPE_SOCKET;
+ break;
+ case 'p' :
+ objectType = TYPE_FIFO;
+ break;
+ }
+
+
+ // now check what we may be linking to
+ if (objectType == TYPE_LINK) {
+ String[] segments = name.split("\\s->\\s"); //$NON-NLS-1$
+
+ // we should have 2 segments
+ if (segments.length == 2) {
+ // update the entry name to not contain the link
+ name = segments[0];
+
+ // and the link name
+ info = segments[1];
+
+ // now get the path to the link
+ String[] pathSegments = info.split(FILE_SEPARATOR);
+ if (pathSegments.length == 1) {
+ // the link is to something in the same directory,
+ // unless the link is ..
+ if ("..".equals(pathSegments[0])) { //$NON-NLS-1$
+ // set the type and we're done.
+ objectType = TYPE_DIRECTORY_LINK;
+ } else {
+ // either we found the object already
+ // or we'll find it later.
+ }
+ }
+ }
+
+ // add an arrow in front to specify it's a link.
+ info = "-> " + info; //$NON-NLS-1$;
+ }
+
+ // get the entry, either from an existing one, or a new one
+ FileEntry entry = getExistingEntry(name);
+ if (entry == null) {
+ entry = new FileEntry(mParentEntry, name, objectType, false /* isRoot */);
+ }
+
+ // add some misc info
+ entry.permissions = permissions;
+ entry.size = size;
+ entry.date = date;
+ entry.time = time;
+ entry.owner = owner;
+ entry.group = group;
+ if (objectType == TYPE_LINK) {
+ entry.info = info;
+ }
+
+ mEntryList.add(entry);
+ }
+ }
+
+ /**
+ * Queries for an already existing Entry per name
+ * @param name the name of the entry
+ * @return the existing FileEntry or null if no entry with a matching
+ * name exists.
+ */
+ private FileEntry getExistingEntry(String name) {
+ for (int i = 0 ; i < mCurrentChildren.length; i++) {
+ FileEntry e = mCurrentChildren[i];
+
+ // since we're going to "erase" the one we use, we need to
+ // check that the item is not null.
+ if (e != null) {
+ // compare per name, case-sensitive.
+ if (name.equals(e.name)) {
+ // erase from the list
+ mCurrentChildren[i] = null;
+
+ // and return the object
+ return e;
+ }
+ }
+ }
+
+ // couldn't find any matching object, return null
+ return null;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public void finishLinks() {
+ // TODO Handle links in the listing service
+ }
+ }
+
+ /**
+ * Classes which implement this interface provide a method that deals with asynchronous
+ * result from <code>ls</code> command on the device.
+ *
+ * @see FileListingService#getChildren(com.android.ddmlib.FileListingService.FileEntry, boolean, com.android.ddmlib.FileListingService.IListingReceiver)
+ */
+ public interface IListingReceiver {
+ public void setChildren(FileEntry entry, FileEntry[] children);
+
+ public void refreshEntry(FileEntry entry);
+ }
+
+ /**
+ * Creates a File Listing Service for a specified {@link Device}.
+ * @param device The Device the service is connected to.
+ */
+ FileListingService(Device device) {
+ mDevice = device;
+ }
+
+ /**
+ * Returns the root element.
+ * @return the {@link FileEntry} object representing the root element or
+ * <code>null</code> if the device is invalid.
+ */
+ public FileEntry getRoot() {
+ if (mDevice != null) {
+ if (mRoot == null) {
+ mRoot = new FileEntry(null /* parent */, "" /* name */, TYPE_DIRECTORY,
+ true /* isRoot */);
+ }
+
+ return mRoot;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the children of a {@link FileEntry}.
+ * <p/>
+ * This method supports a cache mechanism and synchronous and asynchronous modes.
+ * <p/>
+ * If <var>receiver</var> is <code>null</code>, the device side <code>ls</code>
+ * command is done synchronously, and the method will return upon completion of the command.<br>
+ * If <var>receiver</var> is non <code>null</code>, the command is launched is a separate
+ * thread and upon completion, the receiver will be notified of the result.
+ * <p/>
+ * The result for each <code>ls</code> command is cached in the parent
+ * <code>FileEntry</code>. <var>useCache</var> allows usage of this cache, but only if the
+ * cache is valid. The cache is valid only for {@link FileListingService#REFRESH_RATE} ms.
+ * After that a new <code>ls</code> command is always executed.
+ * <p/>
+ * If the cache is valid and <code>useCache == true</code>, the method will always simply
+ * return the value of the cache, whether a {@link IListingReceiver} has been provided or not.
+ *
+ * @param entry The parent entry.
+ * @param useCache A flag to use the cache or to force a new ls command.
+ * @param receiver A receiver for asynchronous calls.
+ * @return The list of children or <code>null</code> for asynchronous calls.
+ *
+ * @see FileEntry#getCachedChildren()
+ */
+ public FileEntry[] getChildren(final FileEntry entry, boolean useCache,
+ final IListingReceiver receiver) {
+ // first thing we do is check the cache, and if we already have a recent
+ // enough children list, we just return that.
+ if (useCache && entry.needFetch() == false) {
+ return entry.getCachedChildren();
+ }
+
+ // if there's no receiver, then this is a synchronous call, and we
+ // return the result of ls
+ if (receiver == null) {
+ doLs(entry);
+ return entry.getCachedChildren();
+ }
+
+ // this is a asynchronous call.
+ // we launch a thread that will do ls and give the listing
+ // to the receiver
+ Thread t = new Thread("ls " + entry.getFullPath()) { //$NON-NLS-1$
+ @Override
+ public void run() {
+ doLs(entry);
+
+ receiver.setChildren(entry, entry.getCachedChildren());
+
+ final FileEntry[] children = entry.getCachedChildren();
+ if (children.length > 0 && children[0].isApplicationPackage()) {
+ final HashMap<String, FileEntry> map = new HashMap<String, FileEntry>();
+
+ for (FileEntry child : children) {
+ String path = child.getFullPath();
+ map.put(path, child);
+ }
+
+ // call pm.
+ String command = PM_FULL_LISTING;
+ try {
+ mDevice.executeShellCommand(command, new MultiLineReceiver() {
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ if (line.length() > 0) {
+ // get the filepath and package from the line
+ Matcher m = sPmPattern.matcher(line);
+ if (m.matches()) {
+ // get the children with that path
+ FileEntry entry = map.get(m.group(1));
+ if (entry != null) {
+ entry.info = m.group(2);
+ receiver.refreshEntry(entry);
+ }
+ }
+ }
+ }
+ }
+ 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;
+ }
+
+ private void doLs(FileEntry entry) {
+ // create a list that will receive the list of the entries
+ ArrayList<FileEntry> entryList = new ArrayList<FileEntry>();
+
+ // create a list that will receive the link to compute post ls;
+ ArrayList<String> linkList = new ArrayList<String>();
+
+ try {
+ // create the command
+ String command = "ls -l " + entry.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);
+ }
+}
+
--- /dev/null
+package com.samsung.slp.common.connection.device;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.Map;
+
+import com.samsung.slp.common.connection.exception.SdbCommandRejectedException;
+import com.samsung.slp.common.connection.exception.ShellCommandUnresponsiveException;
+import com.samsung.slp.common.connection.exception.TimeoutException;
+import com.samsung.slp.common.connection.receiver.IShellOutputReceiver;
+import com.samsung.slp.common.connection.receiver.LogReceiver;
+
+/**
+ * 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 <code>adb devices</code>.
+ *
+ * @param state the device state.
+ * @return a {@link DeviceState} object or <code>null</code> if the state is unknown.
+ */
+ public static DeviceState getState(String state) {
+ for (DeviceState deviceState : values()) {
+ if (deviceState.mState.equals(state)) {
+ return deviceState;
+ }
+ }
+ return null;
+ }
+ }
+//
+// /**
+// * Returns the serial number of the device.
+// */
+// public String getSerialNumber();
+//
+// /**
+// * Returns the name of the AVD the emulator is running.
+// * <p/>This is only valid if {@link #isEmulator()} returns true.
+// * <p/>If the emulator is not running any AVD (for instance it's running from an Android source
+// * tree build), this method will return "<code><build></code>".
+// *
+// * @return the name of the AVD or <code>null</code> if there isn't any.
+// */
+// public String getAvdName();
+//
+ /**
+ * Returns the state of the device.
+ */
+ public DeviceState getState();
+//
+// /**
+// * Returns the device properties. It contains the whole output of 'getprop'
+// */
+// public Map<String, String> getProperties();
+//
+// /**
+// * Returns the number of property for this device.
+// */
+// public int getPropertyCount();
+//
+// /**
+// * Returns a property value.
+// *
+// * @param name the name of the value to return.
+// * @return the value or <code>null</code> if the property does not exist.
+// */
+// 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 <code>true</code> if {@link #getState()} returns {@link DeviceState#ONLINE}.
+// */
+// public boolean isOnline();
+//
+ /**
+ * Returns <code>true</code> if the device is an emulator.
+ */
+ public boolean isEmulator();
+//
+// /**
+// * Returns if the device is offline.
+// *
+// * @return <code>true</code> if {@link #getState()} returns {@link DeviceState#OFFLINE}.
+// */
+// public boolean isOffline();
+//
+// /**
+// * Returns if the device is in bootloader mode.
+// *
+// * @return <code>true</code> if {@link #getState()} returns {@link DeviceState#BOOTLOADER}.
+// */
+// public boolean isBootLoader();
+//
+//// /**
+//// * Returns whether the {@link Device} has {@link Client}s.
+//// */
+//// public SyncService getSyncService()
+//// throws TimeoutException, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Returns a {@link FileListingService} for this device.
+// */
+ public FileListingService getFileListingService();
+//
+//// /**
+//// * Takes a screen shot of the device and returns it as a {@link RawImage}.
+//// *
+//// * @return the screenshot as a <code>RawImage</code> or <code>null</code> if something
+//// * went wrong.
+//// * @throws TimeoutException in case of timeout on the connection.
+//// * @throws SdbCommandRejectedException if adb rejects the command
+//// * @throws IOException in case of I/O error on the connection.
+//// */
+//// public RawImage getScreenshot() throws TimeoutException, SdbCommandRejectedException,
+//// IOException;
+//
+// /**
+// * Executes a shell command on the device, and sends the result to a <var>receiver</var>
+// * <p/>This is similar to calling
+// * <code>executeShellCommand(command, receiver, DdmPreferences.getTimeOut())</code>.
+// *
+// * @param command the shell command to execute
+// * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell
+// * command
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, ShellCommandUnresponsiveException,
+// IOException;
+//
+// /**
+// * Executes a shell command on the device, and sends the result to a <var>receiver</var>.
+// * <p/><var>maxTimeToOutputResponse</var> is used as a maximum waiting time when expecting the
+// * command output from the device.<br>
+// * At any time, if the shell command does not output anything for a period longer than
+// * <var>maxTimeToOutputResponse</var>, then the method will throw
+// * {@link ShellCommandUnresponsiveException}.
+// * <p/>For commands like log output, a <var>maxTimeToOutputResponse</var> value of 0, meaning
+// * that the method will never throw and will block until the receiver's
+// * {@link IShellOutputReceiver#isCancelled()} returns <code>true</code>, should be
+// * used.
+// *
+// * @param command the shell command to execute
+// * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell
+// * command
+// * @param maxTimeToOutputResponse the maximum amount of time during which the command is allowed
+// * to not output any response. A value of 0 means the method will wait forever
+// * (until the <var>receiver</var> cancels the execution) for command output and
+// * never throw.
+// * @throws TimeoutException in case of timeout on the connection when sending the command.
+// * @throws SdbCommandRejectedException if adb rejects the command.
+// * @throws ShellCommandUnresponsiveException in case the shell command doesn't send any output
+// * for a period longer than <var>maxTimeToOutputResponse</var>.
+// * @throws IOException in case of I/O error on the connection.
+// *
+// * @see DdmPreferences#getTimeOut()
+// */
+// public void executeShellCommand(String command, IShellOutputReceiver receiver,
+// int maxTimeToOutputResponse)
+// throws TimeoutException, SdbCommandRejectedException, ShellCommandUnresponsiveException,
+// IOException;
+//
+// /**
+// * Runs the event log service and outputs the event log to the {@link LogReceiver}.
+// * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
+// * @param receiver the receiver to receive the event log entries.
+// * @throws TimeoutException in case of timeout on the connection. This can only be thrown if the
+// * timeout happens during setup. Once logs start being received, no timeout will occur as it's
+// * not possible to detect a difference between no log and timeout.
+// * @throws SdbCommandRejectedException if adb rejects the command
+// * @throws IOException in case of I/O error on the connection.
+// */
+// public void runEventLogService(LogReceiver receiver)
+// throws TimeoutException, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Runs the log service for the given log and outputs the log to the {@link LogReceiver}.
+// * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true.
+// *
+// * @param logname the logname of the log to read from.
+// * @param receiver the receiver to receive the event log entries.
+// * @throws TimeoutException in case of timeout on the connection. This can only be thrown if the
+// * timeout happens during setup. Once logs start being received, no timeout will
+// * occur as it's not possible to detect a difference between no log and timeout.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Creates a port forwarding between a local and a remote port.
+// *
+// * @param localPort the local port to forward
+// * @param remotePort the remote port.
+// * @return <code>true</code> if success.
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Removes a port forwarding between a local and a remote port.
+// *
+// * @param localPort the local port to forward
+// * @param remotePort the remote port.
+// * @return <code>true</code> if success.
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Returns the name of the client by pid or <code>null</code> if pid is unknown
+// * @param pid the pid of the client.
+// */
+// public String getClientName(int pid);
+//
+// /**
+// * Installs an Android application on device.
+// * This is a helper method that combines the syncPackageToDevice, installRemotePackage,
+// * and removePackage steps
+// *
+// * @param packageFilePath the absolute file system path to file on local host to install
+// * @param reinstall set to <code>true</code> if re-install of app should be performed
+// * @return a {@link String} with an error code, or <code>null</code> if success.
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, 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 SdbCommandRejectedException if adb rejects the command
+// * @throws IOException in case of I/O error on the connection.
+// */
+// public String syncPackageToDevice(String localFilePath)
+// throws TimeoutException, SdbCommandRejectedException, IOException;
+//
+// /**
+// * Installs the application package that was pushed to a temporary location on the device.
+// *
+// * @param remoteFilePath absolute file path to package file on device
+// * @param reinstall set to <code>true</code> if re-install of app should be performed
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, 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 SdbCommandRejectedException 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, SdbCommandRejectedException, 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 <code>null</code> if success.
+// * @throws TimeoutException in case of timeout on the connection.
+// * @throws SdbCommandRejectedException 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, SdbCommandRejectedException, 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 SdbCommandRejectedException if adb rejects the command
+// * @throws IOException
+// */
+// public void reboot(String into)
+// throws TimeoutException, SdbCommandRejectedException, IOException;
+}
--- /dev/null
+package com.samsung.slp.common.connection.exception;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when adb refuses a command.
+ */
+public class SdbCommandRejectedException extends IOException {
+ private static final long serialVersionUID = 1L;
+ private final boolean mIsDeviceOffline;
+ private final boolean mErrorDuringDeviceSelection;
+
+ SdbCommandRejectedException(String message) {
+ super(message);
+ mIsDeviceOffline = "device offline".equals(message);
+ mErrorDuringDeviceSelection = false;
+ }
+
+ SdbCommandRejectedException(String message, boolean errorDuringDeviceSelection) {
+ super(message);
+ mErrorDuringDeviceSelection = errorDuringDeviceSelection;
+ mIsDeviceOffline = "device offline".equals(message);
+ }
+
+ /**
+ * Returns true if the error is due to the device being offline.
+ */
+ public boolean isDeviceOffline() {
+ return mIsDeviceOffline;
+ }
+
+ /**
+ * Returns whether adb refused to target a given device for the command.
+ * <p/>If false, adb refused the command itself, if true, it refused to target the given
+ * device.
+ */
+ public boolean wasErrorDuringDeviceSelection() {
+ return mErrorDuringDeviceSelection;
+ }
+}
--- /dev/null
+package com.samsung.slp.common.connection.exception;
+
+/*
+ * 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.
+ */
+
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when a shell command executed on a device takes too long to send its output.
+ * <p/>The command may not actually be unresponsive, it just has spent too much time not outputting
+ * any thing to the console.
+ */
+public class ShellCommandUnresponsiveException extends IOException {
+ private static final long serialVersionUID = 1L;
+}
--- /dev/null
+package com.samsung.slp.common.connection.exception;
+
+/*
+ * 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.
+ */
+
+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;
+}
--- /dev/null
+/*
+ * {common-plugins}
+ *
+ * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Hyunsik Noh <hyunsik.noh@samsung.com>
+ * Hoon Kang <h245.kang@samsung.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+package com.samsung.slp.common.connection.log;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.samsung.slp.common.connection.ui.LogView.LogColors;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LogController {
+
+ private Table table;
+
+ public String deviceId = null;
+ public String deviceType = null;
+
+ private LogOuputReceiver receiver;
+ private BufferedReader logReader = null;
+ private static int STRING_BUFFER_LENGTH = 0;
+
+ //all messages
+ private LogMessage[] allMessageArray;
+ private ArrayList<LogMessage> filteredMessagesList = new ArrayList<LogMessage>();
+ private ArrayList<LogMessage> newMessagesList = new ArrayList<LogMessage>();
+
+ private LogMessageInfo lastMessageInfo = null;
+ private boolean mPendingAsyncRefresh = false;
+ private LogColors colors = null;
+ public boolean isSeletected = false;
+
+ public LogFilter filter = null;
+
+ private int countRemoveMsg = 0;
+
+ /** Represents the oldest message in the buffer */
+ private int startIndex = -1;
+ private String savePath;
+ private String loadPath;
+
+ /**
+ * Represents the next usable item in the buffer to receive new message.
+ * This can be equal to startIndex, but when used startIndex will be
+ * incremented as well.
+ */
+ private int endIndex = -1;
+
+
+ private static Pattern sLogPattern = Pattern.compile(
+ "^\\[\\s(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)" + //$NON-NLS-1$
+ "\\s+(\\d*):[\\s]+([0-9a-fA-F]+)\\s([VDIWE])/(.*)\\]$"); //$NON-NLS-1$
+
+ private static Pattern colorPattern = Pattern.compile("\\[\\d+[mn]");
+
+
+ public LogController( Table t, LogColors c )
+ {
+ //make or find LogFilter related in Selected DeviceMachine
+ filter = new LogFilter();
+
+ table = t;
+ colors = c;
+ }
+
+ public void Start()
+ {
+ StartLogJob startLog = new StartLogJob("Starting LogViewer");
+ startLog.schedule();
+ }
+
+ private class StartLogJob extends Job {
+
+ public StartLogJob(String name) {
+ super(name);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ startGetLog(monitor);
+ } catch (Exception e) {
+// DialogUtil.openErrorDialog("LogUtil cannot run . \n " + e.getMessage());
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ }
+
+
+ private void startGetLog(IProgressMonitor monitor )throws Exception
+ {
+ STRING_BUFFER_LENGTH = filter.getLineCount();
+ allMessageArray = new LogMessage[STRING_BUFFER_LENGTH];
+
+
+ //Check current target's session ID
+ String sessionId = deviceId + "_log_session";
+ session = SessionManager.getSession(sessionId, device, new NullProgressMonitor());
+
+ resetUI(false);
+
+ if (session != null) {
+ // create a new output receiver
+ receiver = new LogOuputReceiver();
+
+ // start the logcat in a different thread
+ new Thread(sessionId) { //$NON-NLS-1$
+ @Override
+ public void run() {
+
+ if (receiver == null || receiver.isCancelled) {
+ // logcat was stopped/cancelled before the device became ready.
+ return;
+ }
+
+ try {
+ executeLogUtil("dlogutil -v long *:v", receiver, session); //$NON-NLS-1$
+ } catch (Exception e) {
+// Log.e("Logcat", e);
+ } finally {
+ // at this point the command is terminated.
+ receiver = null;
+
+ if(session != null) {
+ session.close();
+ session = null;
+ }
+ }
+ }
+ }.start();
+ }
+ }
+
+ public void resetUI(boolean inUiThread) {
+
+ // the ui is static we just empty it.
+ if (table.isDisposed() == false) {
+ if (inUiThread) {
+ table.removeAll();
+ } else {
+ Display d = table.getDisplay();
+
+ // run sync as we need to update right now.
+ d.syncExec(new Runnable() {
+ public void run() {
+ if (table.isDisposed() == false) {
+ table.removeAll();
+ }
+ }
+ });
+ }
+ }
+ }
+
+
+ public void stopGetLog(boolean inUiThread) {
+ if (receiver != null) {
+ receiver.isCancelled = true;
+
+ // when the thread finishes, no one will reference that object
+ // and it'll be destroyed
+ receiver = null;
+
+ // reset the content buffer
+ for (int i = 0 ; i < STRING_BUFFER_LENGTH; i++) {
+ allMessageArray[i] = null;
+ }
+
+ // because it's a circular buffer, it's hard to know if
+ // the array is empty with both start/end at 0 or if it's full
+ // with both start/end at 0 as well. So to mean empty, we use -1
+ startIndex = -1;
+ endIndex = -1;
+
+ resetUI(inUiThread);
+
+ if(session != null) {
+ session.close();
+ session = null;
+ }
+ }
+ }
+
+ private void executeLogUtil(String command, LogOuputReceiver rcvr, ISession session) {
+ try {
+ ArrayList<String> mArray = new ArrayList<String>();
+ String str = null;
+
+ Process logProc;
+ logProc = session.launch(command, null);
+
+ InputStream input = logProc.getInputStream();
+
+ logReader = new BufferedReader(new InputStreamReader(input));
+
+ while (true) {
+
+ if (rcvr != null && rcvr.isCancelled()) {
+// Log.v("ddms", "execute: cancelled");
+ break;
+ }
+
+ mArray.clear();
+
+ while((str = logReader.readLine()) != null) {
+ for( int i = 0; i<str.length(); i++)
+ {
+ char rm = str.charAt(i);
+ if( (int)rm == 27)
+ {
+ str = str.replace( rm, ' ');
+ Matcher colorMatcher = colorPattern.matcher(str);
+ if(colorMatcher.find())
+ {
+ str = colorMatcher.replaceAll("");
+ }
+ break;
+ }
+ }
+ if(!str.trim().equals("")) {
+ mArray.add(str);
+ } else
+ break;
+ }
+
+ if (mArray.size() > 0) {
+ // at this point we've split all the lines.
+ // make the array
+ String[] lines = mArray.toArray(new String[mArray.size()]);
+ rcvr.processNewLines(lines);
+ }
+
+ }
+ } catch (Exception e) {
+ } finally {
+ if (logReader != null) {
+ try {
+ logReader.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ logReader = null;
+ }
+// Log.v("ddms", "execute: returning");
+ }
+ }
+
+ private final class LogOuputReceiver {
+
+ public boolean isCancelled = false;
+
+ public void processNewLines(String[] lines) {
+ if (isCancelled == false) {
+ processLogLines(lines);
+ }
+ }
+
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+
+ }
+
+ protected void processLogLines(String[] lines) {
+ // WARNING: this will not work if the string contains more line than
+ // the buffer holds.
+
+ if (lines.length > STRING_BUFFER_LENGTH) {
+// Log.e("LogCat", "Receiving more lines than STRING_BUFFER_LENGTH");
+ }
+
+ // parse the lines and create LogMessage that are stored in a temporary list
+
+ synchronized (allMessageArray) {
+ for (String line : lines) {
+
+ // ignore empty lines.
+ if (line.length() > 0) {
+ // check for header lines.
+ Matcher matcher = sLogPattern.matcher(line);
+ if (matcher.matches()) {
+ // this is a header line, parse the header and keep it around.
+ lastMessageInfo = new LogMessageInfo();
+
+ lastMessageInfo.time = matcher.group(1);
+ lastMessageInfo.pid = Integer.valueOf(matcher.group(2));
+ if( matcher.group(4).length() > 0)
+ lastMessageInfo.type = String.valueOf(matcher.group(4).charAt(0));
+ else
+ lastMessageInfo.type = " ";
+ lastMessageInfo.tag = matcher.group(5).trim();
+
+ } else {
+
+ // This is not a header line.
+ // Create a new LogMessage and process it.
+ LogMessage mc = new LogMessage();
+
+ if (lastMessageInfo == null) {
+ // The first line of output wasn't preceded
+ // by a header line; make something up so
+ // that users of mc.data don't NPE.
+ lastMessageInfo = new LogMessageInfo();
+ lastMessageInfo.time = "00-00 00:00:00.000 "; //$NON-NLS1$
+ lastMessageInfo.pid = 0;
+ lastMessageInfo.type = "I";
+ lastMessageInfo.tag = "No Tag"; //$NON-NLS1$
+ }
+
+ // If someone printed a log message with
+ // embedded '\n' characters, there will
+ // one header line followed by multiple text lines.
+ // Use the last header that we saw.
+ mc.data = lastMessageInfo;
+
+ // tabs seem to display as only 1 tab so we replace the leading tabs
+ // by 4 spaces.
+
+ mc.msg = line.replaceAll("\t", " "); //$NON-NLS-1$ //$NON-NLS-2$
+// Matcher matcher2 = sLogPattern2.matcher(mc.msg);
+// if(matcher2.find())
+// mc.msg = matcher2.replaceAll("");
+
+
+ // process the new LogMessage.
+ processNewMessage( mc );
+
+
+ }
+ }
+ }
+
+ // if we don't have a pending Runnable that will do the refresh, we ask the Display
+ // to run one in the UI thread.
+ if (mPendingAsyncRefresh == false) {
+ mPendingAsyncRefresh = true;
+
+ try {
+ Display display = table.getDisplay();
+
+ // run in sync because this will update the buffer start/end indexes
+ display.asyncExec(new Runnable() {
+ public void run() {
+
+ asyncRefresh();
+ }
+ });
+ } catch (SWTException e) {
+ // display is disposed, we're probably quitting. Let's stop.
+
+ stopGetLog(false);
+ }
+ }
+ }
+ }
+
+ private static class LogMessage {
+ public LogMessageInfo data;
+ public String msg;
+
+ @Override
+ public String toString() {
+ return "[ " + data.time + " " //$NON-NLS-1$
+ + data.pid + ": "
+ + data.pid + " "
+ + data.type + "/" //$NON-NLS-1$
+ + data.tag + " ]\n"
+ //$NON-NLS-1$
+ + msg;
+ }
+ }
+
+ /** message data, separated from content for multi line messages */
+ private static class LogMessageInfo {
+ public String type;
+ public int pid;
+ public String time;
+ public String tag;
+ }
+
+ /**
+ * Refreshes the UI with new messages.
+ */
+ private void asyncRefresh() {
+ if (table.isDisposed() == false) {
+ synchronized (allMessageArray) {
+ try {
+ // the circular buffer has been updated, let have the filter flush their
+ // display with the new messages.
+ flush();
+ } finally {
+ // the pending refresh is done.
+ mPendingAsyncRefresh = false;
+ }
+ }
+ } else {
+ stopGetLog(true);
+ }
+ }
+
+ /**
+ * Processes a new Message.
+ * <p/>This adds the new message to the buffer, and gives it to the existing filters.
+ * @param newMessage
+ */
+ private void processNewMessage( LogMessage newMessage ) {
+
+ // compute the index where the message goes.
+ // was the buffer empty?
+ int messageIndex = -1;
+ if (startIndex == -1) {
+ messageIndex = startIndex = 0;
+ endIndex = 1;
+ } else {
+ messageIndex = endIndex;
+
+ // check we aren't overwriting start
+ if (startIndex == endIndex) {
+ startIndex = (startIndex + 1) % STRING_BUFFER_LENGTH;
+ }
+
+ // increment the next usable slot index
+ endIndex = (endIndex + 1) % STRING_BUFFER_LENGTH;
+ }
+
+ LogMessage oldMessage = null;
+ if( allMessageArray[messageIndex] != null )
+ oldMessage = allMessageArray[ messageIndex];
+ // then add the new one
+ allMessageArray[messageIndex] = newMessage;
+
+ synchronized (filteredMessagesList) {
+ if (oldMessage != null)
+ {
+ if( filteredMessagesList.size() > STRING_BUFFER_LENGTH)
+ {
+ int index = filteredMessagesList.indexOf(oldMessage);
+ if (index != -1) {
+ // TODO check that index will always be -1 or 0, as only the oldest message is ever removed.
+ filteredMessagesList.remove(index);
+ countRemoveMsg++;
+ }
+ }
+ }
+ }
+
+ boolean filter = accept(newMessage);
+ if (filter) {
+ // at this point the message is accepted, we add it to the list
+ filteredMessagesList.add(newMessage);
+ newMessagesList.add( newMessage );
+ }
+
+ }
+
+
+
+ /**
+ * Filters a message.
+ * @param logMessage the Message
+ * @return true if the message is accepted by the filter.
+ */
+ boolean accept(LogMessage logMessage) {
+ if( logMessage == null )
+ return false;
+ // do the regular filtering now
+
+ String msgLogType = logMessage.data.type;
+
+ if( filter.isLevelD() || filter.isLevelE() || filter.isLevelI() || filter.isLevelV() || filter.isLevelW() || filter.isUserlog())
+ {
+ boolean result = false;
+ if( msgLogType.equals("V"))
+ {
+ result = result | filter.isLevelV();
+ }
+ if( msgLogType.equals("D"))
+ {
+ result = result | filter.isLevelD();
+ }
+ if( msgLogType.equals("I"))
+ {
+ result = result | filter.isLevelI();
+ }
+ if( msgLogType.equals("W"))
+ {
+ result = result | filter.isLevelW();
+ }
+ if( msgLogType.equals("E"))
+ {
+ result = result | filter.isLevelE();
+ }
+ if( logMessage.data.tag.equals(""))
+ {
+ result = result | filter.isUserlog();
+ }
+ if( !result )
+ return result;
+ }
+
+ if ((filter.getComboIndex() == 0))
+ {
+ if( filter.getKeyword().equals("") )
+ {
+ }
+ else
+ {
+ if( !filter.getKeyword().equals( Integer.toString(logMessage.data.pid)))
+ {
+ return false;
+ }
+ }
+ }
+
+ if ((filter.getComboIndex() == 1))
+ {
+ if( filter.getKeyword().equals("") )
+ {
+ }
+ else
+ {
+// if( !filter.getKeyword().equals( logMessage.data.tag))
+ if( !logMessage.data.tag.contains( filter.getKeyword()))
+ {
+ return false;
+ }
+ }
+ }
+
+ if ( filter.getComboIndex() == 2 )
+ {
+ String msg = logMessage.msg;
+ if( filter.getKeyword().equals("") )
+ {
+ }
+ else
+ {
+ if (!msg.contains( filter.getKeyword() ))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ /**
+ * Takes all the accepted messages and display them.
+ * This must be called from a UI thread.
+ */
+ public void flush() {
+
+ if( !isSeletected )
+ return ;
+ // if we are not going to scroll, get the current first item being shown.
+// int topIndex = table.getTopIndex();
+
+ // disable drawing
+ table.setRedraw(false);
+
+ int totalCount = newMessagesList.size();
+
+ try {
+ // remove the items of the old messages.
+ for (int i = 0 ; i < countRemoveMsg && table.getItemCount() > 0 ; i++) {
+ table.remove(0);
+ }
+
+ // add the new items
+ for (int i = 0 ; i < totalCount ; i++) {
+ LogMessage msg = newMessagesList.get(i);
+ addTableItem(msg);
+ }
+ } catch (SWTException e) {
+ // log the error and keep going. Content of the logcat table maybe unexpected
+ // but at least ddms won't crash.
+// Log.e("LogFilter", e);
+ }
+
+ // redraw
+ table.setRedraw(true);
+
+ totalCount = table.getItemCount();
+
+ if (totalCount < 9) {
+
+ table.setTopIndex( 0 );
+ }
+ else
+ {
+ table.setTopIndex(totalCount-7);
+ }
+
+ newMessagesList.clear();
+ countRemoveMsg = 0;
+
+ }
+
+ synchronized public void addOldMsg()
+ {
+ ArrayList<LogMessage> tmpMessages = new ArrayList<LogMessage>();
+ for( int i =0 ; i < filteredMessagesList.size() ; i++)
+ {
+ tmpMessages.add(filteredMessagesList.get( i ));
+ }
+
+ for( int i =0 ; i < newMessagesList.size() ; i++)
+ {
+ tmpMessages.add(newMessagesList.get( i ));
+ }
+
+ newMessagesList.clear();
+ newMessagesList = tmpMessages;
+ flush();
+ }
+
+ private void addTableItem(LogMessage msg) {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setText(0, msg.data.time);
+ item.setText(2, Integer.toString(msg.data.pid));
+ item.setText(3, msg.data.tag);
+ item.setText(4, msg.msg);
+ // add the buffer index as data
+ item.setData(msg);
+
+ if (msg.data.type.equals("I")) {
+ item.setText(1, "Info");
+ item.setForeground(colors.infoColor);
+ } else if (msg.data.type.equals("D")) {
+ item.setText(1, "Debug");
+ item.setForeground(colors.debugColor);
+ } else if (msg.data.type.equals("E")) {
+ item.setText(1,"Error");
+ item.setForeground(colors.errorColor);
+ } else if (msg.data.type.equals("W")) {
+ item.setText(1, "Warning");
+ item.setForeground(colors.warningColor);
+ } else if (msg.data.type.equals("V")) {
+ item.setText(1, "Verbose");
+ item.setForeground(colors.verboseColor);
+ }
+ else {
+ item.setText(1," ");
+ item.setForeground(colors.verboseColor);
+ }
+ }
+
+ synchronized public void refiltering()
+ {
+ table.removeAll();
+
+ filteredMessagesList.clear();
+ ArrayList<LogMessage> tmpMessages = new ArrayList<LogMessage>();
+
+// for( LogMessage logMsg : allMessageArray)
+ for( int i = 0; i < allMessageArray.length ; i++)
+ {
+ LogMessage logMsg = allMessageArray[i];
+ if( accept(logMsg))
+ {
+ filteredMessagesList.add( logMsg );
+ tmpMessages.add( logMsg );
+ }
+ }
+
+ for( LogMessage nLogMsg : newMessagesList )
+ {
+ if( accept( nLogMsg ))
+ {
+ filteredMessagesList.add( nLogMsg );
+ tmpMessages.add( nLogMsg );
+ }
+ }
+ newMessagesList.clear();
+ newMessagesList = tmpMessages;
+ flush();
+ }
+
+ public boolean importLog() throws IOException {
+
+ FileDialog dlg = new FileDialog(table.getParent().getShell(), SWT.SEARCH);
+ String fileName;
+
+ dlg.setText("Import log...");
+ Date date = new Date();
+ dlg.setFileName( date.toString() + ".txt");
+ String defaultPath = loadPath;
+ if (defaultPath == null) {
+ defaultPath = System.getProperty("user.home"); //$NON-NLS-1$
+ }
+ dlg.setFilterPath(defaultPath);
+ dlg.setFilterNames(new String[] {
+ "Text Files (*.txt)"
+ });
+ dlg.setFilterExtensions(new String[] {
+ "*.txt"
+ });
+
+ fileName = dlg.open();
+ ArrayList<String> array = new ArrayList<String>();
+ if (fileName != null) {
+ savePath = dlg.getFilterPath();
+ BufferedReader bufReader = new BufferedReader( new FileReader(fileName));
+ String tmpStr = null;
+ while((tmpStr = bufReader.readLine()) != null)
+ {
+ array.add( tmpStr );
+ }
+
+
+ if (array.size() > 0) {
+ // at this point we've split all the lines.
+ // make the array
+ String[] lines = array.toArray(new String[array.size()]);
+ receiver.processNewLines(lines);
+ }
+ }
+ return true;
+ }
+
+ public boolean exportLog() {
+
+ FileDialog dlg = new FileDialog(table.getParent().getShell(), SWT.SAVE);
+ String fileName;
+
+ dlg.setText("Export log...");
+ Date date = new Date();
+ dlg.setFileName( date.toString() + ".txt");
+ String defaultPath = savePath;
+ if (defaultPath == null) {
+ defaultPath = System.getProperty("user.home"); //$NON-NLS-1$
+ }
+ dlg.setFilterPath(defaultPath);
+ dlg.setFilterNames(new String[] {
+ "Text Files (*.txt)"
+ });
+ dlg.setFilterExtensions(new String[] {
+ "*.txt"
+ });
+
+ fileName = dlg.open();
+ if (fileName != null) {
+ savePath = dlg.getFilterPath();
+
+ int count = table.getItemCount();
+
+ // loop on the selection and output the file.
+ try {
+ FileWriter writer = new FileWriter(fileName);
+
+ for (int i=0 ; i < count ; i++)
+ {
+ TableItem item = table.getItem(i);
+ LogMessage msg = (LogMessage)item.getData();
+ String line = msg.toString();
+ writer.write(line);
+ writer.write('\n');
+ }
+ writer.flush();
+
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void removeMsg()
+ {
+ for (int i = 0 ; i < STRING_BUFFER_LENGTH; i++) {
+ allMessageArray[i] = null;
+ }
+ filteredMessagesList.clear();
+ newMessagesList.clear();
+ }
+
+ public void setSelected( boolean select )
+ {
+ isSeletected = select;
+ }
+}
--- /dev/null
+/*
+ * {common-plugins}
+ *
+ * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Hyunsik Noh <hyunsik.noh@samsung.com>
+ * Hoon Kang <h245.kang@samsung.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+package com.samsung.slp.common.connection.log;
+
+public class LogFilter {
+
+ private boolean levelV = false;
+ private boolean levelD = false;
+ private boolean levelW = false;
+ private boolean levelI = false;
+ private boolean levelE = false;
+ private boolean userlog = false;
+
+ private int lineCount = 2000;
+
+ //0: For Pid
+ //1: For Tag
+ //2: For Message
+ private int comboIndex = 3;
+
+ private String keyword = null;
+
+ public boolean isLevelV() {
+ return levelV;
+ }
+
+ public void setLevelV(boolean levelV) {
+ this.levelV = levelV;
+ }
+
+ public boolean isLevelD() {
+ return levelD;
+ }
+
+ public void setLevelD(boolean levelD) {
+ this.levelD = levelD;
+ }
+
+ public boolean isLevelW() {
+ return levelW;
+ }
+
+ public void setLevelW(boolean levelW) {
+ this.levelW = levelW;
+ }
+
+ public boolean isLevelI() {
+ return levelI;
+ }
+
+ public void setLevelI(boolean levelI) {
+ this.levelI = levelI;
+ }
+
+ public boolean isLevelE() {
+ return levelE;
+ }
+
+ public void setLevelE(boolean levelE) {
+ this.levelE = levelE;
+ }
+
+ public int getLineCount() {
+ return lineCount;
+ }
+
+ public void setLineCount(int lineCount) {
+ this.lineCount = lineCount;
+ }
+
+ public int getComboIndex() {
+ return comboIndex;
+ }
+
+ public void setComboIndex(int index) {
+ this.comboIndex = index;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public void setKeyword(String keyword) {
+ this.keyword = keyword;
+ }
+
+ public boolean isUserlog() {
+ return userlog;
+ }
+
+ public void setUserlog(boolean userlog) {
+ this.userlog = userlog;
+ }
+
+}
--- /dev/null
+package com.samsung.slp.common.connection.receiver;
+
+/*
+ * 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.
+ */
+
+/**
+ * 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();
+};
+
--- /dev/null
+package com.samsung.slp.common.connection.receiver;
+
+/*
+ * 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.
+ */
+
+import java.security.InvalidParameterException;
+
+/**
+ * Receiver able to provide low level parsing for device-side log services.
+ */
+public final class LogReceiver {
+
+ private final static int ENTRY_HEADER_SIZE = 20; // 2*2 + 4*4; see LogEntry.
+
+ /**
+ * Represents a log entry and its raw data.
+ */
+ public final static class LogEntry {
+ /*
+ * See //device/include/utils/logger.h
+ */
+ /** 16bit unsigned: length of the payload. */
+ public int len; /* This is normally followed by a 16 bit padding */
+ /** pid of the process that generated this {@link LogEntry} */
+ public int pid;
+ /** tid of the process that generated this {@link LogEntry} */
+ public int tid;
+ /** Seconds since epoch. */
+ public int sec;
+ /** nanoseconds. */
+ public int nsec;
+ /** The entry's raw data. */
+ public byte[] data;
+ };
+
+ /**
+ * Classes which implement this interface provide a method that deals
+ * with {@link LogEntry} objects coming from log service through a {@link LogReceiver}.
+ * <p/>This interface provides two methods.
+ * <ul>
+ * <li>{@link #newEntry(com.android.ddmlib.log.LogReceiver.LogEntry)} provides a
+ * first level of parsing, extracting {@link LogEntry} objects out of the log service output.</li>
+ * <li>{@link #newData(byte[], int, int)} provides a way to receive the raw information
+ * coming directly from the log service.</li>
+ * </ul>
+ */
+ public interface ILogListener {
+ /**
+ * Sent when a new {@link LogEntry} has been parsed by the {@link LogReceiver}.
+ * @param entry the new log entry.
+ */
+ public void newEntry(LogEntry entry);
+
+ /**
+ * Sent when new raw data is coming from the log service.
+ * @param data the raw data buffer.
+ * @param offset the offset into the buffer signaling the beginning of the new data.
+ * @param length the length of the new data.
+ */
+ public void newData(byte[] data, int offset, int length);
+ }
+
+ /** Current {@link LogEntry} being read, before sending it to the listener. */
+ private LogEntry mCurrentEntry;
+
+ /** Temp buffer to store partial entry headers. */
+ private byte[] mEntryHeaderBuffer = new byte[ENTRY_HEADER_SIZE];
+ /** Offset in the partial header buffer */
+ private int mEntryHeaderOffset = 0;
+ /** Offset in the partial entry data */
+ private int mEntryDataOffset = 0;
+
+ /** Listener waiting for receive fully read {@link LogEntry} objects */
+ private ILogListener mListener;
+
+ private boolean mIsCancelled = false;
+
+ /**
+ * Creates a {@link LogReceiver} with an {@link ILogListener}.
+ * <p/>
+ * The {@link ILogListener} will receive new log entries as they are parsed, in the form
+ * of {@link LogEntry} objects.
+ * @param listener the listener to receive new log entries.
+ */
+ public LogReceiver(ILogListener listener) {
+ mListener = listener;
+ }
+
+
+ /**
+ * Parses new data coming from the log service.
+ * @param data the data buffer
+ * @param offset the offset into the buffer signaling the beginning of the new data.
+ * @param length the length of the new data.
+ */
+ public void parseNewData(byte[] data, int offset, int length) {
+ // notify the listener of new raw data
+ if (mListener != null) {
+ mListener.newData(data, offset, length);
+ }
+
+ // loop while there is still data to be read and the receiver has not be cancelled.
+ while (length > 0 && mIsCancelled == false) {
+ // first check if we have no current entry.
+ if (mCurrentEntry == null) {
+ if (mEntryHeaderOffset + length < ENTRY_HEADER_SIZE) {
+ // if we don't have enough data to finish the header, save
+ // the data we have and return
+ System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, length);
+ mEntryHeaderOffset += length;
+ return;
+ } else {
+ // we have enough to fill the header, let's do it.
+ // did we store some part at the beginning of the header?
+ if (mEntryHeaderOffset != 0) {
+ // copy the rest of the entry header into the header buffer
+ int size = ENTRY_HEADER_SIZE - mEntryHeaderOffset;
+ System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset,
+ size);
+
+ // create the entry from the header buffer
+ mCurrentEntry = createEntry(mEntryHeaderBuffer, 0);
+
+ // since we used the whole entry header buffer, we reset the offset
+ mEntryHeaderOffset = 0;
+
+ // adjust current offset and remaining length to the beginning
+ // of the entry data
+ offset += size;
+ length -= size;
+ } else {
+ // create the entry directly from the data array
+ mCurrentEntry = createEntry(data, offset);
+
+ // adjust current offset and remaining length to the beginning
+ // of the entry data
+ offset += ENTRY_HEADER_SIZE;
+ length -= ENTRY_HEADER_SIZE;
+ }
+ }
+ }
+
+ // at this point, we have an entry, and offset/length have been updated to skip
+ // the entry header.
+
+ // if we have enough data for this entry or more, we'll need to end this entry
+ if (length >= mCurrentEntry.len - mEntryDataOffset) {
+ // compute and save the size of the data that we have to read for this entry,
+ // based on how much we may already have read.
+ int dataSize = mCurrentEntry.len - mEntryDataOffset;
+
+ // we only read what we need, and put it in the entry buffer.
+ System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, dataSize);
+
+ // notify the listener of a new entry
+ if (mListener != null) {
+ mListener.newEntry(mCurrentEntry);
+ }
+
+ // reset some flags: we have read 0 data of the current entry.
+ // and we have no current entry being read.
+ mEntryDataOffset = 0;
+ mCurrentEntry = null;
+
+ // and update the data buffer info to the end of the current entry / start
+ // of the next one.
+ offset += dataSize;
+ length -= dataSize;
+ } else {
+ // we don't have enough data to fill this entry, so we store what we have
+ // in the entry itself.
+ System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, length);
+
+ // save the amount read for the data.
+ mEntryDataOffset += length;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns whether this receiver is canceling the remote service.
+ */
+ public boolean isCancelled() {
+ return mIsCancelled;
+ }
+
+ /**
+ * Cancels the current remote service.
+ */
+ public void cancel() {
+ mIsCancelled = true;
+ }
+
+ /**
+ * Creates a {@link LogEntry} from the array of bytes. This expects the data buffer size
+ * to be at least <code>offset + {@link #ENTRY_HEADER_SIZE}</code>.
+ * @param data the data buffer the entry is read from.
+ * @param offset the offset of the first byte from the buffer representing the entry.
+ * @return a new {@link LogEntry} or <code>null</code> if some error happened.
+ */
+ private LogEntry createEntry(byte[] data, int offset) {
+ if (data.length < offset + ENTRY_HEADER_SIZE) {
+ throw new InvalidParameterException(
+ "Buffer not big enough to hold full LoggerEntry header");
+ }
+
+ // create the new entry and fill it.
+ LogEntry entry = new LogEntry();
+ entry.len = 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 = swap32bitFromArray(data, offset);
+ offset += 4;
+ entry.tid = swap32bitFromArray(data, offset);
+ offset += 4;
+ entry.sec = swap32bitFromArray(data, offset);
+ offset += 4;
+ entry.nsec = swap32bitFromArray(data, offset);
+ offset += 4;
+
+ // allocate the data
+ entry.data = new byte[entry.len];
+
+ return entry;
+ }
+
+ /**
+ * 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 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;
+ }
+
+}
--- /dev/null
+package com.samsung.slp.common.connection.receiver;
+
+/*
+ * 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.
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Base implementation of {@link IShellOutputReceiver}, that takes the raw data coming from the
+ * socket, and convert it into {@link String} objects.
+ * <p/>Additionally, it splits the string by lines.
+ * <p/>Classes extending it must implement {@link #processNewLines(String[])} which receives
+ * new parsed lines as they become available.
+ */
+public abstract class MultiLineReceiver implements IShellOutputReceiver {
+
+ private boolean mTrimLines = true;
+
+ /** unfinished message line, stored for next packet */
+ private String mUnfinishedLine = null;
+
+ private final ArrayList<String> mArray = new ArrayList<String>();
+
+ /**
+ * Set the trim lines flag.
+ * @param trim hether the lines are trimmed, or not.
+ */
+ public void setTrimLine(boolean trim) {
+ mTrimLines = trim;
+ }
+
+ /* (non-Javadoc)
+ * @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput(
+ * byte[], int, int)
+ */
+ 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.
+ * <p/>It is guaranteed that the lines are complete when they are given to this method.
+ * @param lines The array containing the new lines.
+ */
+ public abstract void processNewLines(String[] lines);
+}
--- /dev/null
+package com.samsung.slp.common.connection.sdb;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import com.samsung.slp.common.util.HostUtil;
+
+public class SdbHelper {
+
+ static String CMD_TRACK_DEVICES = "~/sdb/adb devices";
+ static String DEVICES_ATTACHED = "List of devices attached";
+
+ public static String getDeviceList()
+ {
+ String result = null;
+ String response = null;
+ response = HostUtil.returnExecute( CMD_TRACK_DEVICES );
+ if( response.contains(DEVICES_ATTACHED))
+ {
+ result = response;
+ }
+ return result;
+ }
+
+ public static void executeRemoteCommand( String command )
+ {
+ BufferedReader input = null;
+ Process proc = null;
+ Runtime runtime = Runtime.getRuntime();
+ String line = null;
+
+ String[] fullCommand = HostUtil.getCommand(command);
+
+ try {
+ proc= runtime.exec(fullCommand);
+ input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ while((line=input.readLine())!=null){
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package com.samsung.slp.common.connection.ui;
+/*
+ * 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.
+ */
+import java.io.File;
+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 com.samsung.slp.common.connection.device.IDevice;
+
+import java.awt.Panel;
+import java.util.ArrayList;
+
+public class ConnectionsPanel extends Panel {
+
+
+
+
+ /**
+ * 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 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<IUiSelectionListener> mListeners = new ArrayList<IUiSelectionListener>();
+
+ private final ArrayList<IDevice> mDevicesToExpand = new ArrayList<IDevice>();
+
+ private boolean mAdvancedPortSupport;
+
+ /**
+ * A Content provider for the {@link TreeViewer}.
+ * <p/>
+ * The input is a {@link AndroidDebugBridge}. First level elements are {@link IDevice} objects,
+ * and second level elements are {@link Client} object.
+ */
+ private class FileTreeContentProvider implements ITreeContentProvider {
+ public Object[] getChildren(Object parentElement) {
+ return ((File) parentElement).listFiles();
+ }
+
+ public Object getParent(Object element) {
+ return ((File) element).getParentFile();
+ }
+
+ public boolean hasChildren(Object element) {
+ // Get the children
+ Object[] obj = getChildren(element);
+
+ // Return whether the parent has children
+ return obj == null ? false : obj.length > 0;
+
+ }
+
+ public Object[] getElements(Object inputElement) {
+ // These are the root elements of the tree
+ // We don't care what arg0 is, because we just want all
+ // the root nodes in the file system
+ return File.listRoots();
+ }
+
+ 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 FileTreeLabelProvider implements ITableLabelProvider {
+
+ public Image getColumnImage(Object element, int columnIndex) {
+ }
+
+ 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.
+ * <p/>
+ * 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}.
+ * <p/>
+ * 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}.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param client the updated client.
+ * @param changeMask the bit mask describing the changed properties. It can contain
+ * any of the following values: {@link Client#CHANGE_INFO},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
+ * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
+ *
+ * @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) {
+ }
+ }
+ }
+ }
+
+ }
--- /dev/null
+package com.samsung.slp.common.connection.ui;
+
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.part.ViewPart;
+
+import com.samsung.slp.common.conn.device.Device;
+import com.samsung.slp.common.conn.device.DeviceManager;
+import com.samsung.slp.common.conn.device.DeviceModelProvider;
+import com.samsung.slp.common.connection.device.DeviceMonitor;
+
+public class ConnectionsView extends ViewPart{
+
+ private TableViewer Connections;
+
+
+ @Override
+ public void createPartControl(Composite parent) {
+ createViewer(parent);
+
+ }
+
+ private void createViewer(Composite parent)
+ {
+ Connections =
+ new TableViewer( parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL );
+
+ final Table table = Connections.getTable();
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+ table.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Connections.setContentProvider(new ArrayContentProvider());
+ Connections.setInput( DeviceMonitor.mDevices );
+
+ }
+
+ @Override
+ public void setFocus() {
+ // TODO Auto-generated method stub
+
+ }
+
+}
--- /dev/null
+/*
+ * {common-plugins}
+ *
+ * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Hyunsik Noh <hyunsik.noh@samsung.com>
+ * Hoon Kang <h245.kang@samsung.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+package com.samsung.slp.common.connection.ui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.part.ViewPart;
+
+import com.samsung.slp.common.connection.log.LogController;
+
+public class LogView extends ViewPart implements ISelectionListener, IDeviceListener {
+
+ private LogColors colors;
+ private Table table;
+ private Button buttonV;
+ private Button buttonD;
+ private Button buttonI;
+ private Button buttonW;
+ private Button buttonE;
+ private Button buttonSearch;
+ private Button buttonUser;
+
+ private LogController controller = null;
+ private static HashMap<String, LogController> controllerList = new HashMap<String,LogController>();
+
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ setColor( parent );
+ makecontroller( parent );
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+
+ @Override
+ public void setFocus() {
+ }
+
+ private void setColor( Composite parent )
+ {
+ Display d=parent.getDisplay();
+ colors = new LogColors();
+ colors.infoColor=new Color(d, 0, 127, 0);
+ colors.debugColor = new Color(d, 0, 0, 127);
+ colors.errorColor = new Color(d, 255, 0, 0);
+ colors.warningColor = new Color(d, 255, 127, 0);
+ colors.verboseColor = new Color(d, 0, 0, 0);
+ }
+
+ private void makecontroller( Composite parent )
+ {
+ parent.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+ parent.setLayout(new GridLayout(1, false));
+
+ Composite top = new Composite( parent, SWT.NONE );
+ top.setLayoutData(new GridData());
+ top.setLayout(new GridLayout(6, false));
+
+ buttonV = new Button( top, SWT.CHECK);
+ buttonV.setText(" V ");
+ buttonV.setToolTipText("Verbose");
+ buttonV.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+
+ if(buttonV.getSelection())
+ controller.filter.setLevelV( true );
+ else
+ controller.filter.setLevelV( false);
+
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+ buttonD = new Button( top, SWT.CHECK);
+ buttonD.setText(" D ");
+ buttonD.setToolTipText("Debug");
+ buttonD.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if(buttonD.getSelection())
+ controller.filter.setLevelD( true );
+ else
+ controller.filter.setLevelD( false);
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+ buttonI = new Button( top, SWT.CHECK);
+ buttonI.setText(" I ");
+ buttonI.setToolTipText("Info");
+ buttonI.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if(buttonI.getSelection())
+ controller.filter.setLevelI( true );
+ else
+ controller.filter.setLevelI( false);
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+
+ buttonW = new Button( top, SWT.CHECK);
+ buttonW.setText(" W ");
+ buttonW.setToolTipText("Warning");
+ buttonW.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if(buttonW.getSelection())
+ controller.filter.setLevelW( true );
+ else
+ controller.filter.setLevelW( false);
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+
+ buttonE = new Button( top, SWT.CHECK);
+ buttonE.setText(" E ");
+ buttonE.setToolTipText("Error");
+ buttonE.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if(buttonE.getSelection())
+ controller.filter.setLevelE( true );
+ else
+ controller.filter.setLevelE( false);
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+ buttonUser = new Button( top, SWT.CHECK );
+ buttonUser.setText(" UserLog");
+ buttonUser.setToolTipText("User Defined Log");
+ buttonUser.addSelectionListener(new SelectionListener(){
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if(buttonUser.getSelection())
+ controller.filter.setUserlog( true );
+ else
+ controller.filter.setUserlog( false );
+ controller.refiltering();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+
+ Composite mid = new Composite( parent, SWT.NONE );
+ mid.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mid.setLayout(new FillLayout());
+
+ table = new Table( mid, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
+ table.setHeaderVisible( true );
+ table.setLinesVisible( true );
+
+ TableColumn colTime = createTableColumn(table, "Time", SWT.LEFT,
+ "00-00 00:00:00.000" );
+// colTime.setResizable(false);
+
+ TableColumn colLevel = createTableColumn(table, "Level", SWT.LEFT,
+ "Verbose");
+// colLevel.setResizable(false);
+
+ TableColumn colPid = createTableColumn(table, "pid", SWT.LEFT,
+ "99999");
+// colPid.setResizable(false);
+
+ TableColumn colTag = createTableColumn(table, "tag", SWT.LEFT,
+ "ABCDEFGHIJKLMN");
+// colTag.setResizable(false);
+
+ TableColumn colMsg = createTableColumn(table, "Message", SWT.LEFT,
+ "mnopqrstuvwxyz0123456789");
+
+ Composite bottom = new Composite(parent, SWT.NONE);
+ bottom.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ bottom.setLayout(new GridLayout(3, false));
+
+ final Combo combo =new Combo( bottom, SWT.READ_ONLY );
+ String comboItems[] = { "Pid", "Tag", "Message" };
+ combo.setItems( comboItems );
+ combo.setToolTipText("Keyword Search: Pid or Tag, Message");
+
+ final Text filterText = new Text(bottom, SWT.SINGLE | SWT.BORDER);
+ filterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ filterText.setMessage( " Input Keyword ");
+ filterText.addKeyListener( new KeyListener(){
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if( e.keyCode == 13)
+ {
+ int selection = combo.getSelectionIndex();
+ if( selection != 0 && selection != 1)
+ return ;
+
+ controller.filter.setComboIndex( selection );
+ controller.filter.setKeyword( filterText.getText() );
+ controller.refiltering();
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+ });
+
+ buttonSearch = new Button(bottom, SWT.NONE);
+ buttonSearch.setText("Search");
+ buttonSearch.addMouseListener(new MouseListener(){
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ int selection = combo.getSelectionIndex();
+ if( selection != 0 && selection != 1)
+ return ;
+
+ controller.filter.setComboIndex( selection );
+ controller.filter.setKeyword( filterText.getText() );
+ controller.refiltering();
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ });
+
+ turnOnButtons( false );
+ }
+
+ public static TableColumn createTableColumn(Table parent, String header,
+ int style, String sample_text) {
+
+ // create the column
+ TableColumn col = new TableColumn(parent, style);
+
+ // if there is no pref store or the entry is missing, we use the sample
+ // text and pack the column.
+ // Otherwise we just read the width from the prefs and apply it.
+ col.setText(sample_text);
+ col.pack();
+
+ // set the header
+ col.setText(header);
+
+ return col;
+ }
+
+ @Override
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+
+ if( selection.toString().equals("<empty selection>"))
+ {
+ //clear the table
+ clearTable();
+ resetButtons( null );
+ turnOnButtons( false );
+ return ;
+ }
+
+ selectLogController();
+ }
+
+ public void selectLogController()
+ {
+ DeviceMachine machine = DeviceManager.getSelectedDevice();
+ if( machine == null )
+ return ;
+
+ String deviceId = machine.getMachineId();
+
+ if((controller != null )&&(controller == controllerList.get(deviceId)))
+ return ;
+
+ //clear the table
+ clearTable();
+ Iterator<LogController> ite = controllerList.values().iterator();
+ if ( !controllerList.isEmpty())
+ {
+ while(ite.hasNext()){
+ controller = (LogController)ite.next();
+ controller.setSelected(false);
+ }
+ }
+
+ controller = controllerList.get(deviceId);
+ if( controller == null){
+ controller = new LogController( machine, table, colors );
+ controller.Start();
+ controllerList.put( deviceId, controller );
+ controller.setSelected( true );
+ }
+ else
+ {
+ controller.setSelected( true );
+ controller.addOldMsg();
+ }
+
+ turnOnButtons( true );
+ resetButtons( controller );
+ }
+
+ public LogController getController() {
+ return controller;
+ }
+
+ public void turnOnButtons( boolean on )
+ {
+ buttonV.setEnabled(on);
+ buttonD.setEnabled(on);
+ buttonI.setEnabled(on);
+ buttonW.setEnabled(on);
+ buttonE.setEnabled(on);
+ buttonSearch.setEnabled(on);
+ buttonUser.setEnabled(on);
+ }
+
+ public void resetButtons( LogController con )
+ {
+ if( con == null )
+ {
+ buttonV.setSelection( false);
+ buttonD.setSelection( false);
+ buttonI.setSelection( false);
+ buttonW.setSelection( false);
+ buttonE.setSelection( false);
+ buttonSearch.setSelection( false );
+ buttonUser.setSelection( false );
+ }
+ else
+ {
+ buttonV.setSelection( con.filter.isLevelV());
+ buttonD.setSelection( con.filter.isLevelD());
+ buttonI.setSelection( con.filter.isLevelI());
+ buttonW.setSelection( con.filter.isLevelW());
+ buttonE.setSelection( con.filter.isLevelE());
+ buttonSearch.setSelection( true );
+ buttonUser.setSelection( con.filter.isUserlog());
+ }
+ }
+
+ //DeviceView is refreshed.
+ @Override
+ public void ConnectionChanged() {
+
+ table.removeAll();
+
+ if ( !controllerList.isEmpty())
+ {
+ synchronized( controllerList )
+ {
+ Iterator<LogController> ite = controllerList.values().iterator();
+ while(ite.hasNext()){
+ LogController con = (LogController)ite.next();
+ String id = con.deviceId;
+ if( DeviceLauncherManager.getlauncherList().get( id ) == null )
+ {
+ con.removeMsg();
+ controllerList.remove( id );
+ }
+ }
+ }
+ }
+
+ if ( !controllerList.isEmpty())
+ {
+ synchronized( controllerList )
+ {
+ CopyOnWriteArrayList<Device> deviceList = DeviceModelProvider.INSTANCE.getDevices();
+ Device device = deviceList.get(0);
+ if( device != null )
+ {
+ controller = controllerList.get( device.getDeviceId() );
+ if( controller == null){
+ return ;
+ }
+ else
+ {
+ controller.setSelected( true );
+ controller.addOldMsg();
+ }
+ }
+ }
+ }
+ }
+
+ public void clearTable()
+ {
+ if( table != null )
+ {
+ table.removeAll();
+ table.setTopIndex( 0 );
+ }
+ }
+
+ public class LogColors {
+ public Color infoColor;
+ public Color debugColor;
+ public Color errorColor;
+ public Color warningColor;
+ public Color verboseColor;
+ }
+
+ @Override
+ public void SelectionChanged() {
+ // TODO Auto-generated method stub
+
+ }
+}