pymobiledevice3 4.27.0__py3-none-any.whl → 5.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- pymobiledevice3/__main__.py +123 -98
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +351 -117
- pymobiledevice3/ca.py +32 -24
- pymobiledevice3/cli/activation.py +7 -7
- pymobiledevice3/cli/afc.py +19 -19
- pymobiledevice3/cli/amfi.py +4 -4
- pymobiledevice3/cli/apps.py +51 -39
- pymobiledevice3/cli/backup.py +58 -32
- pymobiledevice3/cli/bonjour.py +27 -20
- pymobiledevice3/cli/cli_common.py +112 -81
- pymobiledevice3/cli/companion_proxy.py +4 -4
- pymobiledevice3/cli/completions.py +10 -10
- pymobiledevice3/cli/crash.py +37 -31
- pymobiledevice3/cli/developer.py +601 -519
- pymobiledevice3/cli/diagnostics.py +38 -33
- pymobiledevice3/cli/lockdown.py +82 -72
- pymobiledevice3/cli/mounter.py +84 -67
- pymobiledevice3/cli/notification.py +10 -10
- pymobiledevice3/cli/pcap.py +19 -14
- pymobiledevice3/cli/power_assertion.py +12 -10
- pymobiledevice3/cli/processes.py +10 -10
- pymobiledevice3/cli/profile.py +88 -77
- pymobiledevice3/cli/provision.py +17 -17
- pymobiledevice3/cli/remote.py +188 -111
- pymobiledevice3/cli/restore.py +43 -40
- pymobiledevice3/cli/springboard.py +30 -28
- pymobiledevice3/cli/syslog.py +85 -58
- pymobiledevice3/cli/usbmux.py +21 -20
- pymobiledevice3/cli/version.py +3 -2
- pymobiledevice3/cli/webinspector.py +156 -78
- pymobiledevice3/common.py +1 -1
- pymobiledevice3/exceptions.py +154 -60
- pymobiledevice3/irecv.py +49 -53
- pymobiledevice3/irecv_devices.py +1489 -492
- pymobiledevice3/lockdown.py +400 -251
- pymobiledevice3/lockdown_service_provider.py +5 -7
- pymobiledevice3/osu/os_utils.py +18 -9
- pymobiledevice3/osu/posix_util.py +28 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +19 -19
- pymobiledevice3/remote/common.py +4 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
- pymobiledevice3/remote/core_device/file_service.py +47 -33
- pymobiledevice3/remote/remote_service_discovery.py +53 -35
- pymobiledevice3/remote/remotexpc.py +64 -42
- pymobiledevice3/remote/tunnel_service.py +383 -297
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +16 -16
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +90 -47
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +11 -11
- pymobiledevice3/restore/fdr.py +46 -46
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +130 -133
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +125 -135
- pymobiledevice3/restore/restore.py +535 -523
- pymobiledevice3/restore/restore_options.py +122 -115
- pymobiledevice3/restore/restored_client.py +25 -22
- pymobiledevice3/restore/tss.py +378 -270
- pymobiledevice3/service_connection.py +50 -46
- pymobiledevice3/services/accessibilityaudit.py +137 -127
- pymobiledevice3/services/afc.py +363 -293
- pymobiledevice3/services/amfi.py +21 -18
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +61 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +56 -48
- pymobiledevice3/services/diagnostics.py +971 -968
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +8 -7
- pymobiledevice3/services/house_arrest.py +12 -15
- pymobiledevice3/services/installation_proxy.py +119 -100
- pymobiledevice3/services/lockdown_service.py +12 -5
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +84 -72
- pymobiledevice3/services/mobile_config.py +331 -301
- pymobiledevice3/services/mobile_image_mounter.py +137 -116
- pymobiledevice3/services/mobilebackup2.py +188 -150
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +128 -74
- pymobiledevice3/services/pcapd.py +306 -306
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +16 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +13 -10
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +3 -3
- pymobiledevice3/services/web_protocol/automation_session.py +183 -179
- pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +47 -45
- pymobiledevice3/services/web_protocol/element.py +74 -63
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
- pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
- pymobiledevice3/services/web_protocol/switch_to.py +11 -12
- pymobiledevice3/services/webinspector.py +142 -116
- pymobiledevice3/tcp_forwarder.py +64 -50
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +315 -193
- pymobiledevice3/usbmux.py +197 -148
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +2 -6
- pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
- pymobiledevice3-4.27.0.dist-info/RECORD +0 -172
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
|
@@ -41,21 +41,19 @@ class LockdownServiceProvider:
|
|
|
41
41
|
pass
|
|
42
42
|
|
|
43
43
|
@abstractmethod
|
|
44
|
-
async def aio_start_lockdown_service(
|
|
45
|
-
self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
44
|
+
async def aio_start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
46
45
|
pass
|
|
47
46
|
|
|
48
47
|
@abstractmethod
|
|
49
48
|
def get_value(self, domain: Optional[str] = None, key: Optional[str] = None) -> Any:
|
|
50
49
|
pass
|
|
51
50
|
|
|
52
|
-
def start_lockdown_developer_service(
|
|
53
|
-
self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
51
|
+
def start_lockdown_developer_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
54
52
|
try:
|
|
55
53
|
return self.start_lockdown_service(name, include_escrow_bag=include_escrow_bag)
|
|
56
54
|
except StartServiceError:
|
|
57
|
-
logging.getLogger(self.__module__).
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
logging.getLogger(self.__module__).exception(
|
|
56
|
+
"Failed to connect to required service. Make sure DeveloperDiskImage.dmg has been mounted. "
|
|
57
|
+
"You can do so using: pymobiledevice3 mounter mount"
|
|
60
58
|
)
|
|
61
59
|
raise
|
pymobiledevice3/osu/os_utils.py
CHANGED
|
@@ -13,9 +13,9 @@ DEFAULT_MAX_FAILS = 3
|
|
|
13
13
|
|
|
14
14
|
def is_wsl() -> bool:
|
|
15
15
|
try:
|
|
16
|
-
with open(
|
|
16
|
+
with open("/proc/version") as f:
|
|
17
17
|
version_info = f.read()
|
|
18
|
-
return
|
|
18
|
+
return "Microsoft" in version_info or "WSL" in version_info
|
|
19
19
|
except FileNotFoundError:
|
|
20
20
|
return False
|
|
21
21
|
|
|
@@ -25,20 +25,24 @@ class OsUtils:
|
|
|
25
25
|
_os_name = None
|
|
26
26
|
|
|
27
27
|
@classmethod
|
|
28
|
-
def create(cls) ->
|
|
28
|
+
def create(cls) -> "OsUtils":
|
|
29
29
|
if cls._instance is None:
|
|
30
30
|
cls._os_name = sys.platform
|
|
31
|
-
if cls._os_name ==
|
|
31
|
+
if cls._os_name == "win32":
|
|
32
32
|
from pymobiledevice3.osu.win_util import Win32
|
|
33
|
+
|
|
33
34
|
cls._instance = Win32()
|
|
34
|
-
elif cls._os_name ==
|
|
35
|
+
elif cls._os_name == "darwin":
|
|
35
36
|
from pymobiledevice3.osu.posix_util import Darwin
|
|
37
|
+
|
|
36
38
|
cls._instance = Darwin()
|
|
37
|
-
elif cls._os_name ==
|
|
39
|
+
elif cls._os_name == "linux":
|
|
38
40
|
from pymobiledevice3.osu.posix_util import Linux, Wsl
|
|
41
|
+
|
|
39
42
|
cls._instance = Wsl() if is_wsl() else Linux()
|
|
40
|
-
elif cls._os_name ==
|
|
43
|
+
elif cls._os_name == "cygwin":
|
|
41
44
|
from pymobiledevice3.osu.posix_util import Cygwin
|
|
45
|
+
|
|
42
46
|
cls._instance = Cygwin()
|
|
43
47
|
else:
|
|
44
48
|
raise OSNotSupportedError(cls._os_name)
|
|
@@ -71,8 +75,13 @@ class OsUtils:
|
|
|
71
75
|
def get_ipv6_ips(self) -> list[str]:
|
|
72
76
|
raise FeatureNotSupportedError(self._os_name, inspect.currentframe().f_code.co_name)
|
|
73
77
|
|
|
74
|
-
def set_keepalive(
|
|
75
|
-
|
|
78
|
+
def set_keepalive(
|
|
79
|
+
self,
|
|
80
|
+
sock: socket.socket,
|
|
81
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
82
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
83
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
84
|
+
) -> None:
|
|
76
85
|
raise FeatureNotSupportedError(self._os_name, inspect.currentframe().f_code.co_name)
|
|
77
86
|
|
|
78
87
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
@@ -33,13 +33,16 @@ class Posix(OsUtils):
|
|
|
33
33
|
return 'This command requires root privileges. Consider retrying with "sudo".'
|
|
34
34
|
|
|
35
35
|
def get_ipv6_ips(self) -> list[str]:
|
|
36
|
-
return [
|
|
37
|
-
|
|
36
|
+
return [
|
|
37
|
+
f"{adapter.ips[0].ip[0]}%{adapter.nice_name}"
|
|
38
|
+
for adapter in get_adapters()
|
|
39
|
+
if adapter.ips[0].is_IPv6 and not adapter.nice_name.startswith("tun")
|
|
40
|
+
]
|
|
38
41
|
|
|
39
42
|
def chown_to_non_sudo_if_needed(self, path: Path) -> None:
|
|
40
|
-
if os.getenv(
|
|
43
|
+
if os.getenv("SUDO_UID") is None:
|
|
41
44
|
return
|
|
42
|
-
os.chown(path, int(os.getenv(
|
|
45
|
+
os.chown(path, int(os.getenv("SUDO_UID")), int(os.getenv("SUDO_GID")))
|
|
43
46
|
|
|
44
47
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
45
48
|
return datetime.datetime.fromtimestamp(time_stamp)
|
|
@@ -52,14 +55,19 @@ class Posix(OsUtils):
|
|
|
52
55
|
class Darwin(Posix):
|
|
53
56
|
@property
|
|
54
57
|
def pair_record_path(self) -> Path:
|
|
55
|
-
return Path(
|
|
58
|
+
return Path("/var/db/lockdown/")
|
|
56
59
|
|
|
57
60
|
@property
|
|
58
61
|
def loopback_header(self) -> bytes:
|
|
59
|
-
return struct.pack(
|
|
60
|
-
|
|
61
|
-
def set_keepalive(
|
|
62
|
-
|
|
62
|
+
return struct.pack(">I", socket.AF_INET6)
|
|
63
|
+
|
|
64
|
+
def set_keepalive(
|
|
65
|
+
self,
|
|
66
|
+
sock: socket.socket,
|
|
67
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
68
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
69
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
70
|
+
) -> None:
|
|
63
71
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
64
72
|
sock.setsockopt(socket.IPPROTO_TCP, _DARWIN_TCP_KEEPALIVE, after_idle_sec)
|
|
65
73
|
sock.setsockopt(socket.IPPROTO_TCP, _DARWIN_TCP_KEEPINTVL, interval_sec)
|
|
@@ -69,21 +77,26 @@ class Darwin(Posix):
|
|
|
69
77
|
class Linux(Posix):
|
|
70
78
|
@property
|
|
71
79
|
def pair_record_path(self) -> Path:
|
|
72
|
-
return Path(
|
|
80
|
+
return Path("/var/lib/lockdown/")
|
|
73
81
|
|
|
74
82
|
@property
|
|
75
83
|
def loopback_header(self) -> bytes:
|
|
76
|
-
return b
|
|
77
|
-
|
|
78
|
-
def set_keepalive(
|
|
79
|
-
|
|
84
|
+
return b"\x00\x00\x86\xdd"
|
|
85
|
+
|
|
86
|
+
def set_keepalive(
|
|
87
|
+
self,
|
|
88
|
+
sock: socket.socket,
|
|
89
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
90
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
91
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
92
|
+
) -> None:
|
|
80
93
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
81
94
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec)
|
|
82
95
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
|
|
83
96
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
|
|
84
97
|
|
|
85
98
|
def get_homedir(self) -> Path:
|
|
86
|
-
return Path(
|
|
99
|
+
return Path("~" + os.environ.get("SUDO_USER", "")).expanduser()
|
|
87
100
|
|
|
88
101
|
|
|
89
102
|
class Cygwin(Posix):
|
pymobiledevice3/osu/win_util.py
CHANGED
|
@@ -13,7 +13,7 @@ from pymobiledevice3.usbmux import MuxConnection
|
|
|
13
13
|
class Win32(OsUtils):
|
|
14
14
|
@property
|
|
15
15
|
def is_admin(self) -> bool:
|
|
16
|
-
"""
|
|
16
|
+
"""Check if the current OS user is an Administrator or root.
|
|
17
17
|
See: https://github.com/Preston-Landers/pyuac/blob/master/pyuac/admin.py
|
|
18
18
|
:return: True if the current user is an 'Administrator', otherwise False.
|
|
19
19
|
"""
|
|
@@ -33,7 +33,7 @@ class Win32(OsUtils):
|
|
|
33
33
|
|
|
34
34
|
@property
|
|
35
35
|
def loopback_header(self) -> bytes:
|
|
36
|
-
return b
|
|
36
|
+
return b"\x00\x00\x86\xdd"
|
|
37
37
|
|
|
38
38
|
@property
|
|
39
39
|
def access_denied_error(self) -> str:
|
|
@@ -41,14 +41,20 @@ class Win32(OsUtils):
|
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def pair_record_path(self) -> Path:
|
|
44
|
-
return Path(os.environ.get(
|
|
44
|
+
return Path(os.environ.get("ALLUSERSPROFILE", ""), "Apple", "Lockdown")
|
|
45
45
|
|
|
46
46
|
def get_ipv6_ips(self) -> list[str]:
|
|
47
|
-
return [
|
|
48
|
-
|
|
47
|
+
return [
|
|
48
|
+
f"{adapter.ips[0].ip[0]}%{adapter.ips[0].ip[2]}" for adapter in get_adapters() if adapter.ips[0].is_IPv6
|
|
49
|
+
]
|
|
49
50
|
|
|
50
|
-
def set_keepalive(
|
|
51
|
-
|
|
51
|
+
def set_keepalive(
|
|
52
|
+
self,
|
|
53
|
+
sock: socket.socket,
|
|
54
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
55
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
56
|
+
**kwargs,
|
|
57
|
+
) -> None:
|
|
52
58
|
sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, after_idle_sec * 1000, interval_sec * 1000))
|
|
53
59
|
|
|
54
60
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
@@ -58,4 +64,4 @@ class Win32(OsUtils):
|
|
|
58
64
|
return
|
|
59
65
|
|
|
60
66
|
def wait_return(self):
|
|
61
|
-
input(
|
|
67
|
+
input("Press ENTER to exit>")
|
pymobiledevice3/pair_records.py
CHANGED
|
@@ -15,10 +15,10 @@ from pymobiledevice3.usbmux import PlistMuxConnection
|
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
OSUTILS = get_os_utils()
|
|
18
|
-
PAIRING_RECORD_EXT =
|
|
18
|
+
PAIRING_RECORD_EXT = "plist"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def generate_host_id(hostname: str = None) -> str:
|
|
21
|
+
def generate_host_id(hostname: Optional[str] = None) -> str:
|
|
22
22
|
"""
|
|
23
23
|
Generate a unique host ID based on the hostname.
|
|
24
24
|
|
|
@@ -44,12 +44,11 @@ def get_usbmux_pairing_record(identifier: str, usbmux_address: Optional[str] = N
|
|
|
44
44
|
:return: The pairing record if found, otherwise None.
|
|
45
45
|
:rtype: dict or None
|
|
46
46
|
"""
|
|
47
|
-
with suppress(NotPairedError, MuxException):
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return pair_record
|
|
47
|
+
with suppress(NotPairedError, MuxException), usbmux.create_mux(usbmux_address=usbmux_address) as mux:
|
|
48
|
+
if isinstance(mux, PlistMuxConnection):
|
|
49
|
+
pair_record = mux.get_pair_record(identifier)
|
|
50
|
+
if pair_record is not None:
|
|
51
|
+
return pair_record
|
|
53
52
|
return None
|
|
54
53
|
|
|
55
54
|
|
|
@@ -62,9 +61,9 @@ def get_itunes_pairing_record(identifier: str) -> Optional[dict]:
|
|
|
62
61
|
:return: The pairing record if found, otherwise None.
|
|
63
62
|
:rtype: Optional[dict]
|
|
64
63
|
"""
|
|
65
|
-
filename = OSUTILS.pair_record_path / f
|
|
64
|
+
filename = OSUTILS.pair_record_path / f"{identifier}.plist"
|
|
66
65
|
try:
|
|
67
|
-
with open(filename,
|
|
66
|
+
with open(filename, "rb") as f:
|
|
68
67
|
pair_record = plistlib.load(f)
|
|
69
68
|
except (PermissionError, FileNotFoundError, plistlib.InvalidFileException):
|
|
70
69
|
return None
|
|
@@ -82,16 +81,17 @@ def get_local_pairing_record(identifier: str, pairing_records_cache_folder: Path
|
|
|
82
81
|
:return: The pairing record if found, otherwise None.
|
|
83
82
|
:rtype: Optional[dict]
|
|
84
83
|
"""
|
|
85
|
-
logger.debug(
|
|
86
|
-
path = pairing_records_cache_folder / f
|
|
84
|
+
logger.debug("Looking for pymobiledevice3 pairing record")
|
|
85
|
+
path = pairing_records_cache_folder / f"{identifier}.{PAIRING_RECORD_EXT}"
|
|
87
86
|
if not path.exists():
|
|
88
|
-
logger.debug(f
|
|
87
|
+
logger.debug(f"No pymobiledevice3 pairing record found for device {identifier}")
|
|
89
88
|
return None
|
|
90
89
|
return plistlib.loads(path.read_bytes())
|
|
91
90
|
|
|
92
91
|
|
|
93
|
-
def get_preferred_pair_record(
|
|
94
|
-
|
|
92
|
+
def get_preferred_pair_record(
|
|
93
|
+
identifier: str, pairing_records_cache_folder: Path, usbmux_address: Optional[str] = None
|
|
94
|
+
) -> dict:
|
|
95
95
|
"""
|
|
96
96
|
Look for an existing pair record for the connected device in the following order:
|
|
97
97
|
- usbmuxd
|
|
@@ -121,7 +121,7 @@ def get_preferred_pair_record(identifier: str, pairing_records_cache_folder: Pat
|
|
|
121
121
|
return get_local_pairing_record(identifier, pairing_records_cache_folder)
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
def create_pairing_records_cache_folder(pairing_records_cache_folder: Path = None) -> Path:
|
|
124
|
+
def create_pairing_records_cache_folder(pairing_records_cache_folder: Optional[Path] = None) -> Path:
|
|
125
125
|
"""
|
|
126
126
|
Create the pairing records cache folder if it does not exist.
|
|
127
127
|
|
|
@@ -148,7 +148,7 @@ def get_remote_pairing_record_filename(identifier: str) -> str:
|
|
|
148
148
|
:return: The filename for the remote pairing record.
|
|
149
149
|
:rtype: str
|
|
150
150
|
"""
|
|
151
|
-
return f
|
|
151
|
+
return f"remote_{identifier}"
|
|
152
152
|
|
|
153
153
|
|
|
154
154
|
def iter_remote_pair_records() -> Generator[Path, None, None]:
|
|
@@ -158,7 +158,7 @@ def iter_remote_pair_records() -> Generator[Path, None, None]:
|
|
|
158
158
|
:return: A generator yielding paths to the remote pairing records.
|
|
159
159
|
:rtype: Generator[Path, None, None]
|
|
160
160
|
"""
|
|
161
|
-
return get_home_folder().glob(
|
|
161
|
+
return get_home_folder().glob("remote_*")
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
def iter_remote_paired_identifiers() -> Generator[str, None, None]:
|
|
@@ -169,4 +169,4 @@ def iter_remote_paired_identifiers() -> Generator[str, None, None]:
|
|
|
169
169
|
:rtype: Generator[str, None, None]
|
|
170
170
|
"""
|
|
171
171
|
for file in iter_remote_pair_records():
|
|
172
|
-
yield file.parts[-1].split(
|
|
172
|
+
yield file.parts[-1].split("remote_", 1)[1].split(".", 1)[0]
|
pymobiledevice3/remote/common.py
CHANGED
|
@@ -3,13 +3,13 @@ from enum import Enum
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ConnectionType(Enum):
|
|
6
|
-
USB =
|
|
7
|
-
WIFI =
|
|
6
|
+
USB = "usb"
|
|
7
|
+
WIFI = "wifi"
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class TunnelProtocol(Enum):
|
|
11
|
-
TCP =
|
|
12
|
-
QUIC =
|
|
11
|
+
TCP = "tcp"
|
|
12
|
+
QUIC = "quic"
|
|
13
13
|
|
|
14
14
|
# TODO: make only TCP the default once 3.12 becomes deprecated
|
|
15
15
|
DEFAULT = TCP if sys.version_info >= (3, 13) else QUIC
|
|
@@ -11,45 +11,63 @@ class AppServiceService(CoreDeviceService):
|
|
|
11
11
|
Manage applications
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
SERVICE_NAME =
|
|
14
|
+
SERVICE_NAME = "com.apple.coredevice.appservice"
|
|
15
15
|
|
|
16
16
|
def __init__(self, rsd: RemoteServiceDiscoveryService):
|
|
17
17
|
super().__init__(rsd, self.SERVICE_NAME)
|
|
18
18
|
|
|
19
|
-
async def list_apps(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
async def list_apps(
|
|
20
|
+
self,
|
|
21
|
+
include_app_clips: bool = True,
|
|
22
|
+
include_removable_apps: bool = True,
|
|
23
|
+
include_hidden_apps: bool = True,
|
|
24
|
+
include_internal_apps: bool = True,
|
|
25
|
+
include_default_apps: bool = True,
|
|
26
|
+
) -> list[dict]:
|
|
27
|
+
"""List applications"""
|
|
28
|
+
return await self.invoke(
|
|
29
|
+
"com.apple.coredevice.feature.listapps",
|
|
30
|
+
{
|
|
31
|
+
"includeAppClips": include_app_clips,
|
|
32
|
+
"includeRemovableApps": include_removable_apps,
|
|
33
|
+
"includeHiddenApps": include_hidden_apps,
|
|
34
|
+
"includeInternalApps": include_internal_apps,
|
|
35
|
+
"includeDefaultApps": include_default_apps,
|
|
36
|
+
},
|
|
37
|
+
)
|
|
27
38
|
|
|
28
39
|
async def launch_application(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
self,
|
|
41
|
+
bundle_id: str,
|
|
42
|
+
arguments: Optional[list[str]] = None,
|
|
43
|
+
kill_existing: bool = True,
|
|
44
|
+
start_suspended: bool = False,
|
|
45
|
+
environment: Optional[dict] = None,
|
|
46
|
+
extra_options: Optional[dict] = None,
|
|
47
|
+
) -> list[dict]:
|
|
48
|
+
"""launch application"""
|
|
49
|
+
return await self.invoke(
|
|
50
|
+
"com.apple.coredevice.feature.launchapplication",
|
|
51
|
+
{
|
|
52
|
+
"applicationSpecifier": {
|
|
53
|
+
"bundleIdentifier": {"_0": bundle_id},
|
|
54
|
+
},
|
|
55
|
+
"options": {
|
|
56
|
+
"arguments": arguments if arguments is not None else [],
|
|
57
|
+
"environmentVariables": environment if environment is not None else {},
|
|
58
|
+
"standardIOUsesPseudoterminals": True,
|
|
59
|
+
"startStopped": start_suspended,
|
|
60
|
+
"terminateExisting": kill_existing,
|
|
61
|
+
"user": {"shortName": "mobile"},
|
|
62
|
+
"platformSpecificOptions": plistlib.dumps(extra_options if extra_options is not None else {}),
|
|
63
|
+
},
|
|
64
|
+
"standardIOIdentifiers": {},
|
|
47
65
|
},
|
|
48
|
-
|
|
66
|
+
)
|
|
49
67
|
|
|
50
68
|
async def list_processes(self) -> list[dict]:
|
|
51
|
-
"""
|
|
52
|
-
return (await self.invoke(
|
|
69
|
+
"""List processes"""
|
|
70
|
+
return (await self.invoke("com.apple.coredevice.feature.listprocesses"))["processTokens"]
|
|
53
71
|
|
|
54
72
|
async def list_roots(self) -> dict:
|
|
55
73
|
"""
|
|
@@ -57,10 +75,7 @@ class AppServiceService(CoreDeviceService):
|
|
|
57
75
|
|
|
58
76
|
Can only be performed on certain devices
|
|
59
77
|
"""
|
|
60
|
-
return await self.invoke(
|
|
61
|
-
'rootPoint': {
|
|
62
|
-
'relative': '/'
|
|
63
|
-
}})
|
|
78
|
+
return await self.invoke("com.apple.coredevice.feature.listroots", {"rootPoint": {"relative": "/"}})
|
|
64
79
|
|
|
65
80
|
async def spawn_executable(self, executable: str, arguments: list[str]) -> dict:
|
|
66
81
|
"""
|
|
@@ -68,26 +83,29 @@ class AppServiceService(CoreDeviceService):
|
|
|
68
83
|
|
|
69
84
|
Can only be performed on certain devices
|
|
70
85
|
"""
|
|
71
|
-
return await self.invoke(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
return await self.invoke(
|
|
87
|
+
"com.apple.coredevice.feature.spawnexecutable",
|
|
88
|
+
{
|
|
89
|
+
"executableItem": {
|
|
90
|
+
"url": {
|
|
91
|
+
"_0": {
|
|
92
|
+
"relative": executable,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"standardIOIdentifiers": {},
|
|
97
|
+
"options": {
|
|
98
|
+
"arguments": arguments,
|
|
99
|
+
"environmentVariables": {},
|
|
100
|
+
"standardIOUsesPseudoterminals": True,
|
|
101
|
+
"startStopped": False,
|
|
102
|
+
"user": {
|
|
103
|
+
"active": True,
|
|
76
104
|
},
|
|
77
|
-
|
|
78
|
-
},
|
|
79
|
-
'standardIOIdentifiers': {},
|
|
80
|
-
'options': {
|
|
81
|
-
'arguments': arguments,
|
|
82
|
-
'environmentVariables': {},
|
|
83
|
-
'standardIOUsesPseudoterminals': True,
|
|
84
|
-
'startStopped': False,
|
|
85
|
-
'user': {
|
|
86
|
-
'active': True,
|
|
105
|
+
"platformSpecificOptions": plistlib.dumps({}),
|
|
87
106
|
},
|
|
88
|
-
'platformSpecificOptions': plistlib.dumps({}),
|
|
89
107
|
},
|
|
90
|
-
|
|
108
|
+
)
|
|
91
109
|
|
|
92
110
|
async def monitor_process_termination(self, pid: int) -> dict:
|
|
93
111
|
"""
|
|
@@ -95,33 +113,42 @@ class AppServiceService(CoreDeviceService):
|
|
|
95
113
|
|
|
96
114
|
Can only be performed on certain devices
|
|
97
115
|
"""
|
|
98
|
-
return await self.invoke(
|
|
99
|
-
|
|
116
|
+
return await self.invoke(
|
|
117
|
+
"com.apple.coredevice.feature.monitorprocesstermination",
|
|
118
|
+
{"processToken": {"processIdentifier": XpcInt64Type(pid)}},
|
|
119
|
+
)
|
|
100
120
|
|
|
101
121
|
async def uninstall_app(self, bundle_identifier: str) -> None:
|
|
102
122
|
"""
|
|
103
123
|
Uninstall given application by its bundle identifier
|
|
104
124
|
"""
|
|
105
|
-
await self.invoke(
|
|
125
|
+
await self.invoke("com.apple.coredevice.feature.uninstallapp", {"bundleIdentifier": bundle_identifier})
|
|
106
126
|
|
|
107
127
|
async def send_signal_to_process(self, pid: int, signal: int) -> dict:
|
|
108
128
|
"""
|
|
109
129
|
Send signal to given process by its pid
|
|
110
130
|
"""
|
|
111
|
-
return await self.invoke(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
return await self.invoke(
|
|
132
|
+
"com.apple.coredevice.feature.sendsignaltoprocess",
|
|
133
|
+
{
|
|
134
|
+
"process": {"processIdentifier": XpcInt64Type(pid)},
|
|
135
|
+
"signal": XpcInt64Type(signal),
|
|
136
|
+
},
|
|
137
|
+
)
|
|
115
138
|
|
|
116
|
-
async def fetch_icons(
|
|
117
|
-
|
|
139
|
+
async def fetch_icons(
|
|
140
|
+
self, bundle_identifier: str, width: float, height: float, scale: float, allow_placeholder: bool
|
|
141
|
+
) -> dict:
|
|
118
142
|
"""
|
|
119
143
|
Fetch given application's icons
|
|
120
144
|
"""
|
|
121
|
-
return await self.invoke(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
return await self.invoke(
|
|
146
|
+
"com.apple.coredevice.feature.fetchappicons",
|
|
147
|
+
{
|
|
148
|
+
"width": width,
|
|
149
|
+
"height": height,
|
|
150
|
+
"scale": scale,
|
|
151
|
+
"allowPlaceholder": allow_placeholder,
|
|
152
|
+
"bundleIdentifier": bundle_identifier,
|
|
153
|
+
},
|
|
154
|
+
)
|
|
@@ -7,13 +7,15 @@ from pymobiledevice3.remote.xpc_message import XpcInt64Type, XpcUInt64Type
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def _generate_core_device_version_dict(version: str) -> dict:
|
|
10
|
-
version_components = version.split(
|
|
11
|
-
return {
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
version_components = version.split(".")
|
|
11
|
+
return {
|
|
12
|
+
"components": [XpcUInt64Type(component) for component in version_components],
|
|
13
|
+
"originalComponentsCount": XpcInt64Type(len(version_components)),
|
|
14
|
+
"stringValue": version,
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
CORE_DEVICE_VERSION = _generate_core_device_version_dict(
|
|
18
|
+
CORE_DEVICE_VERSION = _generate_core_device_version_dict("325.3")
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class CoreDeviceService(RemoteService):
|
|
@@ -21,14 +23,15 @@ class CoreDeviceService(RemoteService):
|
|
|
21
23
|
if input_ is None:
|
|
22
24
|
input_ = {}
|
|
23
25
|
response = await self.service.send_receive_request({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
"CoreDevice.CoreDeviceDDIProtocolVersion": XpcInt64Type(0),
|
|
27
|
+
"CoreDevice.action": {},
|
|
28
|
+
"CoreDevice.coreDeviceVersion": CORE_DEVICE_VERSION,
|
|
29
|
+
"CoreDevice.deviceIdentifier": str(uuid.uuid4()),
|
|
30
|
+
"CoreDevice.featureIdentifier": feature_identifier,
|
|
31
|
+
"CoreDevice.input": input_,
|
|
32
|
+
"CoreDevice.invocationIdentifier": str(uuid.uuid4()),
|
|
33
|
+
})
|
|
34
|
+
output = response.get("CoreDevice.output")
|
|
32
35
|
if output is None:
|
|
33
|
-
raise CoreDeviceError(f
|
|
36
|
+
raise CoreDeviceError(f"Failed to invoke: {feature_identifier}. Got error: {response}")
|
|
34
37
|
return output
|
|
@@ -7,7 +7,7 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
7
7
|
Query device information
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
SERVICE_NAME =
|
|
10
|
+
SERVICE_NAME = "com.apple.coredevice.deviceinfo"
|
|
11
11
|
|
|
12
12
|
def __init__(self, rsd: RemoteServiceDiscoveryService):
|
|
13
13
|
super().__init__(rsd, self.SERVICE_NAME)
|
|
@@ -16,13 +16,13 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
16
16
|
"""
|
|
17
17
|
Get device information
|
|
18
18
|
"""
|
|
19
|
-
return await self.invoke(
|
|
19
|
+
return await self.invoke("com.apple.coredevice.feature.getdeviceinfo", {})
|
|
20
20
|
|
|
21
21
|
async def get_display_info(self) -> dict:
|
|
22
22
|
"""
|
|
23
23
|
Get display information
|
|
24
24
|
"""
|
|
25
|
-
return await self.invoke(
|
|
25
|
+
return await self.invoke("com.apple.coredevice.feature.getdisplayinfo", {})
|
|
26
26
|
|
|
27
27
|
async def query_mobilegestalt(self, keys: list[str]) -> dict:
|
|
28
28
|
"""
|
|
@@ -30,10 +30,10 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
30
30
|
|
|
31
31
|
Can only be performed to specific devices
|
|
32
32
|
"""
|
|
33
|
-
return await self.invoke(
|
|
33
|
+
return await self.invoke("com.apple.coredevice.feature.querymobilegestalt", {"keys": keys})
|
|
34
34
|
|
|
35
35
|
async def get_lockstate(self) -> dict:
|
|
36
36
|
"""
|
|
37
37
|
Get lockstate
|
|
38
38
|
"""
|
|
39
|
-
return await self.invoke(
|
|
39
|
+
return await self.invoke("com.apple.coredevice.feature.getlockstate", {})
|