def _setup_stream_server(self): """ Setup stream server Returns: adb shell process, non-blocking stream reader and local port """ localport, deviceport = self.adb.setup_forward( "localabstract:javacap_{}".format) deviceport = deviceport[len("localabstract:"):] # setup agent proc apkpath = self.adb.path_app(self.APP_PKG) cmds = [ "CLASSPATH=" + apkpath, 'exec', 'app_process', '/system/bin', self.SCREENCAP_SERVICE, "--scale", "100", "--socket", "%s" % deviceport, "-lazy", "2>&1" ] proc = self.adb.start_shell(cmds) # check proc output nbsp = NonBlockingStreamReader(proc.stdout, print_output=True, name="javacap_sever") while True: line = nbsp.readline(timeout=5.0) if line is None: raise RuntimeError("javacap server setup timeout") if b"Capture server listening on" in line: break if b"Address already in use" in line: raise RuntimeError("javacap server setup error: %s" % line) return proc, nbsp, localport
def setup_server(self): """ Setup maxtouch server and adb forward Returns: server process """ if self.server_proc: self.server_proc.kill() self.server_proc = None self.localport, deviceport = self.adb.setup_forward( "localabstract:maxpresent_{}".format) deviceport = deviceport[len("localabstract:"):] p = self.adb.start_shell( "app_process -Djava.class.path={0} /data/local/tmp com.netease.maxpresent.MaxPresent socket {1}" .format(self.path_in_android, deviceport)) nbsp = NonBlockingStreamReader(p.stdout, name="airtouch_server", auto_kill=True) line = nbsp.readline(timeout=5.0) if line is None: kill_proc(p) raise RuntimeError("airtouch setup timeout") if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately kill_proc(p) raise RuntimeError("airtouch server quit immediately") self.server_proc = p return p
def logcat(self, grep_str="", extra_args="", read_timeout=10): """ Perform `adb shell logcat` command and search for given patterns Args: grep_str: pattern to filter from the logcat output extra_args: additional logcat arguments read_timeout: time interval to read the logcat, default is 10 Yields: logcat lines containing filtered patterns Returns: None """ cmds = "shell logcat" if extra_args: cmds += " " + extra_args if grep_str: cmds += " | grep " + grep_str logcat_proc = self.start_cmd(cmds) nbsp = NonBlockingStreamReader(logcat_proc.stdout, print_output=False) while True: line = nbsp.readline(read_timeout) if line is None: break else: yield line nbsp.kill() logcat_proc.kill() return
def setup_server(self): """ Setup rotation wacher server Returns: server process """ self.install() if self.ow_proc: self.ow_proc.kill() self.ow_proc = None p = self.adb.start_shell( "app_process -Djava.class.path={0} /data/local/tmp com.example.rotationwatcher.Main" .format(self.path_in_android)) self.nbsp = NonBlockingStreamReader(p.stdout, name="rotation_server", auto_kill=True) if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately raise RuntimeError("rotation watcher server quit immediately") self.ow_proc = p return p
def _setup_stream_server(self, lazy=False): """ Setup minicap process on device Args: lazy: parameter `-l` is used when True Returns: adb shell process, non-blocking stream reader and local port """ localport, deviceport = self.adb.setup_forward("localabstract:minicap_{}".format) deviceport = deviceport[len("localabstract:"):] other_opt = "-l" if lazy else "" params, display_info = self._get_params() proc = self.adb.start_shell( "%s -n '%s' -P %dx%d@%dx%d/%d %s 2>&1" % tuple([self.CMD, deviceport] + list(params) + [other_opt]), ) nbsp = NonBlockingStreamReader(proc.stdout, print_output=True, name="minicap_server") while True: line = nbsp.readline(timeout=5.0) if line is None: raise RuntimeError("minicap server setup timeout") if b"Server start" in line: break if proc.poll() is not None: # minicap server setup error, may be already setup by others # subprocess exit immediately raise RuntimeError("minicap server quit immediately") reg_cleanup(proc.kill) self._stream_rotation = int(display_info["rotation"]) return proc, nbsp, localport
def setup_server(self): """ Setup minitouch server and adb forward Returns: server process """ if self.server_proc: self.server_proc.kill() self.server_proc = None self.localport, deviceport = self.adb.setup_forward( "localabstract:minitouch_{}".format) deviceport = deviceport[len("localabstract:"):] if self.input_event: p = self.adb.start_shell( "/data/local/tmp/minitouch -n '{0}' -d '{1}' 2>&1".format( deviceport, self.input_event)) else: p = self.adb.start_shell( "/data/local/tmp/minitouch -n '{0}' 2>&1".format(deviceport)) nbsp = NonBlockingStreamReader(p.stdout, name="minitouch_server", auto_kill=True) while True: line = nbsp.readline(timeout=3.0) if line is None: kill_proc(p) self.adb.close_proc_pipe(p) raise RuntimeError("minitouch setup timeout") line = line.decode(get_std_encoding(sys.stdout)) # 识别出setup成功的log,并匹配出max_x, max_y m = re.search( "Type \w touch device .+ \((\d+)x(\d+) with \d+ contacts\) detected on .+ \(.+\)", line) if m: self.max_x, self.max_y = int(m.group(1)), int(m.group(2)) break else: self.max_x = 32768 self.max_y = 32768 # nbsp.kill() # 保留,不杀了,后面还会继续读取并pirnt if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately kill_proc(p) raise RuntimeError("minitouch server quit immediately") self.server_proc = p reg_cleanup(kill_proc, self.server_proc) return p
def setup(self): cmd = [self.executable, "--udid", self.udid, "--port", str(self.port), "--resolution", self.resolution] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE) nbsp = NonBlockingStreamReader(proc.stdout, print_output=True, name="minicap_sever") while True: line = nbsp.readline(timeout=10.0) if line is None: raise RuntimeError("minicap setup error") if b"== Banner ==" in line: break if proc.poll() is not None: logging.warn("Minicap server already started, use old one") self.server_proc = proc
def start_recording(self, max_time=1800, bit_rate=None, vertical=None): """ Start screen recording Args: max_time: maximum rate value, default is 1800 bit_rate: bit rate value, default is None vertical: vertical parameters, default is None Raises: RuntimeError: if any error occurs while setup the recording Returns: None if recording did not start, otherwise True """ if getattr(self, "recording_proc", None): raise AirtestError("recording_proc has already started") pkg_path = self.adb.path_app(YOSEMITE_PACKAGE) max_time_param = "-Dduration=%d" % max_time if max_time else "" bit_rate_param = "-Dbitrate=%d" % bit_rate if bit_rate else "" if vertical is None: vertical_param = "" else: vertical_param = "-Dvertical=true" if vertical else "-Dvertical=false" p = self.adb.start_shell( 'CLASSPATH=%s exec app_process %s %s %s /system/bin %s.Recorder --start-record' % (pkg_path, max_time_param, bit_rate_param, vertical_param, YOSEMITE_PACKAGE)) nbsp = NonBlockingStreamReader(p.stdout) while True: line = nbsp.readline(timeout=5).strip() if six.PY3: line = line.decode("utf-8") if line is None: raise RuntimeError("recording setup error") m = re.match( "start result: Record start success! File path:(.*\.mp4)", line.strip()) if m: output = m.group(1) self.recording_proc = p self.recording_file = output return True
def start_recording(self, max_time=1800, bit_rate=None): """ Start screen recording Args: max_time: maximum screen recording time, default is 1800 bit_rate: bit rate value, 450000-8000000, default is None(6000000) Raises: RuntimeError: if any error occurs while setup the recording Returns: None if recording did not start, otherwise True """ if getattr(self, "recording_proc", None): raise AirtestError("recording_proc has already started") pkg_path = self.adb.path_app(YOSEMITE_PACKAGE) max_time_param = "-Dduration=%d" % max_time if max_time else "" # The higher the bitrate, the clearer the video, the default value is 6000000 bit_rate_param = "-Dbitrate=%d" % bit_rate if bit_rate else "" # The video size is square, compatible with horizontal and vertical screens p = self.adb.start_shell( 'CLASSPATH=%s exec app_process %s %s /system/bin %s.Recorder --start-record' % (pkg_path, max_time_param, bit_rate_param, YOSEMITE_PACKAGE)) nbsp = NonBlockingStreamReader(p.stdout) while True: line = nbsp.readline(timeout=5) if line is None: raise RuntimeError("start recording error") if six.PY3: line = line.decode("utf-8") # 如果上次录屏被强制中断,可能会导致无法开始下一次录屏,额外发一个停止录屏指令 if re.search("Record has already started", line): self.stop_recording(is_interrupted=True) continue m = re.match( "start result: Record start success! File path:(.*\.mp4)", line.strip()) if m: output = m.group(1) self.recording_proc = p self.recording_file = output return True
class RotationWatcher(object): """ RotationWatcher class """ def __init__(self, adb, ori_method=ORI_METHOD.MINICAP): self.adb = adb self.ow_proc = None self.nbsp = None self.ow_callback = [] self.ori_method = ori_method self._t = None self._t_kill_event = threading.Event() self.current_orientation = None self.path_in_android = "/data/local/tmp/" + os.path.basename(ROTATIONWATCHER_JAR) reg_cleanup(self.teardown) @on_method_ready('start') def get_ready(self): pass def install(self): """ Install the RotationWatcher package Returns: None """ try: exists_file = self.adb.file_size(self.path_in_android) except: pass else: local_minitouch_size = int(os.path.getsize(ROTATIONWATCHER_JAR)) if exists_file and exists_file == local_minitouch_size: LOGGING.debug("install_rotationwatcher skipped") return self.uninstall() self.adb.push(ROTATIONWATCHER_JAR, self.path_in_android) self.adb.shell("chmod 755 %s" % self.path_in_android) LOGGING.info("install rotationwacher finished") def uninstall(self): """ Uninstall the RotationWatcher package Returns: None """ self.adb.raw_shell("rm %s" % self.path_in_android) def setup_server(self): """ Setup rotation wacher server Returns: server process """ self.install() if self.ow_proc: self.ow_proc.kill() self.ow_proc = None p = self.adb.start_shell( "app_process -Djava.class.path={0} /data/local/tmp com.example.rotationwatcher.Main".format( self.path_in_android)) self.nbsp = NonBlockingStreamReader(p.stdout, name="rotation_server", auto_kill=True) if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately raise RuntimeError("rotation watcher server quit immediately") self.ow_proc = p return p def teardown(self): self._t_kill_event.set() if self.ow_proc: self.ow_proc.kill() # close io.Buffer self.ow_proc.communicate() if self.nbsp: self.nbsp.kill() def start(self): """ Start the RotationWatcher daemon thread Returns: initial orientation """ if self.ori_method == ORI_METHOD.MINICAP: try: self.setup_server() except: # install or setup failed LOGGING.error(traceback.format_exc()) LOGGING.error("RotationWatcher setup failed, use ADBORI instead.") self.ori_method = ORI_METHOD.ADB def _refresh_by_ow(): # 在产生旋转时,nbsp读取到的内容为b"90\r\n",平时读到的是空数据None,进程结束时读到的是b"" line = self.nbsp.readline() if line is not None: if line == b"": self.teardown() if LOGGING is not None: # may be None atexit LOGGING.debug("orientationWatcher has ended") else: print("orientationWatcher has ended") return None ori = int(int(line) / 90) return ori # 每隔1秒读取一次 time.sleep(1) def _refresh_by_adb(): ori = self.adb.getDisplayOrientation() return ori def _run(kill_event): while not kill_event.is_set(): if self.ori_method == ORI_METHOD.ADB: ori = _refresh_by_adb() if self.current_orientation == ori: time.sleep(3) continue else: ori = _refresh_by_ow() if ori is None: # 以前ori=None是进程结束,现在屏幕方向不变时会返回None continue LOGGING.info('update orientation %s->%s' % (self.current_orientation, ori)) self.current_orientation = ori if is_exiting(): self.teardown() for cb in self.ow_callback: try: cb(ori) except: LOGGING.error("cb: %s error" % cb) traceback.print_exc() self.current_orientation = _refresh_by_ow() if self.ori_method != ORI_METHOD.ADB else _refresh_by_adb() self._t = threading.Thread(target=_run, args=(self._t_kill_event, ), name="rotationwatcher") self._t.daemon = True self._t.start() return self.current_orientation def reg_callback(self, ow_callback): """ Args: ow_callback: Returns: """ """方向变化的时候的回调函数,参数一定是ori,如果断掉了,ori传None""" if ow_callback not in self.ow_callback: self.ow_callback.append(ow_callback)