pymobiledevice3 5.0.0__py3-none-any.whl → 5.0.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.

Potentially problematic release.


This version of pymobiledevice3 might be problematic. Click here for more details.

Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +128 -102
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +26 -49
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +25 -18
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +602 -520
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +79 -74
  20. pymobiledevice3/cli/mounter.py +85 -68
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +186 -110
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +157 -79
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +394 -241
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +62 -41
  52. pymobiledevice3/remote/tunnel_service.py +371 -293
  53. pymobiledevice3/remote/utils.py +12 -11
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +35 -54
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +524 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +136 -126
  72. pymobiledevice3/services/afc.py +350 -291
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +60 -46
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +55 -47
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +40 -50
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +330 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +69 -51
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +180 -176
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +2 -2
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +127 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +212 -133
  135. pymobiledevice3/usbmux.py +183 -138
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.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__).error(
58
- 'Failed to connect to required service. Make sure DeveloperDiskImage.dmg has been mounted. '
59
- 'You can do so using: pymobiledevice3 mounter mount'
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
@@ -13,9 +13,9 @@ DEFAULT_MAX_FAILS = 3
13
13
 
14
14
  def is_wsl() -> bool:
15
15
  try:
16
- with open('/proc/version', 'r') as f:
16
+ with open("/proc/version", "r") as f:
17
17
  version_info = f.read()
18
- return 'Microsoft' in version_info or 'WSL' in version_info
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) -> 'OsUtils':
28
+ def create(cls) -> "OsUtils":
29
29
  if cls._instance is None:
30
30
  cls._os_name = sys.platform
31
- if cls._os_name == 'win32':
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 == 'darwin':
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 == 'linux':
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 == 'cygwin':
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(self, sock: socket.socket, after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
75
- interval_sec: int = DEFAULT_INTERVAL_SEC, max_fails: int = DEFAULT_MAX_FAILS) -> None:
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 [f'{adapter.ips[0].ip[0]}%{adapter.nice_name}' for adapter in get_adapters() if
37
- adapter.ips[0].is_IPv6 and not adapter.nice_name.startswith('tun')]
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('SUDO_UID') is None:
43
+ if os.getenv("SUDO_UID") is None:
41
44
  return
42
- os.chown(path, int(os.getenv('SUDO_UID')), int(os.getenv('SUDO_GID')))
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('/var/db/lockdown/')
58
+ return Path("/var/db/lockdown/")
56
59
 
57
60
  @property
58
61
  def loopback_header(self) -> bytes:
59
- return struct.pack('>I', socket.AF_INET6)
60
-
61
- def set_keepalive(self, sock: socket.socket, after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
62
- interval_sec: int = DEFAULT_INTERVAL_SEC, max_fails: int = DEFAULT_MAX_FAILS) -> None:
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('/var/lib/lockdown/')
80
+ return Path("/var/lib/lockdown/")
73
81
 
74
82
  @property
75
83
  def loopback_header(self) -> bytes:
76
- return b'\x00\x00\x86\xdd'
77
-
78
- def set_keepalive(self, sock: socket.socket, after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
79
- interval_sec: int = DEFAULT_INTERVAL_SEC, max_fails: int = DEFAULT_MAX_FAILS) -> None:
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('~' + os.environ.get('SUDO_USER', '')).expanduser()
99
+ return Path("~" + os.environ.get("SUDO_USER", "")).expanduser()
87
100
 
88
101
 
89
102
  class Cygwin(Posix):
@@ -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
- """ Check if the current OS user is an Administrator or root.
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'\x00\x00\x86\xdd'
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('ALLUSERSPROFILE', ''), 'Apple', 'Lockdown')
44
+ return Path(os.environ.get("ALLUSERSPROFILE", ""), "Apple", "Lockdown")
45
45
 
46
46
  def get_ipv6_ips(self) -> list[str]:
47
- return [f'{adapter.ips[0].ip[0]}%{adapter.ips[0].ip[2]}' for adapter in get_adapters() if
48
- adapter.ips[0].is_IPv6]
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(self, sock: socket.socket, after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
51
- interval_sec: int = DEFAULT_INTERVAL_SEC, **kwargs) -> None:
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('Press ENTER to exit>')
67
+ input("Press ENTER to exit>")
@@ -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 = 'plist'
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
- with usbmux.create_mux(usbmux_address=usbmux_address) as mux:
49
- if isinstance(mux, PlistMuxConnection):
50
- pair_record = mux.get_pair_record(identifier)
51
- if pair_record is not None:
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'{identifier}.plist'
64
+ filename = OSUTILS.pair_record_path / f"{identifier}.plist"
66
65
  try:
67
- with open(filename, 'rb') as f:
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('Looking for pymobiledevice3 pairing record')
86
- path = pairing_records_cache_folder / f'{identifier}.{PAIRING_RECORD_EXT}'
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'No pymobiledevice3 pairing record found for device {identifier}')
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(identifier: str, pairing_records_cache_folder: Path,
94
- usbmux_address: Optional[str] = None) -> dict:
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'remote_{identifier}'
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('remote_*')
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('remote_', 1)[1].split('.', 1)[0]
172
+ yield file.parts[-1].split("remote_", 1)[1].split(".", 1)[0]
@@ -3,13 +3,13 @@ from enum import Enum
3
3
 
4
4
 
5
5
  class ConnectionType(Enum):
6
- USB = 'usb'
7
- WIFI = 'wifi'
6
+ USB = "usb"
7
+ WIFI = "wifi"
8
8
 
9
9
 
10
10
  class TunnelProtocol(Enum):
11
- TCP = 'tcp'
12
- QUIC = '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 = 'com.apple.coredevice.appservice'
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(self, include_app_clips: bool = True, include_removable_apps: bool = True,
20
- include_hidden_apps: bool = True, include_internal_apps: bool = True,
21
- include_default_apps: bool = True) -> list[dict]:
22
- """ List applications """
23
- return await self.invoke('com.apple.coredevice.feature.listapps', {
24
- 'includeAppClips': include_app_clips, 'includeRemovableApps': include_removable_apps,
25
- 'includeHiddenApps': include_hidden_apps, 'includeInternalApps': include_internal_apps,
26
- 'includeDefaultApps': include_default_apps})
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
- self, bundle_id: str, arguments: Optional[list[str]] = None, kill_existing: bool = True,
30
- start_suspended: bool = False, environment: Optional[dict] = None, extra_options: Optional[dict] = None) \
31
- -> list[dict]:
32
- """ launch application """
33
- return await self.invoke('com.apple.coredevice.feature.launchapplication', {
34
- 'applicationSpecifier': {
35
- 'bundleIdentifier': {'_0': bundle_id},
36
- },
37
- 'options': {
38
- 'arguments': arguments if arguments is not None else [],
39
- 'environmentVariables': environment if environment is not None else {},
40
- 'standardIOUsesPseudoterminals': True,
41
- 'startStopped': start_suspended,
42
- 'terminateExisting': kill_existing,
43
- 'user': {'shortName': 'mobile'},
44
- 'platformSpecificOptions': plistlib.dumps(extra_options if extra_options is not None else {}),
45
- },
46
- 'standardIOIdentifiers': {
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
- """ List processes """
52
- return (await self.invoke('com.apple.coredevice.feature.listprocesses'))['processTokens']
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('com.apple.coredevice.feature.listroots', {
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('com.apple.coredevice.feature.spawnexecutable', {
72
- 'executableItem': {
73
- 'url': {
74
- '_0': {
75
- 'relative': executable,
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('com.apple.coredevice.feature.monitorprocesstermination', {
99
- 'processToken': {'processIdentifier': XpcInt64Type(pid)}})
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('com.apple.coredevice.feature.uninstallapp', {'bundleIdentifier': bundle_identifier})
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('com.apple.coredevice.feature.sendsignaltoprocess', {
112
- 'process': {'processIdentifier': XpcInt64Type(pid)},
113
- 'signal': XpcInt64Type(signal),
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(self, bundle_identifier: str, width: float, height: float, scale: float,
117
- allow_placeholder: bool) -> dict:
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('com.apple.coredevice.feature.fetchappicons', {
122
- 'width': width,
123
- 'height': height,
124
- 'scale': scale,
125
- 'allowPlaceholder': allow_placeholder,
126
- 'bundleIdentifier': bundle_identifier
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 {'components': [XpcUInt64Type(component) for component in version_components],
12
- 'originalComponentsCount': XpcInt64Type(len(version_components)),
13
- 'stringValue': version}
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('325.3')
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
- 'CoreDevice.CoreDeviceDDIProtocolVersion': XpcInt64Type(0),
25
- 'CoreDevice.action': {},
26
- 'CoreDevice.coreDeviceVersion': CORE_DEVICE_VERSION,
27
- 'CoreDevice.deviceIdentifier': str(uuid.uuid4()),
28
- 'CoreDevice.featureIdentifier': feature_identifier,
29
- 'CoreDevice.input': input_,
30
- 'CoreDevice.invocationIdentifier': str(uuid.uuid4())})
31
- output = response.get('CoreDevice.output')
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'Failed to invoke: {feature_identifier}. Got error: {response}')
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 = 'com.apple.coredevice.deviceinfo'
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('com.apple.coredevice.feature.getdeviceinfo', {})
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('com.apple.coredevice.feature.getdisplayinfo', {})
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('com.apple.coredevice.feature.querymobilegestalt', {'keys': keys})
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('com.apple.coredevice.feature.getlockstate', {})
39
+ return await self.invoke("com.apple.coredevice.feature.getlockstate", {})