$max_len ? substr($txt, 0, $max_len) . "..." : $txt; } function _trim_start($txt, $max_len) { return strlen($txt) > $max_len ? "..." . substr($txt, strlen($txt) - $max_len) : $txt; } function _trim_lines($txt, $max_lines) { $lines = explode("\n", $txt, $max_lines+1); $res = ''; for($i=0;$i 2 && $txt[1] === ':') return $txt; if(!function_exists('lz4_compress')) return $txt; return "4:" . base64_encode(lz4_compress(base64_decode($txt))); } function log($msg) { echo date("Y-m-d H:i:s") . " " . $msg . "\n"; } function err($msg) { taskman\stderr(date("Y-m-d H:i:s") . " " . $msg . "\n"); } function _retry($max_tries, $func) { for($i=0;$i<$max_tries;++$i) { try { $func(); return; } catch(Exception $e) { if(($i+1) == $max_tries) throw $e; } sleep(1); } } function adb_reboot(string $atf_host) { host_exec($atf_host, "killall adb", DEPLOY_OPT_ERR_OK); _retry(3, function() use($atf_host) { host_exec($atf_host, "%{adb}% kill-server"); }); host_exec($atf_host, "%{adb}% start-server"); } function reboot_devices(string $atf_host, array $devices, int $wait_after = 40) { log("Rebooting devices..."); //let's reboot devices $ces = array(); foreach($devices as $device) { $ces[] = Amp\call(function() use($atf_host, $device, $wait_after) { yield host_exec_async($atf_host, "%{adb}% -s $device reboot", DEPLOY_OPT_ERR_OK); yield Amp\delay($wait_after*1000); }); } Amp\Promise\wait(Amp\Promise\all($ces)); } function get_ext_status_async(string $atf_host, string $device, int $timeout = 20) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $timeout) { $file_data = yield device_pull_file_async($atf_host, $device, "atf_status.js", $timeout, /*throw on timeout*/true); if($file_data === null) return null; $file_data = preg_replace('/[[:cntrl:]]/', '', $file_data); return json_decode($file_data, true); }); } function stop_app_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { yield host_exec_async($atf_host, "%{adb}% -s $device shell am force-stop %{package_id}%", DEPLOY_OPT_ERR_OK, 30); }); } function start_app_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { yield host_exec_async($atf_host, "%{adb}% -s $device shell am start -n %{package_id}%/%{activity}%", 0, 30); }); } function start_ext_cmd_on_device_async(string $atf_host, string $device, Cmd $cmd, array $cmd_args) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $cmd, $cmd_args) { yield device_del_file_async($atf_host, $device, 'atf_status.js', DEPLOY_OPT_ERR_OK); yield stop_app_async($atf_host, $device); yield update_ext_cmd_async($atf_host, $device, $cmd, $cmd_args); yield start_app_async($atf_host, $device); }); } function get_devices(string $atf_host, bool $extended = false) { $devices = array(); list($_, $lines) = host_exec($atf_host, "%{adb}% devices -l", DEPLOY_OPT_SILENT); foreach($lines as $line) { if(preg_match('~^(\S+)\s+device(.*)~', $line, $m)) { if($extended) { $devices[$m[1]] = array(); $items = explode(" ", trim($m[2])); foreach($items as $item) { list($k, $v) = explode(":", $item); $devices[$m[1]][$k] = $v; } } else $devices[] = $m[1]; } } return $devices; } function update_ext_cmd_async(string $atf_host, string $device, Cmd $cmd, array $args) { $ext_cmd = array( "device_id" => $device, "module" => $cmd->module, "func" => $cmd->func, "args" => $args, ); return device_put_file_async($atf_host, $device, json_encode($ext_cmd), 'atf_cmd.js'); } function package_dir(string $atf_host) { return "/storage/emulated/0/Android/data/".taskman\deploy_get($atf_host, 'package_id')."/files/"; } function device_put_file_async(string $atf_host, string $device, string $data_or_file, string $device_path, bool $is_file = false, $timeout = 30) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $data_or_file, $device_path, $is_file, $timeout) { if($device_path[0] !== "/") $device_path = package_dir($atf_host) . $device_path; $tmp_remote_file = '%{dir}%'.uniqid(basename($device_path)."_"); if(!$is_file) { $local_path = tempnam("/tmp", "atf_"); taskman\ensure_write($local_path, $data_or_file); } else { $local_path = $data_or_file; if(!is_file($local_path)) throw new Exception("No such local file: $local_path"); } try { //1. let's copy local file to the ATF host as temp one yield host_put_file_async($atf_host, $local_path, $tmp_remote_file); } finally { if(!$is_file) taskman\ensure_rm($local_path); } //2. let's push the temp file to the device $device_dir = dirname($device_path); yield host_exec_async($atf_host, "%{adb}% -s $device shell mkdir -p $device_dir"); yield host_exec_async($atf_host, "%{adb}% -s $device push $tmp_remote_file $device_path", 0, $timeout); yield host_exec_async($atf_host, "%{adb}% -s $device shell chmod -R 777 $device_dir", DEPLOY_OPT_ERR_OK); yield host_exec_async($atf_host, "rm -rf $tmp_remote_file"); }); } function device_del_file(string $atf_host, string $device, string $device_path, $opts = 0) { if($device_path[0] !== "/") $device_path = package_dir($atf_host) . $device_path; host_exec($atf_host, "%{adb}% -s $device shell rm -rf $device_path", $opts); } function device_del_file_async(string $atf_host, string $device, string $device_path, $opts = 0) { if($device_path[0] !== "/") $device_path = package_dir($atf_host) . $device_path; return host_exec_async($atf_host, "%{adb}% -s $device shell rm -rf $device_path", $opts); } function device_blink($atf_host, string $device, int $times = 5) { //disable auto brightness host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness_mode 0"); for($i=0;$i<$times;++$i) { host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 255"); sleep(1); host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 5"); } //enable auto brightness host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 100"); host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness_mode 1"); } function device_put_dir_async(string $atf_host, string $device, string $folder_path, string $device_parent_path, int $timeout = 60) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $folder_path, $device_parent_path, $timeout) { if($device_parent_path[0] !== "/") $device_parent_path = package_dir($atf_host) . $device_parent_path; $remote_path = '/tmp/' . basename($folder_path); yield taskman\deploy_rsync_async($atf_host, "$folder_path/", "$remote_path/", '--delete'); yield host_exec_async($atf_host, "%{adb}% -s $device push --sync $remote_path $device_parent_path", 0, $timeout); }); } function device_pull_file_async(string $atf_host, string $device, string $device_path, int $timeout = 10, bool $throw_error_on_timeout = false) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $device_path, $throw_error_on_timeout, $timeout) { if($device_path[0] !== "/") $device_path = package_dir($atf_host) . $device_path; $tmp_remote_file = '%{dir}%/'.uniqid(basename($device_path)."_"); list($status, $_) = yield host_exec_async($atf_host, "%{adb}% -s $device pull $device_path $tmp_remote_file", DEPLOY_OPT_ERR_OK, $timeout); if($status !== 0) { if($status === false && $throw_error_on_timeout) throw new Exception("Could not pull from device '$device' file '$device_path' due to timeout"); return null; } $file_data = host_get_file($atf_host, $tmp_remote_file); yield host_exec_async($atf_host, "rm -rf $tmp_remote_file"); return $file_data; }); } function get_last_replay_async(string $atf_host, string $device, string $device_replay_path) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $device_replay_path) { $file = package_dir($atf_host) . $device_replay_path; list($status, $contents) = yield host_exec_async($atf_host, "%{adb}% -s $device shell cat $file", DEPLOY_OPT_ERR_OK); return array($status, implode("\n", $contents)); }); } function _multi_curl_async($ch, $get_content) : Amp\Promise { return Amp\call(function() use($ch, $get_content) { $mh = curl_multi_init(); curl_multi_add_handle($mh, $ch); do { $status = curl_multi_exec($mh, $active); yield Amp\delay(1); } while ($active && $status == CURLM_OK); $result = null; if($get_content) $result = curl_multi_getcontent($ch); curl_multi_remove_handle($mh, $ch); curl_multi_close($mh); return $result; }); } 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 del_external_files_async(string $atf_host, string $device, array $external_files) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $external_files) { foreach($external_files as $real_path => $remote_path) yield device_del_file_async($atf_host, $device, $remote_path); }); } function put_external_files_async(string $atf_host, string $device, array $external_files) : Amp\Promise { return Amp\call(function() use($atf_host, $device, $external_files) { foreach($external_files as $real_path => $remote_path) yield device_put_file_async($atf_host, $device, $real_path, $remote_path, true); }); } function get_locat_errors_since(string $atf_host, string $device, int $since_stamp) { $time_spec = gmdate('m-d h:m:s.0', $since_stamp); list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -v UTC -d -t '$time_spec' AndroidRuntime:E Unity:E", DEPLOY_OPT_ERR_OK); _filter_error_logs($lines); return implode("\n", $lines); } function get_logcat_errors(string $atf_host, string $device, int $limit = 0) { list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -d ".($limit > 0 ? "-t $limit" : "")." AndroidRuntime:E Unity:E", DEPLOY_OPT_ERR_OK); _filter_error_logs($lines); return implode("\n", $lines); } function get_locat_unity_since(string $atf_host, string $device, int $since_stamp) { $time_spec = gmdate('m-d h:m:s.0', $since_stamp); list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -v UTC -d -t '$time_spec' Unity:'*'", DEPLOY_OPT_ERR_OK); return implode("\n", $lines); } function get_logcat_unity(string $atf_host, string $device, int $limit = 0) { list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -d Unity:'*'", DEPLOY_OPT_ERR_OK); if($limit > 0) array_splice($lines, 0, sizeof($lines) - $limit); return implode("\n", $lines); } function _filter_error_logs(array &$lines) { $exception_lines = 0; $filtered = array(); for($i=0;$i 0) $filtered[] = $line; } } $lines = $filtered; } 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_temperature_async(string $atf_host, string $device) : Amp\Promise { return Amp\call(function() use($atf_host, $device) { //NOTE: on current devices 16 thermal zone is responsible for GPU temperature probing list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell cat /sys/class/thermal/thermal_zone16/temp", DEPLOY_OPT_ERR_OK, 1); if($code !== 0) return 0; foreach($lines as $idx => $line) { $line = trim($line); if(!$line) continue; return intval($line); } return 0; }); }