pymobiledevice3 5.0.1__py3-none-any.whl → 5.0.3__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 +36 -59
  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 +396 -242
  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 +64 -42
  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 +137 -127
  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 +56 -48
  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 +442 -421
  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 +42 -52
  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 +183 -179
  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 +3 -3
  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 +129 -117
  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.1.dist-info → pymobiledevice3-5.0.3.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.3.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/top_level.txt +0 -0
@@ -7,12 +7,13 @@ import sys
7
7
  import tempfile
8
8
  import time
9
9
  from abc import ABC, abstractmethod
10
+ from collections.abc import AsyncIterable
10
11
  from contextlib import contextmanager, suppress
11
12
  from enum import Enum
12
13
  from functools import wraps
13
14
  from pathlib import Path
14
15
  from ssl import SSLError, SSLZeroReturnError, TLSVersion
15
- from typing import AsyncIterable, Optional
16
+ from typing import Optional
16
17
 
17
18
  import construct
18
19
  from cryptography import x509
@@ -26,32 +27,54 @@ from pymobiledevice3 import usbmux
26
27
  from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_mobdev2
27
28
  from pymobiledevice3.ca import generate_pairing_cert_chain
28
29
  from pymobiledevice3.common import get_home_folder
29
- from pymobiledevice3.exceptions import BadDevError, CannotStopSessionError, ConnectionFailedError, \
30
- ConnectionTerminatedError, DeviceNotFoundError, FatalPairingError, GetProhibitedError, IncorrectModeError, \
31
- InvalidConnectionError, InvalidHostIDError, InvalidServiceError, LockdownError, MissingValueError, \
32
- NoDeviceConnectedError, NotPairedError, PairingDialogResponsePendingError, PairingError, PasswordRequiredError, \
33
- PyMobileDevice3Exception, SetProhibitedError, StartServiceError, UserDeniedPairingError
30
+ from pymobiledevice3.exceptions import (
31
+ BadDevError,
32
+ CannotStopSessionError,
33
+ ConnectionFailedError,
34
+ ConnectionTerminatedError,
35
+ DeviceNotFoundError,
36
+ FatalPairingError,
37
+ GetProhibitedError,
38
+ IncorrectModeError,
39
+ InvalidConnectionError,
40
+ InvalidHostIDError,
41
+ InvalidServiceError,
42
+ LockdownError,
43
+ MissingValueError,
44
+ NoDeviceConnectedError,
45
+ NotPairedError,
46
+ PairingDialogResponsePendingError,
47
+ PairingError,
48
+ PasswordRequiredError,
49
+ PyMobileDevice3Exception,
50
+ SetProhibitedError,
51
+ StartServiceError,
52
+ UserDeniedPairingError,
53
+ )
34
54
  from pymobiledevice3.irecv_devices import IRECV_DEVICES
35
55
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
36
- from pymobiledevice3.pair_records import create_pairing_records_cache_folder, generate_host_id, \
37
- get_preferred_pair_record
56
+ from pymobiledevice3.pair_records import (
57
+ create_pairing_records_cache_folder,
58
+ generate_host_id,
59
+ get_preferred_pair_record,
60
+ )
38
61
  from pymobiledevice3.service_connection import ServiceConnection
39
62
  from pymobiledevice3.usbmux import PlistMuxConnection
40
63
 
41
- SYSTEM_BUID = '30142955-444094379208051516'
42
- RESTORED_SERVICE_TYPE = 'com.apple.mobile.restored'
64
+ SYSTEM_BUID = "30142955-444094379208051516"
65
+ RESTORED_SERVICE_TYPE = "com.apple.mobile.restored"
43
66
 
44
- DEFAULT_LABEL = 'pymobiledevice3'
67
+ DEFAULT_LABEL = "pymobiledevice3"
45
68
  SERVICE_PORT = 62078
46
69
 
47
70
 
48
71
  class DeviceClass(Enum):
49
- IPHONE = 'iPhone'
50
- IPAD = 'iPad'
51
- IPOD = 'iPod'
52
- WATCH = 'Watch'
53
- APPLE_TV = 'AppleTV'
54
- UNKNOWN = 'Unknown'
72
+ IPHONE = "iPhone"
73
+ IPAD = "iPad"
74
+ IPOD = "iPod"
75
+ WATCH = "Watch"
76
+ APPLE_TV = "AppleTV"
77
+ UNKNOWN = "Unknown"
55
78
 
56
79
 
57
80
  def _reconnect_on_remote_close(f):
@@ -60,7 +83,7 @@ def _reconnect_on_remote_close(f):
60
83
  transmitted). When this happens, we'll attempt to reconnect.
61
84
  """
62
85
 
63
- def _reconnect(self: 'LockdownClient'):
86
+ def _reconnect(self: "LockdownClient"):
64
87
  self._reestablish_connection()
65
88
  self.validate_pairing()
66
89
 
@@ -72,7 +95,7 @@ def _reconnect_on_remote_close(f):
72
95
  _reconnect(args[0])
73
96
  return f(*args, **kwargs)
74
97
  except ConnectionAbortedError:
75
- if sys.platform != 'win32':
98
+ if sys.platform != "win32":
76
99
  raise
77
100
  _reconnect(args[0])
78
101
  return f(*args, **kwargs)
@@ -81,9 +104,17 @@ def _reconnect_on_remote_close(f):
81
104
 
82
105
 
83
106
  class LockdownClient(ABC, LockdownServiceProvider):
84
- def __init__(self, service: ServiceConnection, host_id: str, identifier: str = None,
85
- label: str = DEFAULT_LABEL, system_buid: str = SYSTEM_BUID, pair_record: Optional[dict] = None,
86
- pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT):
107
+ def __init__(
108
+ self,
109
+ service: ServiceConnection,
110
+ host_id: str,
111
+ identifier: Optional[str] = None,
112
+ label: str = DEFAULT_LABEL,
113
+ system_buid: str = SYSTEM_BUID,
114
+ pair_record: Optional[dict] = None,
115
+ pairing_records_cache_folder: Optional[Path] = None,
116
+ port: int = SERVICE_PORT,
117
+ ):
87
118
  """
88
119
  Create a LockdownClient instance
89
120
 
@@ -109,21 +140,31 @@ class LockdownClient(ABC, LockdownServiceProvider):
109
140
  self.pairing_records_cache_folder = pairing_records_cache_folder
110
141
  self.port = port
111
142
 
112
- if self.query_type() != 'com.apple.mobile.lockdown':
143
+ if self.query_type() != "com.apple.mobile.lockdown":
113
144
  raise IncorrectModeError()
114
145
 
115
146
  self.all_values = self.get_value()
116
- self.udid = self.all_values.get('UniqueDeviceID')
117
- self.unique_chip_id = self.all_values.get('UniqueChipID')
118
- self.device_public_key = self.all_values.get('DevicePublicKey')
119
- self.product_type = self.all_values.get('ProductType')
147
+ self.udid = self.all_values.get("UniqueDeviceID")
148
+ self.unique_chip_id = self.all_values.get("UniqueChipID")
149
+ self.device_public_key = self.all_values.get("DevicePublicKey")
150
+ self.product_type = self.all_values.get("ProductType")
120
151
 
121
152
  @classmethod
122
- def create(cls, service: ServiceConnection, identifier: str = None, system_buid: str = SYSTEM_BUID,
123
- label: str = DEFAULT_LABEL, autopair: bool = True, pair_timeout: float = None,
124
- local_hostname: str = None,
125
- pair_record: Optional[dict] = None, pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT,
126
- private_key: Optional[RSAPrivateKey] = None, **cls_specific_args):
153
+ def create(
154
+ cls,
155
+ service: ServiceConnection,
156
+ identifier: Optional[str] = None,
157
+ system_buid: str = SYSTEM_BUID,
158
+ label: str = DEFAULT_LABEL,
159
+ autopair: bool = True,
160
+ pair_timeout: Optional[float] = None,
161
+ local_hostname: Optional[str] = None,
162
+ pair_record: Optional[dict] = None,
163
+ pairing_records_cache_folder: Optional[Path] = None,
164
+ port: int = SERVICE_PORT,
165
+ private_key: Optional[RSAPrivateKey] = None,
166
+ **cls_specific_args,
167
+ ):
127
168
  """
128
169
  Create a LockdownClient instance
129
170
 
@@ -145,23 +186,32 @@ class LockdownClient(ABC, LockdownServiceProvider):
145
186
  pairing_records_cache_folder = create_pairing_records_cache_folder(pairing_records_cache_folder)
146
187
 
147
188
  lockdown_client = cls(
148
- service, host_id=host_id, identifier=identifier, label=label, system_buid=system_buid,
149
- pair_record=pair_record, pairing_records_cache_folder=pairing_records_cache_folder, port=port,
150
- **cls_specific_args)
189
+ service,
190
+ host_id=host_id,
191
+ identifier=identifier,
192
+ label=label,
193
+ system_buid=system_buid,
194
+ pair_record=pair_record,
195
+ pairing_records_cache_folder=pairing_records_cache_folder,
196
+ port=port,
197
+ **cls_specific_args,
198
+ )
151
199
  lockdown_client._handle_autopair(autopair, pair_timeout, private_key=private_key)
152
200
  return lockdown_client
153
201
 
154
202
  def __repr__(self) -> str:
155
- return f'<{self.__class__.__name__} ID:{self.identifier} VERSION:{self.product_version} ' \
156
- f'TYPE:{self.product_type} PAIRED:{self.paired}>'
203
+ return (
204
+ f"<{self.__class__.__name__} ID:{self.identifier} VERSION:{self.product_version} "
205
+ f"TYPE:{self.product_type} PAIRED:{self.paired}>"
206
+ )
157
207
 
158
- def __enter__(self) -> 'LockdownClient':
208
+ def __enter__(self) -> "LockdownClient":
159
209
  return self
160
210
 
161
211
  def __exit__(self, exc_type, exc_val, exc_tb) -> None:
162
212
  self.close()
163
213
 
164
- async def __aenter__(self) -> 'LockdownClient':
214
+ async def __aenter__(self) -> "LockdownClient":
165
215
  return self
166
216
 
167
217
  async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
@@ -169,24 +219,24 @@ class LockdownClient(ABC, LockdownServiceProvider):
169
219
 
170
220
  @property
171
221
  def product_version(self) -> str:
172
- return self.all_values.get('ProductVersion') or '1.0'
222
+ return self.all_values.get("ProductVersion") or "1.0"
173
223
 
174
224
  @property
175
225
  def device_class(self) -> DeviceClass:
176
226
  try:
177
- return DeviceClass(self.all_values.get('DeviceClass'))
227
+ return DeviceClass(self.all_values.get("DeviceClass"))
178
228
  except ValueError:
179
- return DeviceClass('Unknown')
229
+ return DeviceClass("Unknown")
180
230
 
181
231
  @property
182
232
  def wifi_mac_address(self) -> str:
183
- return self.all_values.get('WiFiAddress')
233
+ return self.all_values.get("WiFiAddress")
184
234
 
185
235
  @property
186
236
  def short_info(self) -> dict:
187
- keys_to_copy = ['DeviceClass', 'DeviceName', 'BuildVersion', 'ProductVersion', 'ProductType', 'UniqueDeviceID']
237
+ keys_to_copy = ["DeviceClass", "DeviceName", "BuildVersion", "ProductVersion", "ProductType", "UniqueDeviceID"]
188
238
  result = {
189
- 'Identifier': self.identifier,
239
+ "Identifier": self.identifier,
190
240
  }
191
241
  for key in keys_to_copy:
192
242
  result[key] = self.all_values.get(key)
@@ -194,65 +244,65 @@ class LockdownClient(ABC, LockdownServiceProvider):
194
244
 
195
245
  @property
196
246
  def share_iphone_analytics_enabled(self) -> bool:
197
- return self.get_value('com.apple.MobileDeviceCrashCopy', 'ShouldSubmit')
247
+ return self.get_value("com.apple.MobileDeviceCrashCopy", "ShouldSubmit")
198
248
 
199
249
  @property
200
250
  def assistive_touch(self) -> bool:
201
251
  """AssistiveTouch (the on-screen software home button)"""
202
- return bool(self.get_value('com.apple.Accessibility').get('AssistiveTouchEnabledByiTunes', 0))
252
+ return bool(self.get_value("com.apple.Accessibility").get("AssistiveTouchEnabledByiTunes", 0))
203
253
 
204
254
  @assistive_touch.setter
205
255
  def assistive_touch(self, value: bool) -> None:
206
256
  """AssistiveTouch (the on-screen software home button)"""
207
- self.set_value(int(value), 'com.apple.Accessibility', 'AssistiveTouchEnabledByiTunes')
257
+ self.set_value(int(value), "com.apple.Accessibility", "AssistiveTouchEnabledByiTunes")
208
258
 
209
259
  @property
210
260
  def voice_over(self) -> bool:
211
- return bool(self.get_value('com.apple.Accessibility').get('VoiceOverTouchEnabledByiTunes', 0))
261
+ return bool(self.get_value("com.apple.Accessibility").get("VoiceOverTouchEnabledByiTunes", 0))
212
262
 
213
263
  @voice_over.setter
214
264
  def voice_over(self, value: bool) -> None:
215
- self.set_value(int(value), 'com.apple.Accessibility', 'VoiceOverTouchEnabledByiTunes')
265
+ self.set_value(int(value), "com.apple.Accessibility", "VoiceOverTouchEnabledByiTunes")
216
266
 
217
267
  @property
218
268
  def invert_display(self) -> bool:
219
- return bool(self.get_value('com.apple.Accessibility').get('InvertDisplayEnabledByiTunes', 0))
269
+ return bool(self.get_value("com.apple.Accessibility").get("InvertDisplayEnabledByiTunes", 0))
220
270
 
221
271
  @invert_display.setter
222
272
  def invert_display(self, value: bool) -> None:
223
- self.set_value(int(value), 'com.apple.Accessibility', 'InvertDisplayEnabledByiTunes')
273
+ self.set_value(int(value), "com.apple.Accessibility", "InvertDisplayEnabledByiTunes")
224
274
 
225
275
  @property
226
276
  def enable_wifi_connections(self) -> bool:
227
- return self.get_value('com.apple.mobile.wireless_lockdown').get('EnableWifiConnections', False)
277
+ return self.get_value("com.apple.mobile.wireless_lockdown").get("EnableWifiConnections", False)
228
278
 
229
279
  @enable_wifi_connections.setter
230
280
  def enable_wifi_connections(self, value: bool) -> None:
231
- self.set_value(value, 'com.apple.mobile.wireless_lockdown', 'EnableWifiConnections')
281
+ self.set_value(value, "com.apple.mobile.wireless_lockdown", "EnableWifiConnections")
232
282
 
233
283
  @property
234
284
  def ecid(self) -> int:
235
- return self.all_values['UniqueChipID']
285
+ return self.all_values["UniqueChipID"]
236
286
 
237
287
  @property
238
288
  def date(self) -> datetime.datetime:
239
- return datetime.datetime.fromtimestamp(self.get_value(key='TimeIntervalSince1970'))
289
+ return datetime.datetime.fromtimestamp(self.get_value(key="TimeIntervalSince1970"))
240
290
 
241
291
  @property
242
292
  def language(self) -> str:
243
- return self.get_value(key='Language', domain='com.apple.international')
293
+ return self.get_value(key="Language", domain="com.apple.international")
244
294
 
245
295
  @property
246
296
  def locale(self) -> str:
247
- return self.get_value(key='Locale', domain='com.apple.international')
297
+ return self.get_value(key="Locale", domain="com.apple.international")
248
298
 
249
299
  @property
250
300
  def preflight_info(self) -> dict:
251
- return self.get_value(key='PreflightInfo')
301
+ return self.get_value(key="PreflightInfo")
252
302
 
253
303
  @property
254
304
  def firmware_preflight_info(self) -> dict:
255
- return self.get_value(key='FirmwarePreflightInfo')
305
+ return self.get_value(key="FirmwarePreflightInfo")
256
306
 
257
307
  @property
258
308
  def display_name(self) -> str:
@@ -280,35 +330,35 @@ class LockdownClient(ABC, LockdownServiceProvider):
280
330
 
281
331
  @property
282
332
  def developer_mode_status(self) -> bool:
283
- return self.get_value('com.apple.security.mac.amfi', 'DeveloperModeStatus')
333
+ return self.get_value("com.apple.security.mac.amfi", "DeveloperModeStatus")
284
334
 
285
335
  def query_type(self) -> str:
286
- return self._request('QueryType').get('Type')
336
+ return self._request("QueryType").get("Type")
287
337
 
288
338
  def set_language(self, language: str) -> None:
289
- self.set_value(language, key='Language', domain='com.apple.international')
339
+ self.set_value(language, key="Language", domain="com.apple.international")
290
340
 
291
341
  def set_locale(self, locale: str) -> None:
292
- self.set_value(locale, key='Locale', domain='com.apple.international')
342
+ self.set_value(locale, key="Locale", domain="com.apple.international")
293
343
 
294
344
  def set_timezone(self, timezone: str) -> None:
295
- self.set_value(timezone, key='TimeZone')
345
+ self.set_value(timezone, key="TimeZone")
296
346
 
297
347
  def set_uses24hClock(self, value: bool) -> None:
298
- self.set_value(value, key='Uses24HourClock')
348
+ self.set_value(value, key="Uses24HourClock")
299
349
 
300
350
  @_reconnect_on_remote_close
301
351
  def enter_recovery(self):
302
- return self._request('EnterRecovery')
352
+ return self._request("EnterRecovery")
303
353
 
304
354
  def stop_session(self) -> dict:
305
355
  if self.session_id and self.service:
306
- response = self._request('StopSession', {'SessionID': self.session_id})
356
+ response = self._request("StopSession", {"SessionID": self.session_id})
307
357
  self.session_id = None
308
- if not response or response.get('Result') != 'Success':
358
+ if not response or response.get("Result") != "Success":
309
359
  raise CannotStopSessionError()
310
360
  return response
311
- raise PyMobileDevice3Exception('No active session')
361
+ raise PyMobileDevice3Exception("No active session")
312
362
 
313
363
  def validate_pairing(self) -> bool:
314
364
  if self.pair_record is None:
@@ -317,24 +367,24 @@ class LockdownClient(ABC, LockdownServiceProvider):
317
367
  if self.pair_record is None:
318
368
  return False
319
369
 
320
- if (Version(self.product_version) < Version('7.0')) and (self.device_class != DeviceClass.WATCH):
370
+ if (Version(self.product_version) < Version("7.0")) and (self.device_class != DeviceClass.WATCH):
321
371
  try:
322
- self._request('ValidatePair', {'PairRecord': self.pair_record})
372
+ self._request("ValidatePair", {"PairRecord": self.pair_record})
323
373
  except PairingError:
324
374
  return False
325
375
 
326
- self.host_id = self.pair_record.get('HostID', self.host_id)
327
- self.system_buid = self.pair_record.get('SystemBUID', self.system_buid)
376
+ self.host_id = self.pair_record.get("HostID", self.host_id)
377
+ self.system_buid = self.pair_record.get("SystemBUID", self.system_buid)
328
378
 
329
379
  try:
330
- start_session = self._request('StartSession', {'HostID': self.host_id, 'SystemBUID': self.system_buid})
380
+ start_session = self._request("StartSession", {"HostID": self.host_id, "SystemBUID": self.system_buid})
331
381
  except (InvalidHostIDError, InvalidConnectionError):
332
382
  # no host id means there is no such pairing record
333
383
  return False
334
384
 
335
- self.session_id = start_session.get('SessionID')
336
- if start_session.get('EnableSessionSSL'):
337
- if (Version(self.product_version) < Version('5.0')) and (self.device_class != DeviceClass.WATCH):
385
+ self.session_id = start_session.get("SessionID")
386
+ if start_session.get("EnableSessionSSL"):
387
+ if (Version(self.product_version) < Version("5.0")) and (self.device_class != DeviceClass.WATCH):
338
388
  # TLS v1 is the protocol required for versions prior to iOS 5
339
389
  self.service.min_ssl_proto = TLSVersion.SSLv3
340
390
  self.service.max_ssl_proto = TLSVersion.TLSv1
@@ -351,43 +401,48 @@ class LockdownClient(ABC, LockdownServiceProvider):
351
401
 
352
402
  # reload data after pairing
353
403
  self.all_values = self.get_value()
354
- self.udid = self.all_values.get('UniqueDeviceID')
404
+ self.udid = self.all_values.get("UniqueDeviceID")
355
405
 
356
406
  return True
357
407
 
358
408
  @_reconnect_on_remote_close
359
- def pair(self, timeout: float = None, private_key: Optional[RSAPrivateKey] = None) -> None:
360
- self.device_public_key = self.get_value('', 'DevicePublicKey')
409
+ def pair(self, timeout: Optional[float] = None, private_key: Optional[RSAPrivateKey] = None) -> None:
410
+ self.device_public_key = self.get_value("", "DevicePublicKey")
361
411
  if not self.device_public_key:
362
- self.logger.error('Unable to retrieve DevicePublicKey')
412
+ self.logger.error("Unable to retrieve DevicePublicKey")
363
413
  self.service.close()
364
414
  raise PairingError()
365
415
 
366
- self.logger.info('Creating host key & certificate')
416
+ self.logger.info("Creating host key & certificate")
367
417
  host_cert_pem, host_key_pem, device_cert_pem, root_cert_pem, root_key_pem = generate_pairing_cert_chain(
368
418
  self.device_public_key,
369
- private_key=private_key
419
+ private_key=private_key,
370
420
  # TODO: consider parsing product_version to support iOS < 4
371
421
  )
372
422
 
373
- pair_record = {'DeviceCertificate': device_cert_pem,
374
- 'HostCertificate': host_cert_pem,
375
- 'HostID': self.host_id,
376
- 'RootCertificate': root_cert_pem,
377
- 'RootPrivateKey': root_key_pem,
378
- 'WiFiMACAddress': self.wifi_mac_address,
379
- 'SystemBUID': self.system_buid}
423
+ pair_record = {
424
+ "DeviceCertificate": device_cert_pem,
425
+ "HostCertificate": host_cert_pem,
426
+ "HostID": self.host_id,
427
+ "RootCertificate": root_cert_pem,
428
+ "RootPrivateKey": root_key_pem,
429
+ "WiFiMACAddress": self.wifi_mac_address,
430
+ "SystemBUID": self.system_buid,
431
+ }
380
432
 
381
- pair_options = {'PairRecord': pair_record, 'ProtocolVersion': '2',
382
- 'PairingOptions': {'ExtendedPairingErrors': True}}
433
+ pair_options = {
434
+ "PairRecord": pair_record,
435
+ "ProtocolVersion": "2",
436
+ "PairingOptions": {"ExtendedPairingErrors": True},
437
+ }
383
438
 
384
439
  pair = self._request_pair(pair_options, timeout=timeout)
385
440
 
386
- pair_record['HostPrivateKey'] = host_key_pem
387
- escrow_bag = pair.get('EscrowBag')
441
+ pair_record["HostPrivateKey"] = host_key_pem
442
+ escrow_bag = pair.get("EscrowBag")
388
443
 
389
444
  if escrow_bag is not None:
390
- pair_record['EscrowBag'] = pair.get('EscrowBag')
445
+ pair_record["EscrowBag"] = pair.get("EscrowBag")
391
446
 
392
447
  self.pair_record = pair_record
393
448
  self.save_pair_record()
@@ -395,140 +450,150 @@ class LockdownClient(ABC, LockdownServiceProvider):
395
450
 
396
451
  @_reconnect_on_remote_close
397
452
  def pair_supervised(self, keybag_file: Path, timeout: Optional[float] = None) -> None:
398
- with open(keybag_file, 'rb') as keybag_file:
453
+ with open(keybag_file, "rb") as keybag_file:
399
454
  keybag_file = keybag_file.read()
400
455
  private_key = serialization.load_pem_private_key(keybag_file, password=None)
401
456
  cer = x509.load_pem_x509_certificate(keybag_file)
402
457
  public_key = cer.public_bytes(Encoding.DER)
403
458
 
404
- self.device_public_key = self.get_value('', 'DevicePublicKey')
459
+ self.device_public_key = self.get_value("", "DevicePublicKey")
405
460
  if not self.device_public_key:
406
- self.logger.error('Unable to retrieve DevicePublicKey')
461
+ self.logger.error("Unable to retrieve DevicePublicKey")
407
462
  self.service.close()
408
463
  raise PairingError()
409
464
 
410
- self.logger.info('Creating host key & certificate')
465
+ self.logger.info("Creating host key & certificate")
411
466
  host_cert_pem, host_key_pem, device_cert_pem, root_cert_pem, root_key_pem = generate_pairing_cert_chain(
412
467
  self.device_public_key
413
468
  # TODO: consider parsing product_version to support iOS < 4
414
469
  )
415
470
 
416
- pair_record = {'DeviceCertificate': device_cert_pem,
417
- 'HostCertificate': host_cert_pem,
418
- 'HostID': self.host_id,
419
- 'RootCertificate': root_cert_pem,
420
- 'RootPrivateKey': root_key_pem,
421
- 'WiFiMACAddress': self.wifi_mac_address,
422
- 'SystemBUID': self.system_buid}
471
+ pair_record = {
472
+ "DeviceCertificate": device_cert_pem,
473
+ "HostCertificate": host_cert_pem,
474
+ "HostID": self.host_id,
475
+ "RootCertificate": root_cert_pem,
476
+ "RootPrivateKey": root_key_pem,
477
+ "WiFiMACAddress": self.wifi_mac_address,
478
+ "SystemBUID": self.system_buid,
479
+ }
423
480
 
424
- pair_options = {'PairRecord': pair_record, 'ProtocolVersion': '2',
425
- 'PairingOptions': {
426
- 'SupervisorCertificate': public_key,
427
- 'ExtendedPairingErrors': True}}
481
+ pair_options = {
482
+ "PairRecord": pair_record,
483
+ "ProtocolVersion": "2",
484
+ "PairingOptions": {"SupervisorCertificate": public_key, "ExtendedPairingErrors": True},
485
+ }
428
486
 
429
487
  # first pair with SupervisorCertificate as PairingOptions to get PairingChallenge
430
488
  pair = self._request_pair(pair_options, timeout=timeout)
431
- if pair.get('Error') == 'MCChallengeRequired':
432
- extended_response = pair.get('ExtendedResponse')
489
+ if pair.get("Error") == "MCChallengeRequired":
490
+ extended_response = pair.get("ExtendedResponse")
433
491
  if extended_response is not None:
434
- pairing_challenge = extended_response.get('PairingChallenge')
435
- signed_response = PKCS7SignatureBuilder().set_data(pairing_challenge).add_signer(
436
- cer, private_key, hashes.SHA256()).sign(Encoding.DER, [PKCS7Options.Binary])
437
- pair_options = {'PairRecord': pair_record, 'ProtocolVersion': '2', 'PairingOptions': {
438
- 'ChallengeResponse': signed_response, 'ExtendedPairingErrors': True}}
492
+ pairing_challenge = extended_response.get("PairingChallenge")
493
+ signed_response = (
494
+ PKCS7SignatureBuilder()
495
+ .set_data(pairing_challenge)
496
+ .add_signer(cer, private_key, hashes.SHA256())
497
+ .sign(Encoding.DER, [PKCS7Options.Binary])
498
+ )
499
+ pair_options = {
500
+ "PairRecord": pair_record,
501
+ "ProtocolVersion": "2",
502
+ "PairingOptions": {"ChallengeResponse": signed_response, "ExtendedPairingErrors": True},
503
+ }
439
504
  # second pair with Response to Challenge
440
505
  pair = self._request_pair(pair_options, timeout=timeout)
441
506
 
442
- pair_record['HostPrivateKey'] = host_key_pem
443
- escrow_bag = pair.get('EscrowBag')
507
+ pair_record["HostPrivateKey"] = host_key_pem
508
+ escrow_bag = pair.get("EscrowBag")
444
509
 
445
510
  if escrow_bag is not None:
446
- pair_record['EscrowBag'] = pair.get('EscrowBag')
511
+ pair_record["EscrowBag"] = pair.get("EscrowBag")
447
512
 
448
513
  self.pair_record = pair_record
449
514
  self.save_pair_record()
450
515
  self.paired = True
451
516
 
452
517
  @_reconnect_on_remote_close
453
- def unpair(self, host_id: str = None) -> None:
454
- pair_record = self.pair_record if host_id is None else {'HostID': host_id}
455
- self._request('Unpair', {'PairRecord': pair_record, 'ProtocolVersion': '2'}, verify_request=False)
518
+ def unpair(self, host_id: Optional[str] = None) -> None:
519
+ pair_record = self.pair_record if host_id is None else {"HostID": host_id}
520
+ self._request("Unpair", {"PairRecord": pair_record, "ProtocolVersion": "2"}, verify_request=False)
456
521
 
457
522
  @_reconnect_on_remote_close
458
523
  def reset_pairing(self):
459
- return self._request('ResetPairing', {'FullReset': True})
524
+ return self._request("ResetPairing", {"FullReset": True})
460
525
 
461
526
  @_reconnect_on_remote_close
462
- def get_value(self, domain: str = None, key: str = None):
527
+ def get_value(self, domain: Optional[str] = None, key: Optional[str] = None):
463
528
  options = {}
464
529
 
465
530
  if domain:
466
- options['Domain'] = domain
531
+ options["Domain"] = domain
467
532
  if key:
468
- options['Key'] = key
533
+ options["Key"] = key
469
534
 
470
- res = self._request('GetValue', options)
535
+ res = self._request("GetValue", options)
471
536
  if res:
472
- r = res.get('Value')
473
- if hasattr(r, 'data'):
537
+ r = res.get("Value")
538
+ if hasattr(r, "data"):
474
539
  return r.data
475
540
  return r
476
541
 
477
542
  @_reconnect_on_remote_close
478
- def remove_value(self, domain: str = None, key: str = None) -> dict:
543
+ def remove_value(self, domain: Optional[str] = None, key: Optional[str] = None) -> dict:
479
544
  options = {}
480
545
 
481
546
  if domain:
482
- options['Domain'] = domain
547
+ options["Domain"] = domain
483
548
  if key:
484
- options['Key'] = key
549
+ options["Key"] = key
485
550
 
486
- return self._request('RemoveValue', options)
551
+ return self._request("RemoveValue", options)
487
552
 
488
553
  @_reconnect_on_remote_close
489
- def set_value(self, value, domain: str = None, key: str = None) -> dict:
554
+ def set_value(self, value, domain: Optional[str] = None, key: Optional[str] = None) -> dict:
490
555
  options = {}
491
556
 
492
557
  if domain:
493
- options['Domain'] = domain
558
+ options["Domain"] = domain
494
559
  if key:
495
- options['Key'] = key
560
+ options["Key"] = key
496
561
 
497
- options['Value'] = value
498
- return self._request('SetValue', options)
562
+ options["Value"] = value
563
+ return self._request("SetValue", options)
499
564
 
500
565
  def get_service_connection_attributes(self, name: str, include_escrow_bag: bool = False) -> dict:
501
566
  if not self.paired:
502
567
  raise NotPairedError()
503
568
 
504
- options = {'Service': name}
569
+ options = {"Service": name}
505
570
  if include_escrow_bag:
506
- options['EscrowBag'] = self.pair_record['EscrowBag']
571
+ options["EscrowBag"] = self.pair_record["EscrowBag"]
507
572
 
508
- response = self._request('StartService', options)
509
- if not response or response.get('Error'):
510
- if response.get('Error', '') == 'PasswordProtected':
573
+ response = self._request("StartService", options)
574
+ if not response or response.get("Error"):
575
+ if response.get("Error", "") == "PasswordProtected":
511
576
  raise PasswordRequiredError(
512
- 'your device is protected with password, please enter password in device and try again')
513
- raise StartServiceError(response.get('Error'))
577
+ "your device is protected with password, please enter password in device and try again"
578
+ )
579
+ raise StartServiceError(response.get("Error"))
514
580
  return response
515
581
 
516
582
  @_reconnect_on_remote_close
517
583
  def start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
518
584
  attr = self.get_service_connection_attributes(name, include_escrow_bag=include_escrow_bag)
519
- service_connection = self._create_service_connection(attr['Port'])
585
+ service_connection = self._create_service_connection(attr["Port"])
520
586
 
521
- if attr.get('EnableServiceSSL', False):
587
+ if attr.get("EnableServiceSSL", False):
522
588
  with self.ssl_file() as f:
523
589
  service_connection.ssl_start(f)
524
590
  return service_connection
525
591
 
526
- async def aio_start_lockdown_service(
527
- self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
592
+ async def aio_start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
528
593
  attr = self.get_service_connection_attributes(name, include_escrow_bag=include_escrow_bag)
529
- service_connection = self._create_service_connection(attr['Port'])
594
+ service_connection = self._create_service_connection(attr["Port"])
530
595
 
531
- if attr.get('EnableServiceSSL', False):
596
+ if attr.get("EnableServiceSSL", False):
532
597
  with self.ssl_file() as f:
533
598
  await service_connection.aio_ssl_start(f)
534
599
  return service_connection
@@ -538,13 +603,13 @@ class LockdownClient(ABC, LockdownServiceProvider):
538
603
 
539
604
  @contextmanager
540
605
  def ssl_file(self) -> str:
541
- cert_pem = self.pair_record['HostCertificate']
542
- private_key_pem = self.pair_record['HostPrivateKey']
606
+ cert_pem = self.pair_record["HostCertificate"]
607
+ private_key_pem = self.pair_record["HostPrivateKey"]
543
608
 
544
609
  # use delete=False and manage the deletion ourselves because Windows
545
610
  # cannot use in-use files
546
- with tempfile.NamedTemporaryFile('w+b', delete=False) as f:
547
- f.write(cert_pem + b'\n' + private_key_pem)
611
+ with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
612
+ f.write(cert_pem + b"\n" + private_key_pem)
548
613
  filename = f.name
549
614
 
550
615
  try:
@@ -567,54 +632,56 @@ class LockdownClient(ABC, LockdownServiceProvider):
567
632
 
568
633
  @abstractmethod
569
634
  def _create_service_connection(self, port: int) -> ServiceConnection:
570
- """ Used to establish a new ServiceConnection to a given port """
635
+ """Used to establish a new ServiceConnection to a given port"""
571
636
  pass
572
637
 
573
638
  def _request(self, request: str, options: Optional[dict] = None, verify_request: bool = True) -> dict:
574
- message = {'Label': self.label, 'Request': request}
639
+ message = {"Label": self.label, "Request": request}
575
640
  if options:
576
641
  message.update(options)
577
642
  response = self.service.send_recv_plist(message)
578
643
 
579
- if verify_request and response.get('Request') != request:
580
- if response.get('Type') == RESTORED_SERVICE_TYPE:
581
- raise IncorrectModeError(f'Incorrect mode returned. Got: {response}')
582
- raise LockdownError(f'Incorrect response returned. Got: {response}')
644
+ if verify_request and response.get("Request") != request:
645
+ if response.get("Type") == RESTORED_SERVICE_TYPE:
646
+ raise IncorrectModeError(f"Incorrect mode returned. Got: {response}")
647
+ raise LockdownError(f"Incorrect response returned. Got: {response}")
583
648
 
584
- error = response.get('Error')
649
+ error = response.get("Error")
585
650
  if error is not None:
586
651
  # return response if supervisor cert challenge is required, to work with pair_supervisor
587
- if error == 'MCChallengeRequired':
652
+ if error == "MCChallengeRequired":
588
653
  return response
589
- exception_errors = {'PasswordProtected': PasswordRequiredError,
590
- 'PairingDialogResponsePending': PairingDialogResponsePendingError,
591
- 'UserDeniedPairing': UserDeniedPairingError,
592
- 'InvalidHostID': InvalidHostIDError,
593
- 'GetProhibited': GetProhibitedError,
594
- 'SetProhibited': SetProhibitedError,
595
- 'MissingValue': MissingValueError,
596
- 'InvalidService': InvalidServiceError,
597
- 'InvalidConnection': InvalidConnectionError, }
654
+ exception_errors = {
655
+ "PasswordProtected": PasswordRequiredError,
656
+ "PairingDialogResponsePending": PairingDialogResponsePendingError,
657
+ "UserDeniedPairing": UserDeniedPairingError,
658
+ "InvalidHostID": InvalidHostIDError,
659
+ "GetProhibited": GetProhibitedError,
660
+ "SetProhibited": SetProhibitedError,
661
+ "MissingValue": MissingValueError,
662
+ "InvalidService": InvalidServiceError,
663
+ "InvalidConnection": InvalidConnectionError,
664
+ }
598
665
  raise exception_errors.get(error, LockdownError)(error, self.identifier)
599
666
 
600
667
  # iOS < 5: 'Error' is not present, so we need to check the 'Result' instead
601
- if response.get('Result') == 'Failure':
602
- raise LockdownError('', self.identifier)
668
+ if response.get("Result") == "Failure":
669
+ raise LockdownError("", self.identifier)
603
670
 
604
671
  return response
605
672
 
606
673
  def _request_pair(self, pair_options: dict, timeout: Optional[float] = None) -> dict:
607
674
  try:
608
- return self._request('Pair', pair_options)
675
+ return self._request("Pair", pair_options)
609
676
  except PairingDialogResponsePendingError:
610
677
  if timeout == 0:
611
678
  raise
612
679
 
613
- self.logger.info('waiting user pairing dialog...')
680
+ self.logger.info("waiting user pairing dialog...")
614
681
  start = time.time()
615
682
  while timeout is None or time.time() <= start + timeout:
616
683
  with suppress(PairingDialogResponsePendingError):
617
- return self._request('Pair', pair_options)
684
+ return self._request("Pair", pair_options)
618
685
  time.sleep(1)
619
686
  raise PairingDialogResponsePendingError()
620
687
 
@@ -623,7 +690,7 @@ class LockdownClient(ABC, LockdownServiceProvider):
623
690
  self.pair_record = get_preferred_pair_record(self.identifier, self.pairing_records_cache_folder)
624
691
 
625
692
  def save_pair_record(self) -> None:
626
- pair_record_file = self.pairing_records_cache_folder / f'{self.identifier}.plist'
693
+ pair_record_file = self.pairing_records_cache_folder / f"{self.identifier}.plist"
627
694
  pair_record_file.write_bytes(plistlib.dumps(self.pair_record))
628
695
 
629
696
  def _reestablish_connection(self) -> None:
@@ -632,29 +699,39 @@ class LockdownClient(ABC, LockdownServiceProvider):
632
699
 
633
700
 
634
701
  class UsbmuxLockdownClient(LockdownClient):
635
- def __init__(self, service: ServiceConnection, host_id: str, identifier: str = None,
636
- label: str = DEFAULT_LABEL, system_buid: str = SYSTEM_BUID, pair_record: Optional[dict] = None,
637
- pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT,
638
- usbmux_address: Optional[str] = None):
702
+ def __init__(
703
+ self,
704
+ service: ServiceConnection,
705
+ host_id: str,
706
+ identifier: Optional[str] = None,
707
+ label: str = DEFAULT_LABEL,
708
+ system_buid: str = SYSTEM_BUID,
709
+ pair_record: Optional[dict] = None,
710
+ pairing_records_cache_folder: Optional[Path] = None,
711
+ port: int = SERVICE_PORT,
712
+ usbmux_address: Optional[str] = None,
713
+ ):
639
714
  self.usbmux_address = usbmux_address
640
- super().__init__(service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder,
641
- port)
715
+ super().__init__(
716
+ service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder, port
717
+ )
642
718
 
643
719
  @property
644
720
  def short_info(self) -> dict:
645
721
  short_info = super().short_info
646
- short_info['ConnectionType'] = self.service.mux_device.connection_type
722
+ short_info["ConnectionType"] = self.service.mux_device.connection_type
647
723
  return short_info
648
724
 
649
725
  def fetch_pair_record(self) -> None:
650
726
  if self.identifier is not None:
651
- self.pair_record = get_preferred_pair_record(self.identifier, self.pairing_records_cache_folder,
652
- usbmux_address=self.usbmux_address)
727
+ self.pair_record = get_preferred_pair_record(
728
+ self.identifier, self.pairing_records_cache_folder, usbmux_address=self.usbmux_address
729
+ )
653
730
 
654
731
  def _create_service_connection(self, port: int) -> ServiceConnection:
655
- return ServiceConnection.create_using_usbmux(self.identifier, port,
656
- self.service.mux_device.connection_type,
657
- usbmux_address=self.usbmux_address)
732
+ return ServiceConnection.create_using_usbmux(
733
+ self.identifier, port, self.service.mux_device.connection_type, usbmux_address=self.usbmux_address
734
+ )
658
735
 
659
736
 
660
737
  class PlistUsbmuxLockdownClient(UsbmuxLockdownClient):
@@ -666,9 +743,19 @@ class PlistUsbmuxLockdownClient(UsbmuxLockdownClient):
666
743
 
667
744
 
668
745
  class TcpLockdownClient(LockdownClient):
669
- def __init__(self, service: ServiceConnection, host_id: str, hostname: str, identifier: str = None,
670
- label: str = DEFAULT_LABEL, system_buid: str = SYSTEM_BUID, pair_record: Optional[dict] = None,
671
- pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT, keep_alive: bool = True):
746
+ def __init__(
747
+ self,
748
+ service: ServiceConnection,
749
+ host_id: str,
750
+ hostname: str,
751
+ identifier: Optional[str] = None,
752
+ label: str = DEFAULT_LABEL,
753
+ system_buid: str = SYSTEM_BUID,
754
+ pair_record: Optional[dict] = None,
755
+ pairing_records_cache_folder: Optional[Path] = None,
756
+ port: int = SERVICE_PORT,
757
+ keep_alive: bool = True,
758
+ ):
672
759
  """
673
760
  Create a LockdownClient instance
674
761
 
@@ -683,8 +770,9 @@ class TcpLockdownClient(LockdownClient):
683
770
  :param port: lockdownd service port
684
771
  :param keep_alive: use keep-alive to get notified when the connection is lost
685
772
  """
686
- super().__init__(service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder,
687
- port)
773
+ super().__init__(
774
+ service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder, port
775
+ )
688
776
  self._keep_alive = keep_alive
689
777
  self.hostname = hostname
690
778
  self.identifier = hostname
@@ -696,21 +784,30 @@ class TcpLockdownClient(LockdownClient):
696
784
  class RemoteLockdownClient(LockdownClient):
697
785
  def _create_service_connection(self, port: int) -> ServiceConnection:
698
786
  raise NotImplementedError(
699
- 'RemoteXPC service connections should only be created using RemoteServiceDiscoveryService')
787
+ "RemoteXPC service connections should only be created using RemoteServiceDiscoveryService"
788
+ )
700
789
 
701
790
  def _handle_autopair(self, *args, **kwargs):
702
791
  # The RemoteXPC version of lockdown doesn't support pairing operations
703
792
  return None
704
793
 
705
794
  def pair(self, *args, **kwargs) -> None:
706
- raise NotImplementedError('RemoteXPC lockdown version does not support pairing operations')
707
-
708
- def unpair(self, timeout: float = None) -> None:
709
- raise NotImplementedError('RemoteXPC lockdown version does not support pairing operations')
710
-
711
- def __init__(self, service: ServiceConnection, host_id: str, identifier: str = None,
712
- label: str = DEFAULT_LABEL, system_buid: str = SYSTEM_BUID, pair_record: Optional[dict] = None,
713
- pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT):
795
+ raise NotImplementedError("RemoteXPC lockdown version does not support pairing operations")
796
+
797
+ def unpair(self, timeout: Optional[float] = None) -> None:
798
+ raise NotImplementedError("RemoteXPC lockdown version does not support pairing operations")
799
+
800
+ def __init__(
801
+ self,
802
+ service: ServiceConnection,
803
+ host_id: str,
804
+ identifier: Optional[str] = None,
805
+ label: str = DEFAULT_LABEL,
806
+ system_buid: str = SYSTEM_BUID,
807
+ pair_record: Optional[dict] = None,
808
+ pairing_records_cache_folder: Optional[Path] = None,
809
+ port: int = SERVICE_PORT,
810
+ ):
714
811
  """
715
812
  Create a LockdownClient instance
716
813
 
@@ -723,14 +820,24 @@ class RemoteLockdownClient(LockdownClient):
723
820
  :param pairing_records_cache_folder: Use the following location to search and save pair records
724
821
  :param port: lockdownd service port
725
822
  """
726
- super().__init__(service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder,
727
- port)
823
+ super().__init__(
824
+ service, host_id, identifier, label, system_buid, pair_record, pairing_records_cache_folder, port
825
+ )
728
826
 
729
827
 
730
- def create_using_usbmux(serial: str = None, identifier: str = None, label: str = DEFAULT_LABEL, autopair: bool = True,
731
- connection_type: str = None, pair_timeout: float = None, local_hostname: str = None,
732
- pair_record: Optional[dict] = None, pairing_records_cache_folder: Path = None,
733
- port: int = SERVICE_PORT, usbmux_address: Optional[str] = None) -> UsbmuxLockdownClient:
828
+ def create_using_usbmux(
829
+ serial: Optional[str] = None,
830
+ identifier: Optional[str] = None,
831
+ label: str = DEFAULT_LABEL,
832
+ autopair: bool = True,
833
+ connection_type: Optional[str] = None,
834
+ pair_timeout: Optional[float] = None,
835
+ local_hostname: Optional[str] = None,
836
+ pair_record: Optional[dict] = None,
837
+ pairing_records_cache_folder: Optional[Path] = None,
838
+ port: int = SERVICE_PORT,
839
+ usbmux_address: Optional[str] = None,
840
+ ) -> UsbmuxLockdownClient:
734
841
  """
735
842
  Create a UsbmuxLockdownClient instance
736
843
 
@@ -747,8 +854,9 @@ def create_using_usbmux(serial: str = None, identifier: str = None, label: str =
747
854
  :param usbmux_address: usbmuxd address
748
855
  :return: UsbmuxLockdownClient instance
749
856
  """
750
- service = ServiceConnection.create_using_usbmux(serial, port, connection_type=connection_type,
751
- usbmux_address=usbmux_address)
857
+ service = ServiceConnection.create_using_usbmux(
858
+ serial, port, connection_type=connection_type, usbmux_address=usbmux_address
859
+ )
752
860
  try:
753
861
  cls = UsbmuxLockdownClient
754
862
  with usbmux.create_mux(usbmux_address=usbmux_address) as client:
@@ -762,10 +870,17 @@ def create_using_usbmux(serial: str = None, identifier: str = None, label: str =
762
870
  identifier = service.mux_device.serial
763
871
 
764
872
  return cls.create(
765
- service, identifier=identifier, label=label, system_buid=system_buid, local_hostname=local_hostname,
766
- pair_record=pair_record, pairing_records_cache_folder=pairing_records_cache_folder,
873
+ service,
874
+ identifier=identifier,
875
+ label=label,
876
+ system_buid=system_buid,
877
+ local_hostname=local_hostname,
878
+ pair_record=pair_record,
879
+ pairing_records_cache_folder=pairing_records_cache_folder,
767
880
  pair_timeout=pair_timeout,
768
- autopair=autopair, usbmux_address=usbmux_address)
881
+ autopair=autopair,
882
+ usbmux_address=usbmux_address,
883
+ )
769
884
  except Exception:
770
885
  service.close()
771
886
  raise
@@ -783,15 +898,29 @@ def retry_create_using_usbmux(retry_timeout: Optional[float] = None, **kwargs) -
783
898
  while (retry_timeout is None) or (time.time() - start < retry_timeout):
784
899
  try:
785
900
  return create_using_usbmux(**kwargs)
786
- except (NoDeviceConnectedError, ConnectionFailedError, BadDevError, OSError, construct.core.StreamError,
787
- DeviceNotFoundError):
901
+ except (
902
+ NoDeviceConnectedError,
903
+ ConnectionFailedError,
904
+ BadDevError,
905
+ OSError,
906
+ construct.core.StreamError,
907
+ DeviceNotFoundError,
908
+ ):
788
909
  pass
789
910
 
790
911
 
791
- def create_using_tcp(hostname: str, identifier: str = None, label: str = DEFAULT_LABEL, autopair: bool = True,
792
- pair_timeout: float = None, local_hostname: str = None, pair_record: Optional[dict] = None,
793
- pairing_records_cache_folder: Path = None, port: int = SERVICE_PORT,
794
- keep_alive: bool = False) -> TcpLockdownClient:
912
+ def create_using_tcp(
913
+ hostname: str,
914
+ identifier: Optional[str] = None,
915
+ label: str = DEFAULT_LABEL,
916
+ autopair: bool = True,
917
+ pair_timeout: Optional[float] = None,
918
+ local_hostname: Optional[str] = None,
919
+ pair_record: Optional[dict] = None,
920
+ pairing_records_cache_folder: Optional[Path] = None,
921
+ port: int = SERVICE_PORT,
922
+ keep_alive: bool = False,
923
+ ) -> TcpLockdownClient:
795
924
  """
796
925
  Create a TcpLockdownClient instance
797
926
 
@@ -810,18 +939,34 @@ def create_using_tcp(hostname: str, identifier: str = None, label: str = DEFAULT
810
939
  service = ServiceConnection.create_using_tcp(hostname, port, keep_alive=keep_alive)
811
940
  try:
812
941
  return TcpLockdownClient.create(
813
- service, identifier=identifier, label=label, local_hostname=local_hostname, pair_record=pair_record,
814
- pairing_records_cache_folder=pairing_records_cache_folder, pair_timeout=pair_timeout, autopair=autopair,
815
- port=port, hostname=hostname, keep_alive=keep_alive)
942
+ service,
943
+ identifier=identifier,
944
+ label=label,
945
+ local_hostname=local_hostname,
946
+ pair_record=pair_record,
947
+ pairing_records_cache_folder=pairing_records_cache_folder,
948
+ pair_timeout=pair_timeout,
949
+ autopair=autopair,
950
+ port=port,
951
+ hostname=hostname,
952
+ keep_alive=keep_alive,
953
+ )
816
954
  except Exception:
817
955
  service.close()
818
956
  raise
819
957
 
820
958
 
821
- def create_using_remote(service: ServiceConnection, identifier: str = None, label: str = DEFAULT_LABEL,
822
- autopair: bool = True, pair_timeout: float = None, local_hostname: str = None,
823
- pair_record: Optional[dict] = None, pairing_records_cache_folder: Path = None,
824
- port: int = SERVICE_PORT) -> RemoteLockdownClient:
959
+ def create_using_remote(
960
+ service: ServiceConnection,
961
+ identifier: Optional[str] = None,
962
+ label: str = DEFAULT_LABEL,
963
+ autopair: bool = True,
964
+ pair_timeout: Optional[float] = None,
965
+ local_hostname: Optional[str] = None,
966
+ pair_record: Optional[dict] = None,
967
+ pairing_records_cache_folder: Optional[Path] = None,
968
+ port: int = SERVICE_PORT,
969
+ ) -> RemoteLockdownClient:
825
970
  """
826
971
  Create a TcpLockdownClient instance over RSD
827
972
 
@@ -838,35 +983,44 @@ def create_using_remote(service: ServiceConnection, identifier: str = None, labe
838
983
  """
839
984
  try:
840
985
  return RemoteLockdownClient.create(
841
- service, identifier=identifier, label=label, local_hostname=local_hostname, pair_record=pair_record,
842
- pairing_records_cache_folder=pairing_records_cache_folder, pair_timeout=pair_timeout, autopair=autopair,
843
- port=port)
986
+ service,
987
+ identifier=identifier,
988
+ label=label,
989
+ local_hostname=local_hostname,
990
+ pair_record=pair_record,
991
+ pairing_records_cache_folder=pairing_records_cache_folder,
992
+ pair_timeout=pair_timeout,
993
+ autopair=autopair,
994
+ port=port,
995
+ )
844
996
  except Exception:
845
997
  service.close()
846
998
  raise
847
999
 
848
1000
 
849
1001
  async def get_mobdev2_lockdowns(
850
- udid: Optional[str] = None, pair_records: Optional[Path] = None, only_paired: bool = False,
851
- timeout: float = DEFAULT_BONJOUR_TIMEOUT) \
852
- -> AsyncIterable[tuple[str, TcpLockdownClient]]:
1002
+ udid: Optional[str] = None,
1003
+ pair_records: Optional[Path] = None,
1004
+ only_paired: bool = False,
1005
+ timeout: float = DEFAULT_BONJOUR_TIMEOUT,
1006
+ ) -> AsyncIterable[tuple[str, TcpLockdownClient]]:
853
1007
  records = {}
854
1008
  if pair_records is None:
855
1009
  pair_records = get_home_folder()
856
- for file in pair_records.glob('*.plist'):
857
- if file.name.startswith('remote_'):
1010
+ for file in pair_records.glob("*.plist"):
1011
+ if file.name.startswith("remote_"):
858
1012
  # skip RemotePairing records
859
1013
  continue
860
- record_udid = file.parts[-1].strip('.plist')
1014
+ record_udid = file.parts[-1].strip(".plist")
861
1015
  if udid is not None and record_udid != udid:
862
1016
  continue
863
1017
  record = plistlib.loads(file.read_bytes())
864
- records[record['WiFiMACAddress']] = record
1018
+ records[record["WiFiMACAddress"]] = record
865
1019
 
866
1020
  for answer in await browse_mobdev2(timeout=timeout):
867
- if '@' not in answer.instance:
1021
+ if "@" not in answer.instance:
868
1022
  continue
869
- wifi_mac_address = answer.instance.split('@', 1)[0]
1023
+ wifi_mac_address = answer.instance.split("@", 1)[0]
870
1024
  record = records.get(wifi_mac_address)
871
1025
 
872
1026
  if only_paired and record is None: