/* * 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.remote; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeoutException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException; import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance; import org.eclipse.sequoyah.device.framework.model.IInstance; import org.eclipse.ui.PlatformUI; import com.motorola.studio.android.adt.DDMSFacade; import com.motorola.studio.android.adt.ISerialNumbered; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.devices.DevicesManager; import com.motorola.studio.android.remote.instance.RemoteDeviceInstance; /** * Class that contains business methods and utilities. */ public class RemoteDeviceUtils { /** * Handle Remote Device connection. * * @param serialNumber the serial number of the connected device */ public static void connectDevice(final String serialNumber) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { public void run() { /* * Check if it's a remote device */ if (DDMSFacade.isRemote(serialNumber)) { ISerialNumbered instance = DevicesManager.getInstance().getDeviceBySerialNumber(serialNumber); boolean isTransitioning = ((instance != null) ? ((AbstractMobileInstance) instance) .getStateMachineHandler().isTransitioning() : false); StudioLogger.debug("Handle remote device connected event. Serial Number: " + serialNumber + " Instance: " + instance + " Transitioning: " + isTransitioning); /* * If the instance exists and is transitioning, so skip this method, the * connect handler will change the instance status */ if ((instance == null) || ((instance != null) && (!isTransitioning))) { /* * This method is necessary because sometimes (for example when the connection is refuses) * the device appears in the adb devices list but it's not in the "online" state */ boolean onlineDevice = waitForDeviceToBeOnline(serialNumber, instance); if (onlineDevice) { /* * If the device instance already exists */ if (instance == null) { try { StudioLogger .debug("Connecting Remote Device: device doesn't exist, create a new instance"); DevicesManager.getInstance().createInstanceForDevice( serialNumber, RemoteDeviceConstants.DEVICE_ID, getInstanceBuilder(serialNumber), RemoteDeviceConstants.SERVICE_INIT_ID); } catch (SequoyahException e) { StudioLogger .error("Connecting Remote Device: error while creating device instance " + e.getMessage()); } } try { instance = DevicesManager.getInstance().getDeviceBySerialNumber( serialNumber); StudioLogger .debug("Connecting Remote Device: the TmL service will be called"); Map<Object, Object> arguments = new HashMap<Object, Object>(); arguments.put(RemoteDeviceConstants.DUMMY_TRANSITION, true); RemoteDevicePlugin.getConnectServiceHandler().run( (IInstance) instance, arguments); } catch (Exception e) { StudioLogger.error("Error when running TmL connect service: " + e.getMessage()); } } } } } }); } /** * Handle Remote Device disconnection * * @param serialNumber the serial number of the disconnected device */ public static void disconnectDevice(String serialNumber) { if (DDMSFacade.isRemote(serialNumber)) { ISerialNumbered instance = DevicesManager.getInstance().getDeviceBySerialNumber(serialNumber); StudioLogger.debug("Handle remote device disconnected event. Serial Number: " + serialNumber + " Instance: " + instance); if (instance != null) { Object volatileProperty = ((RemoteDeviceInstance) instance).getProperties().get( RemoteDeviceInstance.PROPERTY_VOLATILE); boolean isVolatile = ((volatileProperty != null) ? ((Boolean) volatileProperty).booleanValue() : false); if (!isVolatile) { try { StudioLogger .debug("Disconnecting Remote Device: the device is NOT volatile, the TmL service will be called"); Map<Object, Object> arguments = new HashMap<Object, Object>(); arguments.put(RemoteDeviceConstants.DUMMY_TRANSITION, true); RemoteDevicePlugin.getDisconnectServiceHandler().run((IInstance) instance, arguments); } catch (Exception e) { StudioLogger.error("Error when running TmL disconnect service: " + e.getMessage()); } } else { StudioLogger .debug("Disconnecting Remote Device: the device is volatile, it will be deleted"); DevicesManager.getInstance().deleteInstanceOfDevice(serialNumber); } } } } /* * Wait until the device status becomes online * * @param serialNumber device serial number * @param instance TmL instance, if it exists * @return true if the device became online, false otherwise */ private static boolean waitForDeviceToBeOnline(String serialNumber, ISerialNumbered instance) { StudioLogger.debug("Wait device to be online: " + serialNumber); boolean instanceOnline = false; long timeoutLimit = 0; if (instance != null) { Properties prop = ((IInstance) instance).getProperties(); String timeout = prop.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT); timeoutLimit = System.currentTimeMillis() + (Integer.parseInt(timeout) * 1000); } else { timeoutLimit = System.currentTimeMillis() + (RemoteDeviceConstants.DEFAULT_TIMEOUT * 1000); } while ((instanceOnline = DDMSFacade.isDeviceOnline(serialNumber)) == false) { try { Thread.sleep(1000); } catch (InterruptedException e) { StudioLogger.error("Wait for device to be online: thread has been interrupted"); } try { testTimeout(timeoutLimit); } catch (TimeoutException e) { StudioLogger.warn("Timeout reached wile wating device to be online: " + serialNumber); break; } } return instanceOnline; } /* * Get the instance builder needed by TmL in order to create a new Remote Device instance * * @param serialNumber serial number of the Remote Device that shall be added * @return the instance builder needed by TmL to create a new Remote Device instance */ private static RemoteDeviceInstanceBuilder getInstanceBuilder(String serialNumber) { RemoteDeviceInstanceBuilder instanceBuilder = null; String[] serialNumberParts = serialNumber.split(":"); String host = serialNumberParts[0]; String port = serialNumberParts[1]; Properties props = new Properties(); props.put(RemoteDeviceInstance.PROPERTY_HOST, host); props.put(RemoteDeviceInstance.PROPERTY_PORT, port); props.put(RemoteDeviceInstance.PROPERTY_TIMEOUT, String.valueOf(RemoteDeviceConstants.DEFAULT_TIMEOUT)); // mark this instance as volatile props.put(RemoteDeviceInstance.PROPERTY_VOLATILE, true); instanceBuilder = new RemoteDeviceInstanceBuilder(serialNumber, props); return instanceBuilder; } /* * Compare the device instance with a pair host:port to check if the device * has the same host:port * * @param device the device to be analyzed * @param host host IP or name * @param port port number * @return true if the the device has the same host:port, false otherwise */ public static boolean hasSameHostAndPort(ISerialNumbered device, String host, int port) { boolean returnValue = false; String deviceHost = ((RemoteDeviceInstance) device).getProperties().getProperty( RemoteDeviceInstance.PROPERTY_HOST); String devicePort = ((RemoteDeviceInstance) device).getProperties().getProperty( RemoteDeviceInstance.PROPERTY_PORT); if ((host.equals(deviceHost)) && (String.valueOf(port).equals(devicePort))) { returnValue = true; } return returnValue; } /** * Execute a command. * * @param cmd Array of strings holding the command to * be executed. * * @return The {@link IStatus} of the command execution. * * @throws IOException Exception thrown in case there are problems * executing the command. */ public static IStatus executeCommand(String[] cmd) throws IOException { IStatus status = Status.OK_STATUS; Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec(cmd); try { // wait for the command to finish its execution process.waitFor(); } catch (InterruptedException e) { StudioLogger.error(RemoteDeviceUtils.class, "Problems executing the command"); status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, "Problems executing the command", e); } // in case the is a problem with the command execution, create an error status if (process.exitValue() != 0) { StudioLogger.error(RemoteDeviceUtils.class, "The IP was not found"); status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, "The IP was not found"); } return status; } /* * 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) throws TimeoutException { if (System.currentTimeMillis() > timeoutLimit) { throw new TimeoutException(); } } }