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