/* * 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.emulator.logic; import static com.motorola.studio.android.common.log.StudioLogger.error; import static com.motorola.studio.android.common.log.StudioLogger.info; import java.io.IOException; import java.io.InputStream; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.osgi.util.NLS; import com.motorola.studio.android.adt.DDMSFacade; import com.motorola.studio.android.adt.ISerialNumbered; import com.motorola.studio.android.common.exception.AndroidException; import com.motorola.studio.android.emulator.core.exception.InstanceStartException; import com.motorola.studio.android.emulator.core.exception.StartCancelledException; import com.motorola.studio.android.emulator.core.exception.StartTimeoutException; import com.motorola.studio.android.emulator.i18n.EmulatorNLS; /** * This class is used as an utilities class for operations related to Android Devices * */ public class AndroidLogicUtils { private static final int INITIAL_VNC_PORT_VALUE = 5900; public static final String ORIENTATION_BASE_COMMAND = "sendevent /dev/input/event0 "; /** * Execute the VM process * * @param cmd the command to be executed * @return the VM process * * @throws AndroidException if the command failed to execute */ public static Process executeProcess(final String cmd) throws AndroidException { try { info("Executing command " + cmd); Process vmProcess = Runtime.getRuntime().exec(cmd); return vmProcess; } catch (IOException e) { error("Falied to execute the command: " + cmd); throw new AndroidException(NLS.bind( EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd)); } } /** * Execute the VM process * * @param cmd the command to be executed, described as an array * @return the VM process * * @throws AndroidException if the command failed to execute */ public static Process executeProcess(final String[] cmd) throws AndroidException { String cmdString = ""; for (int i = 0; i < cmd.length; i++) { cmdString += cmd[i] + " "; } return executeProcess(cmd, cmdString); } /** * Execute the VM process * * @param cmd The command to be executed, described as an array * @param cmdToLog The command to be logged. * * @return the VM process * * @throws AndroidException if the command failed to execute */ public static Process executeProcess(final String[] cmd, final String cmdToLog) throws AndroidException { try { info("Executing command " + cmdToLog); Process vmProcess = Runtime.getRuntime().exec(cmd); return vmProcess; } catch (IOException e) { error("Falied to execute the command: " + cmd); throw new AndroidException(NLS.bind( EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd)); } } /** } /** * Checks if the user has canceled the VM startup * * @param monitor A progress monitor that will give the user feedback about this * long running operation * @param instanceHost The IP address of the started emulator instance * * @return True if the operation can proceed, false otherwise * * @throws StartCancelledException If the user has canceled the start process */ public static void testCanceled(IProgressMonitor monitor) throws StartCancelledException { if (monitor.isCanceled()) { info("Operation canceled by the user"); monitor.subTask(EmulatorNLS.MON_AndroidEmulatorStarter_Canceling); throw new StartCancelledException( EmulatorNLS.EXC_AndroidEmulatorStarter_EmulatorStartCanceled); } } /** * 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 */ public static void testTimeout(long timeoutLimit, String timeoutErrorMessage) throws StartTimeoutException { if (System.currentTimeMillis() > timeoutLimit) { error("The emulator was not up within the set timeout"); throw new StartTimeoutException(timeoutErrorMessage); } } /** * Get the relative timeout limit, which is the the current time plus the timeout value * * @param timeout timeout value (in milliseconds) * @return Relative timeout limit */ public static long getTimeoutLimit(int timeout) { return System.currentTimeMillis() + timeout; } /** * Check if the given process is still up and running * * @param p process * @throws InstanceStartException */ public static void testProcessStatus(Process p) throws InstanceStartException { boolean isRunning; int exitCode; try { exitCode = p.exitValue(); isRunning = false; } catch (Exception e) { // emulator process is still running... so everything looks fine... isRunning = true; exitCode = 0; } if (!isRunning) { error("Emulator process is not running! Exit code:" + exitCode); StringBuffer outBuf = null; InputStream inStream = null; int ch; //Getting error output stream String processAnswer = ""; inStream = p.getErrorStream(); outBuf = new StringBuffer(); try { while ((ch = inStream.read()) != -1) { outBuf.append((char) ch + ""); } } catch (IOException e) { error("Cannot read error output stream from Emulator proccess"); } processAnswer = outBuf.toString(); if (processAnswer.length() == 0) { //if no error came from process, get standard output stream inStream = p.getInputStream(); outBuf = new StringBuffer(); try { while ((ch = inStream.read()) != -1) { outBuf.append((char) ch + ""); } } catch (IOException e) { error("Cannot read standard output stream from Emulator proccess"); } processAnswer = outBuf.toString(); } String msg = EmulatorNLS.EXC_AndroidEmulatorStarter_ProcessTerminated; msg += processAnswer; throw new InstanceStartException(msg); } } /** * Kill the communication channel * * @param instance Android instance */ public static void kill(IAndroidLogicInstance instance) { if (instance instanceof ISerialNumbered) { String serialNumber = ((ISerialNumbered) instance).getSerialNumber(); DDMSFacade.kill(serialNumber); Process process = instance.getProcess(); if (process != null) { int tries = 0; Integer exitValue = null; while ((process != null) && (tries < 10) && (exitValue == null)) { try { exitValue = process.exitValue(); } catch (Throwable t) { tries++; try { Thread.sleep(250); } catch (InterruptedException e) { //do nothing } } } process.destroy(); instance.setProcess(null); } } } /** * Get the VNC port forward * * @param serial port number * @return VNC port */ public static int getVncServerPortFoward(String serial) { if (serial == null) { return 0; } int stringSize = serial.length(); String lastTwoNumbers = serial.substring(stringSize - 2, stringSize); int port = INITIAL_VNC_PORT_VALUE; try { port += Integer.valueOf(lastTwoNumbers); } catch (NumberFormatException e) { // do nothing (this should not happen) } return port; } public static int getEmulatorPort(String serial) { if (serial == null) { return 0; } int stringSize = serial.length(); String lastFourNumbers = serial.substring(stringSize - 4, stringSize); int port = 0; try { port = Integer.valueOf(lastFourNumbers); } catch (NumberFormatException e) { // do nothing (this should not happen) } return port; } /** * Checks if the Device is still online... * If the device is not online it is not possible to communicate with it. * Notice it is a verification of the status of the Device wich may be different than the status of the Tml Instance... * * @param serialNumber serial number of the device * * @throws AndroidException If the device is not started */ public static void testDeviceStatus(String serialNumber) throws AndroidException { if (!DDMSFacade.isDeviceOnline(serialNumber)) { info("Device is offline: " + serialNumber); throw new AndroidException(EmulatorNLS.EXC_AndroidLogicUtils_DeviceIsOffline); } } }