pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__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.
Files changed (164) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. misc/understanding_idevice_protocol_layers.md +15 -10
  4. pymobiledevice3/__main__.py +317 -127
  5. pymobiledevice3/_version.py +22 -4
  6. pymobiledevice3/bonjour.py +358 -113
  7. pymobiledevice3/ca.py +253 -16
  8. pymobiledevice3/cli/activation.py +31 -23
  9. pymobiledevice3/cli/afc.py +49 -40
  10. pymobiledevice3/cli/amfi.py +16 -21
  11. pymobiledevice3/cli/apps.py +87 -42
  12. pymobiledevice3/cli/backup.py +160 -90
  13. pymobiledevice3/cli/bonjour.py +44 -40
  14. pymobiledevice3/cli/cli_common.py +204 -198
  15. pymobiledevice3/cli/companion_proxy.py +14 -14
  16. pymobiledevice3/cli/crash.py +105 -56
  17. pymobiledevice3/cli/developer/__init__.py +62 -0
  18. pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
  19. pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
  20. pymobiledevice3/cli/developer/arbitration.py +50 -0
  21. pymobiledevice3/cli/developer/condition.py +33 -0
  22. pymobiledevice3/cli/developer/core_device.py +294 -0
  23. pymobiledevice3/cli/developer/debugserver.py +244 -0
  24. pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
  25. pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
  26. pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
  27. pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
  28. pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
  29. pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
  30. pymobiledevice3/cli/developer/simulate_location.py +51 -0
  31. pymobiledevice3/cli/diagnostics/__init__.py +75 -0
  32. pymobiledevice3/cli/diagnostics/battery.py +47 -0
  33. pymobiledevice3/cli/idam.py +42 -0
  34. pymobiledevice3/cli/lockdown.py +108 -103
  35. pymobiledevice3/cli/mounter.py +158 -99
  36. pymobiledevice3/cli/notification.py +38 -26
  37. pymobiledevice3/cli/pcap.py +45 -24
  38. pymobiledevice3/cli/power_assertion.py +18 -17
  39. pymobiledevice3/cli/processes.py +17 -23
  40. pymobiledevice3/cli/profile.py +165 -109
  41. pymobiledevice3/cli/provision.py +35 -34
  42. pymobiledevice3/cli/remote.py +217 -129
  43. pymobiledevice3/cli/restore.py +159 -143
  44. pymobiledevice3/cli/springboard.py +63 -53
  45. pymobiledevice3/cli/syslog.py +193 -86
  46. pymobiledevice3/cli/usbmux.py +73 -33
  47. pymobiledevice3/cli/version.py +5 -7
  48. pymobiledevice3/cli/webinspector.py +376 -214
  49. pymobiledevice3/common.py +3 -1
  50. pymobiledevice3/exceptions.py +182 -58
  51. pymobiledevice3/irecv.py +52 -53
  52. pymobiledevice3/irecv_devices.py +1489 -464
  53. pymobiledevice3/lockdown.py +473 -275
  54. pymobiledevice3/lockdown_service_provider.py +15 -8
  55. pymobiledevice3/osu/os_utils.py +27 -9
  56. pymobiledevice3/osu/posix_util.py +34 -15
  57. pymobiledevice3/osu/win_util.py +14 -8
  58. pymobiledevice3/pair_records.py +102 -21
  59. pymobiledevice3/remote/common.py +8 -4
  60. pymobiledevice3/remote/core_device/app_service.py +94 -67
  61. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  62. pymobiledevice3/remote/core_device/device_info.py +5 -5
  63. pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
  64. pymobiledevice3/remote/core_device/file_service.py +53 -23
  65. pymobiledevice3/remote/remote_service_discovery.py +79 -45
  66. pymobiledevice3/remote/remotexpc.py +73 -44
  67. pymobiledevice3/remote/tunnel_service.py +442 -317
  68. pymobiledevice3/remote/utils.py +14 -13
  69. pymobiledevice3/remote/xpc_message.py +145 -125
  70. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  71. pymobiledevice3/resources/firmware_notifications.py +20 -16
  72. pymobiledevice3/resources/notifications.txt +144 -0
  73. pymobiledevice3/restore/asr.py +27 -27
  74. pymobiledevice3/restore/base_restore.py +110 -21
  75. pymobiledevice3/restore/consts.py +87 -66
  76. pymobiledevice3/restore/device.py +59 -12
  77. pymobiledevice3/restore/fdr.py +46 -48
  78. pymobiledevice3/restore/ftab.py +19 -19
  79. pymobiledevice3/restore/img4.py +163 -0
  80. pymobiledevice3/restore/mbn.py +587 -0
  81. pymobiledevice3/restore/recovery.py +151 -151
  82. pymobiledevice3/restore/restore.py +562 -544
  83. pymobiledevice3/restore/restore_options.py +131 -110
  84. pymobiledevice3/restore/restored_client.py +51 -31
  85. pymobiledevice3/restore/tss.py +385 -267
  86. pymobiledevice3/service_connection.py +252 -59
  87. pymobiledevice3/services/accessibilityaudit.py +202 -120
  88. pymobiledevice3/services/afc.py +962 -365
  89. pymobiledevice3/services/amfi.py +24 -30
  90. pymobiledevice3/services/companion.py +23 -19
  91. pymobiledevice3/services/crash_reports.py +71 -47
  92. pymobiledevice3/services/debugserver_applist.py +3 -3
  93. pymobiledevice3/services/device_arbitration.py +8 -8
  94. pymobiledevice3/services/device_link.py +101 -79
  95. pymobiledevice3/services/diagnostics.py +973 -967
  96. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  97. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  98. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  99. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  100. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  101. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  102. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  103. pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
  104. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  105. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  106. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  107. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  108. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  109. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  110. pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
  111. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  112. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  113. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  114. pymobiledevice3/services/file_relay.py +10 -10
  115. pymobiledevice3/services/heartbeat.py +9 -8
  116. pymobiledevice3/services/house_arrest.py +16 -15
  117. pymobiledevice3/services/idam.py +20 -0
  118. pymobiledevice3/services/installation_proxy.py +173 -81
  119. pymobiledevice3/services/lockdown_service.py +20 -10
  120. pymobiledevice3/services/misagent.py +22 -19
  121. pymobiledevice3/services/mobile_activation.py +147 -64
  122. pymobiledevice3/services/mobile_config.py +331 -294
  123. pymobiledevice3/services/mobile_image_mounter.py +141 -113
  124. pymobiledevice3/services/mobilebackup2.py +203 -145
  125. pymobiledevice3/services/notification_proxy.py +11 -11
  126. pymobiledevice3/services/os_trace.py +134 -74
  127. pymobiledevice3/services/pcapd.py +314 -302
  128. pymobiledevice3/services/power_assertion.py +10 -9
  129. pymobiledevice3/services/preboard.py +4 -4
  130. pymobiledevice3/services/remote_fetch_symbols.py +21 -14
  131. pymobiledevice3/services/remote_server.py +176 -146
  132. pymobiledevice3/services/restore_service.py +16 -16
  133. pymobiledevice3/services/screenshot.py +15 -12
  134. pymobiledevice3/services/simulate_location.py +7 -7
  135. pymobiledevice3/services/springboard.py +15 -15
  136. pymobiledevice3/services/syslog.py +5 -5
  137. pymobiledevice3/services/web_protocol/alert.py +11 -11
  138. pymobiledevice3/services/web_protocol/automation_session.py +251 -239
  139. pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
  140. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  141. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  142. pymobiledevice3/services/web_protocol/driver.py +114 -111
  143. pymobiledevice3/services/web_protocol/element.py +124 -111
  144. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  145. pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
  146. pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
  147. pymobiledevice3/services/web_protocol/switch_to.py +30 -27
  148. pymobiledevice3/services/webinspector.py +189 -155
  149. pymobiledevice3/tcp_forwarder.py +87 -69
  150. pymobiledevice3/tunneld/__init__.py +0 -0
  151. pymobiledevice3/tunneld/api.py +63 -0
  152. pymobiledevice3/tunneld/server.py +603 -0
  153. pymobiledevice3/usbmux.py +198 -147
  154. pymobiledevice3/utils.py +14 -11
  155. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
  156. pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
  157. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
  158. pymobiledevice3/cli/developer.py +0 -1215
  159. pymobiledevice3/cli/diagnostics.py +0 -99
  160. pymobiledevice3/tunneld.py +0 -524
  161. pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
  162. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
  163. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
  164. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,438 @@
1
+ import contextlib
2
+ import logging
3
+ import os
4
+ import posixpath
5
+ import shlex
6
+ from datetime import datetime
7
+ from enum import IntEnum
8
+ from pathlib import Path
9
+ from typing import Annotated, NamedTuple, Optional
10
+
11
+ import typer
12
+ from click.exceptions import BadParameter, MissingParameter, UsageError
13
+ from typer_injector import InjectingTyper
14
+
15
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json, user_requested_colored_output
16
+ from pymobiledevice3.cli.developer.dvt import core_profile_session, simulate_location, sysmon
17
+ from pymobiledevice3.exceptions import DvtDirListError, UnrecognizedSelectorError
18
+ from pymobiledevice3.services.dvt.dvt_secure_socket_proxy import DvtSecureSocketProxyService
19
+ from pymobiledevice3.services.dvt.instruments.activity_trace_tap import ActivityTraceTap, decode_message_format
20
+ from pymobiledevice3.services.dvt.instruments.application_listing import ApplicationListing
21
+ from pymobiledevice3.services.dvt.instruments.device_info import DeviceInfo
22
+ from pymobiledevice3.services.dvt.instruments.energy_monitor import EnergyMonitor
23
+ from pymobiledevice3.services.dvt.instruments.graphics import Graphics
24
+ from pymobiledevice3.services.dvt.instruments.network_monitor import ConnectionDetectionEvent, NetworkMonitor
25
+ from pymobiledevice3.services.dvt.instruments.notifications import Notifications
26
+ from pymobiledevice3.services.dvt.instruments.process_control import ProcessControl
27
+ from pymobiledevice3.services.dvt.instruments.screenshot import Screenshot
28
+ from pymobiledevice3.services.dvt.testmanaged.xcuitest import XCUITestService
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class MatchedProcessByPid(NamedTuple):
34
+ name: str
35
+ pid: int
36
+
37
+
38
+ cli = InjectingTyper(
39
+ name="dvt",
40
+ help="Drive DVT instrumentation APIs (process control, metrics, traces).",
41
+ no_args_is_help=True,
42
+ )
43
+
44
+ cli.add_typer(sysmon.cli)
45
+ cli.add_typer(core_profile_session.cli)
46
+ cli.add_typer(simulate_location.cli)
47
+
48
+
49
+ @cli.command("proclist")
50
+ def proclist(service_provider: ServiceProviderDep) -> None:
51
+ """Show processes (with start times) via DVT."""
52
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
53
+ processes = DeviceInfo(dvt).proclist()
54
+ for process in processes:
55
+ if "startDate" in process:
56
+ process["startDate"] = str(process["startDate"])
57
+
58
+ print_json(processes)
59
+
60
+
61
+ @cli.command("is-running-pid")
62
+ def is_running_pid(service_provider: ServiceProviderDep, pid: int) -> None:
63
+ """Check if a PID is currently running."""
64
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
65
+ print_json(DeviceInfo(dvt).is_running_pid(pid))
66
+
67
+
68
+ @cli.command("memlimitoff")
69
+ def memlimitoff(service_provider: ServiceProviderDep, pid: int) -> None:
70
+ """Disable jetsam memory limit for a PID."""
71
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
72
+ ProcessControl(dvt).disable_memory_limit_for_pid(pid)
73
+
74
+
75
+ @cli.command("applist")
76
+ def applist(service_provider: ServiceProviderDep) -> None:
77
+ """List installed applications via DVT."""
78
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
79
+ apps = ApplicationListing(dvt).applist()
80
+ print_json(apps)
81
+
82
+
83
+ class Signals(IntEnum):
84
+ """Platform-independent version of `signal.Signals`, allowing names to be used on Windows."""
85
+
86
+ HUP = 1
87
+ INT = 2
88
+ QUIT = 3
89
+ ILL = 4
90
+ TRAP = 5
91
+ ABRT = 6
92
+ EMT = 7
93
+ FPE = 8
94
+ KILL = 9
95
+ BUS = 10
96
+ SEGV = 11
97
+ SYS = 12
98
+ PIPE = 13
99
+ ALRM = 14
100
+ TERM = 15
101
+ URG = 16
102
+ STOP = 17
103
+ TSTP = 18
104
+ CONT = 19
105
+ CHLD = 20
106
+ TTIN = 21
107
+ TTOU = 22
108
+ IO = 23
109
+ XCPU = 24
110
+ XFSZ = 25
111
+ VTALRM = 26
112
+ PROF = 27
113
+ WINCH = 28
114
+ INFO = 29
115
+ USR1 = 30
116
+ USR2 = 31
117
+
118
+
119
+ @cli.command("signal")
120
+ def send_signal(
121
+ service_provider: ServiceProviderDep,
122
+ pid: int,
123
+ sig: Annotated[
124
+ Optional[int],
125
+ typer.Argument(),
126
+ ] = None,
127
+ signal_name: Annotated[
128
+ Optional[str],
129
+ typer.Option("--signal-name", "-s"),
130
+ ] = None,
131
+ ) -> None:
132
+ """Send a signal to a PID (choose numeric SIG or --signal-name)."""
133
+ if sig is not None and signal_name is not None:
134
+ raise UsageError(message="Cannot give SIG and SIGNAL-NAME together")
135
+
136
+ if signal_name is not None:
137
+ normalized_signal_name = signal_name.upper().removeprefix("SIG")
138
+ try:
139
+ sig = Signals[normalized_signal_name]
140
+ except KeyError:
141
+ raise BadParameter(f"{signal_name!r} is not a valid signal") from None
142
+ elif sig is not None:
143
+ try:
144
+ sig = Signals(sig)
145
+ except ValueError:
146
+ raise BadParameter(f"{sig} is not a valid signal") from None
147
+ else:
148
+ raise MissingParameter(param_type="argument|option", param_hint="'SIG|SIGNAL-NAME'")
149
+
150
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
151
+ ProcessControl(dvt).signal(pid, sig)
152
+
153
+
154
+ @cli.command("kill")
155
+ def kill(service_provider: ServiceProviderDep, pid: int) -> None:
156
+ """Kill a process by PID."""
157
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
158
+ ProcessControl(dvt).kill(pid)
159
+
160
+
161
+ @cli.command()
162
+ def process_id_for_bundle_id(service_provider: ServiceProviderDep, app_bundle_identifier: str) -> None:
163
+ """Get PID of a bundle identifier (only returns a valid value if its running)."""
164
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
165
+ print(ProcessControl(dvt).process_identifier_for_bundle_identifier(app_bundle_identifier))
166
+
167
+
168
+ def get_matching_processes(
169
+ service_provider: ServiceProviderDep,
170
+ name: Optional[str] = None,
171
+ bundle_identifier: Optional[str] = None,
172
+ ) -> list[MatchedProcessByPid]:
173
+ result: list[MatchedProcessByPid] = []
174
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
175
+ device_info = DeviceInfo(dvt)
176
+ for process in device_info.proclist():
177
+ current_name = process["name"]
178
+ current_bundle_identifier = process.get("bundleIdentifier", "")
179
+ pid = process["pid"]
180
+ if (bundle_identifier is not None and bundle_identifier in current_bundle_identifier) or (
181
+ name is not None and name in current_name
182
+ ):
183
+ result.append(MatchedProcessByPid(name=current_name, pid=pid))
184
+ return result
185
+
186
+
187
+ @cli.command("pkill")
188
+ def pkill(
189
+ service_provider: ServiceProviderDep,
190
+ expression: str,
191
+ bundle: Annotated[
192
+ bool,
193
+ typer.Option(help="Treat given expression as a bundle-identifier instead of a process name"),
194
+ ] = False,
195
+ ) -> None:
196
+ """Kill all processes containing `expression` in their name."""
197
+ matching_name = expression if not bundle else None
198
+ matching_bundle_identifier = expression if bundle else None
199
+ matching_processes = get_matching_processes(
200
+ service_provider, name=matching_name, bundle_identifier=matching_bundle_identifier
201
+ )
202
+
203
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
204
+ process_control = ProcessControl(dvt)
205
+
206
+ for process in matching_processes:
207
+ logger.info(f"killing {process.name}({process.pid})")
208
+ process_control.kill(process.pid)
209
+
210
+
211
+ @cli.command("launch")
212
+ def launch(
213
+ service_provider: ServiceProviderDep,
214
+ arguments: str,
215
+ kill_existing: Annotated[
216
+ bool,
217
+ typer.Option(help="Whether to kill an existing instance of this process"),
218
+ ] = True,
219
+ suspended: Annotated[
220
+ bool,
221
+ typer.Option(help="Same as WaitForDebugger"),
222
+ ] = False,
223
+ env: Annotated[
224
+ Optional[list[str]],
225
+ typer.Option(
226
+ help="Environment variable to pass to process given as key=value (can be specified multiple times)"
227
+ ),
228
+ ] = None,
229
+ stream: bool = False,
230
+ ) -> None:
231
+ """Launch a process."""
232
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
233
+ parsed_arguments = shlex.split(arguments)
234
+ process_control = ProcessControl(dvt)
235
+ pid = process_control.launch(
236
+ bundle_id=parsed_arguments[0],
237
+ arguments=parsed_arguments[1:],
238
+ kill_existing=kill_existing,
239
+ start_suspended=suspended,
240
+ environment=dict(var.split("=", 1) for var in env or ()),
241
+ )
242
+ print(f"Process launched with pid {pid}")
243
+ while stream:
244
+ for output_received in process_control:
245
+ logging.getLogger(f"PID:{output_received.pid}").info(output_received.message.strip())
246
+
247
+
248
+ @cli.command("shell")
249
+ def dvt_shell(service_provider: ServiceProviderDep) -> None:
250
+ """Launch developer shell (used for pymobiledevice3 R&D)"""
251
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
252
+ dvt.shell()
253
+
254
+
255
+ def show_dirlist(device_info: DeviceInfo, dirname: str, recursive: bool = False) -> None:
256
+ try:
257
+ filenames = device_info.ls(dirname)
258
+ except DvtDirListError:
259
+ return
260
+
261
+ for filename in filenames:
262
+ filename = posixpath.join(dirname, filename)
263
+ print(filename)
264
+ if recursive:
265
+ show_dirlist(device_info, filename, recursive=recursive)
266
+
267
+
268
+ @cli.command("ls")
269
+ def ls(
270
+ service_provider: ServiceProviderDep,
271
+ path: Path,
272
+ recursive: Annotated[
273
+ bool,
274
+ typer.Option("--recursive", "-r"),
275
+ ] = False,
276
+ ) -> None:
277
+ """List directory"""
278
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
279
+ show_dirlist(DeviceInfo(dvt), str(path), recursive=recursive)
280
+
281
+
282
+ @cli.command("device-information")
283
+ def device_information(service_provider: ServiceProviderDep) -> None:
284
+ """Print system information"""
285
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
286
+ device_info = DeviceInfo(dvt)
287
+ info = {
288
+ "hardware": device_info.hardware_information(),
289
+ "network": device_info.network_information(),
290
+ "kernel-name": device_info.mach_kernel_name(),
291
+ "kpep-database": device_info.kpep_database(),
292
+ }
293
+ with contextlib.suppress(UnrecognizedSelectorError):
294
+ info["system"] = device_info.system_information()
295
+ print_json(info)
296
+
297
+
298
+ @cli.command("netstat")
299
+ def netstat(service_provider: ServiceProviderDep) -> None:
300
+ """Print information about current network activity."""
301
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, NetworkMonitor(dvt) as monitor:
302
+ for event in monitor:
303
+ if isinstance(event, ConnectionDetectionEvent):
304
+ local_host, local_port = event.local_address.split(":")
305
+ remote_host, remote_port = event.local_address.split(":")
306
+ logger.info(f"Connection detected: {local_host}:{local_port} -> {remote_host}:{remote_port}")
307
+
308
+
309
+ @cli.command("screenshot")
310
+ def dvt_screenshot(service_provider: ServiceProviderDep, out: Path) -> None:
311
+ """Take device screenshot"""
312
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
313
+ out.write_bytes(Screenshot(dvt).get_screenshot())
314
+
315
+
316
+ @cli.command("xcuitest")
317
+ def xcuitest(service_provider: ServiceProviderDep, bundle_id: str) -> None:
318
+ """
319
+ Start XCUITest
320
+
321
+ \b
322
+ Usage example:
323
+ \b python3 -m pymobiledevice3 developer dvt xcuitest com.facebook.WebDriverAgentRunner.xctrunner
324
+ """
325
+ XCUITestService(service_provider).run(bundle_id)
326
+
327
+
328
+ @cli.command("trace-codes")
329
+ def dvt_trace_codes(service_provider: ServiceProviderDep) -> None:
330
+ """Print KDebug trace codes."""
331
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
332
+ device_info = DeviceInfo(dvt)
333
+ print_json({hex(k): v for k, v in device_info.trace_codes().items()})
334
+
335
+
336
+ @cli.command("name-for-uid")
337
+ def dvt_name_for_uid(service_provider: ServiceProviderDep, uid: int) -> None:
338
+ """Print the assiciated username for the given uid."""
339
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
340
+ device_info = DeviceInfo(dvt)
341
+ print(device_info.name_for_uid(uid))
342
+
343
+
344
+ @cli.command("name-for-gid")
345
+ def dvt_name_for_gid(service_provider: ServiceProviderDep, gid: int) -> None:
346
+ """Print the assiciated group name for the given gid."""
347
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
348
+ device_info = DeviceInfo(dvt)
349
+ print(device_info.name_for_gid(gid))
350
+
351
+
352
+ @cli.command("oslog")
353
+ def dvt_oslog(service_provider: ServiceProviderDep, pid: int) -> None:
354
+ """Sniff device oslog (not very stable, but includes more data and normal syslog)"""
355
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, ActivityTraceTap(dvt) as tap:
356
+ for message in tap:
357
+ message_pid = message.process
358
+ # without message_type maybe signpost have event_type
359
+ message_type = (
360
+ message.message_type
361
+ if hasattr(message, "message_type")
362
+ else message.event_type
363
+ if hasattr(message, "event_type")
364
+ else "unknown"
365
+ )
366
+ sender_image_path = message.sender_image_path
367
+ image_name = os.path.basename(sender_image_path)
368
+ subsystem = message.subsystem
369
+ category = message.category
370
+ timestamp = datetime.now()
371
+
372
+ if pid is not None and message_pid != pid:
373
+ continue
374
+
375
+ formatted_message = decode_message_format(message.message) if message.message else message.name
376
+
377
+ if user_requested_colored_output():
378
+ timestamp = typer.style(str(timestamp), bold=True)
379
+ message_pid = typer.style(str(message_pid), "magenta")
380
+ subsystem = typer.style(subsystem, "green")
381
+ category = typer.style(category, "green")
382
+ image_name = typer.style(image_name, "yellow")
383
+ message_type = typer.style(message_type, "cyan")
384
+
385
+ print(
386
+ f"[{timestamp}][{subsystem}][{category}][{message_pid}][{image_name}] "
387
+ f"<{message_type}>: {formatted_message}"
388
+ )
389
+
390
+
391
+ @cli.command("energy")
392
+ def dvt_energy(service_provider: ServiceProviderDep, pid_list: list[str]) -> None:
393
+ """Monitor the energy consumption for given PIDs"""
394
+
395
+ if len(pid_list) == 0:
396
+ logger.error("pid_list must not be empty")
397
+ return
398
+
399
+ pid_int_list = [int(pid) for pid in pid_list]
400
+
401
+ with (
402
+ DvtSecureSocketProxyService(lockdown=service_provider) as dvt,
403
+ EnergyMonitor(dvt, pid_int_list) as energy_monitor,
404
+ ):
405
+ for telemetry in energy_monitor:
406
+ logger.info(telemetry)
407
+
408
+
409
+ @cli.command("notifications")
410
+ def dvt_notifications(service_provider: ServiceProviderDep) -> None:
411
+ """Monitor memory and app notifications"""
412
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, Notifications(dvt) as notifications:
413
+ for notification in notifications:
414
+ logger.info(notification)
415
+
416
+
417
+ @cli.command("graphics")
418
+ def dvt_graphics(service_provider: ServiceProviderDep) -> None:
419
+ """Monitor graphics-related information"""
420
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, Graphics(dvt) as graphics:
421
+ for stats in graphics:
422
+ logger.info(stats)
423
+
424
+
425
+ @cli.command("har")
426
+ def dvt_har(service_provider: ServiceProviderDep) -> None:
427
+ """
428
+ Enable har-logging
429
+
430
+ \b
431
+ For more information, please read:
432
+ \b https://github.com/doronz88/harlogger?tab=readme-ov-file#enable-http-instrumentation-method
433
+ """
434
+ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
435
+ print("> Press Ctrl-C to abort")
436
+ with ActivityTraceTap(dvt, enable_http_archive_logging=True) as tap:
437
+ while True:
438
+ tap.channel.receive_message()