def _run_server(self, server_name): '''运行系统测试桩 ''' if not self.adb.is_rooted(): try: self.adb.start_service( '%s/.service.HelperService' % server_name, {'serviceName': server_name}) except RuntimeError: logger.warn('start helper server failed') self.adb.start_activity('%s/.activity.StartServiceActivity' % server_name, extra={'serviceName': 'HelperService'}, wait=False) time.sleep(1) return self._server_opend() timeout = 10 time0 = time.time() while time.time() - time0 < timeout: try: ret = self.run_driver_cmd('runServer', server_name, root=self.adb.is_rooted(), retry_count=1, timeout=10) logger.debug('Run server %s' % ret) if 'service run success' in ret: # wait for parent process exit time.sleep(0.5) elif 'service is running' in ret: pass elif 'java.lang.UnsatisfiedLinkError' in ret: raise RuntimeError('启动系统测试桩进程失败:\n%s' % ret) except TimeoutError as e: logger.warn('Run server timeout: %s' % e) if self._server_opend(): return True time.sleep(1) return False
def lock_keyguard(self): '''锁屏 ''' if self.is_keyguard_locked(): return True # 已经是锁屏状态 if self.adb.is_rooted(): self.set_screen_lock_enable(True) if self.adb.get_sdk_version() >= 16: # 发送电源键 self.send_key(KeyCode.KEYCODE_POWER) return True self._last_activity_before_lock = self._get_current_activity( True) # 保存锁屏前的Activity logger.debug('锁屏前Activity为:%r' % self._last_activity_before_lock) ret = self._lock_keyguard() if not ret: logger.warn('lock keyguard failed') self.wake_screen(False) time.sleep(1) self.wake_screen(True) return self.is_keyguard_locked() return True
def send_command(self, cmd_type, **kwds): '''发送命令 ''' curr_thread_id = threading.current_thread().ident self._wait_for_event(curr_thread_id, self._max_block_time) if cmd_type != EnumCommand.CmdHello: self._safe_init_driver() # 确保测试桩连接正常 result = self._client.send_command(cmd_type, **kwds) if result == None: pid = self._adb.get_pid(self._process['name']) if pid > 0 and pid != self._process['id']: # 新进程,需要重新注入 logger.info('process %s id changed: %d %d' % (self._process['name'], self._process['id'], pid)) self._is_init = False # 需要重新初始化 self._process['id'] = pid return self.send_command(cmd_type, **kwds) elif pid == 0: raise ProcessExitError('被测进程已退出,确认是否发生Crash') elif cmd_type != EnumCommand.CmdHello: # hello包不重试 logger.debug('socket error, try to reconnect') for _ in range(3): # 为防止由于设备短暂失联导致的连接断开,这里调一次adb forward self._client = self._create_client() result = self._client.send_command(cmd_type, **kwds) if result != None: return result raise SocketError('Connect Failed') else: raise SocketError('Connect Failed') if 'Error' in result: if result['Error'] == u'控件已失效': raise ControlExpiredError(result['Error']) elif result['Error'] == u'控件类型错误': control_type = self.get_control_type(kwds['Control']) raise TypeError('%s,当前控件类型为:%s' % (result['Error'], control_type)) else: raise AndroidSpyError(result['Error']) return result
def add_monitor_task(self, task, last_time=0): '''添加监控任务 ''' if last_time > 0: last_time += time.time() self._monitor_task_list.append((task, last_time)) logger.debug('add task: %s' % task.__name__)
def _init_driver(self): """初始化测试桩 """ self._client = self._create_client() if self._client != None: # 字段赋值 self._process["name"] = self._process_name self._process["id"] = 0 # process id may change if self.hello() != None: self._process["id"] = self._adb.get_pid(self._process_name) return timeout = 20 time0 = time.time() proc_exist = False while time.time() - time0 < timeout: if not proc_exist: pid = self._adb.get_pid(self._process_name) if pid > 0: proc_exist = True self._process["name"] = self._process_name self._process["id"] = pid break time.sleep(1) if not proc_exist: raise RuntimeError("进程:%s 在%d秒内没有出现" % (self._process_name, timeout)) inject_file = "inject" if self._adb.is_app_process64( pid if self._adb.is_rooted() else self._process_name): # 64 bit process inject_file += "64" timeout = 30 try: if self._adb.is_art(): # Android 5.0上发现注入容易导致进程退出 self._wait_for_cpu_low(20, 10) time0 = time.time() cmdline = "%s/%s %s" % ( self._get_driver_root_path(), inject_file, self._process_name, ) while time.time() - time0 < timeout: if self._adb.is_rooted(): ret = self._adb.run_shell_cmd(cmdline, True, timeout=120, retry_count=1) else: ret = self._adb.run_as(self._process_name, cmdline, timeout=120, retry_count=1) logger.debug("inject result: %s" % ret) if "not found" in ret: raise QT4ADriverNotInstalled( "QT4A driver damaged, please reinstall QT4A driver") if "Inject Success" in ret: break elif "Operation not permitted" in ret: # 可能是进程处于Trace状态 pid = self._adb.get_pid(self._process_name) status = self._adb.get_process_status(pid) tracer_pid = int(status["TracerPid"]) if tracer_pid > 0: if int(status["PPid"]) == tracer_pid: # 使用TRACEME方式防注入 raise Exception("应用使用了防注入逻辑,注入失败") logger.warn("TracerPid is %d" % tracer_pid) self._adb.kill_process(tracer_pid) elif "Function not implemented" in ret: raise Exception( "Please install repacked app on this device") time.sleep(1) except RuntimeError as e: logger.exception("%s\n%s" % (e, self._adb.run_shell_cmd("ps"))) if self._adb.is_rooted(): logger.info(self._adb.dump_stack(self._process_name)) raise e timeout = 10 time0 = time.time() while time.time() - time0 < timeout: if self._client == None: self._client = self._create_client() if self._client != None and self.hello() != None: return time.sleep(0.1) raise RuntimeError("连接测试桩超时")
def _init_driver(self): '''初始化测试桩 ''' self._client = self._create_client() if self._client != None: # 字段赋值 self._process['name'] = self._process_name self._process['id'] = 0 # process id may change if self.hello() != None: self._process['id'] = self._adb.get_pid(self._process_name) return timeout = 20 time0 = time.time() proc_exist = False while time.time() - time0 < timeout: if not proc_exist: pid = self._adb.get_pid(self._process_name) if pid > 0: proc_exist = True self._process['name'] = self._process_name self._process['id'] = pid break time.sleep(1) if not proc_exist: raise RuntimeError('进程:%s 在%d秒内没有出现' % (self._process_name, timeout)) inject_file = 'inject' if self._adb.is_app_process64( pid if self._adb.is_rooted() else self._process_name): # 64 bit process inject_file += '64' timeout = 30 try: if self._adb.is_art(): # Android 5.0上发现注入容易导致进程退出 self._wait_for_cpu_low(20, 10) time0 = time.time() cmdline = '%s/%s %s' % (self._get_driver_root_path(), inject_file, self._process_name) while time.time() - time0 < timeout: if self._adb.is_rooted(): ret = self._adb.run_shell_cmd(cmdline, True, timeout=120, retry_count=1) else: ret = self._adb.run_as(self._process_name, cmdline, timeout=120, retry_count=1) logger.debug('inject result: %s' % ret) if 'not found' in ret: raise QT4ADriverNotInstalled( 'QT4A driver damaged, please reinstall QT4A driver') if 'Inject Success' in ret: break elif 'Operation not permitted' in ret: # 可能是进程处于Trace状态 pid = self._adb.get_pid(self._process_name) status = self._adb.get_process_status(pid) tracer_pid = int(status['TracerPid']) if tracer_pid > 0: if int(status['PPid']) == tracer_pid: # 使用TRACEME方式防注入 raise Exception('应用使用了防注入逻辑,注入失败') logger.warn('TracerPid is %d' % tracer_pid) self._adb.kill_process(tracer_pid) time.sleep(1) except RuntimeError as e: logger.exception('%s\n%s' % (e, self._adb.run_shell_cmd('ps'))) if self._adb.is_rooted(): logger.info(self._adb.dump_stack(self._process_name)) raise e timeout = 10 time0 = time.time() while time.time() - time0 < timeout: if self._client == None: self._client = self._create_client() if self._client != None and self.hello() != None: return time.sleep(0.1) raise RuntimeError('连接测试桩超时')
def run_server(self, server_name=''): '''运行测试桩进程,创建服务端 ''' if server_name == '': server_name = self.service_name from qt4a.androiddriver.androiddriver import AndroidDriver port = AndroidDriver.get_process_name_hash(server_name, self.get_device_id()) logger.info('[DeviceDriver] port is %d' % port) addr = '127.0.0.1' if not self._is_local_device: addr = self.adb.host_name if self._client: self._client.close() self._client = None time0 = time.time() timeout = 20 kill_server = False server_type = 'localabstract' if self.adb.is_rooted() and self.adb.is_selinux_opened(): # 创建TCP服务端 server_name = str(self.service_port) server_type = 'tcp' while time.time() - time0 < timeout: if not kill_server and time.time() - time0 >= timeout // 2: # server进程存在问题,强杀 self._kill_server() if self._client: self._client.close() self._client = None kill_server = True ret = self._run_server(server_name) logger.debug('[DeviceDriver] Server %s process created: %s' % (server_name, ret)) while port < 65536: self._client = self._create_client(addr, port) port_is_opened = AndroidSpyClient.server_opened(port) if not port_is_opened: new_port = self.adb.forward(port, server_name, server_type) if new_port != port: self._client = self._create_client(addr, new_port) logger.info('[AndroidDevice] new port=%d' % new_port) port = new_port try: hello_rsp = self.hello() if hello_rsp == None: if port_is_opened: # 需要释放端口 ret = self.adb.remove_forward(port) logger.info( '[AndroidDevice] remove %d forward: %s' % (port, ret)) break except RuntimeError: port += 1 logger.info('[AndroidDevice] new port is %d' % port) continue return self._client raise RuntimeError('连接系统测试桩超时')