From a222b937d7b530ead4b4829396ad042a86eb6a6c Mon Sep 17 00:00:00 2001 From: "hyunsik.noh" Date: Thu, 25 Aug 2011 16:50:02 +0900 Subject: [PATCH] [Title] Add new plugin for basic sdb conn [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) --- com.samsung.slp.common.connection/.classpath | 7 + com.samsung.slp.common.connection/.project | 28 + .../.settings/org.eclipse.jdt.core.prefs | 8 + .../META-INF/MANIFEST.MF | 13 + com.samsung.slp.common.connection/build.properties | 4 + .../icons/Clear Log.gif | Bin 0 -> 372 bytes .../icons/Connections View.gif | Bin 0 -> 1035 bytes .../icons/Export Log.gif | Bin 0 -> 378 bytes .../icons/Import Log.gif | Bin 0 -> 380 bytes .../icons/Log View.gif | Bin 0 -> 388 bytes com.samsung.slp.common.connection/plugin.xml | 119 +++ .../slp/common/connection/ConnectionActivator.java | 57 ++ .../slp/common/connection/device/Device.java | 404 ++++++++++ .../common/connection/device/DeviceMonitor.java | 41 ++ .../connection/device/FileListingService.java | 773 +++++++++++++++++++ .../slp/common/connection/device/IDevice.java | 380 ++++++++++ .../exception/SdbCommandRejectedException.java | 56 ++ .../ShellCommandUnresponsiveException.java | 29 + .../connection/exception/TimeoutException.java | 27 + .../slp/common/connection/log/LogController.java | 819 +++++++++++++++++++++ .../slp/common/connection/log/LogFilter.java | 111 +++ .../connection/receiver/IShellOutputReceiver.java | 45 ++ .../common/connection/receiver/LogReceiver.java | 276 +++++++ .../connection/receiver/MultiLineReceiver.java | 130 ++++ .../slp/common/connection/sdb/SdbHelper.java | 45 ++ .../slp/common/connection/ui/ConnectionsPanel.java | 712 ++++++++++++++++++ .../slp/common/connection/ui/ConnectionsView.java | 51 ++ .../samsung/slp/common/connection/ui/LogView.java | 485 ++++++++++++ 28 files changed, 4620 insertions(+) create mode 100644 com.samsung.slp.common.connection/.classpath create mode 100644 com.samsung.slp.common.connection/.project create mode 100644 com.samsung.slp.common.connection/.settings/org.eclipse.jdt.core.prefs create mode 100644 com.samsung.slp.common.connection/META-INF/MANIFEST.MF create mode 100644 com.samsung.slp.common.connection/build.properties create mode 100644 com.samsung.slp.common.connection/icons/Clear Log.gif create mode 100644 com.samsung.slp.common.connection/icons/Connections View.gif create mode 100644 com.samsung.slp.common.connection/icons/Export Log.gif create mode 100644 com.samsung.slp.common.connection/icons/Import Log.gif create mode 100644 com.samsung.slp.common.connection/icons/Log View.gif create mode 100644 com.samsung.slp.common.connection/plugin.xml create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/Device.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/DeviceMonitor.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/FileListingService.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/IDevice.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/SdbCommandRejectedException.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/ShellCommandUnresponsiveException.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/TimeoutException.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogController.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogFilter.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/IShellOutputReceiver.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/LogReceiver.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/MultiLineReceiver.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsPanel.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java create mode 100644 com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/LogView.java diff --git a/com.samsung.slp.common.connection/.classpath b/com.samsung.slp.common.connection/.classpath new file mode 100644 index 0000000..ad32c83 --- /dev/null +++ b/com.samsung.slp.common.connection/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/com.samsung.slp.common.connection/.project b/com.samsung.slp.common.connection/.project new file mode 100644 index 0000000..166c074 --- /dev/null +++ b/com.samsung.slp.common.connection/.project @@ -0,0 +1,28 @@ + + + com.samsung.slp.common.connection + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + 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 index 0000000..c678288 --- /dev/null +++ b/com.samsung.slp.common.connection/.settings/org.eclipse.jdt.core.prefs @@ -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 index 0000000..e657bef --- /dev/null +++ b/com.samsung.slp.common.connection/META-INF/MANIFEST.MF @@ -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 index 0000000..34d2e4d --- /dev/null +++ b/com.samsung.slp.common.connection/build.properties @@ -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 index 0000000000000000000000000000000000000000..ca43e26bde63c9cbe7ac89ee34deaaf59f5c5d04 GIT binary patch literal 372 zcmZ?wbhEHb6krfwxXJ(m|NsAAFQa};LFcTM>jk%ftI;X`wd;c#Hik8Ab}3usR=(P; zVzo!*8t>}0K@A(C=A6mf_Na8{zyA6E<;LOt|G)qJ|NGznkAMDu`t$$W-~Ye<{r~yz|NlRqx{HmQ^Gu6VtaB1<(_-us zBORwz>Q5>&sL8Z&a&iK?2kb7Q2*sZ)j0_Bh3_2jyAU`p%O*}B8z(ePlNKf121`ibh zc6MjO78ey^ucJvT1Jndh1Z)hDlRM{L;wZY!=)(@1%Tl|HHrV8Be)z*u&skkkR!&Yz zQBk5>Lfu)TqN=i6O+v$2Us6U%Nx7SirCXnc)lhTdBxdG?EX+)XT9WeOstP@;R;@SG lmQ+#L)+4dgS?B1nV>?#4xg2IXb=r`1mm$;A3l|(2tO5QAm}>w4 literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..111e3f9ea91495bd402ee2b7c80b288599c19238 GIT binary patch literal 1035 zcmZ?wbhEHb6krfw_|5nA%?v1*7F0MbFn?-z$&BdoS>EN#f(oaG6i?5cxHG$NYfk^x z;wd|HL#oY_CmKh$YWw7A`{voC_E{x%+Gk9%OPk=DKhvjjX+YiDu$E1cZCfJSHpg{t zN$lR5)U!3EdrL~s=FI*rnZ29yCvPvAx}##&9_NDD&IPmF@}}fW-kv^T%iJC37VWth z(Y7L`Z(Z@s?N#%3)h*iFw&GyR(tVRQ9GkKA^oj$QRvx?@*tjIHenE8Ss_2dtaXo9} zd)6fMtV!-)pVGfBYwG6A$s02!ZOEOzC2!j1!dcrZ7woQCxTj&szLpgSI#wR+U3+Bq zjx!ri+?cZEq;Nu(Zhxdkt2b}8w&Wxo(S8kyJ`Jg<+8Ps0>=$_PHA@OL%Zj!t$n|PQ zZb(YmnUlM}boSnP9F+nb<@}trg51rbJgp*pJz@frB!&8=G^ZMxEwpu7;^nx|Gh}Ua z*wSc@N`8(Ce$E;Ju6jZ4Mj^RbDvHxIbQT#dKDtzAg?`lT7?%UCZin1l54gr0k10A` z6nH!!^lWh9)q;k*bs2Y43Lj?|-Oc;^_wVoDzkmMx`R&`c&!0bk`0(M)n>R0CzI^)h z>BEN)@7}$8^XARVmoJ|?ckblLlShsm*}s23Fy6uOKcWf6pDc_F3@aIQK%oH26ATLh_MI@nRFN=vdhY(oataZ?8+^s#c}IL(~@>^W!D`!7bmfDcQu;yMXZ>> z$jr*YBc-#WD0u;!oG8CNMlyZ0Rsa`E{W4v-^ZYEiG(k7b+VlFjxZsUqfYF literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..0c7a2cea0514914967315730088a759d067f90c1 GIT binary patch literal 378 zcmV-=0fqiYNk%w1VGsZi0M!5h00030|Nl>YuTp@qR)VuccB)2qt44UMNO`PFd#zG{ zuv(nVZnolex#NDl<%h!OlgH_u%{)${-B`Tyzp|M2_&>iYld`v3R+|NH*` z@%;bt{Qvg-|N8#_`Tqa@{qOtmwEO9v^yaSl-ForbgZtiv|NQccb2Ec(HTlzKb7VVg zU_EABKVexxmU%Cdb})QtII(sPrE3hVaSfkj3Y%aGpJWT4WeY+=LjV8&0000000000 z00000A^8LW0027xEC2ui01yBW000J;z`_BM5DW-}fgo`x?4Aoo!|{MbB9MoJv3TwZ z2BJWLR4fh7Cyc^wyD2bljH0XSES8J@67!}g7#SHA92^se6DeLQbar)zC=)7PG87jb z9Uh2>Gc#T+jgC5;A)Y%e6dDpB8;G4VAtatI6dxO{oFclqCN(d=z`r&l#Ka{uUdc~3 YI5;OOIm*&IH!C;O(l^)G+&e)4JF=IwivR!s literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..dfcb6e13c7bdb180950aa243f271b03171a737e1 GIT binary patch literal 380 zcmV-?0fYWWNk%w1VGsZi0M!5h00030|Nl>YuTp@qR)VuccB)2qt44UMNO`PFd#zG{ zuv(nVZnolex#NDl<%h!OlgH_u%{)${-B`Tyzp|M2_&>iYld`v3R+|NH*` z@%;bt{Qvg-|N8#_`Tqa@{qOtmwEO9v^yaSl-ForbgZtiv|NQccb2Ec(HTlzKb7VVg zU_EABKVexxmU%Cdb})QtII(sPrE3hVaSfkj3Y%aGpJWT4WeY+=LjV8&0000000000 z00000A^8LW0027xEC2ui01yBW000J=z`_BM5DW-}fgo`x?4Aoo!|{MbB9MoJv3TwZ z2BJWLR4fh7Cyc^wyD2bljH0XSES8J@67!}g7#SHA92^se6DeLQbar)zC=)7PG87jb z9Uh2>Gc#T+jgC4KGa;T&Efg9OARCA?B&MA^E)*Xdu7@=yBE7vcFTujWG$kX)$2MNe aPdO_mI5^78%r`4H)Y&^X*xKGZK>$1YxwSn2 literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..9007253b22fe7fb3f952927b3b2dacf9e13a78fd GIT binary patch literal 388 zcmZ?wbhEHb6krfwxXJ(m|NsC0{OMb)|I`a-9(?-v&A}`y!oSJcCb>Lkd3E8+>sOvW zdhq7q{nu~be17x#vw!XSpoWcMO`BcHR=Jh0cB@$JQMtyudTmg{hNwAb^0qxH-TAm~ z@6+}}&nF*$Irq%#pMU@V`S<_o=l`#N{(t%N|A*iIU;X<3`q%&OfB*mf_y6Oc|DXQ+ z|MvI)uYdo4{`=ouY}}k@TAX5?lVF<`W1kr5IIU8DQkg+briGJ}6VRPt_o5JrKUo+V z7%UldK%yW&F|aK>;CZyD(M4qv$CIF$Pv%LUZM*!j#%>;;`{c@JY9dN3%|~=J&!zhw z$=mH}y56^C^<$4EllG;2-shsGuCA=5rP!{hA7@ZfR?5P~%*7ICuB581t)s}o%)`&a zJjK$mw~UjAm6eHy%il;zLqShdk&U09ZwH&dv68OlRz)rz9$wzH{wAl-n6R*M^Kf&p c$6bq?z{bUX<@&w2J6G@DV}JDco+E=b09yaB+yDRo literal 0 HcmV?d00001 diff --git a/com.samsung.slp.common.connection/plugin.xml b/com.samsung.slp.common.connection/plugin.xml new file mode 100644 index 0000000..782e02f --- /dev/null +++ b/com.samsung.slp.common.connection/plugin.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 index 0000000..ac810a4 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ConnectionActivator.java @@ -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 index 0000000..853f2fa --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/Device.java @@ -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 mProperties = new HashMap(); +// private final Map mMountPoints = new HashMap(); +// + private DeviceMonitor mMonitor; +// +// private static final String LOG_TAG = "Device"; +// +// /** +// * Socket for the connection monitoring client connection/disconnection. +// */ +// private SocketChannel mSocketChannel; +// +// /** +// * Output receiver for "pm install package.apk" command line. +// */ +// private static final class InstallReceiver extends MultiLineReceiver { +// +// private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ +// private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ +// +// private String mErrorMessage = null; +// +// public InstallReceiver() { +// } +// +// @Override +// public void processNewLines(String[] lines) { +// for (String line : lines) { +// if (line.length() > 0) { +// if (line.startsWith(SUCCESS_OUTPUT)) { +// mErrorMessage = null; +// } else { +// Matcher m = FAILURE_PATTERN.matcher(line); +// if (m.matches()) { +// mErrorMessage = m.group(1); +// } +// } +// } +// } +// } +// +// public boolean isCancelled() { +// return false; +// } +// +// public String getErrorMessage() { +// return mErrorMessage; +// } +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#getSerialNumber() +// */ + public String getSerialNumber() { + return mSerialNumber; + } +// +// /** {@inheritDoc} */ +// public String getAvdName() { +// return mAvdName; +// } +// +// /** +// * Sets the name of the AVD +// */ +// void setAvdName(String avdName) { +// if (isEmulator() == false) { +// throw new IllegalArgumentException( +// "Cannot set the AVD name of the device is not an emulator"); +// } +// +// mAvdName = avdName; +// } +// + /* + * (non-Javadoc) + * @see com.android.ddmlib.IDevice#getState() + */ + public DeviceState getState() { + return mState; + } + + /** + * Changes the state of the device. + */ + void setState(DeviceState state) { + mState = state; + } +// +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#getProperties() +// */ +// public Map getProperties() { +// return Collections.unmodifiableMap(mProperties); +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#getPropertyCount() +// */ +// public int getPropertyCount() { +// return mProperties.size(); +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#getProperty(java.lang.String) +// */ +// public String getProperty(String name) { +// return mProperties.get(name); +// } +// +// public String getMountPoint(String name) { +// return mMountPoints.get(name); +// } +// +// +// @Override +// public String toString() { +// return mSerialNumber; +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#isOnline() +// */ +// public boolean isOnline() { +// return mState == DeviceState.ONLINE; +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#isEmulator() +// */ + public boolean isEmulator() { + return mSerialNumber.matches(RE_EMULATOR_SN); + } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#isOffline() +// */ +// public boolean isOffline() { +// return mState == DeviceState.OFFLINE; +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#isBootLoader() +// */ +// public boolean isBootLoader() { +// return mState == DeviceState.BOOTLOADER; +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#hasClients() +// */ +// public boolean hasClients() { +// return mClients.size() > 0; +// } +// +// /* +// * (non-Javadoc) +// * @see com.android.ddmlib.IDevice#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 index 0000000..7342960 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/DeviceMonitor.java @@ -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 mDevices = new ArrayList(); + + + public void processDeviceData() + { + ArrayList list = new ArrayList(); + + 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 index 0000000..f59c76e --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/FileListingService.java @@ -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. + *

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.
+ * The output format looks like:
+ * /data/app/myapp.apk=com.mypackage.myapp */ + private final static Pattern sPmPattern = Pattern.compile("^package:(.+?)=(.+)$"); //$NON-NLS-1$ + + /** Top level data folder. */ + public final static String DIRECTORY_DATA = "data"; //$NON-NLS-1$ + /** Top level sdcard folder. */ + public final static String DIRECTORY_SDCARD = "sdcard"; //$NON-NLS-1$ + /** Top level mount folder. */ + public final static String DIRECTORY_MNT = "mnt"; //$NON-NLS-1$ + /** Top level system folder. */ + public final static String DIRECTORY_SYSTEM = "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 mThreadList = new ArrayList(); + + /** + * Represents an entry in a directory. This can be a file or a directory. + */ + public final static class FileEntry { + /** Pattern to escape filenames for shell command consumption. */ + private final static Pattern sEscapePattern = Pattern.compile( + "([\\\\()*+?\"'#/\\s])"); //$NON-NLS-1$ + + /** + * Comparator object for FileEntry + */ + private static Comparator sEntryComparator = new Comparator() { + public int compare(FileEntry o1, FileEntry o2) { + if (o1 instanceof FileEntry && o2 instanceof FileEntry) { + FileEntry fe1 = (FileEntry)o1; + FileEntry fe2 = (FileEntry)o2; + return fe1.name.compareTo(fe2.name); + } + return 0; + } + }; + + FileEntry parent; + String name; + String info; + String permissions; + String size; + String date; + String time; + String owner; + String group; + int type; + boolean isAppPackage; + + boolean isRoot; + + /** + * Indicates whether the entry content has been fetched yet, or not. + */ + long fetchTime = 0; + + final ArrayList mChildren = new ArrayList(); + + /** + * Creates a new file entry. + * @param parent parent entry or null if entry is root + * @param name name of the entry. + * @param type entry type. Can be one of the following: {@link FileListingService#TYPE_FILE}, + * {@link FileListingService#TYPE_DIRECTORY}, {@link FileListingService#TYPE_OTHER}. + */ + 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 ls. + */ + public String getSize() { + return size; + } + + /** + * Returns the size of the entry. + */ + public int getSizeValue() { + return Integer.parseInt(size); + } + + /** + * Returns the date string of the entry, as returned by ls. + */ + public String getDate() { + return date; + } + + /** + * Returns the time string of the entry, as returned by ls. + */ + public String getTime() { + return time; + } + + /** + * Returns the permission string of the entry, as returned by ls. + */ + public String getPermissions() { + return permissions; + } + + /** + * Returns the extra info for the entry. + *

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

For an application apk file it will be the application package as returned + * by the Package Manager. + */ + public String getInfo() { + return info; + } + + /** + * Return the full path of the entry. + * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator. + */ + public String getFullPath() { + if (isRoot) { + return FILE_ROOT; + } + StringBuilder pathBuilder = new StringBuilder(); + fillPathBuilder(pathBuilder, false); + + return pathBuilder.toString(); + } + + /** + * Return the fully escaped path of the entry. This path is safe to use in a + * shell command line. + * @return a path string using {@link FileListingService#FILE_SEPARATOR} as separator + */ + public String getFullEscapedPath() { + StringBuilder pathBuilder = new StringBuilder(); + fillPathBuilder(pathBuilder, true); + + return pathBuilder.toString(); + } + + /** + * Returns the path as a list of segments. + */ + public String[] getPathSegments() { + ArrayList list = new ArrayList(); + fillPathSegments(list); + + return list.toArray(new String[list.size()]); + } + + /** + * Returns true if the entry is a directory, false otherwise; + */ + public int getType() { + return type; + } + + /** + * Returns if the entry is a folder or a link to a folder. + */ + public boolean isDirectory() { + return type == TYPE_DIRECTORY || type == TYPE_DIRECTORY_LINK; + } + + /** + * Returns the parent entry. + */ + public FileEntry getParent() { + return parent; + } + + /** + * Returns the cached children of the entry. This returns the cache created from calling + * FileListingService.getChildren(). + */ + public FileEntry[] getCachedChildren() { + return mChildren.toArray(new FileEntry[mChildren.size()]); + } + + /** + * Returns the child {@link FileEntry} matching the name. + * This uses the cached children list. + * @param name the name of the child to return. + * @return the FileEntry matching the name or null. + */ + public FileEntry findChild(String name) { + for (FileEntry entry : mChildren) { + if (entry.name.equals(name)) { + return entry; + } + } + return null; + } + + /** + * Returns whether the entry is the root. + */ + public boolean isRoot() { + return isRoot; + } + + void addChild(FileEntry child) { + mChildren.add(child); + } + + void setChildren(ArrayList newChildren) { + mChildren.clear(); + mChildren.addAll(newChildren); + } + + boolean needFetch() { + if (fetchTime == 0) { + return true; + } + long current = System.currentTimeMillis(); + if (current-fetchTime > REFRESH_TEST) { + return true; + } + + return false; + } + + /** + * Returns if the entry is a valid application package. + */ + public boolean isApplicationPackage() { + return isAppPackage; + } + + /** + * Returns if the file name is an application package name. + */ + public boolean isAppFileName() { + Matcher m = 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 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 mEntryList; + private ArrayList 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 entryList, + ArrayList linkList) { + mParentEntry = parentEntry; + mCurrentChildren = parentEntry.getCachedChildren(); + mEntryList = entryList; + mLinkList = linkList; + } + + @Override + public void processNewLines(String[] lines) { + for (String line : lines) { + // no need to handle empty lines. + if (line.length() == 0) { + continue; + } + + // run the line through the regexp + Matcher m = sLsPattern.matcher(line); + if (m.matches() == false) { + continue; + } + + // get the name + String name = m.group(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 ls command on the device. + * + * @see FileListingService#getChildren(com.android.ddmlib.FileListingService.FileEntry, boolean, com.android.ddmlib.FileListingService.IListingReceiver) + */ + public interface IListingReceiver { + public void setChildren(FileEntry entry, FileEntry[] children); + + public void refreshEntry(FileEntry entry); + } + + /** + * Creates a File Listing Service for a specified {@link Device}. + * @param device The Device the service is connected to. + */ + FileListingService(Device device) { + mDevice = device; + } + + /** + * Returns the root element. + * @return the {@link FileEntry} object representing the root element or + * null if the device is invalid. + */ + public FileEntry getRoot() { + if (mDevice != null) { + if (mRoot == null) { + mRoot = new FileEntry(null /* parent */, "" /* name */, TYPE_DIRECTORY, + true /* isRoot */); + } + + return mRoot; + } + + return null; + } + + /** + * Returns the children of a {@link FileEntry}. + *

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

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

+ * The result for each ls command is cached in the parent + * FileEntry. useCache allows usage of this cache, but only if the + * cache is valid. The cache is valid only for {@link FileListingService#REFRESH_RATE} ms. + * After that a new ls command is always executed. + *

+ * If the cache is valid and useCache == true, the method will always simply + * return the value of the cache, whether a {@link IListingReceiver} has been provided or not. + * + * @param entry The parent entry. + * @param useCache A flag to use the cache or to force a new ls command. + * @param receiver A receiver for asynchronous calls. + * @return The list of children or null for asynchronous calls. + * + * @see FileEntry#getCachedChildren() + */ + public FileEntry[] getChildren(final FileEntry entry, boolean useCache, + final IListingReceiver receiver) { + // first thing we do is check the cache, and if we already have a recent + // enough children list, we just return that. + if (useCache && entry.needFetch() == false) { + return entry.getCachedChildren(); + } + + // if there's no receiver, then this is a synchronous call, and we + // return the result of ls + if (receiver == null) { + doLs(entry); + return entry.getCachedChildren(); + } + + // this is a asynchronous call. + // we launch a thread that will do ls and give the listing + // to the receiver + Thread t = new Thread("ls " + entry.getFullPath()) { //$NON-NLS-1$ + @Override + public void run() { + doLs(entry); + + receiver.setChildren(entry, entry.getCachedChildren()); + + final FileEntry[] children = entry.getCachedChildren(); + if (children.length > 0 && children[0].isApplicationPackage()) { + final HashMap map = new HashMap(); + + for (FileEntry child : children) { + String path = child.getFullPath(); + map.put(path, child); + } + + // call pm. + String command = PM_FULL_LISTING; + try { + mDevice.executeShellCommand(command, new MultiLineReceiver() { + @Override + public void processNewLines(String[] lines) { + for (String line : lines) { + if (line.length() > 0) { + // get the filepath and package from the line + Matcher m = sPmPattern.matcher(line); + if (m.matches()) { + // get the children with that path + FileEntry entry = map.get(m.group(1)); + if (entry != null) { + entry.info = m.group(2); + receiver.refreshEntry(entry); + } + } + } + } + } + public boolean isCancelled() { + return false; + } + }); + } catch (Exception e) { + // adb failed somehow, we do nothing. + } + } + + + // if another thread is pending, launch it + synchronized (mThreadList) { + // first remove ourselves from the list + mThreadList.remove(this); + + // then launch the next one if applicable. + if (mThreadList.size() > 0) { + Thread t = mThreadList.get(0); + t.start(); + } + } + } + }; + + // we don't want to run multiple ls on the device at the same time, so we + // store the thread in a list and launch it only if there's no other thread running. + // the thread will launch the next one once it's done. + synchronized (mThreadList) { + // add to the list + mThreadList.add(t); + + // if it's the only one, launch it. + if (mThreadList.size() == 1) { + t.start(); + } + } + + // and we return null. + return null; + } + + private void doLs(FileEntry entry) { + // create a list that will receive the list of the entries + ArrayList entryList = new ArrayList(); + + // create a list that will receive the link to compute post ls; + ArrayList linkList = new ArrayList(); + + try { + // create the command + String command = "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 index 0000000..289fe59 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/device/IDevice.java @@ -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 adb devices. + * + * @param state the device state. + * @return a {@link DeviceState} object or null if the state is unknown. + */ + public static DeviceState getState(String state) { + for (DeviceState deviceState : values()) { + if (deviceState.mState.equals(state)) { + return deviceState; + } + } + return null; + } + } +// +// /** +// * Returns the serial number of the device. +// */ +// public String getSerialNumber(); +// +// /** +// * Returns the name of the AVD the emulator is running. +// *

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

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

This is similar to calling +// * executeShellCommand(command, receiver, DdmPreferences.getTimeOut()). +// * +// * @param command the shell command to execute +// * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell +// * command +// * @throws TimeoutException in case of timeout on the connection. +// * @throws 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 receiver. +// *

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

For commands like log output, a maxTimeToOutputResponse value of 0, meaning +// * that the method will never throw and will block until the receiver's +// * {@link IShellOutputReceiver#isCancelled()} returns true, should be +// * used. +// * +// * @param command the shell command to execute +// * @param receiver the {@link IShellOutputReceiver} that will receives the output of the shell +// * command +// * @param maxTimeToOutputResponse the maximum amount of time during which the command is allowed +// * to not output any response. A value of 0 means the method will wait forever +// * (until the receiver cancels the execution) for command output and +// * never throw. +// * @throws TimeoutException in case of timeout on the connection when sending the command. +// * @throws SdbCommandRejectedException if adb rejects the command. +// * @throws ShellCommandUnresponsiveException in case the shell command doesn't send any output +// * for a period longer than maxTimeToOutputResponse. +// * @throws IOException in case of I/O error on the connection. +// * +// * @see DdmPreferences#getTimeOut() +// */ +// public void executeShellCommand(String command, IShellOutputReceiver receiver, +// int maxTimeToOutputResponse) +// throws TimeoutException, SdbCommandRejectedException, ShellCommandUnresponsiveException, +// IOException; +// +// /** +// * Runs the event log service and outputs the event log to the {@link LogReceiver}. +// *

This call is blocking until {@link LogReceiver#isCancelled()} returns true. +// * @param receiver the receiver to receive the event log entries. +// * @throws TimeoutException in case of timeout on the connection. This can only be thrown if the +// * timeout happens during setup. Once logs start being received, no timeout will occur as it's +// * not possible to detect a difference between no log and timeout. +// * @throws 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}. +// *

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 true 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 true 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 null if pid is unknown +// * @param pid the pid of the client. +// */ +// public String getClientName(int pid); +// +// /** +// * Installs an Android application on device. +// * This is a helper method that combines the syncPackageToDevice, installRemotePackage, +// * and removePackage steps +// * +// * @param packageFilePath the absolute file system path to file on local host to install +// * @param reinstall set to true if re-install of app should be performed +// * @return a {@link String} with an error code, or null if success. +// * @throws TimeoutException in case of timeout on the connection. +// * @throws 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 true 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 null 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 index 0000000..95336fe --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/SdbCommandRejectedException.java @@ -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. + *

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 index 0000000..4ee3611 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/ShellCommandUnresponsiveException.java @@ -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. + *

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 index 0000000..ac1670a --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/exception/TimeoutException.java @@ -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 index 0000000..a20d284 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogController.java @@ -0,0 +1,819 @@ +/* + * {common-plugins} + * + * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Hyunsik Noh + * Hoon Kang + * + * 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 filteredMessagesList = new ArrayList(); + private ArrayList newMessagesList = new ArrayList(); + + 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 mArray = new ArrayList(); + 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 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. + *

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 tmpMessages = new ArrayList(); + 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 tmpMessages = new ArrayList(); + +// 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 array = new ArrayList(); + 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 index 0000000..151c2e4 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/log/LogFilter.java @@ -0,0 +1,111 @@ +/* + * {common-plugins} + * + * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Hyunsik Noh + * Hoon Kang + * + * 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 index 0000000..b13639b --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/IShellOutputReceiver.java @@ -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 index 0000000..f4d4764 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/LogReceiver.java @@ -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}. + *

This interface provides two methods. + *

    + *
  • {@link #newEntry(com.android.ddmlib.log.LogReceiver.LogEntry)} provides a + * first level of parsing, extracting {@link LogEntry} objects out of the log service output.
  • + *
  • {@link #newData(byte[], int, int)} provides a way to receive the raw information + * coming directly from the log service.
  • + *
+ */ + public interface ILogListener { + /** + * Sent when a new {@link LogEntry} has been parsed by the {@link LogReceiver}. + * @param entry the new log entry. + */ + public void newEntry(LogEntry entry); + + /** + * Sent when new raw data is coming from the log service. + * @param data the raw data buffer. + * @param offset the offset into the buffer signaling the beginning of the new data. + * @param length the length of the new data. + */ + public void newData(byte[] data, int offset, int length); + } + + /** Current {@link LogEntry} being read, before sending it to the listener. */ + private LogEntry mCurrentEntry; + + /** Temp buffer to store partial entry headers. */ + private byte[] mEntryHeaderBuffer = new byte[ENTRY_HEADER_SIZE]; + /** Offset in the partial header buffer */ + private int mEntryHeaderOffset = 0; + /** Offset in the partial entry data */ + private int mEntryDataOffset = 0; + + /** Listener waiting for receive fully read {@link LogEntry} objects */ + private ILogListener mListener; + + private boolean mIsCancelled = false; + + /** + * Creates a {@link LogReceiver} with an {@link ILogListener}. + *

+ * The {@link ILogListener} will receive new log entries as they are parsed, in the form + * of {@link LogEntry} objects. + * @param listener the listener to receive new log entries. + */ + public LogReceiver(ILogListener listener) { + mListener = listener; + } + + + /** + * Parses new data coming from the log service. + * @param data the data buffer + * @param offset the offset into the buffer signaling the beginning of the new data. + * @param length the length of the new data. + */ + public void parseNewData(byte[] data, int offset, int length) { + // notify the listener of new raw data + if (mListener != null) { + mListener.newData(data, offset, length); + } + + // loop while there is still data to be read and the receiver has not be cancelled. + while (length > 0 && mIsCancelled == false) { + // first check if we have no current entry. + if (mCurrentEntry == null) { + if (mEntryHeaderOffset + length < ENTRY_HEADER_SIZE) { + // if we don't have enough data to finish the header, save + // the data we have and return + System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, length); + mEntryHeaderOffset += length; + return; + } else { + // we have enough to fill the header, let's do it. + // did we store some part at the beginning of the header? + if (mEntryHeaderOffset != 0) { + // copy the rest of the entry header into the header buffer + int size = ENTRY_HEADER_SIZE - mEntryHeaderOffset; + System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, + size); + + // create the entry from the header buffer + mCurrentEntry = createEntry(mEntryHeaderBuffer, 0); + + // since we used the whole entry header buffer, we reset the offset + mEntryHeaderOffset = 0; + + // adjust current offset and remaining length to the beginning + // of the entry data + offset += size; + length -= size; + } else { + // create the entry directly from the data array + mCurrentEntry = createEntry(data, offset); + + // adjust current offset and remaining length to the beginning + // of the entry data + offset += ENTRY_HEADER_SIZE; + length -= ENTRY_HEADER_SIZE; + } + } + } + + // at this point, we have an entry, and offset/length have been updated to skip + // the entry header. + + // if we have enough data for this entry or more, we'll need to end this entry + if (length >= mCurrentEntry.len - mEntryDataOffset) { + // compute and save the size of the data that we have to read for this entry, + // based on how much we may already have read. + int dataSize = mCurrentEntry.len - mEntryDataOffset; + + // we only read what we need, and put it in the entry buffer. + System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, dataSize); + + // notify the listener of a new entry + if (mListener != null) { + mListener.newEntry(mCurrentEntry); + } + + // reset some flags: we have read 0 data of the current entry. + // and we have no current entry being read. + mEntryDataOffset = 0; + mCurrentEntry = null; + + // and update the data buffer info to the end of the current entry / start + // of the next one. + offset += dataSize; + length -= dataSize; + } else { + // we don't have enough data to fill this entry, so we store what we have + // in the entry itself. + System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, length); + + // save the amount read for the data. + mEntryDataOffset += length; + return; + } + } + } + + /** + * Returns whether this receiver is canceling the remote service. + */ + public boolean isCancelled() { + return mIsCancelled; + } + + /** + * Cancels the current remote service. + */ + public void cancel() { + mIsCancelled = true; + } + + /** + * Creates a {@link LogEntry} from the array of bytes. This expects the data buffer size + * to be at least offset + {@link #ENTRY_HEADER_SIZE}. + * @param data the data buffer the entry is read from. + * @param offset the offset of the first byte from the buffer representing the entry. + * @return a new {@link LogEntry} or null if some error happened. + */ + private LogEntry createEntry(byte[] data, int offset) { + if (data.length < offset + ENTRY_HEADER_SIZE) { + throw new InvalidParameterException( + "Buffer not big enough to hold full LoggerEntry header"); + } + + // create the new entry and fill it. + LogEntry entry = new LogEntry(); + entry.len = 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 index 0000000..d8a8ef7 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/receiver/MultiLineReceiver.java @@ -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. + *

Additionally, it splits the string by lines. + *

Classes extending it must implement {@link #processNewLines(String[])} which receives + * new parsed lines as they become available. + */ +public abstract class MultiLineReceiver implements IShellOutputReceiver { + + private boolean mTrimLines = true; + + /** unfinished message line, stored for next packet */ + private String mUnfinishedLine = null; + + private final ArrayList mArray = new ArrayList(); + + /** + * Set the trim lines flag. + * @param trim hether the lines are trimmed, or not. + */ + public void setTrimLine(boolean trim) { + mTrimLines = trim; + } + + /* (non-Javadoc) + * @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput( + * byte[], int, int) + */ + public final void addOutput(byte[] data, int offset, int length) { + if (isCancelled() == false) { + String s = null; + try { + s = new String(data, offset, length, "ISO-8859-1"); //$NON-NLS-1$ + } catch (UnsupportedEncodingException e) { + // normal encoding didn't work, try the default one + s = new String(data, offset,length); + } + + // ok we've got a string + if (s != null) { + // if we had an unfinished line we add it. + if (mUnfinishedLine != null) { + s = mUnfinishedLine + s; + mUnfinishedLine = null; + } + + // now we split the lines + mArray.clear(); + int start = 0; + do { + int index = s.indexOf("\r\n", start); //$NON-NLS-1$ + + // if \r\n was not found, this is an unfinished line + // and we store it to be processed for the next packet + if (index == -1) { + mUnfinishedLine = s.substring(start); + break; + } + + // so we found a \r\n; + // extract the line + String line = s.substring(start, index); + if (mTrimLines) { + line = line.trim(); + } + mArray.add(line); + + // move start to after the \r\n we found + start = index + 2; + } while (true); + + if (mArray.size() > 0) { + // at this point we've split all the lines. + // make the array + String[] lines = mArray.toArray(new String[mArray.size()]); + + // send it for final processing + processNewLines(lines); + } + } + } + } + + /* (non-Javadoc) + * @see com.android.ddmlib.adb.IShellOutputReceiver#flush() + */ + public final void flush() { + if (mUnfinishedLine != null) { + processNewLines(new String[] { mUnfinishedLine }); + } + + done(); + } + + /** + * Terminates the process. This is called after the last lines have been through + * {@link #processNewLines(String[])}. + */ + public void done() { + // do nothing. + } + + /** + * Called when new lines are being received by the remote process. + *

It is guaranteed that the lines are complete when they are given to this method. + * @param lines The array containing the new lines. + */ + public abstract void processNewLines(String[] lines); +} diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java new file mode 100644 index 0000000..61c806e --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/sdb/SdbHelper.java @@ -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 index 0000000..257be34 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsPanel.java @@ -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 mListeners = new ArrayList(); + + private final ArrayList mDevicesToExpand = new ArrayList(); + + private boolean mAdvancedPortSupport; + + /** + * A Content provider for the {@link TreeViewer}. + *

+ * The input is a {@link AndroidDebugBridge}. First level elements are {@link IDevice} objects, + * and second level elements are {@link Client} object. + */ + private class 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. + *

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

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

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

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

+ * This is sent from a non UI thread. + * @param client the updated client. + * @param changeMask the bit mask describing the changed properties. It can contain + * any of the following values: {@link Client#CHANGE_INFO}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, + * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} + * + * @see IClientChangeListener#clientChanged(Client, int) + */ + public void clientChanged(final Client client, final int changeMask) { + exec(new Runnable() { + public void run() { + if (mTree.isDisposed() == false) { + // refresh the client + mTreeViewer.refresh(client); + + if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == + Client.CHANGE_DEBUGGER_STATUS && + client.getClientData().getDebuggerConnectionStatus() == + DebuggerStatus.WAITING) { + // make sure the device is expanded. Normally the setSelection below + // will auto expand, but the children of device may not already exist + // at this time. Forcing an expand will make the TreeViewer create them. + IDevice device = client.getDevice(); + if (mTreeViewer.getExpandedState(device) == false) { + mTreeViewer.setExpandedState(device, true); + } + + // create and set the selection + TreePath treePath = new TreePath(new Object[] { device, client}); + TreeSelection treeSelection = new TreeSelection(treePath); + mTreeViewer.setSelection(treeSelection); + + if (mAdvancedPortSupport) { + client.setAsSelectedClient(); + } + + // notify the listener of a possible selection change. + notifyListeners(device, client); + } + } else { + // tree is disposed, we need to do something. + // lets remove ourselves from the listener. + AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); + AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); + AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); + } + } + }); + } + + private void loadImages(Display display) { + ImageLoader loader = ImageLoader.getDdmUiLibLoader(); + + if (mDeviceImage == null) { + mDeviceImage = loader.loadImage(display, "device.png", //$NON-NLS-1$ + ICON_WIDTH, ICON_WIDTH, + display.getSystemColor(SWT.COLOR_RED)); + } + if (mEmulatorImage == null) { + mEmulatorImage = loader.loadImage(display, + "emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ + display.getSystemColor(SWT.COLOR_BLUE)); + } + if (mThreadImage == null) { + mThreadImage = loader.loadImage(display, ICON_THREAD, + ICON_WIDTH, ICON_WIDTH, + display.getSystemColor(SWT.COLOR_YELLOW)); + } + if (mHeapImage == null) { + mHeapImage = loader.loadImage(display, ICON_HEAP, + ICON_WIDTH, ICON_WIDTH, + display.getSystemColor(SWT.COLOR_BLUE)); + } + if (mWaitingImage == null) { + mWaitingImage = loader.loadImage(display, + "debug-wait.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ + display.getSystemColor(SWT.COLOR_RED)); + } + if (mDebuggerImage == null) { + mDebuggerImage = loader.loadImage(display, + "debug-attach.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ + display.getSystemColor(SWT.COLOR_GREEN)); + } + if (mDebugErrorImage == null) { + mDebugErrorImage = loader.loadImage(display, + "debug-error.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ + display.getSystemColor(SWT.COLOR_RED)); + } + } + + /** + * Returns a display string representing the state of the device. + * @param d the device + */ + private static String getStateString(IDevice d) { + DeviceState deviceState = d.getState(); + if (deviceState == DeviceState.ONLINE) { + return "Online"; + } else if (deviceState == DeviceState.OFFLINE) { + return "Offline"; + } else if (deviceState == DeviceState.BOOTLOADER) { + return "Bootloader"; + } + + return "??"; + } + + /** + * Executes the {@link Runnable} in the UI thread. + * @param runnable the runnable to execute. + */ + private void exec(Runnable runnable) { + try { + Display display = mTree.getDisplay(); + display.asyncExec(runnable); + } catch (SWTException e) { + // tree is disposed, we need to do something. lets remove ourselves from the listener. + AndroidDebugBridge.removeDebugBridgeChangeListener(this); + AndroidDebugBridge.removeDeviceChangeListener(this); + AndroidDebugBridge.removeClientChangeListener(this); + } + } + + private void notifyListeners() { + // get the selection + TreeItem[] items = mTree.getSelection(); + + Client client = null; + IDevice device = null; + + if (items.length == 1) { + Object object = items[0].getData(); + if (object instanceof Client) { + client = (Client)object; + device = client.getDevice(); + } else if (object instanceof IDevice) { + device = (IDevice)object; + } + } + + notifyListeners(device, client); + } + + private void notifyListeners(IDevice selectedDevice, Client selectedClient) { + if (selectedDevice != mCurrentDevice || selectedClient != mCurrentClient) { + mCurrentDevice = selectedDevice; + mCurrentClient = selectedClient; + + for (IUiSelectionListener listener : mListeners) { + // notify the listener with a try/catch-all to make sure this thread won't die + // because of an uncaught exception before all the listeners were notified. + try { + listener.selectionChanged(selectedDevice, selectedClient); + } catch (Exception e) { + } + } + } + } + + } diff --git a/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java new file mode 100644 index 0000000..6bbf930 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/ConnectionsView.java @@ -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 index 0000000..1226217 --- /dev/null +++ b/com.samsung.slp.common.connection/src/com/samsung/slp/common/connection/ui/LogView.java @@ -0,0 +1,485 @@ +/* + * {common-plugins} + * + * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Hyunsik Noh + * Hoon Kang + * + * 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 controllerList = new HashMap(); + + @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("")) + { + //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 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 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 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 + + } +} -- 2.7.4