def test_logcat_thread_func(self): adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) adb._log_list = [] adb._logcat_running = True adb._log_pipe = MockPopen() adb._log_pipe.stdout.write( b'08-05 10:46:13.395 100 22878 I DEBUG (22878): Revision: 0\n') adb._log_pipe.stdout.write( b'08-05 10:46:13.395 101 22878 I DEBUG (22878): Revision: 0\n') def stop_thread(): time.sleep(1) adb._logcat_running = False adb._log_pipe.stdout.write(b'\n') t = threading.Thread(target=stop_thread) t.daemon = True t.start() process = {'pid': 100, 'proc_name': '<pre-initialized>', 'ppid': 1} def mock_list_process(): if process['proc_name'] == '<pre-initialized>': proc = copy.copy(process) process['proc_name'] = 'com.qta.qt4a' return [proc] else: return [process] ADB.list_process = mock.Mock(side_effect=mock_list_process) adb._logcat_thread_func([])
def test_list_process(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) result = adb.list_process() self.assertEqual(len(result), 172) self.assertEqual(result[0]['ppid'], 0) self.assertEqual(result[0]['pid'], 1) self.assertEqual(result[0]['proc_name'], '/init')
def test_list_dir(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) ADB.is_rooted = mock.Mock(return_value=True) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) dir_list, file_list = adb.list_dir('/data/data') self.assertEqual(len(dir_list), 89) self.assertEqual(len(file_list), 0) self.assertEqual(dir_list[0]['name'], 'com.android.apps.tag') self.assertEqual(dir_list[0]['attr'], 'rwxr-x--x')
def _get_app(self): ADB.is_rooted = mock.Mock(return_value=False) ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', 'test') adb = ADB(adb_backend) device = Device(adb) return AndroidDemoApp(device)
def __init__(self, adb): self._adb = adb self._device_id = adb.device_name self._is_local_device = ADB.is_local_device(self._device_id) self._client = None self._server_pid = 0 self._timeout = 40
def install_package(self, pkg_path, overwrite=False): '''安装应用 ''' from qt4a.androiddriver.util import get_file_md5 if not os.path.exists(pkg_path): raise RuntimeError('APK: %r not exist' % pkg_path) pkg_size = os.path.getsize(pkg_path) pkg_md5 = get_file_md5(pkg_path) pkg_name = ADB._get_package_name(pkg_path) if self.is_package_installed(pkg_name, pkg_size, pkg_md5): logger.info('APP %s [%d]%s is installed' % (pkg_name, pkg_size, pkg_md5)) return True self.adb.install_apk(pkg_path, overwrite) return True
def __init__(self, id_or_adb_backend=None): '''获取一个Android设备,获取成功后则独占该设备。 :param device_id: 获取制定设备id的Android设备。如果为空则任意获取一台空闲设备。 ''' self._device = None if isinstance(id_or_adb_backend, str): adb_backend = LocalADBBackend.open_device(id_or_adb_backend) elif not id_or_adb_backend: local_device_list = LocalDeviceProvider.list() if not local_device_list: raise RuntimeError('No local device found') adb_backend = LocalADBBackend.open_device(local_device_list[0]) else: adb_backend = id_or_adb_backend self._adb = ADB.open_device(adb_backend) self._device_driver = DeviceDriver(self._adb) Device.device_list.append(self)
def install_qt4a_driver(args): from qt4a.androiddriver.adb import ADB from qt4a.androiddriver.androiddriver import copy_android_driver device_id = None device_list = ADB.list_device() if args.serialno and args.serialno not in device_list: raise RuntimeError('Device %s not found' % args.serialno) elif args.serialno: device_id = args.serialno else: if len(device_list) == 0: raise RuntimeError('No Android device found') elif len(device_list) == 1: device_id = device_list[0] elif len(device_list) > 1: text = '\nCurrent Android device list:\n' for i, dev in enumerate(device_list): text += '%d. %s\n' % ((i + 1), dev) while True: print(text) result = raw_input( 'Please input the index of device to install driver:\n') if result.isdigit(): if int(result) > len(device_list): sys.stderr.write( '\nIndex %s out of range\nValid index range: [1, %d]\n' % (result, len(device_list))) time.sleep(0.1) continue device_id = device_list[int(result) - 1] else: if not result in device_list: sys.stderr.write('\nDevice %r not exist\n' % result) time.sleep(0.1) continue device_id = result break print('Device "%s" will install driver...' % device_id) copy_android_driver(device_id, args.force) print('Install QT4A driver to %s completely.' % device_id)
def test_save_log(self): adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) adb.start_logcat() adb.insert_logcat('test', 2019, '0101', '10:51:42.899', 'I', 'test', 1, '我们') adb.insert_logcat('test', 2019, '0101', '10:51:42.899', 'I', 'test', 1, u'中国'.encode('gbk')) adb.insert_logcat('test', 2019, '0101', '10:51:42.899', 'I', 'test', 1, u'\ub274') save_path = tempfile.mkstemp('.log')[1] adb.save_log(save_path) with open(save_path, 'rb') as fp: text = fp.read() try: text = text.decode('utf8') except: text = text.decode('gbk') self.assertIn(u'我们', text) self.assertIn(u'中国', text)
def _get_device_driver(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) return DeviceDriver(adb)
def test_get_cpu_abi(self): for arch in ['armeabi-v7a', 'x86']: ADB.run_shell_cmd = mock.Mock(return_value=arch) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_cpu_abi(), arch)
def test_get_system_version(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) ADB.is_rooted = mock.Mock(return_value=False) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_system_version(), '5.0.2')
def test_get_uid(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_uid('com.tencent.mobileqq'), 'u0_a1309')
def test_get_device_imei(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) ADB.is_rooted = mock.Mock(return_value=False) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_device_imei(), '99000567737777')
def test_get_device_model(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) ADB.is_rooted = mock.Mock(return_value=False) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_device_model(), 'Xiaomi MI 4C')
def test_get_pid(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_pid('android.process.media'), 3157)
def _create_driver(self): adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) return AndroidDriver(DeviceDriver(adb), None)
def test_get_sdk_version(self): ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd) adb_backend = LocalADBBackend('127.0.0.1', '') adb = ADB(adb_backend) self.assertEqual(adb.get_sdk_version(), 21)
def copy_android_driver(device_id_or_adb, force=False, root_path=None, enable_acc=True): '''测试前的测试桩拷贝 ''' from qt4a.androiddriver.adb import ADB from qt4a.androiddriver.util import AndroidPackage, version_cmp if isinstance(device_id_or_adb, ADB): adb = device_id_or_adb else: adb = ADB.open_device(device_id_or_adb) if not root_path: current_path = os.path.abspath(__file__) if not os.path.exists(current_path) and '.egg' in current_path: # in egg egg_path = current_path while not os.path.exists(egg_path): egg_path = os.path.dirname(egg_path) assert (egg_path.endswith('.egg')) root_path = os.path.join(tempfile.mkdtemp(), 'tools') extract_from_zipfile(egg_path, 'qt4a/androiddriver/tools', root_path) else: root_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'tools') dst_path = '/data/local/tmp/qt4a/' current_version_file = os.path.join(root_path, 'version.txt') f = open(current_version_file, 'r') current_version = int(f.read()) f.close() if not force: version_file = dst_path + 'version.txt' version = adb.run_shell_cmd('cat %s' % version_file) if version and not 'No such file or directory' in version and current_version <= int( version): # 不需要拷贝测试桩 logger.warn('忽略本次测试桩拷贝:当前版本为%s,设备中版本为%s' % (current_version, int(version))) return try: adb.chmod(dst_path[:-1], '777') except: pass rooted = adb.is_rooted() cpu_abi = adb.get_cpu_abi() print('Current CPU arch: %s' % cpu_abi) use_pie = False if adb.get_sdk_version() >= 21 and cpu_abi != 'arm64-v8a': use_pie = True file_list = [ os.path.join(cpu_abi, 'droid_inject'), os.path.join(cpu_abi, 'libdexloader.so'), os.path.join(cpu_abi, 'screenkit'), os.path.join(cpu_abi, 'libandroidhook.so'), 'inject', 'AndroidSpy.jar', 'SpyHelper.jar', 'SpyHelper.sh' ] if cpu_abi == 'arm64-v8a': file_list.append(os.path.join(cpu_abi, 'droid_inject64')) file_list.append(os.path.join(cpu_abi, 'libdexloader64.so')) file_list.append('inject64') if adb.get_sdk_version() >= 21: file_list.append(os.path.join(cpu_abi, 'libandroidhook_art.so')) if rooted and adb.is_selinux_opened(): # 此时如果还是开启状态说明关闭selinux没有生效,主要是三星手机上面 adb.run_shell_cmd('rm -r %s' % dst_path, True) # adb.run_shell_cmd('chcon u:object_r:shell_data_file:s0 %slibdexloader.so' % dst_path, True) # 恢复文件context,否则拷贝失败 # adb.run_shell_cmd('chcon u:object_r:shell_data_file:s0 %slibandroidhook.so' % dst_path, True) for file in file_list: file_path = os.path.join(root_path, file) if use_pie and not '.' in file and os.path.exists(file_path + '_pie'): file_path += '_pie' if not os.path.exists(file_path): continue save_name = os.path.split(file)[-1] if save_name.endswith('_art.so'): save_name = save_name.replace('_art', '') adb.push_file(file_path, dst_path + save_name) adb.chmod('%sdroid_inject' % dst_path, 755) adb.chmod('%sinject' % dst_path, 755) adb.chmod('%sscreenkit' % dst_path, 755) adb.run_shell_cmd('ln -s %sscreenkit %sscreenshot' % (dst_path, dst_path)) if cpu_abi == 'arm64-v8a': adb.chmod('%sdroid_inject64' % dst_path, 755) adb.chmod('%sinject64' % dst_path, 755) try: print(adb.run_shell_cmd('rm -R %scache' % dst_path, rooted)) # 删除目录 rm -rf except RuntimeError as e: logger.warn('%s' % e) # logger.info(adb.run_shell_cmd('mkdir %scache' % (dst_path), True)) #必须使用root权限,不然生成odex文件会失败 adb.mkdir('%scache' % (dst_path), 777) qt4a_helper_package = 'com.test.androidspy' apk_path = os.path.join(root_path, 'QT4AHelper.apk') if adb.get_package_path(qt4a_helper_package): # 判断版本 installed_version = adb.get_package_version(qt4a_helper_package) package = AndroidPackage(apk_path) install_version = package.version if version_cmp(install_version, installed_version) > 0: adb.install_apk(apk_path, True) else: adb.install_apk(apk_path) # adb.install_apk(os.path.join(root_path, 'QT4AMockApp.apk'), True) version_file_path = os.path.join(root_path, 'version.txt') dst_version_file_path = dst_path + os.path.split(version_file_path)[-1] adb.push_file(version_file_path, dst_version_file_path + '.tmp') # 拷贝版本文件 if rooted and adb.is_selinux_opened(): # 此时如果还是开启状态说明关闭selinux没有生效,主要是三星手机上面 # 获取sdcars context if adb.get_sdk_version() >= 23: import re sdcard_path = adb.get_sdcard_path() result = adb.run_shell_cmd('ls -Z %s' % sdcard_path) # u:object_r:media_rw_data_file:s0 u:object_r:rootfs:s0 pattern = re.compile(r'\s+(u:object_r:.+:s0)\s+') ret = pattern.search(result) if not ret: raise RuntimeError('get sdcard context failed: %s' % result) context = ret.group(1) logger.info('sdcard context is %s' % context) adb.run_shell_cmd('chcon %s %s' % (context, dst_path), True) # make app access adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %sSpyHelper.jar' % dst_path, True) adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %sSpyHelper.sh' % dst_path, True) # 不修改文件context无法加载so adb.run_shell_cmd( 'chcon u:object_r:system_file:s0 %slibdexloader.so' % dst_path, True) adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %slibandroidhook.so' % dst_path, True) adb.run_shell_cmd( 'chcon %s %sAndroidSpy.jar' % (context, dst_path), True) adb.run_shell_cmd('chcon %s %scache' % (context, dst_path), True) else: # 不修改文件context无法加载so adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %slibdexloader.so' % dst_path, True) adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %slibandroidhook.so' % dst_path, True) adb.run_shell_cmd( 'chcon u:object_r:app_data_file:s0 %scache' % dst_path, True) if rooted: if adb.get_sdk_version() < 24: # 7.0以上发现生成的dex与运行时生成的dex有差别,可能导致crash logger.info( adb.run_shell_cmd( 'sh %sSpyHelper.sh loadDex %sAndroidSpy.jar %scache' % (dst_path, dst_path, dst_path), rooted)) adb.chmod('%scache/AndroidSpy.dex' % dst_path, 666) else: if not 'usage:' in adb.run_shell_cmd('sh %sSpyHelper.sh' % dst_path): adb.mkdir('%scache/dalvik-cache' % dst_path, 777) if rooted and adb.is_selinux_opened() and adb.get_sdk_version() >= 23: # 提升权限 try: adb.list_dir('/system/bin/app_process32') except RuntimeError: adb.copy_file('/system/bin/app_process', '%sapp_process' % dst_path) else: adb.copy_file('/system/bin/app_process32', '%sapp_process' % dst_path) adb.chmod('%sapp_process' % dst_path, 755) adb.run_shell_cmd( 'chcon u:object_r:system_file:s0 %sapp_process' % dst_path, True) adb.run_shell_cmd( 'mv %s %s' % (dst_version_file_path + '.tmp', dst_version_file_path), rooted) # 同步手机时间 device_driver = DeviceDriver(adb) try: input_method = 'com.test.androidspy/.service.QT4AKeyboardService' device_driver.modify_system_setting('secure', 'enabled_input_methods', input_method) device_driver.modify_system_setting('secure', 'default_input_method', input_method) if enable_acc: device_driver.modify_system_setting( 'secure', 'enabled_accessibility_services', 'com.test.androidspy/com.test.androidspy.service.QT4AAccessibilityService' ) device_driver.modify_system_setting('secure', 'accessibility_enabled', 1) except: logger.exception('set default input method failed') try: device_driver.modify_system_setting('system', 'time_12_24', 24) device_driver.modify_system_setting('system', 'screen_off_timeout', 600 * 1000) except: logger.exception('set system time failed')
def copy_android_driver(device_id_or_adb, force=False, root_path=None, enable_acc=True): """测试前的测试桩拷贝 """ from qt4a.androiddriver.adb import ADB if isinstance(device_id_or_adb, ADB): adb = device_id_or_adb else: adb = ADB.open_device(device_id_or_adb) if not root_path: current_path = os.path.abspath(__file__) if not os.path.exists(current_path) and ".egg" in current_path: # in egg egg_path = current_path while not os.path.exists(egg_path): egg_path = os.path.dirname(egg_path) assert egg_path.endswith(".egg") root_path = os.path.join(tempfile.mkdtemp(), "tools") extract_from_zipfile(egg_path, "qt4a/androiddriver/tools", root_path) else: root_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "tools") dst_path = "/data/local/tmp/qt4a/" current_version_file = os.path.join(root_path, "version.txt") f = open(current_version_file, "r") current_version = int(f.read()) f.close() if not force: version_file = dst_path + "version.txt" version = adb.run_shell_cmd("cat %s" % version_file) if (version and not "No such file or directory" in version and current_version <= int(version)): install_qt4a_helper(adb, root_path) # 避免QT4A助手被意外删除的情况 # 不需要拷贝测试桩 logger.warn("忽略本次测试桩拷贝:当前版本为%s,设备中版本为%s" % (current_version, int(version))) return try: adb.chmod(dst_path[:-1], "777") except: pass rooted = adb.is_rooted() cpu_abi = adb.get_cpu_abi() print("Current CPU arch: %s" % cpu_abi) # use_pie = False # if adb.get_sdk_version() >= 21 and cpu_abi != "arm64-v8a": # use_pie = True file_list = [ os.path.join(cpu_abi, "droid_inject"), os.path.join(cpu_abi, "libdexloader.so"), os.path.join(cpu_abi, "screenkit"), "inject", "AndroidSpy.jar", "SpyHelper.jar", "SpyHelper.sh", ] if cpu_abi == "arm64-v8a": file_list.append(os.path.join(cpu_abi, "droid_inject64")) file_list.append(os.path.join(cpu_abi, "libdexloader64.so")) file_list.append("inject64") if adb.get_sdk_version() >= 21: file_list.append(os.path.join(cpu_abi, "libandroidhook_art.so")) if rooted and adb.is_selinux_opened(): # 此时如果还是开启状态说明关闭selinux没有生效,主要是三星手机上面 adb.run_shell_cmd("rm -r %s" % dst_path, True) # adb.run_shell_cmd('chcon u:object_r:shell_data_file:s0 %slibdexloader.so' % dst_path, True) # 恢复文件context,否则拷贝失败 # adb.run_shell_cmd('chcon u:object_r:shell_data_file:s0 %slibandroidhook.so' % dst_path, True) for file in file_list: file_path = os.path.join(root_path, file) # if use_pie and not "." in file and os.path.exists(file_path + "_pie"): # file_path += "_pie" if not os.path.exists(file_path): continue save_name = os.path.split(file)[-1] if save_name.endswith("_art.so"): save_name = save_name.replace("_art", "") adb.push_file(file_path, dst_path + save_name) adb.chmod("%sdroid_inject" % dst_path, 755) adb.chmod("%sinject" % dst_path, 755) adb.chmod("%sscreenkit" % dst_path, 755) adb.run_shell_cmd("ln -s %sscreenkit %sscreenshot" % (dst_path, dst_path)) if cpu_abi == "arm64-v8a": adb.chmod("%sdroid_inject64" % dst_path, 755) adb.chmod("%sinject64" % dst_path, 755) try: print(adb.run_shell_cmd("rm -R %scache" % dst_path, rooted)) # 删除目录 rm -rf except RuntimeError as e: logger.warn("%s" % e) # logger.info(adb.run_shell_cmd('mkdir %scache' % (dst_path), True)) #必须使用root权限,不然生成odex文件会失败 adb.mkdir("%scache" % (dst_path), 777) install_qt4a_helper(adb, root_path) version_file_path = os.path.join(root_path, "version.txt") dst_version_file_path = dst_path + os.path.split(version_file_path)[-1] adb.push_file(version_file_path, dst_version_file_path + ".tmp") # 拷贝版本文件 if rooted and adb.is_selinux_opened(): # 此时如果还是开启状态说明关闭selinux没有生效,主要是三星手机上面 # 获取sdcars context if adb.get_sdk_version() >= 23: sdcard_path = adb.get_sdcard_path() result = adb.run_shell_cmd("ls -Z %s" % sdcard_path) # u:object_r:media_rw_data_file:s0 u:object_r:rootfs:s0 pattern = re.compile(r"\s+(u:object_r:.+:s0)\s+") ret = pattern.search(result) if not ret: raise RuntimeError("get sdcard context failed: %s" % result) context = ret.group(1) logger.info("sdcard context is %s" % context) adb.run_shell_cmd("chcon %s %s" % (context, dst_path), True) # make app access adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %sSpyHelper.jar" % dst_path, True) adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %sSpyHelper.sh" % dst_path, True) # 不修改文件context无法加载so adb.run_shell_cmd( "chcon u:object_r:system_file:s0 %slibdexloader.so" % dst_path, True) adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %slibandroidhook.so" % dst_path, True) adb.run_shell_cmd( "chcon %s %sAndroidSpy.jar" % (context, dst_path), True) adb.run_shell_cmd("chcon %s %scache" % (context, dst_path), True) else: # 不修改文件context无法加载so adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %slibdexloader.so" % dst_path, True) adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %slibandroidhook.so" % dst_path, True) adb.run_shell_cmd( "chcon u:object_r:app_data_file:s0 %scache" % dst_path, True) if rooted: if adb.get_sdk_version() < 24: # 7.0以上发现生成的dex与运行时生成的dex有差别,可能导致crash logger.info( adb.run_shell_cmd( "sh %sSpyHelper.sh loadDex %sAndroidSpy.jar %scache" % (dst_path, dst_path, dst_path), rooted, )) adb.chmod("%scache/AndroidSpy.dex" % dst_path, 666) else: if not "usage:" in adb.run_shell_cmd("sh %sSpyHelper.sh" % dst_path): adb.mkdir("%scache/dalvik-cache" % dst_path, 777) if rooted and adb.is_selinux_opened() and adb.get_sdk_version() >= 23: # 提升权限 try: adb.list_dir("/system/bin/app_process32") except RuntimeError: adb.copy_file("/system/bin/app_process", "%sapp_process" % dst_path) else: adb.copy_file("/system/bin/app_process32", "%sapp_process" % dst_path) adb.chmod("%sapp_process" % dst_path, 755) adb.run_shell_cmd( "chcon u:object_r:system_file:s0 %sapp_process" % dst_path, True) adb.run_shell_cmd( "mv %s %s" % (dst_version_file_path + ".tmp", dst_version_file_path), rooted) # 同步手机时间 device_driver = DeviceDriver(adb) try: input_method = "com.test.androidspy/.service.QT4AKeyboardService" device_driver.modify_system_setting("secure", "enabled_input_methods", input_method) device_driver.modify_system_setting("secure", "default_input_method", input_method) if enable_acc: device_driver.modify_system_setting( "secure", "enabled_accessibility_services", "com.test.androidspy/com.test.androidspy.service.QT4AAccessibilityService", ) device_driver.modify_system_setting("secure", "accessibility_enabled", 1) except: logger.exception("set default input method failed") try: device_driver.modify_system_setting("system", "time_12_24", 24) device_driver.modify_system_setting("system", "screen_off_timeout", 600 * 1000) except: logger.exception("set system time failed")