/* * Copyright (C) 2013 Ustream Inc. * author chaotx <lombai.ferenc@ustream.tv> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ package com.robin.device; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.safs.android.auto.lib.DUtilities; import org.testng.Assert; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.IDevice; import com.android.ddmlib.RawImage; import com.robin.reporter.Reporter; public class DevicePool { private static final boolean DEVICE_READY = true; private static final boolean DEVICE_IN_USE = false; private static final IDeviceChangeListener DEVICE_LISTENER = new RobinDeviceChangeListener(); /** * Stores device usage flags, true if free, false when used. */ private static final Map<IDevice, Boolean> DEVICES = Collections .synchronizedMap(new ConcurrentHashMap<IDevice, Boolean>()); private static final int WAITDEVICETIMEOUT = 5000; public static void init(final int minDeviceNumber) { DUtilities.getAndroidDebugBridge(); AndroidDebugBridge.addDeviceChangeListener(DEVICE_LISTENER); synchronized (DEVICES) { waitForConnectedDevices(minDeviceNumber); } } public static void addDeviceToList(final IDevice device) { synchronized (DEVICES) { DEVICES.put(device, DEVICE_READY); Reporter.log("Device Pool added device: '" + getDeviceDescriptionString(device), true); DEVICES.notifyAll(); } } public static void removeDeviceFromList(final IDevice device) { synchronized (DEVICES) { DEVICES.remove(device); Reporter.log("Device Pool removed device : '" + device.getSerialNumber(), true); DEVICES.notifyAll(); } } public static IDevice getDeviceForExecution(final String selectorRegexp) { IDevice deviceToLock; synchronized (DEVICES) { if (isDeviceExists(selectorRegexp)) { while (true) { deviceToLock = getFirstUnlockedMatchingDevice(selectorRegexp); if (deviceToLock != null) { DEVICES.put(deviceToLock, DEVICE_IN_USE); Reporter.log("Locked '" + getDeviceDescriptionString(deviceToLock) + "' for execution. (selector: " + selectorRegexp + ")", true); return deviceToLock; } else { try { DEVICES.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } else { Assert.fail("No matching device found for '" + selectorRegexp + "'. Devices list: " + getDevicesListString()); } } return null; } private static String getDevicesListString() { String listString = ""; for (IDevice device : DEVICES.keySet()) { listString += "[" + getDeviceDescriptionString(device) + "]"; } return listString; } private static IDevice getFirstUnlockedMatchingDevice(final String name) { List<IDevice> matchingDevices = getMatchingDeviceList(name); for (IDevice matchingDevice : matchingDevices) { if (DEVICES.get(matchingDevice)) { return matchingDevice; } } return null; } public static boolean isDeviceExists(final String name) { return getMatchingDeviceList(name).size() > 0; } public static List<IDevice> getMatchingDeviceList( final String deviceSelectorRegexp) { ArrayList<IDevice> matchingDevices = new ArrayList<IDevice>(); Set<IDevice> devices = DEVICES.keySet(); for (IDevice device : devices) { if (getDeviceDescriptionString(device).matches( deviceSelectorRegexp)) { matchingDevices.add(device); } } return matchingDevices; } public static String getDeviceApiLevel(final IDevice device) { return device.getProperty(IDevice.PROP_BUILD_API_LEVEL); } public static String getDeviceManufacturer(final IDevice device) { return device.getProperty("ro.product.manufacturer"); } public static Locale getDeviceLanguage(final IDevice device) { final String language = device.getProperty("persist.sys.language"); final String country = device.getProperty("persist.sys.country"); return new Locale(language, country); } public static String getDeviceModel(final IDevice device) { return device.getProperty("ro.product.model"); } public static String getDeviceDisplayResolution(final IDevice device) { RawImage tmpImage; try { tmpImage = device.getScreenshot(); } catch (Exception e) { return "?x?" + e.getMessage(); } return "" + tmpImage.width + "x" + tmpImage.height; } public static String getDeviceDescriptionString(final IDevice device) { if (device != null) { final String sep = "_"; String name = getDeviceManufacturer(device) + sep + getDeviceModel(device) + sep + getDeviceApiLevel(device) + sep + getDeviceDisplayResolution(device) + sep + device.getSerialNumber() + sep + getDeviceLanguage(device); return name.replace(" ", "_"); } return "???"; } public static void unlockDevice(final Method method, final IDevice device) { synchronized (DEVICES) { final String deviceDescriptionString = getDeviceDescriptionString(device); if (DEVICES.containsKey(device)) { if (!DEVICES.get(device)) { DEVICES.put(device, DEVICE_READY); Reporter.log("Unlocked '" + deviceDescriptionString + "', now waits for execution.", true); DEVICES.notifyAll(); } else { DEVICES.notifyAll(); Assert.fail("The '" + deviceDescriptionString + "' device is unlocked already."); } } else { DEVICES.notifyAll(); Assert.fail("No matching ('" + deviceDescriptionString + "') device found for method " + method.getDeclaringClass().getName() + "." + method.getName() + "."); } } } public static void waitForConnectedDevices(final int numOfDevices) { synchronized (DEVICES) { boolean success = true; while (DEVICES.size() < numOfDevices && success) { success = waitForDevices("waiting for " + numOfDevices + " device(s) to connect."); } } } private static boolean waitForDevices(final String timeoutMessage) { try { long tBefore = System.currentTimeMillis(); DEVICES.wait(WAITDEVICETIMEOUT); if (System.currentTimeMillis() - tBefore > WAITDEVICETIMEOUT) { return false; } } catch (InterruptedException e) { return false; } return true; } }