def get_apk_activity(path): tmp = apkutils2.APK(path).get_manifest() data = tmp['application']['activity'] activity_list = [] for activity in data: activity_list.append(activity['@android:name']) return activity_list
def parse_apk(path: str): """ Parse APK Returns: dict contains "package" and "main_activity" """ import apkutils2 apk = apkutils2.APK(path) package_name = apk.manifest.package_name main_activity = apk.manifest.main_activity return { "package": package_name, "main_activity": main_activity, }
def _install_apk(self, path: str): assert path, "Invalid %s" % path try: m = apkutils.APK(path).manifest info = self._device.package_info(m.package_name) if info and m.version_code == info[ 'version_code'] and m.version_name == info['version_name']: logger.debug("%s already installed %s", self, path) else: print(info, ":", m.version_code, m.version_name) logger.debug("%s install %s", self, path) self._device.install(path) except Exception as e: traceback.print_exc() logger.warning("%s Install apk %s error %s", self, path, e)
def app_install_local(serial: str, apk_path: str, launch: bool = False) -> str: """ install apk to device Returns: package name Raises: AdbInstallError, FileNotFoundError """ # 解析apk文件 device = adbclient.device(serial) try: apk = apkutils.APK(apk_path) except apkutils.apkfile.BadZipFile: raise InstallError("ApkParse", "Bad zip file") # 提前将重名包卸载 package_name = apk.manifest.package_name pkginfo = device.package_info(package_name) if pkginfo: logger.debug("uninstall: %s", package_name) device.uninstall(package_name) # 解锁手机,防止锁屏 # ud = u2.connect_usb(serial) # ud.open_identify() try: # 推送到手机 dst = "/data/local/tmp/tmp-%d.apk" % int(time.time() * 1000) logger.debug("push %s %s", apk_path, dst) device.sync.push(apk_path, dst) logger.debug("install-remote %s", dst) # 调用pm install安装 device.install_remote(dst) except adbutils.errors.AdbInstallError as e: raise InstallError("install", e.output) # finally: # 停止uiautomator2服务 # logger.debug("uiautomator2 stop") # ud.session().press("home") # ud.service("uiautomator").stop() # 启动应用 if launch: logger.debug("launch %s", package_name) device.app_start(package_name) return package_name
def install(self, apk_path: str, force: bool = False): """ sdk = self.getprop('ro.build.version.sdk') sdk > 23 support -g Args: force (bool): uninstall package before install Raises: AdbInstallError """ dst = "/data/local/tmp/tmp-{}.apk".format(int(time.time() * 1000)) self.sync.push(apk_path, dst) if force: apk = apkutils2.APK(apk_path) package_name = apk.manifest.package_name self.uninstall(package_name) self.install_remote(dst, clean=True)
def get_apk_info(path): tmp = apkutils2.APK(path).get_manifest() info = { 'versionCode': str(tmp.get('@android:versionCode')), 'versionName': str(tmp.get('@android:versionName')), 'package': str(tmp.get('@package')) } # # 获取appkey和channel # data =tmp['application']['meta-data'] # for key in data: # if key['@android:name'] == 'UMENG_CHANNEL': # info['channel'] = str(key['@android:value']) # continue # elif key['@android:name'] == 'XiaoYing_AppKey': # info['appkey'] = str(key['@android:value']) # continue # if not 'channel' in info: # info['channel'] = '' # if not 'appkey' in info: # info['appkey'] = '' return info
def install(self, path_or_url: str, nolaunch: bool = False, uninstall: bool = False, silent: bool = False, callback: typing.Callable[[str], None] = None): """ Install APK to device Args: path_or_url: local path or http url nolaunch: do not launch app after install uninstall: uninstall app before install silent: disable log message print callback: only two event now: <"BEFORE_INSTALL" | "FINALLY"> Raises: AdbInstallError, BrokenPipeError """ if re.match(r"^https?://", path_or_url): resp = requests.get(path_or_url, stream=True) resp.raise_for_status() length = int(resp.headers.get("Content-Length", 0)) r = ReadProgress(resp.raw, length) print("tmpfile path:", r.filepath()) else: length = os.stat(path_or_url).st_size fd = open(path_or_url, "rb") r = ReadProgress(fd, length, source_path=path_or_url) def _dprint(*args): if not silent: print(*args) dst = "/data/local/tmp/tmp-%d.apk" % (int(time.time() * 1000)) _dprint("push to %s" % dst) start = time.time() self.sync.push(r, dst) # parse apk package-name apk = apkutils2.APK(r.filepath()) package_name = apk.manifest.package_name main_activity = apk.manifest.main_activity if main_activity and main_activity.find(".") == -1: main_activity = "." + main_activity version_name = apk.manifest.version_name _dprint("packageName:", package_name) _dprint("mainActivity:", main_activity) _dprint("apkVersion: {}".format(version_name)) _dprint("Success pushed, time used %d seconds" % (time.time() - start)) new_dst = "/data/local/tmp/{}-{}.apk".format(package_name, version_name) self.shell(["mv", dst, new_dst]) dst = new_dst info = self.sync.stat(dst) print("verify pushed apk, md5: %s, size: %s" % (r._hash, humanize(info.size))) assert info.size == r.copied if uninstall: _dprint("Uninstall app first") self.uninstall(package_name) _dprint("install to android system ...") try: start = time.time() if callback: callback("BEFORE_INSTALL") self.install_remote(dst, clean=True) _dprint("Success installed, time used %d seconds" % (time.time() - start)) if not nolaunch: _dprint("Launch app: %s/%s" % (package_name, main_activity)) self.app_start(package_name, main_activity) except AdbInstallError as e: if e.reason in [ "INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE", "INSTALL_FAILED_UPDATE_INCOMPATIBLE", "INSTALL_FAILED_VERSION_DOWNGRADE" ]: _dprint("uninstall %s because %s" % (package_name, e.reason)) self.uninstall(package_name) self.install_remote(dst, clean=True) _dprint("Success installed, time used %d seconds" % (time.time() - start)) if not nolaunch: _dprint("Launch app: %s/%s" % (package_name, main_activity)) self.app_start(package_name, main_activity) # self.shell([ # 'am', 'start', '-n', package_name + "/" + main_activity # ]) elif e.reason == "INSTALL_FAILED_CANCELLED_BY_USER": _dprint("Catch error %s, reinstall" % e.reason) self.install_remote(dst, clean=True) _dprint("Success installed, time used %d seconds" % (time.time() - start)) else: # print to console print( "Failure " + e.reason + "\n" + "Remote apk is not removed. Manually install command:\n\t" + "adb shell pm install -r -t " + dst) raise finally: if callback: callback("FINALLY")
def main(): parser = argparse.ArgumentParser() # formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-s", "--serial", help="device serial number") parser.add_argument("-V", "--server-version", action="store_true", help="show adb server version") parser.add_argument("-l", "--list", action="store_true", help="list devices") parser.add_argument("-i", "--install", help="install from local apk or url") parser.add_argument( "--install-confirm", action="store_true", help="auto confirm when install (based on uiautomator2)") parser.add_argument("-u", "--uninstall", help="uninstall apk") parser.add_argument("-L", "--launch", action="store_true", help="launch after install") parser.add_argument("--qrcode", help="show qrcode of the specified file") parser.add_argument("--clear", action="store_true", help="clear all data when uninstall") parser.add_argument("--list-packages", action="store_true", help="list packages installed") parser.add_argument("-p", "--package", help="show package info in json format") parser.add_argument("--grep", help="filter matched package names") parser.add_argument("--connect", type=str, help="connect remote device") parser.add_argument("--shell", action="store_true", help="run shell command") parser.add_argument("--minicap", action="store_true", help="install minicap and minitouch to device") parser.add_argument("--screenshot", type=str, help="take screenshot") parser.add_argument("-b", "--browser", help="open browser in device") parser.add_argument( "--push", help= "push local to remote, arg is colon seperated, eg some.txt:/sdcard/s.txt" ) parser.add_argument( "--pull", help="push local to remote, arg is colon seperated, eg /sdcard/some.txt" ) parser.add_argument("--dump-info", action="store_true", help="dump info for developer") parser.add_argument("--track", action="store_true", help="trace device status") parser.add_argument("args", nargs="*", help="arguments") args = parser.parse_args() if args.connect: adbclient.connect(args.connect) return if args.server_version: print("ADB Server version: {}".format(adbclient.server_version())) return if args.list: rows = [] for d in adbclient.device_list(): rows.append([d.serial, d.shell("getprop ro.product.model")]) lens = [] for col in zip(*rows): lens.append(max([len(v) for v in col])) format = " ".join(["{:<" + str(l) + "}" for l in lens]) for row in rows: print(format.format(*row)) return if args.qrcode: from http.server import ThreadingHTTPServer from http.server import SimpleHTTPRequestHandler filename = args.qrcode port = 8000 url = "http://%s:%d/%s" % (current_ip(), port, filename) print("File URL:", url) try: import qrcode qr = qrcode.QRCode(border=2) qr.add_data(url) qr.print_ascii(tty=True) except ImportError: print( "In order to show QRCode, you need install with: pip3 install qrcode" ) httpd = ThreadingHTTPServer(('', port), SimpleHTTPRequestHandler) httpd.serve_forever() return if args.dump_info: print("==== ADB Info ====") print("Path:", adbutils.adb_path()) print("Server version:", adbclient.server_version()) print("") print(">> List of devices attached") for d in adbclient.device_list(): print("-", d.serial, d.prop.name, d.prop.model) return if args.track: for event in adbclient.track_devices(): asctime = datetime.datetime.now().strftime("%H:%M:%S.%f") print("{} {} -> {}".format(asctime[:-3], event.serial, event.status)) return ## Device operation d = adbclient.device(args.serial) if args.shell: output = d.shell(args.args) print(output) return if args.install: if re.match(r"^https?://", args.install): resp = requests.get(args.install, stream=True) resp.raise_for_status() length = int(resp.headers.get("Content-Length", 0)) r = ReadProgress(resp.raw, length) print("tmpfile path:", r.filepath()) else: length = os.stat(args.install).st_size fd = open(args.install, "rb") r = ReadProgress(fd, length, source_path=args.install) dst = "/data/local/tmp/tmp-%d.apk" % (int(time.time() * 1000)) print("push to %s" % dst) start = time.time() d.sync.push(r, dst) # parse apk package-name apk = apkutils2.APK(r.filepath()) package_name = apk.manifest.package_name main_activity = apk.manifest.main_activity version_name = apk.manifest.version_name print("packageName:", package_name) print("mainActivity:", main_activity) print("apkVersion: {}".format(version_name)) print("success pushed, time used %d seconds" % (time.time() - start)) new_dst = "/data/local/tmp/{}-{}.apk".format(package_name, version_name) d.shell(["mv", dst, new_dst]) dst = new_dst info = d.sync.stat(dst) print("verify pushed apk, md5: %s, size: %s" % (r._hash, humanize(info.size))) assert info.size == r.copied print("install to android system ...") if args.install_confirm: # Beta import uiautomator2 as u2 ud = u2.connect(args.serial) ud.press("home") ud.watcher.when("继续安装").click() ud.watcher.when("允许").click() ud.watcher.when("安装").click() ud.watcher.start(2.0) try: start = time.time() d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) if args.launch: print("Launch app: %s/%s" % (package_name, main_activity)) d.shell( ['am', 'start', '-n', package_name + "/" + main_activity]) except AdbInstallError as e: if e.reason in [ "INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE", "INSTALL_FAILED_UPDATE_INCOMPATIBLE", "INSTALL_FAILED_VERSION_DOWNGRADE" ]: print("uninstall %s because %s" % (package_name, e.reason)) d.uninstall(package_name) d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) elif e.reason == "INSTALL_FAILED_CANCELLED_BY_USER": print("Catch error %s, reinstall" % e.reason) d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) else: sys.exit( "Failure " + e.reason + "\n" + "Remote apk is not removed. Manually install command:\n\t" + "adb shell pm install -r -t " + dst) elif args.uninstall: d.shell(["pm", "uninstall", args.uninstall]) elif args.list_packages: patten = re.compile(args.grep or ".*") for p in d.list_packages(): if patten.search(p): print(p) elif args.screenshot: if args.minicap: def adb_shell(cmd: list): print("Run:", " ".join(["adb", "shell"] + cmd)) return d.shell(cmd).strip() json_output = adb_shell([ "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-i", "2&>/dev/null" ]) if not json_output.startswith("{"): raise RuntimeError("Invalid json format", json_output) data = json.loads(json_output) w, h, r = data["width"], data["height"], data["rotation"] d.shell([ "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-P", "{0}x{1}@{0}x{1}/{2}".format(w, h, r), "-s", ">/sdcard/minicap.jpg" ]) d.sync.pull("/sdcard/minicap.jpg", args.screenshot) else: remote_tmp_path = "/data/local/tmp/screenshot.png" d.shell(["rm", remote_tmp_path]) d.shell(["screencap", "-p", remote_tmp_path]) d.sync.pull(remote_tmp_path, args.screenshot) elif args.minicap: # without args.screenshot _setup_minicap(d) elif args.push: local, remote = args.push.split(":", 1) length = os.stat(local).st_size with open(local, "rb") as fd: r = ReadProgress(fd, length) d.sync.push(r, remote, filesize=length) elif args.pull: remote_path = args.pull target_path = os.path.basename(remote_path) finfo = d.sync.stat(args.pull) if finfo.mode == 0 and finfo.size == 0: sys.exit(f"remote file '{remote_path}' does not exist") bytes_so_far = 0 for chunk in d.sync.iter_content(remote_path): bytes_so_far += len(chunk) percent = bytes_so_far / finfo.size * 100 if finfo.size != 0 else 100.0 print( f"\rDownload to {target_path} ... [{bytes_so_far} / {finfo.size}] %.1f %%" % percent, end="", flush=True) print(f"{remote_path} pulled to {target_path}") elif args.browser: d.open_browser(args.browser) elif args.package: info = d.package_info(args.package) print(json.dumps(info, indent=4))
def app_install_local(serial: str, apk_path: str, launch: bool = False) -> str: """ install apk to device Returns: package name Raises: AdbInstallError, FileNotFoundError """ # 解析apk文件 device = adbclient.device(serial) try: apk = apkutils.APK(apk_path) except apkutils.apkfile.BadZipFile: raise InstallError("ApkParse", "Bad zip file") # 提前将重名包卸载 package_name = apk.manifest.package_name pkginfo = device.package_info(package_name) if pkginfo: logger.debug("uninstall: %s", package_name) device.uninstall(package_name) # 解锁手机,防止锁屏 # ud = u2.connect_usb(serial) # ud.open_identify() try: # 推送到手机 dst = "/data/local/tmp/tmp-%d.apk" % int(time.time() * 1000) logger.debug("push %s %s", apk_path, dst) device.sync.push(apk_path, dst) logger.debug("install-remote %s", dst) if platform.system() == 'Linux': cmd = "sudo adb shell getprop ro.product.brand" else: cmd = "adb shell getprop ro.product.brand" brand = os.popen(cmd).read() logger.debug("app_install_local: device brand is ===>>> %s", brand) if 'vivo' in brand.lower() or 'oppo' in brand.lower(): logger.debug("f**k %s!!!", brand.lower()) t1 = threading.Thread(target=fuck_vivo_oppo) t1.start() # 调用pm install安装 device.install_remote(dst, flags=["-r", "-t", "-g"]) except adbutils.errors.AdbInstallError as e: raise InstallError("install", e.output) # finally: # 停止uiautomator2服务 # logger.debug("uiautomator2 stop") # ud.session().press("home") # ud.service("uiautomator").stop() # 启动应用 if launch: logger.debug("launch %s", package_name) device.app_start(package_name) return package_name