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 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, name="start_recording_" + str(id(self))) # 进程p必须要保留到stop_recording执行时、或是退出前才进行清理,否则会导致录屏进程提前终止 reg_cleanup(kill_proc, p) while True: line = nbsp.readline(timeout=5) if line is None: nbsp.kill() kill_proc(p) 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 nbsp.kill() 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)