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.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- misc/understanding_idevice_protocol_layers.md +15 -10
- pymobiledevice3/__main__.py +317 -127
- pymobiledevice3/_version.py +22 -4
- pymobiledevice3/bonjour.py +358 -113
- pymobiledevice3/ca.py +253 -16
- pymobiledevice3/cli/activation.py +31 -23
- pymobiledevice3/cli/afc.py +49 -40
- pymobiledevice3/cli/amfi.py +16 -21
- pymobiledevice3/cli/apps.py +87 -42
- pymobiledevice3/cli/backup.py +160 -90
- pymobiledevice3/cli/bonjour.py +44 -40
- pymobiledevice3/cli/cli_common.py +204 -198
- pymobiledevice3/cli/companion_proxy.py +14 -14
- pymobiledevice3/cli/crash.py +105 -56
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +108 -103
- pymobiledevice3/cli/mounter.py +158 -99
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +45 -24
- pymobiledevice3/cli/power_assertion.py +18 -17
- pymobiledevice3/cli/processes.py +17 -23
- pymobiledevice3/cli/profile.py +165 -109
- pymobiledevice3/cli/provision.py +35 -34
- pymobiledevice3/cli/remote.py +217 -129
- pymobiledevice3/cli/restore.py +159 -143
- pymobiledevice3/cli/springboard.py +63 -53
- pymobiledevice3/cli/syslog.py +193 -86
- pymobiledevice3/cli/usbmux.py +73 -33
- pymobiledevice3/cli/version.py +5 -7
- pymobiledevice3/cli/webinspector.py +376 -214
- pymobiledevice3/common.py +3 -1
- pymobiledevice3/exceptions.py +182 -58
- pymobiledevice3/irecv.py +52 -53
- pymobiledevice3/irecv_devices.py +1489 -464
- pymobiledevice3/lockdown.py +473 -275
- pymobiledevice3/lockdown_service_provider.py +15 -8
- pymobiledevice3/osu/os_utils.py +27 -9
- pymobiledevice3/osu/posix_util.py +34 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +102 -21
- pymobiledevice3/remote/common.py +8 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
- pymobiledevice3/remote/core_device/file_service.py +53 -23
- pymobiledevice3/remote/remote_service_discovery.py +79 -45
- pymobiledevice3/remote/remotexpc.py +73 -44
- pymobiledevice3/remote/tunnel_service.py +442 -317
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +20 -16
- pymobiledevice3/resources/notifications.txt +144 -0
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +110 -21
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +59 -12
- pymobiledevice3/restore/fdr.py +46 -48
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +163 -0
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +151 -151
- pymobiledevice3/restore/restore.py +562 -544
- pymobiledevice3/restore/restore_options.py +131 -110
- pymobiledevice3/restore/restored_client.py +51 -31
- pymobiledevice3/restore/tss.py +385 -267
- pymobiledevice3/service_connection.py +252 -59
- pymobiledevice3/services/accessibilityaudit.py +202 -120
- pymobiledevice3/services/afc.py +962 -365
- pymobiledevice3/services/amfi.py +24 -30
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +71 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +101 -79
- pymobiledevice3/services/diagnostics.py +973 -967
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +9 -8
- pymobiledevice3/services/house_arrest.py +16 -15
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/installation_proxy.py +173 -81
- pymobiledevice3/services/lockdown_service.py +20 -10
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +147 -64
- pymobiledevice3/services/mobile_config.py +331 -294
- pymobiledevice3/services/mobile_image_mounter.py +141 -113
- pymobiledevice3/services/mobilebackup2.py +203 -145
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +134 -74
- pymobiledevice3/services/pcapd.py +314 -302
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +21 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +15 -12
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +11 -11
- pymobiledevice3/services/web_protocol/automation_session.py +251 -239
- pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +114 -111
- pymobiledevice3/services/web_protocol/element.py +124 -111
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
- pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
- pymobiledevice3/services/web_protocol/switch_to.py +30 -27
- pymobiledevice3/services/webinspector.py +189 -155
- pymobiledevice3/tcp_forwarder.py +87 -69
- pymobiledevice3/tunneld/__init__.py +0 -0
- pymobiledevice3/tunneld/api.py +63 -0
- pymobiledevice3/tunneld/server.py +603 -0
- pymobiledevice3/usbmux.py +198 -147
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
- pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
- pymobiledevice3/cli/developer.py +0 -1215
- pymobiledevice3/cli/diagnostics.py +0 -99
- pymobiledevice3/tunneld.py +0 -524
- pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import typing
|
|
3
|
+
from collections.abc import Generator
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from enum import Enum, IntEnum
|
|
5
6
|
|
|
@@ -21,14 +22,58 @@ class AXAuditInspectorFocus_v1(SerializedObject):
|
|
|
21
22
|
|
|
22
23
|
@property
|
|
23
24
|
def caption(self) -> str:
|
|
24
|
-
return self._fields.get(
|
|
25
|
+
return self._fields.get("CaptionTextValue_v1")
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def spoken_description(self) -> str:
|
|
29
|
+
return self._fields.get("SpokenDescriptionValue_v1")
|
|
25
30
|
|
|
26
31
|
@property
|
|
27
32
|
def element(self) -> bytes:
|
|
28
|
-
return self._fields.get(
|
|
33
|
+
return self._fields.get("ElementValue_v1")
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def platform_identifier(self) -> str:
|
|
37
|
+
"""Converts the element bytes to a hexadecimal string."""
|
|
38
|
+
return self.element.identifier.hex().upper()
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def estimated_uid(self) -> str:
|
|
42
|
+
"""Generates a UID from the platform identifier."""
|
|
43
|
+
hex_value = self.platform_identifier
|
|
44
|
+
|
|
45
|
+
if len(hex_value) % 2 != 0:
|
|
46
|
+
raise ValueError("Hex value length must be even.")
|
|
47
|
+
|
|
48
|
+
hex_bytes = bytes.fromhex(hex_value)
|
|
49
|
+
|
|
50
|
+
if len(hex_bytes) < 16:
|
|
51
|
+
raise ValueError("Hex value must contain at least 16 bytes.")
|
|
52
|
+
|
|
53
|
+
# Extract TimeLow bytes (indexes 12 to 15)
|
|
54
|
+
time_low_bytes = hex_bytes[12:16]
|
|
55
|
+
time_low = time_low_bytes.hex().upper()
|
|
56
|
+
|
|
57
|
+
# Extract ClockSeq bytes (indexes 0 to 1)
|
|
58
|
+
clock_seq_bytes = hex_bytes[0:2]
|
|
59
|
+
clock_seq = clock_seq_bytes.hex().upper()
|
|
60
|
+
|
|
61
|
+
# Construct UID with placeholder values for unused parts
|
|
62
|
+
uid = f"{time_low}-0000-0000-{clock_seq}-000000000000"
|
|
63
|
+
|
|
64
|
+
return uid
|
|
65
|
+
|
|
66
|
+
def to_dict(self) -> dict:
|
|
67
|
+
"""Serializes the focus element into a dictionary."""
|
|
68
|
+
return {
|
|
69
|
+
"platform_identifier": self.platform_identifier,
|
|
70
|
+
"estimated_uid": self.estimated_uid,
|
|
71
|
+
"caption": self.caption,
|
|
72
|
+
"spoken_description": self.spoken_description,
|
|
73
|
+
}
|
|
29
74
|
|
|
30
75
|
def __str__(self):
|
|
31
|
-
return f
|
|
76
|
+
return f"<Focused ElementCaption: {self.caption}>"
|
|
32
77
|
|
|
33
78
|
|
|
34
79
|
class AXAuditElement_v1(SerializedObject):
|
|
@@ -37,10 +82,10 @@ class AXAuditElement_v1(SerializedObject):
|
|
|
37
82
|
|
|
38
83
|
@property
|
|
39
84
|
def identifier(self) -> bytes:
|
|
40
|
-
return self._fields[
|
|
85
|
+
return self._fields["PlatformElementValue_v1"]
|
|
41
86
|
|
|
42
87
|
def __repr__(self):
|
|
43
|
-
return f
|
|
88
|
+
return f"<Element: {self.identifier}>"
|
|
44
89
|
|
|
45
90
|
|
|
46
91
|
class AXAuditInspectorSection_v1(SerializedObject):
|
|
@@ -54,7 +99,7 @@ class AXAuditElementAttribute_v1(SerializedObject):
|
|
|
54
99
|
|
|
55
100
|
|
|
56
101
|
class AXAuditDeviceSetting_v1(SerializedObject):
|
|
57
|
-
FIELDS = (
|
|
102
|
+
FIELDS = ("IdentiifierValue_v1", "CurrentValueNumber_v1")
|
|
58
103
|
|
|
59
104
|
def __init__(self, fields):
|
|
60
105
|
super().__init__(fields)
|
|
@@ -64,14 +109,14 @@ class AXAuditDeviceSetting_v1(SerializedObject):
|
|
|
64
109
|
|
|
65
110
|
@property
|
|
66
111
|
def key(self) -> str:
|
|
67
|
-
return self._fields[
|
|
112
|
+
return self._fields["IdentiifierValue_v1"]
|
|
68
113
|
|
|
69
114
|
@property
|
|
70
115
|
def value(self) -> typing.Any:
|
|
71
|
-
return self._fields[
|
|
116
|
+
return self._fields["CurrentValueNumber_v1"]
|
|
72
117
|
|
|
73
118
|
def __str__(self) -> str:
|
|
74
|
-
return f
|
|
119
|
+
return f"<AXAuditDeviceSetting_v1 {self.key} = {self.value}>"
|
|
75
120
|
|
|
76
121
|
|
|
77
122
|
class AuditType(IntEnum):
|
|
@@ -86,21 +131,27 @@ class AuditType(IntEnum):
|
|
|
86
131
|
|
|
87
132
|
|
|
88
133
|
AUDIT_TYPE_DESCRIPTIONS = {
|
|
89
|
-
AuditType.DYNAMIC_TEXT:
|
|
90
|
-
AuditType.DYNAMIC_TEXT_ALT:
|
|
91
|
-
AuditType.TEXT_CLIPPED:
|
|
92
|
-
AuditType.ELEMENT_DETECTION:
|
|
93
|
-
AuditType.SUFFICIENT_ELEMENT_DESCRIPTION:
|
|
94
|
-
AuditType.HIT_REGION:
|
|
95
|
-
AuditType.CONTRAST:
|
|
96
|
-
AuditType.CONTRAST_ALT:
|
|
134
|
+
AuditType.DYNAMIC_TEXT: "testTypeDynamicText",
|
|
135
|
+
AuditType.DYNAMIC_TEXT_ALT: "testTypeDynamicText",
|
|
136
|
+
AuditType.TEXT_CLIPPED: "testTypeTextClipped",
|
|
137
|
+
AuditType.ELEMENT_DETECTION: "testTypeElementDetection",
|
|
138
|
+
AuditType.SUFFICIENT_ELEMENT_DESCRIPTION: "testTypeSufficientElementDescription",
|
|
139
|
+
AuditType.HIT_REGION: "testTypeHitRegion",
|
|
140
|
+
AuditType.CONTRAST: "testTypeContrast",
|
|
141
|
+
AuditType.CONTRAST_ALT: "testTypeContrast",
|
|
97
142
|
}
|
|
98
143
|
|
|
99
144
|
|
|
100
145
|
class AXAuditIssue_v1(SerializedObject):
|
|
101
|
-
FIELDS = (
|
|
102
|
-
|
|
103
|
-
|
|
146
|
+
FIELDS = (
|
|
147
|
+
"ElementRectValue_v1",
|
|
148
|
+
"IssueClassificationValue_v1",
|
|
149
|
+
"FontSizeValue_v1",
|
|
150
|
+
"MLGeneratedDescriptionValue_v1",
|
|
151
|
+
"ElementLongDescExtraInfo_v1",
|
|
152
|
+
"BackgroundColorValue_v1",
|
|
153
|
+
"ForegroundColorValue_v1",
|
|
154
|
+
)
|
|
104
155
|
|
|
105
156
|
def __init__(self, fields):
|
|
106
157
|
super().__init__(fields)
|
|
@@ -111,11 +162,11 @@ class AXAuditIssue_v1(SerializedObject):
|
|
|
111
162
|
|
|
112
163
|
@property
|
|
113
164
|
def rect(self) -> str:
|
|
114
|
-
return self._fields[
|
|
165
|
+
return self._fields["ElementRectValue_v1"]
|
|
115
166
|
|
|
116
167
|
@property
|
|
117
168
|
def issue_type(self) -> typing.Any:
|
|
118
|
-
issue_classification = self._fields[
|
|
169
|
+
issue_classification = self._fields["IssueClassificationValue_v1"]
|
|
119
170
|
if issue_classification in AUDIT_TYPE_DESCRIPTIONS:
|
|
120
171
|
return AUDIT_TYPE_DESCRIPTIONS[AuditType(issue_classification)]
|
|
121
172
|
else:
|
|
@@ -123,36 +174,36 @@ class AXAuditIssue_v1(SerializedObject):
|
|
|
123
174
|
|
|
124
175
|
@property
|
|
125
176
|
def ml_generated_description(self) -> typing.Any:
|
|
126
|
-
return self._fields[
|
|
177
|
+
return self._fields["MLGeneratedDescriptionValue_v1"]
|
|
127
178
|
|
|
128
179
|
@property
|
|
129
180
|
def long_description_extra_info(self) -> typing.Any:
|
|
130
|
-
return self._fields[
|
|
181
|
+
return self._fields["ElementLongDescExtraInfo_v1"]
|
|
131
182
|
|
|
132
183
|
@property
|
|
133
184
|
def font_size(self) -> typing.Any:
|
|
134
|
-
return self._fields[
|
|
185
|
+
return self._fields["FontSizeValue_v1"]
|
|
135
186
|
|
|
136
187
|
@property
|
|
137
188
|
def foreground_color(self) -> typing.Any:
|
|
138
|
-
return self._fields[
|
|
189
|
+
return self._fields["ForegroundColorValue_v1"]
|
|
139
190
|
|
|
140
191
|
@property
|
|
141
192
|
def background_color(self) -> typing.Any:
|
|
142
|
-
return self._fields[
|
|
193
|
+
return self._fields["BackgroundColorValue_v1"]
|
|
143
194
|
|
|
144
195
|
def json(self) -> dict:
|
|
145
196
|
resp = {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
197
|
+
"element_rect_value": self.rect,
|
|
198
|
+
"issue_classification": self.issue_type,
|
|
199
|
+
"font_size": self.font_size,
|
|
200
|
+
"ml_generated_description": self.ml_generated_description,
|
|
201
|
+
"long_description_extra_info": self.long_description_extra_info,
|
|
151
202
|
}
|
|
152
203
|
# Include foreground and background colors when issue type is 'testTypeContrast'
|
|
153
|
-
if self._fields[
|
|
154
|
-
resp[
|
|
155
|
-
resp[
|
|
204
|
+
if self._fields["IssueClassificationValue_v1"] in {AuditType.CONTRAST, AuditType.CONTRAST_ALT}:
|
|
205
|
+
resp["foreground_color"] = self.foreground_color
|
|
206
|
+
resp["background_color"] = self.background_color
|
|
156
207
|
return resp
|
|
157
208
|
|
|
158
209
|
def __str__(self) -> str:
|
|
@@ -160,12 +211,12 @@ class AXAuditIssue_v1(SerializedObject):
|
|
|
160
211
|
|
|
161
212
|
|
|
162
213
|
SERIALIZABLE_OBJECTS = {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
214
|
+
"AXAuditDeviceSetting_v1": AXAuditDeviceSetting_v1,
|
|
215
|
+
"AXAuditInspectorFocus_v1": AXAuditInspectorFocus_v1,
|
|
216
|
+
"AXAuditElement_v1": AXAuditElement_v1,
|
|
217
|
+
"AXAuditInspectorSection_v1": AXAuditInspectorSection_v1,
|
|
218
|
+
"AXAuditElementAttribute_v1": AXAuditElementAttribute_v1,
|
|
219
|
+
"AXAuditIssue_v1": AXAuditIssue_v1,
|
|
169
220
|
}
|
|
170
221
|
|
|
171
222
|
|
|
@@ -188,22 +239,22 @@ def deserialize_object(d):
|
|
|
188
239
|
return [deserialize_object(x) for x in d]
|
|
189
240
|
return d
|
|
190
241
|
|
|
191
|
-
if
|
|
242
|
+
if "ObjectType" not in d:
|
|
192
243
|
# simple dictionary
|
|
193
244
|
new_dict = {}
|
|
194
245
|
for k, v in d.items():
|
|
195
246
|
new_dict[k] = deserialize_object(v)
|
|
196
247
|
return new_dict
|
|
197
248
|
|
|
198
|
-
if d[
|
|
199
|
-
return deserialize_object(d[
|
|
249
|
+
if d["ObjectType"] == "passthrough":
|
|
250
|
+
return deserialize_object(d["Value"])
|
|
200
251
|
else:
|
|
201
|
-
return SERIALIZABLE_OBJECTS[d[
|
|
252
|
+
return SERIALIZABLE_OBJECTS[d["ObjectType"]](deserialize_object(d["Value"]))
|
|
202
253
|
|
|
203
254
|
|
|
204
255
|
class AccessibilityAudit(RemoteServer):
|
|
205
|
-
SERVICE_NAME =
|
|
206
|
-
RSD_SERVICE_NAME =
|
|
256
|
+
SERVICE_NAME = "com.apple.accessibility.axAuditDaemon.remoteserver"
|
|
257
|
+
RSD_SERVICE_NAME = "com.apple.accessibility.axAuditDaemon.remoteserver.shim.remote"
|
|
207
258
|
|
|
208
259
|
def __init__(self, lockdown: LockdownServiceProvider):
|
|
209
260
|
if isinstance(lockdown, LockdownClient):
|
|
@@ -214,7 +265,7 @@ class AccessibilityAudit(RemoteServer):
|
|
|
214
265
|
# flush previously received messages
|
|
215
266
|
self.recv_plist()
|
|
216
267
|
self.product_version = Version(lockdown.product_version)
|
|
217
|
-
if Version(lockdown.product_version) >= Version(
|
|
268
|
+
if Version(lockdown.product_version) >= Version("15.0"):
|
|
218
269
|
self.recv_plist()
|
|
219
270
|
|
|
220
271
|
@property
|
|
@@ -223,19 +274,19 @@ class AccessibilityAudit(RemoteServer):
|
|
|
223
274
|
return self.recv_plist()[0]
|
|
224
275
|
|
|
225
276
|
def run_audit(self, value: list) -> list[AXAuditIssue_v1]:
|
|
226
|
-
if self.product_version >= Version(
|
|
277
|
+
if self.product_version >= Version("15.0"):
|
|
227
278
|
self.broadcast.deviceBeginAuditTypes_(MessageAux().append_obj(value))
|
|
228
279
|
else:
|
|
229
280
|
self.broadcast.deviceBeginAuditCaseIDs_(MessageAux().append_obj(value))
|
|
230
281
|
|
|
231
282
|
while True:
|
|
232
283
|
message = self.recv_plist()
|
|
233
|
-
if message[1] is None or message[0] !=
|
|
284
|
+
if message[1] is None or message[0] != "hostDeviceDidCompleteAuditCategoriesWithAuditIssues:":
|
|
234
285
|
continue
|
|
235
|
-
return deserialize_object(message[1])[0][
|
|
286
|
+
return deserialize_object(message[1])[0]["value"]
|
|
236
287
|
|
|
237
288
|
def supported_audits_types(self) -> None:
|
|
238
|
-
if self.product_version >= Version(
|
|
289
|
+
if self.product_version >= Version("15.0"):
|
|
239
290
|
self.broadcast.deviceAllSupportedAuditTypes()
|
|
240
291
|
else:
|
|
241
292
|
self.broadcast.deviceAllAuditCaseIDs()
|
|
@@ -253,7 +304,7 @@ class AccessibilityAudit(RemoteServer):
|
|
|
253
304
|
def set_app_monitoring_enabled(self, value: bool) -> None:
|
|
254
305
|
self.broadcast.deviceSetAppMonitoringEnabled_(MessageAux().append_obj(value), expects_reply=False)
|
|
255
306
|
|
|
256
|
-
def set_monitored_event_type(self, event_type: int = None) -> None:
|
|
307
|
+
def set_monitored_event_type(self, event_type: typing.Optional[int] = None) -> None:
|
|
257
308
|
if event_type is None:
|
|
258
309
|
event_type = 0
|
|
259
310
|
self.broadcast.deviceInspectorSetMonitoredEventType_(MessageAux().append_obj(event_type), expects_reply=False)
|
|
@@ -264,9 +315,9 @@ class AccessibilityAudit(RemoteServer):
|
|
|
264
315
|
def set_show_visuals(self, value: bool) -> None:
|
|
265
316
|
self.broadcast.deviceInspectorShowVisuals_(MessageAux().append_obj(int(value)), expects_reply=False)
|
|
266
317
|
|
|
267
|
-
def iter_events(
|
|
268
|
-
|
|
269
|
-
|
|
318
|
+
def iter_events(
|
|
319
|
+
self, app_monitoring_enabled=True, monitored_event_type: typing.Optional[int] = None
|
|
320
|
+
) -> typing.Generator[Event, None, None]:
|
|
270
321
|
self.set_app_monitoring_enabled(app_monitoring_enabled)
|
|
271
322
|
self.set_monitored_event_type(monitored_event_type)
|
|
272
323
|
|
|
@@ -274,98 +325,129 @@ class AccessibilityAudit(RemoteServer):
|
|
|
274
325
|
message = self.recv_plist()
|
|
275
326
|
if message[1] is None:
|
|
276
327
|
continue
|
|
277
|
-
data = [x[
|
|
328
|
+
data = [x["value"] for x in message[1]]
|
|
278
329
|
yield Event(name=message[0], data=deserialize_object(data))
|
|
279
330
|
|
|
280
331
|
def move_focus_next(self) -> None:
|
|
281
332
|
self.move_focus(Direction.Next)
|
|
282
333
|
|
|
283
334
|
def perform_press(self, element: bytes) -> None:
|
|
284
|
-
"""
|
|
335
|
+
"""simulate click (can be used only for processes with task_for_pid-allow"""
|
|
285
336
|
element = {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
}
|
|
337
|
+
"ObjectType": "AXAuditElement_v1",
|
|
338
|
+
"Value": {
|
|
339
|
+
"ObjectType": "passthrough",
|
|
340
|
+
"Value": {
|
|
341
|
+
"PlatformElementValue_v1": {"ObjectType": "passthrough"},
|
|
342
|
+
"Value": element,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
296
345
|
}
|
|
297
346
|
|
|
298
347
|
action = {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
348
|
+
"ObjectType": "AXAuditElementAttribute_v1",
|
|
349
|
+
"Value": {
|
|
350
|
+
"ObjectType": "passthrough",
|
|
351
|
+
"Value": {
|
|
352
|
+
"AttributeNameValue_v1": {
|
|
353
|
+
"ObjectType": "passthrough",
|
|
354
|
+
"Value": "AXAction-2010",
|
|
306
355
|
},
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
356
|
+
"DisplayAsTree_v1": {
|
|
357
|
+
"ObjectType": "passthrough",
|
|
358
|
+
"Value": 0,
|
|
310
359
|
},
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
360
|
+
"HumanReadableNameValue_v1": {
|
|
361
|
+
"ObjectType": "passthrough",
|
|
362
|
+
"Value": "Activate",
|
|
314
363
|
},
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
364
|
+
"IsInternal_v1": {
|
|
365
|
+
"ObjectType": "passthrough",
|
|
366
|
+
"Value": 0,
|
|
318
367
|
},
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
368
|
+
"PerformsActionValue_v1": {
|
|
369
|
+
"ObjectType": "passthrough",
|
|
370
|
+
"Value": 1,
|
|
322
371
|
},
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
372
|
+
"SettableValue_v1": {
|
|
373
|
+
"ObjectType": "passthrough",
|
|
374
|
+
"Value": 0,
|
|
326
375
|
},
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
376
|
+
"ValueTypeValue_v1": {
|
|
377
|
+
"ObjectType": "passthrough",
|
|
378
|
+
"Value": 1,
|
|
330
379
|
},
|
|
331
|
-
}
|
|
332
|
-
}
|
|
380
|
+
},
|
|
381
|
+
},
|
|
333
382
|
}
|
|
334
383
|
|
|
335
384
|
self.broadcast.deviceElement_performAction_withValue_(
|
|
336
|
-
MessageAux().append_obj(element).append_obj(action).append_obj(0), expects_reply=False
|
|
385
|
+
MessageAux().append_obj(element).append_obj(action).append_obj(0), expects_reply=False
|
|
386
|
+
)
|
|
337
387
|
|
|
338
388
|
def move_focus(self, direction: Direction) -> None:
|
|
339
389
|
options = {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
390
|
+
"ObjectType": "passthrough",
|
|
391
|
+
"Value": {
|
|
392
|
+
"allowNonAX": {
|
|
393
|
+
"ObjectType": "passthrough",
|
|
394
|
+
"Value": 0,
|
|
395
|
+
},
|
|
396
|
+
"direction": {
|
|
397
|
+
"ObjectType": "passthrough",
|
|
398
|
+
"Value": direction.value,
|
|
345
399
|
},
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
400
|
+
"includeContainers": {
|
|
401
|
+
"ObjectType": "passthrough",
|
|
402
|
+
"Value": 1,
|
|
349
403
|
},
|
|
350
|
-
|
|
351
|
-
'ObjectType': 'passthrough',
|
|
352
|
-
'Value': 1,
|
|
353
|
-
}
|
|
354
|
-
}
|
|
404
|
+
},
|
|
355
405
|
}
|
|
356
406
|
|
|
357
407
|
self.broadcast.deviceInspectorMoveWithOptions_(MessageAux().append_obj(options), expects_reply=False)
|
|
358
408
|
|
|
359
409
|
def set_setting(self, name: str, value: typing.Any) -> None:
|
|
360
|
-
setting = {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
410
|
+
setting = {
|
|
411
|
+
"ObjectType": "AXAuditDeviceSetting_v1",
|
|
412
|
+
"Value": {
|
|
413
|
+
"ObjectType": "passthrough",
|
|
414
|
+
"Value": {
|
|
415
|
+
"CurrentValueNumber_v1": {"ObjectType": "passthrough", "Value": True},
|
|
416
|
+
"EnabledValue_v1": {"ObjectType": "passthrough", "Value": True},
|
|
417
|
+
"IdentiifierValue_v1": {"ObjectType": "passthrough", "Value": name},
|
|
418
|
+
"SettingTypeValue_v1": {"ObjectType": "passthrough", "Value": 3},
|
|
419
|
+
"SliderTickMarksValue_v1": {"ObjectType": "passthrough", "Value": 0},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
}
|
|
369
423
|
self.broadcast.deviceUpdateAccessibilitySetting_withValue_(
|
|
370
|
-
MessageAux().append_obj(setting).append_obj({
|
|
371
|
-
expects_reply=False
|
|
424
|
+
MessageAux().append_obj(setting).append_obj({"ObjectType": "passthrough", "Value": value}),
|
|
425
|
+
expects_reply=False,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
def reset_settings(self) -> None:
|
|
429
|
+
self.broadcast.deviceResetToDefaultAccessibilitySettings()
|
|
430
|
+
|
|
431
|
+
def iter_elements(self) -> Generator[AXAuditInspectorFocus_v1, None, None]:
|
|
432
|
+
iterator = self.iter_events()
|
|
433
|
+
|
|
434
|
+
# every focus change is expected publish a "hostInspectorCurrentElementChanged:"
|
|
435
|
+
self.move_focus_next()
|
|
436
|
+
|
|
437
|
+
visited_identifiers = set()
|
|
438
|
+
|
|
439
|
+
for event in iterator:
|
|
440
|
+
if event.name != "hostInspectorCurrentElementChanged:":
|
|
441
|
+
# ignore any other events
|
|
442
|
+
continue
|
|
443
|
+
|
|
444
|
+
# each such event should contain exactly one element that became in focus
|
|
445
|
+
current_item = event.data[0]
|
|
446
|
+
current_identifier = current_item.platform_identifier
|
|
447
|
+
|
|
448
|
+
if current_identifier in visited_identifiers:
|
|
449
|
+
break # Exit if we've seen this element before (loop detected)
|
|
450
|
+
|
|
451
|
+
yield current_item
|
|
452
|
+
visited_identifiers.add(current_identifier)
|
|
453
|
+
self.move_focus_next()
|