
io.selendroid.server.model.DeviceStore 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.server.model;
import io.selendroid.SelendroidCapabilities;
import io.selendroid.android.AndroidApp;
import io.selendroid.android.AndroidDevice;
import io.selendroid.android.AndroidEmulator;
import io.selendroid.android.AndroidEmulatorPowerStateListener;
import io.selendroid.android.DeviceManager;
import io.selendroid.android.HardwareDeviceListener;
import io.selendroid.android.impl.DefaultAndroidEmulator;
import io.selendroid.android.impl.DefaultHardwareDevice;
import io.selendroid.device.DeviceTargetPlatform;
import io.selendroid.exceptions.AndroidDeviceException;
import io.selendroid.exceptions.AndroidSdkException;
import io.selendroid.exceptions.DeviceStoreException;
import io.selendroid.exceptions.SelendroidException;
import io.selendroid.server.model.impl.DefaultPortFinder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DeviceStore {
private static final Logger log = Logger.getLogger(DeviceStore.class.getName());
private List devicesInUse = new ArrayList();
private Map> androidDevices =
new HashMap>();
private EmulatorPortFinder androidEmulatorPortFinder = null;
private boolean clearData = true;
private AndroidEmulatorPowerStateListener emulatorPowerStateListener = null;
private DeviceManager deviceManager = null;
public DeviceStore(Integer emulatorPort, DeviceManager deviceManager) {
this.deviceManager = deviceManager;
androidEmulatorPortFinder = new DefaultPortFinder(emulatorPort, emulatorPort + 30);
}
public DeviceStore(EmulatorPortFinder androidEmulatorPortFinder,
DeviceManager deviceManager) {
this.deviceManager = deviceManager;
this.androidEmulatorPortFinder = androidEmulatorPortFinder;
}
public Integer nextEmulatorPort() {
return androidEmulatorPortFinder.next();
}
/**
* After a test session a device should be released. That means id will be removed from the list
* of devices in use and in case of an emulator it will be stopped.
*
* @param device The device to release
* @see {@link #findAndroidDevice(SelendroidCapabilities)}
*/
public void release(AndroidDevice device, AndroidApp aut) {
if (devicesInUse.contains(device)) {
// stop the app anyway - better in case people do use snapshots
try {
device.kill(aut);
} catch (Exception e) {
e.printStackTrace();
}
if (clearData) {
try {
device.clearUserData(aut);
} catch (AndroidSdkException e) {
e.printStackTrace();
}
}
if (device instanceof AndroidEmulator) {
AndroidEmulator emulator = (AndroidEmulator) device;
try {
emulator.stop();
} catch (AndroidDeviceException e) {
log.severe("Failed to stop emulator: " + e.getMessage());
}
androidEmulatorPortFinder.release(emulator.getPort());
}
devicesInUse.remove(device);
}
}
/* package */void initAndroidDevices(HardwareDeviceListener hardwareDeviceListener,
boolean shouldKeepAdbAlive) throws AndroidDeviceException {
emulatorPowerStateListener = new DefaultEmulatorPowerStateListener();
deviceManager.initialize(hardwareDeviceListener, emulatorPowerStateListener);
List emulators = DefaultAndroidEmulator.listAvailableAvds();
addEmulators(emulators);
if (getDevices().isEmpty()) {
SelendroidException e =
new SelendroidException(
"No android virtual devices were found. "
+ "Please start the android tool and create emulators and restart the selendroid-standalone "
+ "or plugin an Android hardware device via USB.");
log.warning("Warning: " + e);
}
}
public synchronized void addDevice(AndroidDevice androidDevice) throws AndroidDeviceException {
if (androidDevice == null) {
log.info("No Android devices were found.");
return;
}
if (androidDevice instanceof AndroidEmulator) {
throw new AndroidDeviceException(
"For adding emulator instances please use #addEmulator method.");
}
if (androidDevice.isDeviceReady() == true) {
log.info("Adding: " + androidDevice);
addDeviceToStore(androidDevice);
}
}
public void addEmulators(List emulators) throws AndroidDeviceException {
if (emulators == null || emulators.isEmpty()) {
log.info("No emulators has been found.");
return;
}
for (AndroidEmulator emulator : emulators) {
log.info("Adding: " + emulator);
addDeviceToStore((AndroidDevice) emulator);
}
}
/**
* Internal method to add an actual device to the store.
*
* @param device The device to add.
* @throws AndroidDeviceException
*/
protected synchronized void addDeviceToStore(AndroidDevice device) throws AndroidDeviceException {
if (androidDevices.containsKey(device.getTargetPlatform())) {
if (androidDevices.get(device.getTargetPlatform()) == null) {
androidDevices.put(device.getTargetPlatform(), new ArrayList());
}
androidDevices.get(device.getTargetPlatform()).add((AndroidDevice) device);
} else {
List devices = new ArrayList();
devices.add((AndroidDevice) device);
androidDevices.put(device.getTargetPlatform(), devices);
}
}
/**
* Finds a device for the requested capabilities. important note: if the device is not any
* longer used, call the {@link #release(AndroidDevice, AndroidApp)} method.
*
* @param caps The desired test session capabilities.
* @return Matching device for a test session.
* @throws DeviceStoreException
* @see {@link #release(AndroidDevice, AndroidApp)}
*/
public synchronized AndroidDevice findAndroidDevice(SelendroidCapabilities caps)
throws DeviceStoreException {
if (caps == null) {
throw new IllegalArgumentException("Error: capabilities are null");
}
if (androidDevices.isEmpty()) {
throw new DeviceStoreException(
"Fatal Error: Device Store does not contain any Android Device.");
}
String platformVersion = caps.getPlatformVersion();
List devices = null;
if (platformVersion == null || platformVersion.isEmpty()) {
devices = new ArrayList();
for (List list : androidDevices.values()) {
devices.addAll(list);
}
} else {
DeviceTargetPlatform platform = DeviceTargetPlatform.fromPlatformVersion(platformVersion);
devices = androidDevices.get(platform);
}
if (devices == null) {
devices = new ArrayList();
}
// keep a list of emulators that aren't started to be used as backup
List potentialMatches = new ArrayList();
for (AndroidDevice device : devices) {
log.info("Evaluating if this device is a match for this session: " + device.toString());
// logic of precedence to select a device -
// screen size must match the capabilities, if no screen size capability sent this is ignored
// enforce isEmulator capability request
// enforce API level capability
// 1) running unused device or running unused emulator
// 2) non-running emulator
if (device.screenSizeMatches(caps.getScreenSize())) {
if (devicesInUse.contains(device)) {
log.info("Device is in use.");
continue;
}
if (caps.getEmulator() == null
|| (caps.getEmulator() == true && device instanceof DefaultAndroidEmulator)
|| (caps.getEmulator() == false && device instanceof DefaultHardwareDevice)) {
String serial = caps.getSerial();
if (serial != null && device.getSerial().equals(serial)) {
devicesInUse.add(device);
return device;
}
if (device instanceof DefaultAndroidEmulator && !((DefaultAndroidEmulator) device).isEmulatorStarted()) {
potentialMatches.add(device);
continue;
}
devicesInUse.add(device);
return device;
}
}
}
if (potentialMatches.size() > 0) {
log.info("Using potential match: " + potentialMatches.get(0));
devicesInUse.add(potentialMatches.get(0));
return potentialMatches.get(0);
}
throw new DeviceStoreException("No devices are found. "
+ "This can happen if the devices are in use or no device screen "
+ "matches the required capabilities.");
}
private boolean isEmulatorSwitchedOff(AndroidDevice device) throws DeviceStoreException {
if (device instanceof AndroidEmulator) {
try {
return !((AndroidEmulator) device).isEmulatorStarted();
} catch (AndroidDeviceException e) {
throw new DeviceStoreException(e);
}
}
return true;
}
public List getDevices() {
List devices = new ArrayList();
for (Map.Entry> entry : androidDevices.entrySet()) {
devices.addAll(entry.getValue());
}
return devices;
}
/**
* For testing only
*/
/* package */List getDevicesInUse() {
return devicesInUse;
}
/**
* For testing only
*/
/* package */Map> getDevicesList() {
return androidDevices;
}
/**
* Removes the given device from store so that it cannot be any longer be used for testing. This
* can happen if e.g. the hardware device gets unplugged from the computer.
*
* @param device the device to remove.
* @throws DeviceStoreException when parameter is not type of 'DefaultHardwareDevice'.
*/
public void removeAndroidDevice(AndroidDevice device) throws DeviceStoreException {
if (device == null) {
return;
}
boolean hardwareDevice = device instanceof DefaultHardwareDevice;
if (hardwareDevice == false) {
throw new DeviceStoreException("Only devices of type 'DefaultHardwareDevice' can be removed.");
}
release(device, null);
DeviceTargetPlatform apiLevel = device.getTargetPlatform();
if (androidDevices.containsKey(apiLevel)) {
log.info("Removing: " + device);
androidDevices.get(apiLevel).remove(device);
if (androidDevices.get(apiLevel).isEmpty()) {
androidDevices.remove(apiLevel);
}
} else {
log.warning("The target platform version of the device is not found in device store.");
log.warning("The device was propably already removed.");
}
}
public void setClearData(boolean clearData) {
this.clearData = clearData;
}
class DefaultEmulatorPowerStateListener implements AndroidEmulatorPowerStateListener {
@Override
public void onDeviceStarted(String avdName, String serial) {
AndroidEmulator emulator = findEmulator(avdName);
if (emulator != null) {
Integer port = Integer.parseInt(serial.replace("emulator-", ""));
emulator.setSerial(port);
emulator.setWasStartedBySelendroid(false);
}
}
AndroidEmulator findEmulator(String avdName) {
for (AndroidDevice device : getDevices()) {
if (device instanceof AndroidEmulator) {
AndroidEmulator emulator = (AndroidEmulator) device;
if (avdName.equals(emulator.getAvdName())) {
return emulator;
}
}
}
return null;
}
@Override
public void onDeviceStopped(String avdName) {
// do nothing
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy