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("-u", "--uninstall", help="uninstall apk") 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("--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("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.devices(): 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 d = adbclient.device(args.serial) if args.shell: output = d.shell(args.args) print(output) return if args.install: dst = "/data/local/tmp/tmp-%d.apk" % (int(time.time() * 1000)) print("push to %s" % dst) 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) else: length = os.stat(args.install).st_size fd = open(args.install, "rb") r = ReadProgress(fd, length) start = time.time() d.sync.push(r, dst) print("Success pushed, time used %d seconds" % (time.time() - start)) new_dst = "/data/local/tmp/tmp-%s.apk" % r._hash[:8] 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 ...") try: start = time.time() d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) except AdbInstallError as e: 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.minicap: def cache_download(url, dst): if os.path.exists(dst): print("Use cached", dst) return print("Download {} from {}".format(dst, url)) resp = requests.get(url, stream=True) resp.raise_for_status() length = int(resp.headers.get("Content-Length", 0)) r = ReadProgress(resp.raw, length) with open(dst + ".cached", "wb") as f: shutil.copyfileobj(r, f) shutil.move(dst + ".cached", dst) def push_zipfile(path: str, dest: str, mode=0o755, zipfile_path: str = "vendor/stf-binaries-master.zip"): """ push minicap and minitouch from zip """ with zipfile.ZipFile(zipfile_path) as z: if path not in z.namelist(): print("WARNING: stf stuff %s not found", path) return with z.open(path) as f: d.sync.push(f, dest, mode) zipfile_path = "stf-binaries.zip" cache_download( "https://github.com/openatx/stf-binaries/archive/0.2.zip", zipfile_path) zip_folder = "stf-binaries-0.2" sdk = d.getprop("ro.build.version.sdk") # eg 26 abi = d.getprop('ro.product.cpu.abi') # eg arm64-v8a abis = (d.getprop('ro.product.cpu.abilist').strip() or abi).split(",") # return print("sdk: %s, abi: %s, support-abis: %s" % (sdk, abi, ','.join(abis))) print("Push minicap+minicap.so to device") prefix = zip_folder + "/node_modules/minicap-prebuilt/prebuilt/" push_zipfile(prefix + abi + "/lib/android-" + sdk + "/minicap.so", "/data/local/tmp/minicap.so", 0o644, zipfile_path) push_zipfile(prefix + abi + "/bin/minicap", "/data/local/tmp/minicap", 0o0755, zipfile_path) print("Push minitouch to device") prefix = zip_folder + "/node_modules/minitouch-prebuilt/prebuilt/" push_zipfile(prefix + abi + "/bin/minitouch", "/data/local/tmp/minitouch", 0o0755, zipfile_path) # check if minicap installed output = d.shell([ "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-i" ]) print(output) print( "If you see JSON output, it means minicap installed successfully") if args.screenshot: 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)
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("--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("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.devices(): 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 d = adbclient.device(args.serial) if args.shell: output = d.shell(args.args) print(output) return if args.install: dst = "/data/local/tmp/tmp-%d.apk" % (int(time.time() * 1000)) print("push to %s" % dst) 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) else: length = os.stat(args.install).st_size fd = open(args.install, "rb") r = ReadProgress(fd, length) start = time.time() d.sync.push(r, dst) print("Success pushed, time used %d seconds" % (time.time() - start)) new_dst = "/data/local/tmp/tmp-%s.apk" % r._hash[:8] 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.xpath.when("允许").click() ud.xpath.when("安装").click() ud.xpath.watch_background(2.0) for i in range(3): try: start = time.time() d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) break except AdbInstallError as e: if i < 2 and e.reason == "INSTALL_FAILED_CANCELLED_BY_USER": print("Catch error %s, reinstall" % e.reason) continue 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: json_output = d.shell([ "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-i", "2&>/dev/null" ]).strip() 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.browser: d.open_browser(args.browser) elif args.package: info = d.package_info(args.package) print(json.dumps(info, indent=4))
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("--current", action="store_true", help="show current package info") 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 SimpleHTTPRequestHandler, ThreadingHTTPServer 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: def _callback(event_name: str, ud): name = "_INSTALL_" if event_name == "BEFORE_INSTALL": print("== Enable popup window watcher") ud.press("home") ud.watcher(name).when("允许").click() ud.watcher(name).when("继续安装").click() ud.watcher(name).when("安装").click() ud.watcher.start() elif event_name == "FINALLY": print("== Stop popup window watcher") ud.watcher.remove(name) ud.watcher.stop() if args.install_confirm: import uiautomator2 as u2 ud = u2.connect(args.serial) _callback = functools.partial(_callback, ud=ud) else: _callback = None d.install(args.install, uninstall=True, callback=_callback) elif args.uninstall: d.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.current: package_name = d.current_app()['package'] info = d.package_info(package_name) print(json.dumps(info, indent=4, default=str)) elif args.package: info = d.package_info(args.package) print(json.dumps(info, indent=4, default=str))
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 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("--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("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.devices(): 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 d = adbclient.device(args.serial) if args.shell: output = d.shell(args.args) print(output) return if args.install: dst = "/data/local/tmp/tmp-%d.apk" % (int(time.time() * 1000)) print("push to %s" % dst) 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) else: length = os.stat(args.install).st_size fd = open(args.install, "rb") r = ReadProgress(fd, length) start = time.time() d.sync.push(r, dst) print("Success pushed, time used %d seconds" % (time.time() - start)) new_dst = "/data/local/tmp/tmp-%s.apk" % r._hash[:8] 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.xpath.when("允许").click() ud.xpath.when("安装").click() ud.xpath.watch_background(2.0) for i in range(3): try: start = time.time() d.install_remote(dst, clean=True) print("Success installed, time used %d seconds" % (time.time() - start)) break except AdbInstallError as e: if i < 2 and e.reason == "INSTALL_FAILED_CANCELLED_BY_USER": print("Catch error %s, reinstall" % e.reason) continue 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.minicap: def cache_download(url, dst): if os.path.exists(dst): print("Use cached", dst) return print("Download {} from {}".format(dst, url)) resp = requests.get(url, stream=True) resp.raise_for_status() length = int(resp.headers.get("Content-Length", 0)) r = ReadProgress(resp.raw, length) with open(dst + ".cached", "wb") as f: shutil.copyfileobj(r, f) shutil.move(dst + ".cached", dst) def push_zipfile(path: str, dest: str, mode=0o755, zipfile_path: str = "vendor/stf-binaries-master.zip"): """ push minicap and minitouch from zip """ with zipfile.ZipFile(zipfile_path) as z: if path not in z.namelist(): print("WARNING: stf stuff %s not found", path) return with z.open(path) as f: d.sync.push(f, dest, mode) zipfile_path = "stf-binaries.zip" cache_download( "https://github.com/openatx/stf-binaries/archive/0.2.zip", zipfile_path) zip_folder = "stf-binaries-0.2" sdk = d.getprop("ro.build.version.sdk") # eg 26 abi = d.getprop('ro.product.cpu.abi') # eg arm64-v8a abis = (d.getprop('ro.product.cpu.abilist').strip() or abi).split(",") # return print("sdk: %s, abi: %s, support-abis: %s" % (sdk, abi, ','.join(abis))) print("Push minicap+minicap.so to device") prefix = zip_folder + "/node_modules/minicap-prebuilt/prebuilt/" push_zipfile(prefix + abi + "/lib/android-" + sdk + "/minicap.so", "/data/local/tmp/minicap.so", 0o644, zipfile_path) push_zipfile(prefix + abi + "/bin/minicap", "/data/local/tmp/minicap", 0o0755, zipfile_path) print("Push minitouch to device") prefix = zip_folder + "/node_modules/minitouch-prebuilt/prebuilt/" push_zipfile(prefix + abi + "/bin/minitouch", "/data/local/tmp/minitouch", 0o0755, zipfile_path) # check if minicap installed output = d.shell([ "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-i" ]) print(output) print( "If you see JSON output, it means minicap installed successfully") elif args.screenshot: 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.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.browser: d.open_browser(args.browser) elif args.package: info = d.package_info(args.package) print(json.dumps(info, indent=4))