def get_imei(self): '''获取设备imei号 ''' try: return self.adb.get_device_imei() except RuntimeError, e: logger.warn('Read device imei by dumpsys failed: %s' % e) return self._device_driver.get_device_imei()
def clear_camera_default_app(self): '''清除默认相机应用 ''' from androiddriver.util import logger if self.is_rooted(): return self._device_driver.clear_default_app( 'android.media.action.IMAGE_CAPTURE') else: logger.warn('clear_camera_default_app need root')
def recv_data(data_len): data = '' while len(data) < data_len: try: buff = sock.recv(data_len - len(data)) if not buff: logger.warn('screenshot socket closed') return data += buff except socket.error, e: logger.warn('recv screenshot data error: %s' % e) return
def pull_file(self, src_path, dst_path): '''将手机中的文件拷贝到PC中 :param src_path: 手机中的源路径 :type src_path: string :param dst_path: PC中的目的路径 :type dst_path: string ''' self.adb.list_dir(src_path) try: ret = self.adb.pull_file(src_path, dst_path) if 'does not exist' not in ret: return except RuntimeError, e: logger.warn('pull file failed: %r' % e) if src_path.startswith('/data/local/tmp/'): raise e
def push_file(self, src_path, dst_path): '''向手机中拷贝文件 :param src_path: PC上的源路径 :type src_path: string :param dst_path: 手机上的目标路径 :type dst_path: string ''' if not os.path.exists(src_path): raise RuntimeError('File: %s not exist' % src_path) file_size = os.path.getsize(src_path) is_zip = False if file_size >= 5 * 1024 * 1024: is_zip = True zip_file_path = src_path + '.zip' zip_file = zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) if dst_path[-1] == '/': # filename not specified filename = os.path.split(src_path)[-1] else: filename = dst_path.split('/')[-1] zip_file.write(src_path, filename) zip_file.close() src_path = zip_file_path dst_path += '.zip' ret = self.adb.push_file(src_path, dst_path) if is_zip: os.remove(src_path) if not self._device_driver.unzip_file( dst_path, dst_path[:dst_path.rfind('/')]): logger.warn('unzip file %s failed' % dst_path) ret = self.adb.push_file(src_path[:-4], dst_path[:-4]) elif dst_path.startswith('/data/'): self.adb.chmod(dst_path[:-4], '744') self.delete_file(dst_path) dst_path = dst_path[:-4] try: self.run_shell_cmd('touch "%s"' % dst_path) # 修改文件修改时间 except: logger.exception('touch file %s error' % dst_path) return ret
def extract_crash_from_logcat(self, log_list): '''检测logcat日志中是否有crash发生并萃取出相关日志 ''' from androiddriver.util import logger as qt4a_logger pattern_list = self.extract_crash_by_patterns() if self._target_crash_proc_list == []: # 表示用户不关心任何进程的crash问题,则不对crash进行提取 return None, None if not isinstance(pattern_list, list) and not isinstance( pattern_list, tuple) and not len(pattern_list) == 2: raise RuntimeError('传入的pattern_list不是列表或二元组') if isinstance(pattern_list, tuple): pattern_list = [pattern_list] new_pattern_list = [] for proc in self._target_crash_proc_list: if not isinstance(proc, str): qt4a_logger.warn('传入的process不是字符串类型') continue for pattern in pattern_list: if not isinstance(pattern, tuple) or not len(pattern) == 2: qt4a_logger.warn('传入的pattern不是二元组') else: new_pattern_list.append((proc, ) + pattern) new_pattern_list.append( (proc, ) + (r'AndroidRuntime', r'FATAL EXCEPTION:.*')) # system crash1:java crash # new_pattern_list.append((proc,) + (r'dalvikvm', r'threadid=.*: thread exiting with uncaught exception .*')) #与system crash1重复,暂时注释掉,优先取system crash1 native_crash_pattern = r'pid: \d+, tid: \d+, name: .*>>> ' + '(' + proc + ')' + r' <<<' new_pattern_list.append( (r'/system/bin/debuggerd', r'DEBUG', native_crash_pattern)) # system crash2:native crash # new_pattern_list.append((r'/system/bin/debuggerd', r'DEBUG', r'signal \d+ \(.*\), code \d+ \(.*\), fault addr.*')) #fault addr crash与上一行的system crash2重复,优先取system crash2 if new_pattern_list == []: # 因为可能随着需求变更,没有规则写入 return None, None crash_info = '' pattern = r'\[(.*)\(\d+\)\] \[.*\] (.)/(.*)\((\d+)\): (.*)' form_log_dict = {} regex = re.compile(pattern) for log in log_list: res = regex.match(log) if res: cur_line = { 'process_name': res.group(1), 'level': res.group(2), 'tag': res.group(3), 'part_log': res.group(5), 'line_log': log } # res.group(1)是进程名process_name的正则表达式,res.group(2)是错误级别level的正则表达式,res.group(3)是标签tag的正则表达式,res.group(4)是线程id的正则表达式,res.group(5)是线程id后的冒号后开始到本行结尾内容的正则表达式,line_log存储整行日志log。 if form_log_dict.has_key(res.group(4)): form_log_dict[res.group(4)].append(cur_line) else: form_log_dict[res.group(4)] = [cur_line] else: qt4a_logger.warn("提取crash日志时,log:%s无法按格式%s解析" % (log, pattern)) crash_list = [] for tlog_list in form_log_dict.values(): is_crashed = False tag = '' last_pattern_list = [] for tlog_dict in tlog_list: if is_crashed == True and tag == tlog_dict['tag']: crash_info += tlog_dict['line_log'] + '\n' crash_list.append(tlog_dict) continue if not last_pattern_list: for pat_tuple in new_pattern_list: process_name_reg = re.compile(pat_tuple[0]) if not process_name_reg.match( tlog_list[0]['process_name']): continue last_pattern_list.append(pat_tuple) if len(last_pattern_list) == 0: break is_crashed = False for pat_tuple in last_pattern_list: tag_reg = re.compile(pat_tuple[1]) tag = tlog_dict['tag'] if not tag_reg.match(tag): continue part_log_reg = re.compile(pat_tuple[2]) if not part_log_reg.match(tlog_dict['part_log']): continue is_crashed = True crash_list.append(tlog_dict) crash_info += tlog_dict['line_log'] + '\n' break if crash_info: crash_type = self.check_crash_type(crash_list) crash_path = '%s_%s.crash.log' % (self.__class__.__name__, int(time.time())) with open(crash_path, 'w') as fd: fd.write(crash_info) return crash_type, crash_path return None, None