Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.selendroid.android.impl.DefaultAndroidEmulator Maven / Gradle / Ivy
/*
* Copyright 2012-2013 eBay Software Foundation and selendroid committers.
*
* 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 io.selendroid.android.impl;
import io.selendroid.android.AndroidEmulator;
import io.selendroid.android.AndroidSdk;
import io.selendroid.android.TelnetClient;
import io.selendroid.device.DeviceTargetPlatform;
import io.selendroid.exceptions.AndroidDeviceException;
import io.selendroid.exceptions.SelendroidException;
import io.selendroid.exceptions.ShellCommandException;
import io.selendroid.io.ShellCommand;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import com.android.ddmlib.IDevice;
import com.beust.jcommander.internal.Lists;
public class DefaultAndroidEmulator extends AbstractDevice implements AndroidEmulator {
private static final String EMULATOR_SERIAL_PREFIX = "emulator-";
private static final Logger log = Logger.getLogger(DefaultAndroidEmulator.class.getName());
public static final String ANDROID_EMULATOR_HARDWARE_CONFIG = "hardware-qemu.ini";
public static final String FILE_LOCKING_SUFIX = ".lock";
private String screenSize;
private DeviceTargetPlatform targetPlatform;
private String avdName;
private File avdRootFolder;
private Locale locale = null;
private boolean wasStartedBySelendroid;
protected DefaultAndroidEmulator() {
this.wasStartedBySelendroid = Boolean.FALSE;
}
public DefaultAndroidEmulator(String avdName, String abi, String screenSize, String target,
File avdFilePath) {
this.avdName = avdName;
this.screenSize = screenSize;
this.avdRootFolder = avdFilePath;
this.targetPlatform = DeviceTargetPlatform.fromInt(target);
this.wasStartedBySelendroid = !isEmulatorStarted();
}
public File getAvdRootFolder() {
return avdRootFolder;
}
public String getScreenSize() {
return screenSize;
}
public DeviceTargetPlatform getTargetPlatform() {
return targetPlatform;
}
/*
* (non-Javadoc)
*
* @see io.selendroid.android.impl.AndroidEmulator#isEmulatorAlreadyExistent()
*/
@Override
public boolean isEmulatorAlreadyExistent() {
File emulatorFolder =
new File(FileUtils.getUserDirectory(), File.separator + ".android" + File.separator + "avd"
+ File.separator + getAvdName() + ".avd");
return emulatorFolder.exists();
}
public String getAvdName() {
return avdName;
}
public static List listAvailableAvds() throws AndroidDeviceException {
List avds = Lists.newArrayList();
CommandLine cmd = new CommandLine(AndroidSdk.android());
cmd.addArgument("list", false);
cmd.addArgument("avds", false);
String output = null;
try {
output = ShellCommand.exec(cmd, 20000);
} catch (ShellCommandException e) {
throw new AndroidDeviceException(e);
}
Map startedDevices = mapDeviceNamesToSerial();
String[] avdsOutput = StringUtils.splitByWholeSeparator(output, "---------");
if (avdsOutput != null && avdsOutput.length > 0) {
for (int i = 0; i < avdsOutput.length; i++) {
if (avdsOutput[i].contains("Name:") == false) {
continue;
}
String element = avdsOutput[i];
String avdName = extractValue("Name: (.*?)$", element);
String abi = extractValue("ABI: (.*?)$", element);
String screenSize = extractValue("Skin: (.*?)$", element);
String target = extractValue("\\(API level (.*?)\\)", element);
File avdFilePath = new File(extractValue("Path: (.*?)$", element));
DefaultAndroidEmulator emulator =
new DefaultAndroidEmulator(avdName, abi, screenSize, target, avdFilePath);
if (startedDevices.containsKey(avdName)) {
emulator.setSerial(startedDevices.get(avdName));
}
avds.add(emulator);
}
}
return avds;
}
private static Map mapDeviceNamesToSerial() {
Map mapping = new HashMap();
CommandLine command = new CommandLine(AndroidSdk.adb());
command.addArgument("devices");
Scanner scanner;
try {
scanner = new Scanner(ShellCommand.exec(command));
} catch (ShellCommandException e) {
return mapping;
}
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
Pattern pattern = Pattern.compile("emulator-\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String serial = matcher.group(0);
Integer port = Integer.valueOf(serial.replaceAll("emulator-", ""));
TelnetClient client = null;
try {
client = new TelnetClient(port);
String avdName = client.sendCommand("avd name");
mapping.put(avdName, port);
} catch (AndroidDeviceException e) {
// ignore
} finally {
if (client != null) {
client.close();
}
}
Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
socket = new Socket("127.0.0.1", port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if (in.readLine() == null) {
throw new AndroidDeviceException("error");
}
out.write("avd name\r\n");
out.flush();
in.readLine();// OK
String avdName = in.readLine();
mapping.put(avdName, port);
} catch (Exception e) {
// ignore
} finally {
try {
out.close();
in.close();
socket.close();
} catch (Exception e) {
// do nothing
}
}
}
}
scanner.close();
return mapping;
}
@Override
public boolean isEmulatorStarted() {
File lockedEmulatorHardwareConfig =
new File(avdRootFolder, ANDROID_EMULATOR_HARDWARE_CONFIG + FILE_LOCKING_SUFIX);
return lockedEmulatorHardwareConfig.exists();
}
@Override
public String toString() {
return "AndroidEmulator [screenSize=" + screenSize + ", targetPlatform=" + targetPlatform
+ ", serial=" + serial + ", avdName=" + avdName + "]";
}
public void setSerial(int port) {
this.port = port;
serial = EMULATOR_SERIAL_PREFIX + port;
}
public Integer getPort() {
if (isSerialConfigured()) {
return Integer.parseInt(serial.replace(EMULATOR_SERIAL_PREFIX, ""));
}
return null;
}
@Override
public void start(Locale locale, int emulatorPort, Map options)
throws AndroidDeviceException {
if (isEmulatorStarted()) {
throw new SelendroidException("Error - Android emulator is already started " + this);
}
Long timeout = null;
String emulatorOptions = null;
String display = null;
if (options != null) {
if (options.containsKey(TIMEOUT_OPTION)) {
timeout = (Long) options.get(TIMEOUT_OPTION);
}
if (options.containsKey(DISPLAY_OPTION)) {
display = (String) options.get(DISPLAY_OPTION);
}
if (options.containsKey(EMULATOR_OPTIONS)) {
emulatorOptions = (String) options.get(EMULATOR_OPTIONS);
}
}
if (display != null) {
log.info("Using display " + display + " for running the emulator");
}
if (timeout == null) {
timeout = 120000L;
}
log.info("Using timeout of '" + timeout / 1000 + "' seconds to start the emulator.");
this.locale = locale;
CommandLine cmd = new CommandLine(AndroidSdk.emulator());
cmd.addArgument("-no-snapshot-save", false);
cmd.addArgument("-avd", false);
cmd.addArgument(avdName, false);
cmd.addArgument("-port", false);
cmd.addArgument(String.valueOf(emulatorPort), false);
if (locale != null) {
cmd.addArgument("-prop", false);
cmd.addArgument("persist.sys.language=" + locale.getLanguage(), false);
cmd.addArgument("-prop", false);
cmd.addArgument("persist.sys.country=" + locale.getCountry(), false);
}
if (emulatorOptions != null && emulatorOptions.isEmpty() == false) {
cmd.addArgument(emulatorOptions, false);
}
long start = System.currentTimeMillis();
long timemoutEnd = start + timeout;
try {
ShellCommand.execAsync(display, cmd);
} catch (ShellCommandException e) {
throw new SelendroidException("unable to start the emulator: " + this);
}
setSerial(emulatorPort);
Boolean adbKillServerAttempted = false;
while (isDeviceReady() == false) {
if (!adbKillServerAttempted && System.currentTimeMillis() - start > 10000) {
CommandLine adbDevicesCmd = new CommandLine(AndroidSdk.adb());
adbDevicesCmd.addArgument("devices", false);
String devices = "";
try {
devices = ShellCommand.exec(adbDevicesCmd, 20000);
} catch (ShellCommandException e) {
// pass
}
if (!devices.contains(String.valueOf(emulatorPort))) {
CommandLine resetAdb = new CommandLine(AndroidSdk.adb());
resetAdb.addArgument("kill-server", false);
try {
ShellCommand.exec(resetAdb, 20000);
} catch (ShellCommandException e) {
throw new SelendroidException("unable to kill the adb server");
}
}
adbKillServerAttempted = true;
}
if (timemoutEnd >= System.currentTimeMillis()) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
} else {
throw new AndroidDeviceException("The emulator with avd '" + getAvdName()
+ "' was not started after " + (System.currentTimeMillis() - start) / 1000
+ " seconds.");
}
}
log.info("Emulator start took: " + (System.currentTimeMillis() - start) / 1000 + " seconds");
log.info("Please have in mind, starting an emulator takes usually about 45 seconds.");
unlockEmulatorScreen();
waitForLauncherToComplete();
// we observed that emulators can sometimes not be 'fully loaded'
// if we click on the All Apps button and wait for it to load it is more likely to be in a
// usable state.
allAppsGridView();
waitForLauncherToComplete();
setWasStartedBySelendroid(true);
}
public void unlockEmulatorScreen() throws AndroidDeviceException {
CommandLine event82 = new CommandLine(AndroidSdk.adb());
if (isSerialConfigured()) {
event82.addArgument("-s", false);
event82.addArgument(serial, false);
}
event82.addArgument("shell", false);
event82.addArgument("input", false);
event82.addArgument("keyevent", false);
event82.addArgument("82", false);
try {
ShellCommand.exec(event82, 20000);
} catch (ShellCommandException e) {
throw new AndroidDeviceException(e);
}
CommandLine event4 = new CommandLine(AndroidSdk.adb());
if (isSerialConfigured()) {
event4.addArgument("-s", false);
event4.addArgument(serial, false);
}
event4.addArgument("shell", false);
event4.addArgument("input", false);
event4.addArgument("keyevent", false);
event4.addArgument("4", false);
try {
ShellCommand.exec(event4, 20000);
} catch (ShellCommandException e) {
throw new AndroidDeviceException(e);
}
}
private void waitForLauncherToComplete() throws AndroidDeviceException {
waitForLauncherToComplete(true);
}
private void waitForLauncherToComplete(Boolean delay) throws AndroidDeviceException {
CommandLine event = new CommandLine(AndroidSdk.adb());
if (isSerialConfigured()) {
event.addArgument("-s", false);
event.addArgument(serial, false);
}
event.addArgument("shell", false);
event.addArgument("ps", false);
String homeScreenLaunched = null;
try {
homeScreenLaunched = ShellCommand.exec(event, 20000);
} catch (ShellCommandException e) {
throw new AndroidDeviceException(e);
}
if (homeScreenLaunched != null && homeScreenLaunched.contains("S com.android.launcher")) {
if (!delay) return;
} else {
// it's still running, sleep for a bit
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
waitForLauncherToComplete(true);
}
// it's done right? ... well, maybe... check again after waiting a second
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
waitForLauncherToComplete(false);
}
private void allAppsGridView() throws AndroidDeviceException {
String[] dimensions = screenSize.split("x");
int x = Integer.parseInt(dimensions[0]);
int y = Integer.parseInt(dimensions[1]);
if (x > y) {
y = y / 2;
x = x - 30;
} else {
x = x / 2;
y = y - 30;
}
List coordinates = new ArrayList();
coordinates.add("3 0 " + x);
coordinates.add("3 1 " + y);
coordinates.add("1 330 1");
coordinates.add("0 0 0");
coordinates.add("1 330 0");
coordinates.add("0 0 0");
for (String coordinate : coordinates) {
CommandLine event1 = new CommandLine(AndroidSdk.adb());
if (isSerialConfigured()) {
event1.addArgument("-s", false);
event1.addArgument(serial, false);
}
event1.addArgument("shell", false);
event1.addArgument("sendevent", false);
event1.addArgument("dev/input/event0", false);
event1.addArgument(coordinate, false);
try {
ShellCommand.exec(event1);
} catch (ShellCommandException e) {
throw new AndroidDeviceException(e);
}
}
try {
Thread.sleep(750);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void stopEmulator() throws AndroidDeviceException {
TelnetClient client = null;
try {
client = new TelnetClient(getPort());
client.sendQuietly("kill");
} catch (AndroidDeviceException e) {
// ignore
} finally {
if (client != null) {
client.close();
}
}
}
@Override
public void stop() throws AndroidDeviceException {
if (wasStartedBySelendroid) {
stopEmulator();
Boolean killed = false;
while (isEmulatorStarted()) {
log.info("emulator still running, sleeping 0.5, waiting for it to release the lock");
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
if (!killed) {
try {
stopEmulator();
} catch (AndroidDeviceException sce) {
killed = true;
}
}
}
}
}
@Override
public Locale getLocale() {
return locale;
}
@Override
public void setIDevice(IDevice iDevice) {
super.device = iDevice;
}
public String getSerial() {
return serial;
}
public void setWasStartedBySelendroid(boolean wasStartedBySelendroid) {
this.wasStartedBySelendroid = wasStartedBySelendroid;
}
}