[Title] Add new plugin for basic sdb conn
authorhyunsik.noh <hyunsik.noh@samsung.com>
Thu, 25 Aug 2011 07:50:02 +0000 (16:50 +0900)
committerhyunsik.noh <hyunsik.noh@samsung.com>
Thu, 25 Aug 2011 07:50:02 +0000 (16:50 +0900)
[Type]      Enhancement
[Module]    // Module Name - (Main / Sub)
[Priority]  // Importance : Critical / Major / Minor
[CQ#]       // CQ Issue Number
[Redmine#]  // Redmine Isuue Number
[Problem]   // Problem Description
[Cause]     // Cause Description
[Solution]  // Solution Description
[TestCase]  // Executed the test-target (How to)

28 files changed:
com.samsung.slp.common.connection/.classpath [new file with mode: 0644]
com.samsung.slp.common.connection/.project [new file with mode: 0644]
com.samsung.slp.common.connection/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
com.samsung.slp.common.connection/META-INF/MANIFEST.MF [new file with mode: 0644]
com.samsung.slp.common.connection/build.properties [new file with mode: 0644]
com.samsung.slp.common.connection/icons/Clear Log.gif [new file with mode: 0644]
com.samsung.slp.common.connection/icons/Connections View.gif [new file with mode: 0644]
com.samsung.slp.common.connection/icons/Export Log.gif [new file with mode: 0644]
com.samsung.slp.common.connection/icons/Import Log.gif [new file with mode: 0644]
com.samsung.slp.common.connection/icons/Log View.gif [new file with mode: 0644]
com.samsung.slp.common.connection/plugin.xml [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/Device.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/DeviceMonitor.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/FileListingService.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/IDevice.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/SdbCommandRejectedException.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/ShellCommandUnresponsiveException.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/TimeoutException.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogController.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogFilter.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/IShellOutputReceiver.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/LogReceiver.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/MultiLineReceiver.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsPanel.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java [new file with mode: 0644]
com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/LogView.java [new file with mode: 0644]

diff --git a/com.samsung.slp.common.connection/.classpath b/com.samsung.slp.common.connection/.classpath
new file mode 100644 (file)
index 0000000..ad32c83
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
diff --git a/com.samsung.slp.common.connection/.project b/com.samsung.slp.common.connection/.project
new file mode 100644 (file)
index 0000000..166c074
--- /dev/null
@@ -0,0 +1,28 @@
+<?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>
diff --git a/com.samsung.slp.common.connection/.settings/org.eclipse.jdt.core.prefs b/com.samsung.slp.common.connection/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..c678288
--- /dev/null
@@ -0,0 +1,8 @@
+#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
diff --git a/com.samsung.slp.common.connection/META-INF/MANIFEST.MF b/com.samsung.slp.common.connection/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..e657bef
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/com.samsung.slp.common.connection/build.properties b/com.samsung.slp.common.connection/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/com.samsung.slp.common.connection/icons/Clear Log.gif b/com.samsung.slp.common.connection/icons/Clear Log.gif
new file mode 100644 (file)
index 0000000..ca43e26
Binary files /dev/null and b/com.samsung.slp.common.connection/icons/Clear Log.gif differ
diff --git a/com.samsung.slp.common.connection/icons/Connections View.gif b/com.samsung.slp.common.connection/icons/Connections View.gif
new file mode 100644 (file)
index 0000000..111e3f9
Binary files /dev/null and b/com.samsung.slp.common.connection/icons/Connections View.gif differ
diff --git a/com.samsung.slp.common.connection/icons/Export Log.gif b/com.samsung.slp.common.connection/icons/Export Log.gif
new file mode 100644 (file)
index 0000000..0c7a2ce
Binary files /dev/null and b/com.samsung.slp.common.connection/icons/Export Log.gif differ
diff --git a/com.samsung.slp.common.connection/icons/Import Log.gif b/com.samsung.slp.common.connection/icons/Import Log.gif
new file mode 100644 (file)
index 0000000..dfcb6e1
Binary files /dev/null and b/com.samsung.slp.common.connection/icons/Import Log.gif differ
diff --git a/com.samsung.slp.common.connection/icons/Log View.gif b/com.samsung.slp.common.connection/icons/Log View.gif
new file mode 100644 (file)
index 0000000..9007253
Binary files /dev/null and b/com.samsung.slp.common.connection/icons/Log View.gif differ
diff --git a/com.samsung.slp.common.connection/plugin.xml b/com.samsung.slp.common.connection/plugin.xml
new file mode 100644 (file)
index 0000000..782e02f
--- /dev/null
@@ -0,0 +1,119 @@
+<?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
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java
new file mode 100644 (file)
index 0000000..ac810a4
--- /dev/null
@@ -0,0 +1,57 @@
+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;
+       }
+
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/Device.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/Device.java
new file mode 100644 (file)
index 0000000..853f2fa
--- /dev/null
@@ -0,0 +1,404 @@
+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);
+//    } 
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/DeviceMonitor.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/DeviceMonitor.java
new file mode 100644 (file)
index 0000000..7342960
--- /dev/null
@@ -0,0 +1,41 @@
+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);
+            }
+               }
+       }
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/FileListingService.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/FileListingService.java
new file mode 100644 (file)
index 0000000..f59c76e
--- /dev/null
@@ -0,0 +1,773 @@
+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);
+    }
+}
+
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/IDevice.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/IDevice.java
new file mode 100644 (file)
index 0000000..289fe59
--- /dev/null
@@ -0,0 +1,380 @@
+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>&lt;build&gt;</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;
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/SdbCommandRejectedException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/SdbCommandRejectedException.java
new file mode 100644 (file)
index 0000000..95336fe
--- /dev/null
@@ -0,0 +1,56 @@
+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;
+    }
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/ShellCommandUnresponsiveException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/ShellCommandUnresponsiveException.java
new file mode 100644 (file)
index 0000000..4ee3611
--- /dev/null
@@ -0,0 +1,29 @@
+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;
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/TimeoutException.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/TimeoutException.java
new file mode 100644 (file)
index 0000000..ac1670a
--- /dev/null
@@ -0,0 +1,27 @@
+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;
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogController.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogController.java
new file mode 100644 (file)
index 0000000..a20d284
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * {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;          
+    }
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogFilter.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogFilter.java
new file mode 100644 (file)
index 0000000..151c2e4
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * {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;
+       }
+       
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/IShellOutputReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/IShellOutputReceiver.java
new file mode 100644 (file)
index 0000000..b13639b
--- /dev/null
@@ -0,0 +1,45 @@
+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();
+};
+
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/LogReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/LogReceiver.java
new file mode 100644 (file)
index 0000000..f4d4764
--- /dev/null
@@ -0,0 +1,276 @@
+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;
+    }
+    
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/MultiLineReceiver.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/MultiLineReceiver.java
new file mode 100644 (file)
index 0000000..d8a8ef7
--- /dev/null
@@ -0,0 +1,130 @@
+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);
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java
new file mode 100644 (file)
index 0000000..61c806e
--- /dev/null
@@ -0,0 +1,45 @@
+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();
+               }
+       }
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsPanel.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsPanel.java
new file mode 100644 (file)
index 0000000..257be34
--- /dev/null
@@ -0,0 +1,712 @@
+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) {
+                       }
+                   }
+               }
+           }
+
+       }
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java
new file mode 100644 (file)
index 0000000..6bbf930
--- /dev/null
@@ -0,0 +1,51 @@
+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
+               
+       }
+
+}
diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/LogView.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/LogView.java
new file mode 100644 (file)
index 0000000..1226217
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * {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
+               
+       }
+}