atf_host = $atf_host; $this->max_installs_in_progress = $max_installs_in_progress; $this->apk_name = $apk_name; $this->override_external_files = $override_external_files; $this->external_files = $external_files; } function isInstalled(string $device) { return isset($this->installs[$device]) && $this->installs[$device] == self::ST_INSTALLED; } function forgetInstall(string $device) { unset($this->installs[$device]); } function countInstallsInProgress() { $c = 0; foreach($this->installs as $status) if($status == self::ST_IN_PROGRESS) ++$c; return $c; } function installAsync(string $device, bool $force = false) : Amp\Promise { return Amp\call(function() use($device, $force) { while($this->countInstallsInProgress() >= $this->max_installs_in_progress) yield Amp\delay(1000); if($force) unset($this->installs[$device]); if(isset($this->installs[$device])) return; $this->installs[$device] = self::ST_IN_PROGRESS; try { yield host_exec_async($this->atf_host, "%{adb}% -s $device shell am force-stop %{package_id}%", DEPLOY_OPT_ERR_OK); if($this->apk_name !== null) { yield host_exec_async($this->atf_host, "%{adb}% -s $device uninstall %{package_id}%", DEPLOY_OPT_ERR_OK, 30); yield host_exec_async($this->atf_host, "%{adb}% -s $device install -r %{dir}%/{$this->apk_name}", 0, 300); //delete overrides only if there's a new apk file uploaded yield del_external_files_async($this->atf_host, $device, $this->external_files); } if($this->override_external_files) yield put_external_files_async($this->atf_host, $device, $this->external_files); $this->installs[$device] = self::ST_INSTALLED; } catch(Exception $e) { err("Error during install *$device*: " . $e->getMessage()); unset($this->installs[$device]); throw $e; } }); } function getSize() { list($status, $lines) = host_exec($this->atf_host, "ls -sd -- %{dir}%/{$this->apk_name}", DEPLOY_OPT_ERR_OK|DEPLOY_OPT_SILENT); if($status != 0) return 0; $items = explode(' ', $lines[0]); //ls -s returns size in blocks where each block is 512 bytes long return intval($items[0])*512; } } class AdbDevicePool implements IDevicePool { private string $atf_host; function __construct(string $atf_host) { $this->atf_host = $atf_host; } function get() : array { return get_devices($this->atf_host); } } class FixedDevicePool implements IDevicePool { private array $devices; function __construct(array $devices) { $this->devices = $devices; } function get() : array { return $this->devices; } } class CachedDevices implements IDevicePool { private IDevicePool $provider; private ?array $cached = null; private int $last_cache_time; private int $keep_cache_time; function __construct(IDevicePool $provider, int $keep_cache_time) { $this->provider = $provider; $this->keep_cache_time = $keep_cache_time; } function get() : array { if($this->cached === null || (time() - $this->last_cache_time) > $this->keep_cache_time) { $this->cached = $this->provider->get(); $this->last_cache_time = time(); } return $this->cached; } } function device_screen(string $atf_host, string $device) { $screen_file_name = '%{dir}%/'.uniqid("screen_").'.png'; try { host_exec($atf_host, "%{adb}% -s $device exec-out screencap -p > $screen_file_name"); } catch(Exception $e) { return false; } $data = host_get_file($atf_host, $screen_file_name); host_exec($atf_host, "rm -rf $screen_file_name"); return $data; } function device_pss_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { $res = yield device_mem_async($atf_host, $device); return $res['total']; }); } function device_mem_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell dumpsys meminfo -s %{package_id}%", DEPLOY_OPT_ERR_OK, 20); $res = array( 'total' => 0, 'native' => 0, 'java' => 0, 'system' => 0, 'graphics' => 0, ); if($code !== 0) return $res; foreach($lines as $idx => $line) { $items = preg_split('/\s+/', trim($line)); if($items && $items[0] === "TOTAL:") $res['total'] = intval($items[1]); else if($items && $items[0] === "TOTAL" && $items[1] === "PSS:") $res['total'] = intval($items[2]); else if($items && $items[0] === "Native" && $items[1] === "Heap:") $res['native'] = intval($items[2]); else if($items && $items[0] === "Java" && $items[1] === "Heap:") $res['java'] = intval($items[2]); else if($items && $items[0] === "System:") $res['system'] = intval($items[1]); else if($items && $items[0] === "Graphics:") $res['graphics'] = intval($items[1]); } return $res; }); } function device_cpuinfo_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell dumpsys cpuinfo -s %{package_id}%", DEPLOY_OPT_ERR_OK, 20); $res = array( 'la1' => 0, 'la5' => 0, 'la15' => 0 ); if($code !== 0 || count($lines) == 0) return $res; foreach($lines as $idx => $line) { if(strpos($line, 'Load:') !== false) { $items = explode(':', $line); $sub_items = explode('/', $items[1]); $res['la1'] = floatval(trim($sub_items[0])); $res['la5'] = floatval(trim($sub_items[1])); $res['la15'] = floatval(trim($sub_items[2])); break; } } return $res; }); } function device_cputop_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell top -q -o'%CPU,PID,CMDLINE' -n1 -s1 | grep -w %{package_id}%", DEPLOY_OPT_ERR_OK, 20); $cpu = 0; if($code !== 0 || count($lines) == 0) return $cpu; foreach($lines as $idx => $line) { $items = preg_split('/\s+/', trim($line)); if(count($items) > 0) { $cpu = intval($items[0]); break; } } return $cpu; }); } function device_temperature_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell dumpsys thermalservice", DEPLOY_OPT_ERR_OK, 1); if($code !== 0) return array(); return device_parse_soc_temps($lines); }); } function device_parse_soc_temps($thermal_output_lines) { $cpu_temp = null; $gpu_temp = null; $hal_section_found = false; foreach($thermal_output_lines as $line) { if(!$hal_section_found) { if(strpos($line, 'Current temperatures from HAL:') !== false) $hal_section_found = true; continue; } if(strpos($line, 'mType=0') !== false) //Type 0 indicates CPI $cpu_temp = device_parse_thermal_line($line); if(strpos($line, 'mType=1') !== false) //Type 1 indicates GPU $gpu_temp = device_parse_thermal_line($line); if($cpu_temp && $gpu_temp) break; } return [ 'cpu_temp' => $cpu_temp, 'gpu_temp' => $gpu_temp ]; } function device_parse_thermal_line($line) { return preg_match('/mValue=([^,]+)/', $line, $matches) ? floatval($matches[1]) : null; }