/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.adt; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import com.android.ddmlib.AdbCommandRejectedException; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; import com.android.ddmlib.Client; import com.android.ddmlib.ClientData; import com.android.ddmlib.EmulatorConsole; import com.android.ddmlib.FileListingService; import com.android.ddmlib.FileListingService.FileEntry; import com.android.ddmlib.IDevice; import com.android.ddmlib.IDevice.DeviceState; import com.android.ddmlib.MultiLineReceiver; import com.android.ddmlib.SyncException; import com.android.ddmlib.SyncService; import com.android.ddmlib.SyncService.ISyncProgressMonitor; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import com.android.ide.eclipse.ddms.DdmsPlugin; import com.motorola.studio.android.AndroidPlugin; import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType; import com.motorola.studio.android.common.IAndroidConstants; import com.motorola.studio.android.common.exception.AndroidException; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.i18n.AndroidNLS; import com.motorola.studio.android.utilities.TelnetFrameworkAndroid; /** * DESCRIPTION: * This class is the common interface to functionalities from DDMS * * RESPONSIBILITY: * Centralizes the access to DDMS. * * COLABORATORS: * None. * * USAGE: * Use the public methods to use DDMS */ public class DDMSFacade { /** * Command for switching back to USB connection mode. */ private static final String USB_SWITCH_BACK_COMMAND = "usb"; //$NON-NLS-1$ /** * Argument which indicates the device to apply a certain command. */ private static final String DEVICE_ID_INDICATOR = "-s"; //$NON-NLS-1$ private static final String DEFAULT_WIRELESS_DEVICE_PROPERTY = "tiwlan0"; //$NON-NLS-1$ /** * Map containing all connected devices. * It is being kept for us not to depend on ADT every time we need one, preventing * deadlocks. */ private static final Map<String, IDevice> connectedDevices = new HashMap<String, IDevice>(); /** * Set containing the serial numbers of the devices completely loaded. * A device is considered completely loaded if it has already loaded the HOME application. */ private static final Set<String> completelyUpDevices = new HashSet<String>(); /** * Folder located inside the SDK folder containing some sdk tools. */ static final String TOOLS_FOLDER = IAndroidConstants.FD_TOOLS; /** * Folder located inside the SDK folder and containing the ADB. */ static final String PLATFORM_TOOLS_FOLDER = IAndroidConstants.FD_PLATFORM_TOOLS; /** * adb (android debug bridge) command. */ static final String ADB_COMMAND = "adb"; //$NON-NLS-1$ /** * Command to concatenate with "adb" to have the device shell. */ static final String SHELL_CMD = "shell"; //$NON-NLS-1$ /** * Options to be used with adb to indicate run operation. */ private static final String AM_CMD = "am"; //$NON-NLS-1$ /** * Command to concatenate with "am" to have an activity executed at the device. */ private static final String START_CMD = "start"; //$NON-NLS-1$ /** * Parameter for running in debug mode. */ private static final String ADB_AM_DEBUG = "-D"; //$NON-NLS-1$ /** * Parameter provided before the application package/name. */ private static final String ADB_AM_NAME = "-n"; //$NON-NLS-1$ /** * Parameter for selecting emulator instance. */ static final String ADB_INSTANCE_PARAMETER = DEVICE_ID_INDICATOR; //$NON-NLS-1$ /** * Folder for the SDK. */ private static final String SDCARD_FOLDER = "sdcard"; //$NON-NLS-1$ /** * Folder for the SDK. */ private static final String MNT_SDCARD_FOLDER = "mnt/sdcard"; //$NON-NLS-1$ /* * TCP/IP */ private static final String CONNECT_TCPIP_CMD = "connect"; //$NON-NLS-1$ private static final String DISCONNECT_TCPIP_CMD = "disconnect"; //$NON-NLS-1$ private static final String TCPIP_CMD = "tcpip"; //$NON-NLS-1$ private static final String IFCONFIG_CMD = "ifconfig"; //$NON-NLS-1$ static Object consoleLock = new Object(); private static Map<String, String> avdNameMap = new HashMap<String, String>(); /** * Property from device which represents the wi-fi value to use ipconfig command. */ private static final String WIFI_INTERFACE_DEVICE_PROPERTY = "wifi.interface"; //$NON-NLS-1$ // IP validation private static final String ZERO_TO_255_PATTERN = "((\\d)|(\\d\\d)|([0-1]\\d\\d)|(2[0-4]\\d)|(25[0-5]))"; //$NON-NLS-1$ private static final String IP_PATTERN = "(" + ZERO_TO_255_PATTERN + "\\." //$NON-NLS-1$ //$NON-NLS-2$ + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN //$NON-NLS-1$ //$NON-NLS-2$ + ")+"; //$NON-NLS-1$ /** * Must be called only once, during AndroidPlugin start-up. * This method configures all necessary device listeners. */ public static void setup() { AndroidPlugin.getDefault().addSDKLoaderListener(new Runnable() { public void run() { AndroidDebugBridge adb = AndroidDebugBridge.getBridge(); if (adb == null) { AndroidDebugBridge.disconnectBridge(); DdmsPlugin.setToolsLocation(AdtPlugin.getOsAbsoluteAdb(), true, AdtPlugin.getOsAbsoluteHprofConv(), AdtPlugin.getOsAbsoluteTraceview()); } if (adb != null) { IDevice[] x = adb.getDevices(); IDevice[] newDevices = x; List<IDevice> oldDevList = new ArrayList<IDevice>(connectedDevices.values()); for (IDevice newDev : newDevices) { String serialNum = newDev.getSerialNumber(); if (connectedDevices.containsKey(serialNum)) { IDevice oldDev = connectedDevices.get(serialNum); oldDevList.remove(oldDev); if (oldDev.getState().compareTo((newDev).getState()) != 0) { if ((newDev).getState() == DeviceState.OFFLINE) { deviceDisconnected(newDev); } else if ((newDev).getState() == DeviceState.ONLINE) { deviceConnected(newDev); } } } else { deviceConnected(newDev); } } for (IDevice oldDev : oldDevList) { deviceDisconnected(oldDev); } } } }); // Adds listener for the HOME application. It adds the serial number of the // device to a collection when it identifies that the HOME application has // loaded AndroidDebugBridge.addClientChangeListener(new IClientChangeListener() { public void clientChanged(Client client, int changeMask) { if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME) { final Client finalClient = client; Thread t = new Thread() { @Override public void run() { String applicationName = finalClient.getClientData().getClientDescription(); if (applicationName != null) { IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); if (home.equals(applicationName)) { String serialNum = finalClient.getDevice().getSerialNumber(); synchronized (completelyUpDevices) { StudioLogger.debug("Completely Up Device: " + serialNum); //$NON-NLS-1$ completelyUpDevices.add(serialNum); } } } } }; t.start(); } } }); } static void deviceStatusChanged(IDevice device) { StudioLogger.debug("Device changed: " + device.getSerialNumber()); //$NON-NLS-1$ synchronized (connectedDevices) { connectedDevices.put(device.getSerialNumber(), device); } if ((device).getState() == DeviceState.ONLINE) { IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); if (device.getClient(home) != null) { synchronized (completelyUpDevices) { StudioLogger.debug("Completely Up Device: " + device.getSerialNumber()); //$NON-NLS-1$ if (!completelyUpDevices.contains(device.getSerialNumber())) { completelyUpDevices.add(device.getSerialNumber()); } } } } } /** * Registers a device as connected * * @param device */ static void deviceConnected(IDevice device) { final String serialNumber = device.getSerialNumber(); StudioLogger.debug("Device connected: " + serialNumber); //$NON-NLS-1$ synchronized (connectedDevices) { connectedDevices.put(serialNumber, device); } if (!device.isEmulator() && !device.hasClients()) { boolean timeout = false; long startTime = System.currentTimeMillis(); int maxInterval = 10000; do { try { Thread.sleep(250); } catch (InterruptedException e) { //do nothing } long currentTime = System.currentTimeMillis(); timeout = ((startTime + maxInterval) < currentTime); } while (!device.hasClients() && !timeout); if (timeout) { synchronized (completelyUpDevices) { //put the device up anyway. completelyUpDevices.add(serialNumber); } } } if (device.hasClients()) { // When a device is connected, look for the HOME application and add // the device serial number to a collection if it is already running. IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); if (device.getClient(home) != null) { StudioLogger.debug("Completely Up Device: " + serialNumber); //$NON-NLS-1$ synchronized (completelyUpDevices) { completelyUpDevices.add(serialNumber); } } } StudioAndroidEventManager.fireEvent(EventType.DEVICE_CONNECTED, serialNumber); } /** * Unregisters a device as connected * * @param device */ static void deviceDisconnected(IDevice device) { final String serialNumber = device.getSerialNumber(); StudioLogger.debug("Device disconnected: " + serialNumber); //$NON-NLS-1$ synchronized (completelyUpDevices) { completelyUpDevices.remove(serialNumber); } synchronized (connectedDevices) { connectedDevices.remove(serialNumber); } StudioAndroidEventManager.fireEvent(EventType.DEVICE_DISCONNECTED, serialNumber); avdNameMap.remove(device.getSerialNumber()); } /** * Get all connected device serial numbers * * @return */ public static Collection<String> getConnectedSerialNumbers() { return connectedDevices.keySet(); } /** * Get the Device associated with the given serial number * * @param serialNumber Serial number of the device to retrieve * @return Device associated with the given serial number */ public static IDevice getDeviceBySerialNumber(String serialNumber) { return connectedDevices.get(serialNumber); } /** * Runs an activity at the given device * * @param serialNumber The serial number of the device to have the activity executed * @param activityName The activity to execute * @param debugMode Whether the activity shall be run in debug mode or not * @param processOut The output stream of the process running "adb" * * @return An IStatus object with the result of the operation */ public static IStatus runActivity(String serialNumber, String activityName, boolean debugMode, OutputStream processOut) { IStatus status = Status.OK_STATUS; // Return if no instance is selected if (serialNumber == null) { StudioLogger.error("Abort run operation. Serial number is null."); //$NON-NLS-1$ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer); } // Return if instance is not started if (!isDeviceOnline(serialNumber)) { StudioLogger.error("Abort run operation. Device is not online."); //$NON-NLS-1$ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ } try { String[] cmd = createRunCommand(serialNumber, activityName, debugMode); executeCommand(cmd, processOut); } catch (IOException e) { StudioLogger.error("Deploy: Could not execute adb install command."); //$NON-NLS-1$ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); } return status; } static String executeCommand(String[] cmd, OutputStream out) throws IOException { return executeCommand(cmd, out, null); } /** * DOCUMENT ME!! * @param cmd * @param out * @param serialNumber * @return * @throws IOException */ static String executeCommand(String[] cmd, OutputStream out, String serialNumber) throws IOException { String fullCmd = ""; //$NON-NLS-1$ if (out != null) { for (String cmdArg : cmd) { fullCmd += cmdArg + " "; //$NON-NLS-1$ } out.write(fullCmd.getBytes()); out.write("\n".getBytes()); //$NON-NLS-1$ } Runtime r = Runtime.getRuntime(); Process p = r.exec(cmd); String command_results = ""; //$NON-NLS-1$ InputStream processIn = p.getInputStream(); final BufferedReader br = new BufferedReader(new InputStreamReader(processIn)); String line; try { while ((line = br.readLine()) != null) { command_results += line; command_results += "\n"; //$NON-NLS-1$ if (out != null) { if (serialNumber != null) { out.write((serialNumber + ": ").getBytes()); //$NON-NLS-1$ } out.write(line.getBytes()); out.write("\n".getBytes()); //$NON-NLS-1$ } } } finally { br.close(); } return command_results; } /** * See http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html * to understand how Process.exec works and its problems * * @param cmd Command to be executed. * @param out Output Stream. * @param timeout Timeout (secs.) * @param monitor {@link IProgressMonitor} * * @return the {@link IStatus} of this process execution. * * @throws IOException Exception thrown in case there is any problem * executing the command. */ private static IStatus executeRemoteDevicesCommand(String[] cmd, OutputStream out, int timeout, String timeoutMsg, IStopCondition stopCondition, IProgressMonitor monitor) throws IOException { IStatus status = Status.OK_STATUS; long timeoutLimit = -1; if (timeout != 0) { timeoutLimit = System.currentTimeMillis() + (timeout * 1000); } String fullCmd = ""; //$NON-NLS-1$ for (String cmdArg : cmd) { fullCmd += cmdArg + " "; //$NON-NLS-1$ } if (out != null) { out.write(fullCmd.getBytes()); out.write("\n".getBytes()); //$NON-NLS-1$ } Runtime r = Runtime.getRuntime(); Process p = r.exec(cmd); int errorCode = 0; // inputStream / errorStream; String[] commandResults = new String[] { "", "" //$NON-NLS-1$ //$NON-NLS-2$ }; commandResults = readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(), p.getErrorStream(), out); while (!stopCondition.canStop()) { if ((monitor != null) && (monitor.isCanceled())) { p.destroy(); return Status.CANCEL_STATUS; } try { errorCode = p.exitValue(); if (errorCode != 0) { break; } } catch (IllegalThreadStateException e) { // Process is still running... Proceed with loop } try { Thread.sleep(1000); } catch (InterruptedException e) { StudioLogger.error("Execute command: thread has been interrupted"); //$NON-NLS-1$ } if (timeout > 0) { try { testTimeout(timeoutLimit, ((timeoutMsg != null) ? timeoutMsg : AndroidNLS.ERR_GenericTimeout)); } catch (TimeoutException e) { p.destroy(); StudioLogger.debug("The timeout " + timeout //$NON-NLS-1$ + " has been reached when executing the command " + fullCmd); //$NON-NLS-1$ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage(), e); } } } commandResults = readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(), p.getErrorStream(), out); if (errorCode != 0) { StudioLogger.debug("Command " + cmd + " returned an error code: " + errorCode); //$NON-NLS-1$ //$NON-NLS-2$ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, NLS.bind( AndroidNLS.ERR_CommandError, errorCode) + "\n" //$NON-NLS-1$ + ((!commandResults[1].equals("")) ? commandResults[1] //$NON-NLS-1$ : commandResults[0])); } else { status = new Status(IStatus.OK, AndroidPlugin.PLUGIN_ID, commandResults[0]); } return status; } /** * Defines a stop condition */ interface IStopCondition { public boolean canStop(); } /** * @param commandResults * @param errorResults * @param inputStream * @param errorStream * @param out */ private static String[] readCmdOutputFromStreams(String commandResults, String errorResults, InputStream inputStream, InputStream errorStream, OutputStream out) { String[] results = new String[2]; String line = ""; //$NON-NLS-1$ BufferedReader brInput = new BufferedReader(new InputStreamReader(inputStream)); BufferedReader brError = new BufferedReader(new InputStreamReader(errorStream)); try { // input stream if (brInput.ready()) { while ((line = brInput.readLine()) != null) { commandResults += line; commandResults += "\n"; //$NON-NLS-1$ if (out != null) { out.write(line.getBytes()); out.write("\n".getBytes()); //$NON-NLS-1$ } } } // error stream if (brError.ready()) { while ((line = brError.readLine()) != null) { errorResults += "\n"; //$NON-NLS-1$ if (out != null) { out.write(line.getBytes()); out.write("\n".getBytes()); //$NON-NLS-1$ } } } } catch (IOException e) { StudioLogger.error("Cannot read command outputs"); //$NON-NLS-1$ } finally { try { brInput.close(); brError.close(); } catch (IOException e) { StudioLogger.error("Could not close console stream: " + e.getMessage()); } } results[0] = commandResults; results[1] = errorResults; return results; } /** * Checks if the timeout limit has reached. * * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds. * @throws StartTimeoutException When the system time limit is overtaken. */ private static void testTimeout(long timeoutLimit, String timeoutErrorMessage) throws TimeoutException { if (System.currentTimeMillis() > timeoutLimit) { throw new TimeoutException(timeoutErrorMessage); } } /** * Creates a string with the command that should * be called in order to run the application. */ private static String[] createRunCommand(String serialNumber, String activityName, boolean debugMode) { String cmd[]; String sdkPath = SdkUtils.getSdkPath(); // The tools folder should exist and be here, but double-cheking // once more wont kill File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator); if (!f.exists()) { StudioLogger .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ + File.separator); } else { if (!f.isDirectory()) { StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ + File.separator); } } String completeAppPath = activityName.substring(0, activityName.lastIndexOf(".")) + "/" + activityName; //$NON-NLS-1$ //$NON-NLS-2$ if (debugMode) { // If debugMode option is checked, create command with the -D paramater String cmdTemp[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD, ADB_AM_DEBUG, ADB_AM_NAME, completeAppPath }; cmd = cmdTemp; } else { // If debugMode option is unchecked, create command without the -D paramater String cmdTemp[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD, ADB_AM_NAME, completeAppPath }; cmd = cmdTemp; } return cmd; } /** * Check if the device is Online (i.e. if it's possible to communicate with it) * Notice it is a verification of the status of the Device which may be different than the status of the Tml Instance. * * @param serialNumber * @return true if the Device is online, false otherwise */ public static boolean isDeviceOnline(String serialNumber) { IDevice device = getDeviceBySerialNumber(serialNumber); if ((device == null) || !device.isOnline()) { return false; } return true; } /** * Return true if the Device is being shown on the OFFLINE state. * * @param serialNumber Device�s serial number. * * @return <code>true</code> in case the Device if offline, * <code>false</code> otherwise. */ public static boolean isDeviceOffline(String serialNumber) { IDevice device = getDeviceBySerialNumber(serialNumber); return ((device == null) || ((device != null) && device.isOffline())); } /** * Check if the device is completely loaded * A device is completely loaded when it loads the HOME application * * @param serialNumber * @return true if the Device has completely loaded; false otherwise */ public static boolean isDeviceCompletelyLoaded(String serialNumber) { return completelyUpDevices.contains(serialNumber); } /** * Tests if the device represented by the serial number (if it exists) is an emulator * * @param serialNumber * @return true if it is an emulator, false if not or non existent */ public static boolean isEmulator(String serialNumber) { IDevice device = getDeviceBySerialNumber(serialNumber); if ((device != null) && device.isEmulator()) { return true; } return false; } public static boolean isRemote(String serialNumber) { // firstly, test if the serial number has the format "anything:digits" Pattern p = Pattern.compile("(.)+:(\\d)+"); //$NON-NLS-1$ Matcher m = p.matcher(serialNumber); if (m.matches()) { IDevice device = getDeviceBySerialNumber(serialNumber); if ((device != null) && !device.isEmulator()) { return true; } } return false; } /** * Execute an app in the Device * * @param serialNumber Serial number of the device where to execute the command * @param remoteCommand command to be executed remotely on the Device * @param monitor monitor associated with the operation * * @return The lines read from the command output * * @throws IOException */ public static Collection<String> execRemoteApp(String serialNumber, String remoteCommand, final IProgressMonitor monitor) throws IOException { return executeShellCmd(serialNumber, remoteCommand, monitor); } /** * Execute an app in the Device * * @param serialNumber Serial number of the device where to execute the command * @param remoteCommands commands to be executed remotely on the Device * @param monitor monitor associated with the operation * * @throws IOException */ public static Map<String, Collection<String>> execRemoteApp(String serialNumber, Collection<String> remoteCommands, final IProgressMonitor monitor) throws IOException { Map<String, Collection<String>> cmdAnswers = new LinkedHashMap<String, Collection<String>>(); for (String remoteCommand : remoteCommands) { StudioLogger.debug(remoteCommand); Collection<String> answers = executeShellCmd(serialNumber, remoteCommand, monitor); cmdAnswers.put(remoteCommand, answers); } return cmdAnswers; } private static Collection<String> executeShellCmd(String serialNumber, final String cmd, final IProgressMonitor monitor) { final Collection<String> results = new ArrayList<String>(); IDevice d = getDeviceBySerialNumber(serialNumber); if (d != null) { try { d.executeShellCommand(cmd, new MultiLineReceiver() { public boolean isCancelled() { return monitor.isCanceled(); } @Override public void processNewLines(String[] lines) { for (String line : lines) { if ((!line.equals("")) && (!line.equals(cmd))) //$NON-NLS-1$ { results.add(line); } } } }, 0); } catch (Exception e) { StudioLogger.error(DDMSFacade.class, "Error executing shell command " + cmd //$NON-NLS-1$ + " at device " + serialNumber, e); //$NON-NLS-1$ } } return results; } /** * Retrieves all properties from the device with provided serial number. * @param serialNumber * @return */ public static Properties getDeviceProperties(String serialNumber) { Properties instanceProperties = new Properties(); if (serialNumber != null) { String key = ""; //$NON-NLS-1$ String value = ""; //$NON-NLS-1$ Collection<String> lines; try { lines = execRemoteApp(serialNumber, "getprop", new NullProgressMonitor()); //$NON-NLS-1$ for (String line : lines) { String[] split = line.split("]:"); //$NON-NLS-1$ if (split.length >= 2) { if (!split[0].equals("")) //$NON-NLS-1$ { key = split[0].substring(1, split[0].length()); if (!split[1].equals("")) //$NON-NLS-1$ { value = split[1].substring(2, split[1].length() - 1); instanceProperties.setProperty(key, value); } } } } } catch (IOException e) { StudioLogger.error("IOException while executing an app on device. " + e.getMessage()); } } return instanceProperties; } /** * Retrieves a given property from the device with provided serial number. * * @param serialNumber * @param propertyName * * @return */ public static String getDeviceProperty(String serialNumber, String propertyName) { String result = null; IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber); if (device != null) { result = device.getProperty(propertyName); } return result; } /** * Get the name of the VM associated to the emulator running in the given deviceSerial identification. * * @param deviceSerial identification of the emulator whose vm name must be retrieved. * @return the name of the VM used by the emulator running with the given id, * or null if the vmname could be retrieved. */ public static String getVmName(final IDevice d) { String vmName = null; String serialNumber = d.getSerialNumber(); int MAX_TRIES = 120; int tries = 0; while ((vmName == null) && (tries < MAX_TRIES)) { synchronized (avdNameMap) { vmName = avdNameMap.get(serialNumber); } if (vmName == null) { vmName = d.getAvdName(); } if (vmName == null) { try { Thread.sleep(1000); } catch (InterruptedException e) { //do nothing } finally { tries++; } } //try to get vmname by telnet as last option if (vmName == null) { vmName = getVmNameByTelnet(serialNumber); } } if (vmName != null) { synchronized (avdNameMap) { if (avdNameMap.get(serialNumber) == null) { avdNameMap.put(serialNumber, vmName); } } } return vmName; } private static String getVmNameByTelnet(String serialNumber) { Pattern pattern = Pattern.compile("emulator-([0-9]+)"); //$NON-NLS-1$ TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid(); Matcher matcher = pattern.matcher(serialNumber); matcher.matches(); String avdName = null; try { Integer telnetPort = Integer.valueOf(matcher.group(1)); telnet.connect("localhost", telnetPort); //$NON-NLS-1$ String avdNameRaw = telnet.write("avd name\r\n", new String[] //$NON-NLS-1$ { "KO" //$NON-NLS-1$ }); String split = avdNameRaw.contains("\r\n") ? "\r\n" : "\n"; String[] outputArray = avdNameRaw.split(split); //$NON-NLS-1$ if (outputArray.length > 2) { avdName = outputArray[2]; } if (avdName != null) { avdNameMap.put(serialNumber, avdName); } } catch (NumberFormatException e) { avdName = serialNumber; } catch (IOException e) { avdName = serialNumber; } finally { try { telnet.disconnect(); } catch (IOException e) { //Do nothing. } } return avdName; } /** * Get the Device associated with the Android VM * * @param vmName Android VM name * @return Device associated with the given Android VM */ public static IDevice getDeviceWithVmName(String vmName) { IDevice toReturn = null; if (vmName != null) { Collection<IDevice> devices = connectedDevices.values(); for (IDevice d : devices) { if (d.isEmulator()) { String deviceVmName = DDMSFacade.getVmName(d); if (vmName.equals(deviceVmName)) { toReturn = d; break; } } } } return toReturn; } /** * Securely get the name of the AVD associated to the given device. * * @param serialNumber The serialNumber of the device that we want the AVD name for * @return the name of the AVD used by the emulator running with the given device, * or null if the vmname could be retrieved. */ public static String getNameBySerialNumber(String serialNumber) { String avdName = null; IDevice d = getDeviceBySerialNumber(serialNumber); avdName = getNameByDevice(d); return avdName; } /** * Securely get the serial number of the given instance. * * @param instanceName The name of the instance we want the serial number of * @return the serial number of the given instance, or <code>null</code> if no instance with the * given name is online */ public static String getSerialNumberByName(String instanceName) { String serialNumber = null; if (instanceName != null) { List<IDevice> devices = null; synchronized (connectedDevices) { devices = new ArrayList<IDevice>(connectedDevices.size()); devices.addAll(connectedDevices.values()); } if (devices != null) { for (IDevice dev : devices) { if (instanceName.equals(getNameByDevice(dev))) { serialNumber = dev.getSerialNumber(); break; } } } } return serialNumber; } public static Collection<String> getRunningApplications(String serialNumber) { Collection<String> apps = new ArrayList<String>(); if (serialNumber != null) { IDevice dev = getDeviceBySerialNumber(serialNumber); if (dev != null) { Client[] clients = dev.getClients(); if ((clients != null) && (clients.length > 0)) { for (Client c : clients) { apps.add(c.getClientData().getClientDescription()); } } } } return apps; } /** * Dumps a HPROF file based on a client description and a device serial number * @param clientDescription A client description of a running application */ public static IStatus dumpHprofFile(String clientDescription, String serialNumber, IProgressMonitor monitor) { IStatus status = Status.OK_STATUS; monitor.beginTask(AndroidNLS.DumpHprofFile_GeneratingMemoryAnalysisOutput, 100); // Retrive running apps monitor.setTaskName(AndroidNLS.DumpHprofFile_GettingRunningApplications); IDevice dev = getDeviceBySerialNumber(serialNumber); Client[] clients = dev.getClients(); monitor.worked(25); // Store the shell final Shell[] shell = new Shell[1]; PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { shell[0] = new Shell(PlatformUI.getWorkbench().getDisplay()); } }); MotodevHProfDumpHandler hprofHandler = new MotodevHProfDumpHandler(shell[0], monitor); monitor.setTaskName(AndroidNLS.DumpHprofFile_SettingApplicationToAnalyse); hprofHandler.setSelectedApp(clientDescription); monitor.worked(25); try { // Find a client with matching description and dum the HPROF file for (Client c : clients) { if (c.getClientData().getClientDescription().equals(clientDescription)) { // Set our handler as the HprofDumpHandler ClientData.setHprofDumpHandler(hprofHandler); monitor.setTaskName(AndroidNLS.DumpHprofFile_DumpingHprofFile); c.dumpHprof(); synchronized (DDMSFacade.class) { DDMSFacade.class.wait(); } monitor.worked(50); } } } catch (Exception e) { // Status not ok status = Status.CANCEL_STATUS; } finally { monitor.done(); } return status; } /** * Gets the AVD name of the device * * @param d The device to be searched for the AVD name * * @return The AVD name */ private static String getNameByDevice(final IDevice d) { String name = null; if (d != null) { if (d.isEmulator()) { name = getVmName(d); } else { name = d.getSerialNumber(); } } return name; } /** * Create port forward for a given VM * * @param serialNumber Android serial number * @param from port number from * @param to port number to * @return true is the port forward was successful, false otherwise */ public static boolean createForward(String serialNumber, int from, int to) { boolean ok = true; IDevice device = getDeviceBySerialNumber(serialNumber); try { device.createForward(from, to); } catch (Exception e) { StudioLogger.error(DDMSFacade.class, "Error creating forward of device: " //$NON-NLS-1$ + serialNumber + " from " + from + " to " + to, e); //$NON-NLS-1$ //$NON-NLS-2$ ok = false; } return ok; } /** * Kill the communication channel * * @param serialNumber The serial number of the device to kill */ public static void kill(String serialNumber) { if (isDeviceOnline(serialNumber)) { IDevice deviceToKill = getDeviceBySerialNumber(serialNumber); if (deviceToKill != null) { synchronized (consoleLock) { EmulatorConsole console = EmulatorConsole.getConsole(deviceToKill); if (console != null) { console.kill(); } } } } } /** * Push files to device * * @param serialNumber Android device serial number * @param localDir local folder path * @param fileNames files to transfer * @param remoteDir destination folder path * @param timeout timeout for the operation * @param monitor monitor associated with the operation */ public static IStatus pushFiles(String serialNumber, String localDir, Collection<String> fileNames, String remoteDir, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { return transferFiles(true, serialNumber, localDir, fileNames, remoteDir, timeout, monitor, outputStream); } /** * Push files to device * * @param serialNumber Android device serial number * @param localFiles destination local files * @param remoteFiles remote files to transfer as localFiles to desktop * @param timeout timeout for the operation * @param monitor monitor associated with the operation */ public static IStatus pushFiles(String serialNumber, List<File> localFiles, List<String> remoteFiles, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { return transferFiles(true, serialNumber, localFiles, remoteFiles, timeout, monitor, outputStream); } /** * Pull files from device * * @param serialNumber Android device serial number * @param localDir local folder path * @param fileNames files to transfer * @param remoteDir destination folder path * @param timeout timeout for the operation * @param monitor monitor associated with the operation */ public static IStatus pullFiles(String serialNumber, String localDir, Collection<String> fileNames, String remoteDir, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { return transferFiles(false, serialNumber, localDir, fileNames, remoteDir, timeout, monitor, outputStream); } /** * Pull files from device * * @param serialNumber Android device serial number * @param localFiles local files to transfer as remoteFiles to device * @param remoteFiles destination remote files * @param timeout timeout for the operation * @param monitor monitor associated with the operation */ public static IStatus pullFiles(String serialNumber, List<File> localFiles, List<String> remoteFiles, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { return transferFiles(false, serialNumber, localFiles, remoteFiles, timeout, monitor, outputStream); } /** * Get the service used to transfer files to the Device * * @param device Device * @param timeout timeout for the operation * @param monitor monitor associated with the operation * @return The service used to transfer files to the Device * * @throws AndroidException */ private static SyncService getSyncService(IDevice device, int timeout, final IProgressMonitor monitor) throws AndroidException { SyncService service = null; long timeoutLimit = System.currentTimeMillis() + timeout; do { if ((device != null) && (device.isOnline())) { try { service = device.getSyncService(); } catch (IOException e) { StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ } catch (com.android.ddmlib.TimeoutException e) { StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ } catch (AdbCommandRejectedException e) { StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ } } try { Thread.sleep(100); } catch (InterruptedException e) { //do nothing } if (monitor.isCanceled()) { StudioLogger.info("Operation canceled by the user"); //$NON-NLS-1$ return null; } if (System.currentTimeMillis() > timeoutLimit) { StudioLogger.error("The emulator was not up within the set timeout"); //$NON-NLS-1$ throw new AndroidException( "Timeout while preparing to transfer files to the Device. " + device); //$NON-NLS-1$ } } while (service == null); return service; } private static IStatus transferFiles(boolean isPush, String serialNumber, String localDir, Collection<String> fileNames, String remoteDir, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { List<File> localList = new ArrayList<File>(); List<String> remoteList = new ArrayList<String>(); for (String name : fileNames) { localList.add(new File(localDir, name)); remoteList.add(remoteDir + "/" + name); //$NON-NLS-1$ } return transferFiles(isPush, serialNumber, localList, remoteList, timeout, monitor, outputStream); } private static IStatus transferFiles(boolean isPush, String serialNumber, List<File> localFiles, List<String> remoteFiles, int timeout, final IProgressMonitor monitor, OutputStream outputStream) { if (localFiles.size() != remoteFiles.size()) { return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, AndroidNLS.ERR_DDMSFacade_IncompatibleFileLists); } IStatus status = Status.OK_STATUS; IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber); SyncService service = null; try { service = getSyncService(device, timeout, monitor); if (service == null) { status = Status.CANCEL_STATUS; } else { final ISyncProgressMonitor syncMonitor = new ISyncProgressMonitor() { public void start(int i) { //do nothing } public void stop() { //do nothing } public void advance(int i) { //do nothing } public boolean isCanceled() { return monitor.isCanceled(); } public void startSubTask(String s) { //do nothing } }; FileListingService flService = device.getFileListingService(); for (int i = 0; i < localFiles.size(); i++) { File localFile = localFiles.get(i); String remotePath = remoteFiles.get(i); String absLocalFile = localFile.getAbsolutePath(); String resultMessage = null; if (isPush) { StudioLogger.debug("Push " + absLocalFile + " to " + remotePath); //$NON-NLS-1$ //$NON-NLS-2$ try { service.pushFile(absLocalFile, remotePath, syncMonitor); } catch (SyncException e1) { StudioLogger .debug("Push result: " + "SyncException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ resultMessage = NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ } catch (FileNotFoundException e1) { StudioLogger.debug("Push result: " + "FileNotFoundException occured " //$NON-NLS-1$ //$NON-NLS-2$ + e1.getMessage()); resultMessage = NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ } catch (IOException e1) { StudioLogger .debug("Push result: " + "IOException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ resultMessage = NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ } catch (com.android.ddmlib.TimeoutException e1) { StudioLogger .debug("Push result: " + "TimeoutException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ resultMessage = NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ } if ((outputStream != null) && (resultMessage != null)) { try { outputStream.write(resultMessage.getBytes()); outputStream.write('\n'); outputStream.flush(); } catch (Exception e) { // Do nothing } } } else { FileEntry f1 = null; FileEntry f2 = null; f2 = flService.getRoot(); flService.getChildren(f2, false, null); String[] dirs = remotePath.split("/"); //$NON-NLS-1$ for (int j = 1; j < (dirs.length - 1); j++) { f1 = f2.findChild(dirs[j]); flService.getChildren(f1, false, null); f2 = f1; } final FileEntry fileToPull = f2.findChild(dirs[dirs.length - 1]); if (fileToPull != null) { try { service.pullFile(fileToPull, absLocalFile, syncMonitor); } catch (FileNotFoundException e) { resultMessage = e.getLocalizedMessage(); StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ } catch (SyncException e) { resultMessage = e.getLocalizedMessage(); StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ } catch (IOException e) { resultMessage = e.getLocalizedMessage(); StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ } catch (com.android.ddmlib.TimeoutException e) { resultMessage = e.getLocalizedMessage(); StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ } if ((outputStream != null) && (resultMessage != null)) { String message = NLS.bind(AndroidNLS.CON_ConsolePull, fileToPull.getFullPath(), absLocalFile) + ": " + resultMessage; //$NON-NLS-1$ try { outputStream.write(message.getBytes()); outputStream.write('\n'); outputStream.flush(); } catch (IOException e) { //do nothing } } } else { resultMessage = NLS.bind(AndroidNLS.DDMSFacade_Remote_File_Not_Found, remotePath); StudioLogger.debug("Pull result: File not found " + remotePath); //$NON-NLS-1$ } } if (resultMessage != null) { status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, resultMessage); } if (syncMonitor.isCanceled()) { status = Status.CANCEL_STATUS; break; } } } } catch (AndroidException e) { status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); } catch (NullPointerException e1) { status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, AndroidNLS.ERR_DDMSFacade_FileNotFound); } finally { if (service != null) { service.close(); } } return status; } /** * Check if the application is running in the device with specified serial number * @param serialNumber * @param applicationName * @return true if it is running, false otherwise */ public static boolean isApplicationRunning(String serialNumber, String applicationName) { IDevice dev = null; boolean running = false; dev = connectedDevices.get(serialNumber); if (dev != null) { running = dev.getClient(applicationName) != null; } return running; } /** * Connect to a Remote Device given its IP/Port * * @param device the Remote Device Instance * @param host device host (IP) * @param port device port * @param timeout the maximum time allowed to successfully connect to the device * @param monitor the monitor of the operation * @return the status of the operation * @throws IOException */ public static IStatus connectTcpIp(final ISerialNumbered device, String host, String port, int timeout, IProgressMonitor monitor) throws IOException { SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgConnectingToDeviceViaTCPIP, 10); IStatus status = Status.OK_STATUS; String serialNumber = device.getSerialNumber(); if (!isDeviceOnline(serialNumber)) // check if it's already connected { String[] cmd = createConnectTcpIpCommand(host, port); status = executeRemoteDevicesCommand( cmd, null, timeout, NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileConnecting, device.getDeviceName()), new IStopCondition() { public boolean canStop() { String serialNumber = device.getSerialNumber(); if (serialNumber != null) { return isDeviceOnline(serialNumber); } else { return false; } } }, subMonitor.newChild(1000)); } subMonitor.worked(1000); return status; } /** * Method which switches the device connection mode from TCP/IP * to USB. * * @param device {@link ISerialNumbered} device to have its connection mode changed. * @param host The IP of the device. * @param port The port in which the TCP/IP connection is established. * @param timeout The maximum time which the switching operation is attempted. * @param monitor The {@link IProgressMonitor} which this operation is being computed. * * @return Returns the {@link IStatus} of this operation. * * @throws IOException Exception thrown in case something goes wrong while trying * to switch the device connection mode from TCP/IP to USB. */ public static IStatus switchFromTCPConnectionModeToUSBConnectionMode( final ISerialNumbered device, String host, String port, int timeout, IProgressMonitor monitor) throws IOException { SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingDeviceFromTCPIPToUSB, 10); IStatus status = Status.OK_STATUS; String serialNumber = device.getSerialNumber(); if (isDeviceOnline(serialNumber)) // check if it's already connected { String[] cmd = createSwitchToUSBConnectionModeCommand(host, port); status = executeRemoteDevicesCommand(cmd, null, timeout, NLS.bind( AndroidNLS.DDMSFacade_MsgTimeoutReachedSwitchingFromTCPToUSB, device.getDeviceName()), new IStopCondition() { public boolean canStop() { String serialNumber = device.getSerialNumber(); if (serialNumber != null) { return isDeviceOnline(serialNumber); } else { return false; } } }, subMonitor.newChild(1000)); } subMonitor.worked(1000); if (status.isOK()) { StudioLogger.collectUsageData(StudioLogger.WHAT_REMOTE_USB, StudioLogger.KIND_REMOTE_DEVICE, StudioLogger.DESCRIPTION_DEFAULT, AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getVersion() .toString()); } return status; } /** * Get the wireless ip from the connected handset * @param serialNumber * @param monitor * @return the ip or null if not possible to retrieve it */ public static String getWirelessIPfromHandset(String serialNumber, IProgressMonitor monitor) { String handset_wireless_ip = null; IDevice device = null; device = connectedDevices.get(serialNumber); if (device != null) { // get the wi-fi name for executing the ipconfig command String wifiProperty = device.getProperty(WIFI_INTERFACE_DEVICE_PROPERTY); if (wifiProperty == null) { wifiProperty = DEFAULT_WIRELESS_DEVICE_PROPERTY; } // execute ipconfig command Collection<String> answers = executeShellCmd(serialNumber, IFCONFIG_CMD + " " + wifiProperty, monitor); //$NON-NLS-1$ // Success message - for example // [tiwlan0: ip 192.168.0.174 mask 255.255.255.0 flags [up broadcast running multicast]] if (answers != null) { String result = answers.toString(); if (result != null) { // splits the result of the shell command and gets the third position // that should be the IP number String[] result_splited = result.split(" "); //$NON-NLS-1$ if (result_splited.length >= 3) { // check whether there is an IP Pattern pattern = Pattern.compile(IP_PATTERN); Matcher matcher = pattern.matcher(result); if (matcher.find()) { handset_wireless_ip = result_splited[2]; } } } } } return handset_wireless_ip; } /** * Switch adb connection mode of an specific device to TCPIP * * @param deviceName name of the handset instance * @param host wireless ip of the handset instance * @param port number of the port to be using during the connection * @param timeout the maximum time allowed to successfully connect to the device * @param monitor the monitor of the operation * @return the status of the operation * * @throws IOException Exception thrown in case there are problems switching the device. */ public static IStatus switchUSBtoTcpIp(String deviceName, final String serialNumber, String port, int timeout, IProgressMonitor monitor) throws IOException { SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingFromUSBConnection, 10); IStatus status = Status.OK_STATUS; if (isDeviceOnline(serialNumber)) { String[] cmd = createSwitchToTcpIpCommand(serialNumber, port); status = executeRemoteDevicesCommand(cmd, null, timeout, NLS.bind(AndroidNLS.ERR_WirelessRemoteDevice_TimeoutWhileConnecting, deviceName), new IStopCondition() { public boolean canStop() { if (serialNumber != null) { return isDeviceOffline(serialNumber); } else { return false; } } }, subMonitor.newChild(1000)); } monitor.worked(1000); return status; } /** * Disconnect from a Remote Device given its IP/Port * * @param device the Remote Device Instance * @param host device host (IP) * @param port device port * @param timeout the maximum time allowed to successfully disconnect from the device * @param monitor the monitor of the operation * @return the status of the operation * @throws IOException */ public static IStatus disconnectTcpIp(final ISerialNumbered device, String host, String port, int timeout, IProgressMonitor monitor) throws IOException { IStatus status = Status.OK_STATUS; String serialNumber = device.getSerialNumber(); if (isDeviceOnline(serialNumber)) // check if it's already disconnected { String[] cmd = createDisconnectTcpIpCommand(host, port); status = executeRemoteDevicesCommand( cmd, null, timeout, NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileDisconnecting, device.getDeviceName()), new IStopCondition() { public boolean canStop() { String serialNumber = device.getSerialNumber(); return !isDeviceOnline(serialNumber); } }, monitor); } return status; } /** * Creates a string with the command that should * be called in order to connect to an IP/Port * * @param host device host (IP) * @param port device port * @return the command to be used to connect to an IP/Port */ private static String[] createConnectTcpIpCommand(String host, String port) { String hostPort = host + ":" + port; //$NON-NLS-1$ String sdkPath = SdkUtils.getSdkPath(); String cmd[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, CONNECT_TCPIP_CMD, hostPort }; return cmd; } /** * Creates a string with the command switches * a device from the TCP/IP connection mode * to the USB connection mode. * * @param host Device host (IP). * @param port Device port. * * @return The command to be used to switch back to USB connection mode. */ private static String[] createSwitchToUSBConnectionModeCommand(String host, String port) { String hostPort = host + ":" + port; //$NON-NLS-1$ String sdkPath = SdkUtils.getSdkPath(); String cmd[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, DEVICE_ID_INDICATOR, hostPort, USB_SWITCH_BACK_COMMAND }; return cmd; } /** * Creates a string with the command that should * be called in order to switch adb connection from USB to TPCIP mode * * @param serialNumber device serial number * @param port device port * @return the command to be used to switch adb connection to TCPIP mode */ private static String[] createSwitchToTcpIpCommand(String serialNumber, String port) { String sdkPath = SdkUtils.getSdkPath(); String cmd[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, ADB_INSTANCE_PARAMETER, serialNumber, TCPIP_CMD, port }; return cmd; } /** * Creates a string with the command that should * be called to delete a file from device * * @param serialNumber * @param file * @param folder * @return */ private static String[] createDeleteFileFromDeviceCommand(String serialNumber, String file, String folder) { String sdkPath = SdkUtils.getSdkPath(); // The tools folder should exist and be here, but double-cheking // once more wont kill File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator); if (!f.exists()) { StudioLogger .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ + File.separator); } else { if (!f.isDirectory()) { StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ + File.separator); } } String cmd[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, "rm /" + folder + "/" + file //$NON-NLS-1$ //$NON-NLS-2$ }; return cmd; } /** * Uses the ADB shell command to remove a file from the device * * @param serialNumber * @param fileName * @param sdCardFolder * @return * @throws IOException */ private static boolean deleteFileFromDevice(String serialNumber, String fileName, String folder) throws IOException { String command[] = createDeleteFileFromDeviceCommand(serialNumber, fileName, folder); IStatus status = executeRemoteDevicesCommand(command, null, 1000, "", new IStopCondition() //$NON-NLS-1$ { public boolean canStop() { return true; } }, null); return status.isOK(); } /** * Check if a device identified by the serial number has a mounted SDCard * @param serialNumber the serial number * @return true if the device has a SDCard * @throws IOException */ public static boolean hasSDCard(String serialNumber) throws IOException { boolean hasSdCard = false; File tempSdCardFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ boolean tempCopiedOnSdCardFile = pushFileToDevice(serialNumber, SDCARD_FOLDER, tempSdCardFile); if (tempCopiedOnSdCardFile) { //trying to write on /sdcard folder (it works for phones previous from Platform 2.2) if (!deleteFileFromDevice(serialNumber, tempSdCardFile.getName(), SDCARD_FOLDER)) { StudioLogger .error("DDMSFacade: Could not delete tempfile from /sdcard when checking if card is enabled"); //$NON-NLS-1$ } hasSdCard = true; tempSdCardFile.delete(); } else { File tempMntFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ boolean tempCopiedOnMntFile = pushFileToDevice(serialNumber, MNT_SDCARD_FOLDER, tempSdCardFile); if (tempCopiedOnMntFile) { //trying to write on /mnt/sdcard folder (it works for phones since Platform 2.2) if (!deleteFileFromDevice(serialNumber, tempMntFile.getName(), MNT_SDCARD_FOLDER)) { StudioLogger .error("DDMSFacade: Could not delete tempfile from /mnt/sdcard when checking if card is enabled"); //$NON-NLS-1$ } hasSdCard = true; tempMntFile.delete(); } } return hasSdCard; } /** * * @param serialNumber * @param sdCardFolder * @param tempFile * @return true if manages to push file into the folder specified on device */ private static boolean pushFileToDevice(String serialNumber, String folder, File file) { Collection<String> files = new ArrayList<String>(); files.add(file.getName()); Path path = new Path(file.getAbsolutePath()); IStatus status = pushFiles(serialNumber, path.removeLastSegments(1).toString(), files, folder, 2000, new NullProgressMonitor(), null); return status.isOK(); } /** * Creates a string with the command that should * be called in order to disconnect from an IP/Port * * @param host device host (IP) * @param port device port * @return the command to be used to disconnect from an IP/Port */ private static String[] createDisconnectTcpIpCommand(String host, String port) { String hostPort = host + ":" + port; //$NON-NLS-1$ String sdkPath = SdkUtils.getSdkPath(); String cmd[] = { sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, DISCONNECT_TCPIP_CMD, hostPort }; return cmd; } public static void deleteFile(String serialNumber, String path) throws IOException { DDMSFacade.execRemoteApp(serialNumber, "rm " + path, //$NON-NLS-1$ new NullProgressMonitor()); } }