package com.thoughtworks.calabash.android;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import static java.lang.String.format;
public class AndroidBridge {
public static final String BOOT_ANIM_STOPPED = "stopped";
public static final String EMULATOR_PREFIX = "emulator-";
private final Environment environment;
private DeviceList deviceList;
private DeviceList newDeviceList;
public static final int POLL_RATE_IN_SECONDS = 5;
public AndroidBridge(Environment environment) {
this.environment = environment;
}
public boolean isAppInstalled(String appPackageName, final String serialNo) throws CalabashException {
String[] cmd = new String[]{environment.getAdb(), "-s", serialNo, "shell", "pm", "path", appPackageName};
String output = Utils.runCommand(cmd, format("could not check if app %s is installed on %s", appPackageName, serialNo));
return output.contains(appPackageName);
}
public String launchEmulator(AndroidConfiguration configuration) throws CalabashException {
deviceList = getDeviceList();
String deviceSerial = configuration.getSerial();
if (deviceSerial != null) {
checkDeviceIsRunning(deviceList, deviceSerial);
return deviceSerial;
}
String deviceName = configuration.getDeviceName();
if (deviceName != null) {
final String newSerial = launchEmulatorWithName(deviceName);
if (newSerial == null) {
CalabashLogger.error("Could not find launched emulator's serial from device list");
throw new CalabashException("Emulator launch Failed");
}
ConditionalWaiter waitForBootAnim = new ConditionalWaiter(new ICondition(format("Wait for Device %s to be ready", newSerial)) {
@Override
public boolean test() throws CalabashException {
return isBootAnimationOver(newSerial);
}
});
ConditionalWaiter waitForPackageManager = new ConditionalWaiter(new ICondition(format("Wait for Package manager to be available on %s", newSerial)) {
@Override
public boolean test() throws CalabashException {
return isPackageManagerAvailable(newSerial);
}
});
waitForBootAnim.run(configuration.getTimeToWaitInSecForEmulatorLaunch() / POLL_RATE_IN_SECONDS, POLL_RATE_IN_SECONDS);
waitForPackageManager.run(5, POLL_RATE_IN_SECONDS);
unlockKeyguard(newSerial);
return newSerial;
}
if (deviceList.size() == 1) {
CalabashLogger.info("Only one emualtor/device is connected");
return deviceList.get(0).getSerial();
}
throw new CalabashException("Could not get the device serial, set the serial or devicename in the AndroidConfiguration");
}
private String launchEmulatorWithName(String deviceName) throws CalabashException {
String[] launchCommand = getLaunchCommand(deviceName);
String launchedDeviceSerial = getSerialIfDeviceAlreadyLaunched(deviceList, deviceName);
if (launchedDeviceSerial != null) {
return launchedDeviceSerial;
}
Process process = Utils.runCommandInBackGround(launchCommand, format("failed to launch the emulator %s", deviceName));
ConditionalWaiter waitForNewEmulatorLaunch = new ConditionalWaiter(new ICondition(format("waiting for emulator with name %s to launch", deviceName)) {
public boolean test() throws CalabashException {
newDeviceList = getDeviceList();
return deviceList.size() < newDeviceList.size();
}
});
waitForNewEmulatorLaunch.run(5, 5);
return getNewSerial(deviceList, newDeviceList);
}
private String getSerialIfDeviceAlreadyLaunched(DeviceList deviceList, String deviceName) throws CalabashException {
CalabashLogger.info("Checking if %s is already launched", deviceName);
for (Device device : deviceList.devices) {
String serial = device.getSerial();
try {
if (isDeviceRunningWithSerial(deviceName, serial)) {
CalabashLogger.info("%s is running with serial %s", deviceName, serial);
return serial;
}
} catch (IOException e) {
CalabashLogger.error(e);
throw new CalabashException("could not get the device name from serial " + deviceName, e);
}
}
return null;
}
private boolean isDeviceRunningWithSerial(String deviceName, String serial) throws IOException, CalabashException {
int index = serial.indexOf(EMULATOR_PREFIX);
if (index != -1) {
String port = serial.substring(EMULATOR_PREFIX.length());
Socket emulatorSocket = new Socket("localhost", Integer.parseInt(port));
BufferedReader in = new BufferedReader(new InputStreamReader(emulatorSocket.getInputStream()));
PrintWriter out = new PrintWriter(emulatorSocket.getOutputStream(), true);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
in.readLine();
in.readLine();
out.println("avd name");
String runningDeviceName = in.readLine();
if (deviceName.equals(runningDeviceName.trim())) {
return true;
}
} catch (Exception e) {
throw new CalabashException(String.format("could not get the device name from serial %s", serial), e);
} finally {
emulatorSocket.close();
in.close();
out.close();
br.close();
}
}
return false;
}
private String getNewSerial(DeviceList oldDeviceList, DeviceList newDeviceList) {
for (Device device : newDeviceList.devices) {
if (!oldDeviceList.contains(device))
return device.getSerial();
}
return null;
}
private DeviceList getDeviceList() throws CalabashException {
String listDeviceOutput = Utils.runCommand(getDeviceListCommand(), "could not list all devices");
return new DeviceList(listDeviceOutput);
}
private boolean isBootAnimationOver(String serial) throws CalabashException {
String[] bootAnimationCommand = getBootAnimationCommand(serial);
String result = Utils.runCommand(bootAnimationCommand);
return result.equals(BOOT_ANIM_STOPPED);
}
private void checkDeviceIsRunning(DeviceList deviceList, String serial) throws CalabashException {
for (Device device : deviceList.devices) {
if (device.getSerial().equals(serial)) {
if (!device.getState().equals("device")) {
throw new CalabashException(format("%s's state: %s. Cannot install app", serial, device.getState()));
}
return;
}
}
throw new CalabashException(format("%s not found in the device list, installation failed", serial));
}
public void unlockKeyguard(String serial) throws CalabashException {
String[] unlockCommand = {environment.getAdb(), "-s", serial, "shell", "input", "keyevent", "82"};
Utils.runCommand(unlockCommand, "failed to unlock the keyguard");
}
private boolean isPackageManagerAvailable(String serial) throws CalabashException {
String[] deviceReadyCommand = getPackageManagerAvailableCommand(serial);
String output = Utils.runCommand(deviceReadyCommand);
return output.contains("package");
}
private String[] getLaunchCommand(String deviceName) {
return new String[]{environment.getEmulator(), "-avd", deviceName};
}
private String[] getDeviceListCommand() {
return new String[]{environment.getAdb(), "devices"};
}
private String[] getBootAnimationCommand(String serial) {
return new String[]{environment.getAdb(), "-s", serial, "shell", "getprop", "init.svc.bootanim"};
}
private String[] getPackageManagerAvailableCommand(String serial) {
return new String[]{environment.getAdb(), "-s", serial, "shell", "pm", "path", "android"};
}
}