forked from xinzhengzhang/bis
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Added] a lite wrapped version of pymobiledevice3
- Loading branch information
1 parent
1b4a925
commit 1bb2ac3
Showing
11 changed files
with
1,677 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
common --enable_bzlmod |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
""" | ||
This python program is a very simplified version of pymobiledevice3(https://github.com/doronz88/pymobiledevice3) | ||
""" | ||
|
||
load("@rules_python//python:defs.bzl", "py_binary") | ||
load("@rules_python//python:pip.bzl", "compile_pip_requirements") | ||
load("@my_deps//:requirements.bzl", "requirement") | ||
|
||
compile_pip_requirements( | ||
name = "requirements", | ||
extra_args = [ | ||
"--allow-unsafe", | ||
"--resolver=backtracking", | ||
], | ||
requirements_in = "requirements.in", | ||
requirements_txt = "requirements_lock.txt", | ||
) | ||
|
||
py_binary( | ||
name = "pymobiledevicelite", | ||
srcs = ["pymobiledevicelite.py"], | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
requirement("pymobiledevice3"),WatchLaterWatchLater | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
"""pymobiledeveice3 binary""" | ||
module( | ||
name = "pymobiledevice3", | ||
version = "0.0.1", | ||
) | ||
|
||
bazel_dep(name = "rules_python", version = "0.25.0") | ||
|
||
python = use_extension("@rules_python//python/extensions:python.bzl", "python") | ||
|
||
python.toolchain( | ||
configure_coverage_tool = True, | ||
python_version = "3.10", | ||
is_default = True, | ||
) | ||
|
||
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") | ||
pip.parse( | ||
hub_name = "my_deps", | ||
python_version = "3.10", | ||
requirements_lock = "//:requirements_lock.txt", | ||
) | ||
use_repo(pip, "my_deps") |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
from pymobiledevice3.exceptions import AppInstallError | ||
from pymobiledevice3.lockdown import LockdownClient | ||
from pymobiledevice3.services.afc import AfcService | ||
from pymobiledevice3.services.lockdown_service import LockdownService | ||
from typing import Callable, List, Mapping | ||
import click, os, json, posixpath | ||
|
||
GET_APPS_ADDITIONAL_INFO = {'ReturnAttributes': ['CFBundleIdentifier', 'StaticDiskUsage', 'DynamicDiskUsage']} | ||
|
||
class SimplifiedInstallationProxyService(LockdownService): | ||
SERVICE_NAME = 'com.apple.mobile.installation_proxy' | ||
RSD_SERVICE_NAME = 'com.apple.mobile.installation_proxy.shim.remote' | ||
|
||
def __init__(self, lockdown: LockdownClient): | ||
if isinstance(lockdown, LockdownClient): | ||
super().__init__(lockdown, self.SERVICE_NAME) | ||
else: | ||
super().__init__(lockdown, self.RSD_SERVICE_NAME) | ||
|
||
def _watch_completion(self, handler: Callable = None, *args) -> None: | ||
while True: | ||
response = self.service.recv_plist() | ||
if not response: | ||
break | ||
error = response.get('Error') | ||
if error: | ||
raise AppInstallError(f'{error}: {response.get("ErrorDescription")}') | ||
completion = response.get('PercentComplete') | ||
if completion: | ||
if handler: | ||
self.logger.debug('calling handler') | ||
handler(completion, *args) | ||
self.logger.info(f'{response.get("PercentComplete")}% Complete') | ||
if response.get('Status') == 'Complete': | ||
return | ||
raise AppInstallError() | ||
|
||
def upgrade(self, ipa_path: str, options: Mapping = None, handler: Callable = None, *args) -> None: | ||
""" upgrade given ipa from device path """ | ||
self.install_from_local(ipa_path, 'Upgrade', options, handler, args) | ||
|
||
def install_from_local(self, ipa_path: str, cmd='Install', options: Mapping = None, handler: Callable = None, | ||
*args) -> None: | ||
""" upload given ipa onto device and install it """ | ||
if options is None: | ||
options = {"PackageType": "Developer"} | ||
remote_path = posixpath.join('/', os.path.basename(ipa_path)) | ||
with AfcService(self.lockdown) as afc: | ||
afc.set_file_contents(remote_path, open(ipa_path, 'rb').read()) | ||
cmd = {'Command': cmd, | ||
'ClientOptions': options, | ||
'PackagePath': remote_path} | ||
self.service.send_plist(cmd) | ||
while True: | ||
response = self.service.recv_plist() | ||
if not response: | ||
break | ||
click.echo(json.dumps(response)) | ||
error = response.get('Error') | ||
if error: | ||
raise AppInstallError(f'{error}: {response.get("ErrorDescription")}') | ||
if response.get('Status') == 'Complete': | ||
return | ||
raise AppInstallError() | ||
|
||
def lookup(self, options: Mapping = None) -> Mapping: | ||
""" search installation database """ | ||
if options is None: | ||
options = {} | ||
cmd = {'Command': 'Lookup', 'ClientOptions': options} | ||
return self.service.send_recv_plist(cmd).get('LookupResult') | ||
|
||
def get_apps(self, app_types: List[str] = None) -> Mapping[str, Mapping]: | ||
""" get applications according to given criteria """ | ||
result = self.lookup() | ||
# query for additional info | ||
additional_info = self.lookup(GET_APPS_ADDITIONAL_INFO) | ||
for bundle_identifier, app in additional_info.items(): | ||
result[bundle_identifier].update(app) | ||
# filter results | ||
filtered_result = {} | ||
for bundle_identifier, app in result.items(): | ||
if (app_types is None) or (app['ApplicationType'] in app_types): | ||
filtered_result[bundle_identifier] = app | ||
return filtered_result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# -*- mode: python ; coding: utf-8 -*- | ||
|
||
|
||
a = Analysis( | ||
['main.py'], | ||
pathex=[], | ||
binaries=[], | ||
datas=[], | ||
hiddenimports=[], | ||
hookspath=[], | ||
hooksconfig={}, | ||
runtime_hooks=[], | ||
excludes=[], | ||
noarchive=False, | ||
) | ||
pyz = PYZ(a.pure) | ||
|
||
exe = EXE( | ||
pyz, | ||
a.scripts, | ||
[], | ||
exclude_binaries=True, | ||
name='main', | ||
debug=False, | ||
bootloader_ignore_signals=False, | ||
strip=False, | ||
upx=True, | ||
console=True, | ||
disable_windowed_traceback=False, | ||
argv_emulation=False, | ||
target_arch=None, | ||
codesign_identity=None, | ||
entitlements_file=None, | ||
) | ||
coll = COLLECT( | ||
exe, | ||
a.binaries, | ||
a.datas, | ||
strip=False, | ||
upx=True, | ||
upx_exclude=[], | ||
name='main', | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import asyncio, click, json | ||
from packaging.version import Version | ||
from pymobiledevice3.exceptions import NoDeviceConnectedError | ||
from pymobiledevice3.cli.cli_common import Command | ||
from pymobiledevice3.cli.remote import get_device_list | ||
from pymobiledevice3.lockdown import create_using_usbmux, LockdownClient | ||
from pymobiledevice3.tcp_forwarder import LockdownTcpForwarder | ||
from pymobiledevice3.usbmux import list_devices | ||
from installationProxyService import SimplifiedInstallationProxyService | ||
from typing import Optional | ||
from untils import tunnel_task | ||
|
||
@click.group() | ||
def cli(): | ||
pass | ||
|
||
@click.command() | ||
def list_device(): | ||
""" list connected devices """ | ||
connected_devices = [] | ||
for device in list_devices(): | ||
udid = device.serial | ||
|
||
lockdown = create_using_usbmux(udid, autopair=False, connection_type=device.connection_type) | ||
connected_devices.append(lockdown.short_info) | ||
json_str = json.dumps(connected_devices) | ||
click.echo(json_str) | ||
|
||
@click.command(cls=Command) | ||
@click.argument('app_path', type=click.Path(exists=True)) | ||
def install_app(service_provider: LockdownClient, app_path: str): | ||
""" install application from the specific path """ | ||
proxyService = SimplifiedInstallationProxyService(lockdown=service_provider) | ||
proxyService.install_from_local(app_path) | ||
|
||
@click.command(cls=Command) | ||
@click.argument('local_port', type=click.INT, required=False) | ||
def debug_server(service_provider: LockdownClient, local_port: Optional[int] = None): | ||
""" | ||
if local_port is provided, start a debugserver at remote listening on a given port locally. | ||
if local_port is not provided and iOS version >= 17.0 then just print the connect string | ||
Please note the connection must be done soon afterwards using your own lldb client. | ||
This can be done using the following commands within lldb shell: | ||
(lldb) platform select remote-ios | ||
(lldb) platform connect connect://localhost:<local_port> | ||
""" | ||
|
||
if Version(service_provider.product_version) < Version('17.0'): | ||
service_name = 'com.apple.debugserver.DVTSecureSocketProxy' | ||
else: | ||
service_name = 'com.apple.internal.dt.remote.debugproxy' | ||
|
||
if local_port is not None: | ||
click.echo(json.dumps({"host": '127.0.0.1', "port": local_port})) | ||
LockdownTcpForwarder(service_provider, local_port, service_name).start() | ||
elif Version(service_provider.product_version) >= Version('17.0'): | ||
debugserver_port = service_provider.get_service_port(service_name) | ||
click.echo(json.dumps({"host": service_provider.service.address[0], "port": debugserver_port})) | ||
else: | ||
click.BadOptionUsage('--local_port', 'local_port is required for iOS < 17.0') | ||
|
||
@click.command(name='start-quic-tunnel') | ||
@click.option('--udid', help='UDID for a specific device to look for') | ||
def cli_start_quic_tunnel(udid: str): | ||
""" start quic tunnel """ | ||
devices = get_device_list() | ||
rsd = [device for device in devices if device.udid == udid] | ||
if len(rsd) > 0: | ||
rsd = rsd[0] | ||
else: | ||
raise NoDeviceConnectedError() | ||
|
||
if udid is not None and rsd.udid != udid: | ||
raise NoDeviceConnectedError() | ||
|
||
asyncio.run(tunnel_task(rsd), debug=True) | ||
|
||
@click.command(cls=Command) | ||
def installed_app_path(service_provider: LockdownClient): | ||
""" get installed app path """ | ||
result = SimplifiedInstallationProxyService(service_provider).get_apps(['User']) | ||
click.echo(json.dumps(result)) | ||
|
||
cli.add_command(list_device) | ||
cli.add_command(install_app) | ||
cli.add_command(debug_server) | ||
cli.add_command(cli_start_quic_tunnel) | ||
cli.add_command(installed_app_path) | ||
|
||
if __name__ == '__main__': | ||
cli() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# -*- mode: python ; coding: utf-8 -*- | ||
|
||
|
||
a = Analysis( | ||
['pymobiledevicelite.py'], | ||
pathex=[], | ||
binaries=[], | ||
datas=[], | ||
hiddenimports=[], | ||
hookspath=[], | ||
hooksconfig={}, | ||
runtime_hooks=[], | ||
excludes=[], | ||
noarchive=False, | ||
) | ||
pyz = PYZ(a.pure) | ||
|
||
exe = EXE( | ||
pyz, | ||
a.scripts, | ||
a.binaries, | ||
a.datas, | ||
[], | ||
name='pymobiledevicelite', | ||
debug=False, | ||
bootloader_ignore_signals=False, | ||
strip=False, | ||
upx=True, | ||
upx_exclude=[], | ||
runtime_tmpdir=None, | ||
console=True, | ||
disable_windowed_traceback=False, | ||
argv_emulation=False, | ||
target_arch='universal2', | ||
codesign_identity=None, | ||
entitlements_file=None, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--extra-index-url https://pypi.python.org/simple/ | ||
|
||
pymobiledevice3==2.17.2 |
Oops, something went wrong.