androidctl 0.1.0__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 (187) hide show
  1. androidctl/__init__.py +5 -0
  2. androidctl/__main__.py +4 -0
  3. androidctl/_version.py +1 -0
  4. androidctl/app.py +73 -0
  5. androidctl/cli_options.py +27 -0
  6. androidctl/command_payloads.py +264 -0
  7. androidctl/command_views.py +157 -0
  8. androidctl/commands/__init__.py +1 -0
  9. androidctl/commands/actions.py +236 -0
  10. androidctl/commands/adb_wireless.py +157 -0
  11. androidctl/commands/close.py +30 -0
  12. androidctl/commands/connect.py +69 -0
  13. androidctl/commands/execute.py +179 -0
  14. androidctl/commands/list_apps.py +26 -0
  15. androidctl/commands/observe.py +26 -0
  16. androidctl/commands/open.py +41 -0
  17. androidctl/commands/plumbing.py +58 -0
  18. androidctl/commands/run_pipeline.py +307 -0
  19. androidctl/commands/screenshot.py +29 -0
  20. androidctl/commands/setup.py +301 -0
  21. androidctl/commands/wait.py +60 -0
  22. androidctl/daemon/__init__.py +1 -0
  23. androidctl/daemon/client.py +348 -0
  24. androidctl/daemon/discovery.py +190 -0
  25. androidctl/daemon/launcher.py +26 -0
  26. androidctl/daemon/owner.py +349 -0
  27. androidctl/errors/__init__.py +1 -0
  28. androidctl/errors/mapping.py +149 -0
  29. androidctl/errors/models.py +16 -0
  30. androidctl/exit_codes.py +8 -0
  31. androidctl/output.py +147 -0
  32. androidctl/parsing/__init__.py +1 -0
  33. androidctl/parsing/duration.py +17 -0
  34. androidctl/parsing/open_target.py +51 -0
  35. androidctl/parsing/refs.py +12 -0
  36. androidctl/parsing/screen_id.py +10 -0
  37. androidctl/parsing/wait.py +70 -0
  38. androidctl/renderers/__init__.py +110 -0
  39. androidctl/renderers/_paths.py +109 -0
  40. androidctl/renderers/xml.py +234 -0
  41. androidctl/renderers/xml_projection.py +732 -0
  42. androidctl/resources/__init__.py +1 -0
  43. androidctl/resources/androidctl-agent-0.1.0-release.apk +0 -0
  44. androidctl/setup/__init__.py +1 -0
  45. androidctl/setup/accessibility.py +159 -0
  46. androidctl/setup/adb.py +586 -0
  47. androidctl/setup/apk_resource.py +29 -0
  48. androidctl/setup/pairing.py +70 -0
  49. androidctl/setup/verify.py +175 -0
  50. androidctl/workspace/__init__.py +3 -0
  51. androidctl/workspace/resolve.py +27 -0
  52. androidctl-0.1.0.dist-info/METADATA +217 -0
  53. androidctl-0.1.0.dist-info/RECORD +187 -0
  54. androidctl-0.1.0.dist-info/WHEEL +5 -0
  55. androidctl-0.1.0.dist-info/entry_points.txt +3 -0
  56. androidctl-0.1.0.dist-info/licenses/LICENSE +674 -0
  57. androidctl-0.1.0.dist-info/top_level.txt +3 -0
  58. androidctl_contracts/__init__.py +55 -0
  59. androidctl_contracts/_version.py +1 -0
  60. androidctl_contracts/_wire_helpers.py +31 -0
  61. androidctl_contracts/base.py +142 -0
  62. androidctl_contracts/command_catalog.py +414 -0
  63. androidctl_contracts/command_results.py +630 -0
  64. androidctl_contracts/daemon_api.py +335 -0
  65. androidctl_contracts/errors.py +44 -0
  66. androidctl_contracts/paths.py +5 -0
  67. androidctl_contracts/public_screen.py +579 -0
  68. androidctl_contracts/user_state.py +23 -0
  69. androidctl_contracts/vocabulary.py +82 -0
  70. androidctld/__init__.py +5 -0
  71. androidctld/__main__.py +63 -0
  72. androidctld/_version.py +1 -0
  73. androidctld/actions/__init__.py +1 -0
  74. androidctld/actions/action_target.py +142 -0
  75. androidctld/actions/capabilities.py +539 -0
  76. androidctld/actions/executor.py +894 -0
  77. androidctld/actions/focus_confirmation.py +177 -0
  78. androidctld/actions/focused_input_admissibility.py +120 -0
  79. androidctld/actions/fresh_current.py +176 -0
  80. androidctld/actions/postconditions.py +473 -0
  81. androidctld/actions/repair.py +101 -0
  82. androidctld/actions/request_builder.py +204 -0
  83. androidctld/actions/settle.py +146 -0
  84. androidctld/actions/submit_confirmation.py +211 -0
  85. androidctld/actions/submit_routing.py +311 -0
  86. androidctld/actions/type_confirmation.py +257 -0
  87. androidctld/app_targets.py +71 -0
  88. androidctld/artifacts/__init__.py +1 -0
  89. androidctld/artifacts/models.py +26 -0
  90. androidctld/artifacts/screen_lookup.py +241 -0
  91. androidctld/artifacts/screen_payloads.py +109 -0
  92. androidctld/artifacts/writer.py +286 -0
  93. androidctld/auth/__init__.py +1 -0
  94. androidctld/auth/active_registry.py +266 -0
  95. androidctld/auth/secret_files.py +52 -0
  96. androidctld/auth/token_store.py +59 -0
  97. androidctld/commands/__init__.py +1 -0
  98. androidctld/commands/assembly.py +231 -0
  99. androidctld/commands/command_models.py +254 -0
  100. androidctld/commands/dispatch.py +99 -0
  101. androidctld/commands/executor.py +31 -0
  102. androidctld/commands/from_boundary.py +175 -0
  103. androidctld/commands/handlers/__init__.py +15 -0
  104. androidctld/commands/handlers/action.py +439 -0
  105. androidctld/commands/handlers/connect.py +94 -0
  106. androidctld/commands/handlers/list_apps.py +215 -0
  107. androidctld/commands/handlers/observe.py +121 -0
  108. androidctld/commands/handlers/screenshot.py +105 -0
  109. androidctld/commands/handlers/wait.py +286 -0
  110. androidctld/commands/models.py +65 -0
  111. androidctld/commands/open_targets.py +56 -0
  112. androidctld/commands/orchestration.py +353 -0
  113. androidctld/commands/registry.py +116 -0
  114. androidctld/commands/result_builders.py +40 -0
  115. androidctld/commands/result_models.py +555 -0
  116. androidctld/commands/results.py +108 -0
  117. androidctld/commands/semantic_command_names.py +17 -0
  118. androidctld/commands/semantic_error_mapping.py +93 -0
  119. androidctld/commands/semantic_truth.py +135 -0
  120. androidctld/commands/service.py +67 -0
  121. androidctld/config.py +75 -0
  122. androidctld/daemon/__init__.py +1 -0
  123. androidctld/daemon/active_slot.py +326 -0
  124. androidctld/daemon/envelope.py +30 -0
  125. androidctld/daemon/http_host.py +123 -0
  126. androidctld/daemon/ingress.py +112 -0
  127. androidctld/daemon/ownership_probe.py +204 -0
  128. androidctld/daemon/server.py +286 -0
  129. androidctld/daemon/service.py +99 -0
  130. androidctld/device/__init__.py +1 -0
  131. androidctld/device/action_models.py +154 -0
  132. androidctld/device/action_serialization.py +121 -0
  133. androidctld/device/adapters.py +220 -0
  134. androidctld/device/bootstrap.py +153 -0
  135. androidctld/device/connectors.py +231 -0
  136. androidctld/device/errors.py +100 -0
  137. androidctld/device/interfaces.py +58 -0
  138. androidctld/device/parsing.py +320 -0
  139. androidctld/device/rpc.py +483 -0
  140. androidctld/device/schema.py +114 -0
  141. androidctld/device/types.py +161 -0
  142. androidctld/errors/__init__.py +94 -0
  143. androidctld/logging/__init__.py +22 -0
  144. androidctld/observation.py +98 -0
  145. androidctld/protocol.py +53 -0
  146. androidctld/refs/__init__.py +1 -0
  147. androidctld/refs/models.py +54 -0
  148. androidctld/refs/repair.py +284 -0
  149. androidctld/refs/service.py +422 -0
  150. androidctld/rendering/__init__.py +1 -0
  151. androidctld/rendering/screen_xml.py +256 -0
  152. androidctld/runtime/__init__.py +21 -0
  153. androidctld/runtime/kernel.py +548 -0
  154. androidctld/runtime/lifecycle.py +19 -0
  155. androidctld/runtime/models.py +48 -0
  156. androidctld/runtime/screen_state.py +117 -0
  157. androidctld/runtime/state_repo.py +70 -0
  158. androidctld/runtime/store.py +76 -0
  159. androidctld/runtime_policy.py +127 -0
  160. androidctld/schema/__init__.py +5 -0
  161. androidctld/schema/base.py +132 -0
  162. androidctld/schema/core.py +35 -0
  163. androidctld/schema/daemon_api.py +108 -0
  164. androidctld/schema/persistence.py +161 -0
  165. androidctld/schema/persistence_io.py +41 -0
  166. androidctld/schema/validation_errors.py +309 -0
  167. androidctld/semantics/__init__.py +1 -0
  168. androidctld/semantics/compiler.py +610 -0
  169. androidctld/semantics/continuity.py +107 -0
  170. androidctld/semantics/labels.py +252 -0
  171. androidctld/semantics/models.py +25 -0
  172. androidctld/semantics/policy.py +23 -0
  173. androidctld/semantics/public_models.py +123 -0
  174. androidctld/semantics/registries.py +13 -0
  175. androidctld/semantics/submit_refs.py +417 -0
  176. androidctld/semantics/surface.py +254 -0
  177. androidctld/semantics/targets.py +167 -0
  178. androidctld/snapshots/__init__.py +1 -0
  179. androidctld/snapshots/models.py +219 -0
  180. androidctld/snapshots/refresh.py +273 -0
  181. androidctld/snapshots/schema.py +74 -0
  182. androidctld/snapshots/service.py +138 -0
  183. androidctld/text_equivalence.py +67 -0
  184. androidctld/waits/__init__.py +1 -0
  185. androidctld/waits/evaluators.py +216 -0
  186. androidctld/waits/loop.py +305 -0
  187. androidctld/waits/matcher.py +41 -0
@@ -0,0 +1 @@
1
+ """Packaged Android Device Agent APK resources."""
@@ -0,0 +1 @@
1
+ """Host-side setup helpers."""
@@ -0,0 +1,159 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from dataclasses import dataclass
5
+
6
+ from androidctl.setup import adb as setup_adb
7
+
8
+ ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services"
9
+ ACCESSIBILITY_ENABLED = "accessibility_enabled"
10
+ ACCESSIBILITY_ENABLED_VALUE = "1"
11
+ MANUAL_ACCESSIBILITY_FALLBACK = (
12
+ "open Android App info, allow restricted settings if shown, then enable "
13
+ "AndroidCtl Accessibility in Accessibility settings"
14
+ )
15
+ DEFAULT_ENABLE_ATTEMPTS = 5
16
+ DEFAULT_ENABLE_RETRY_DELAY_SECONDS = 0.5
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class AccessibilityEnableResult:
21
+ changed_service_list: bool
22
+ enabled_services: str
23
+
24
+
25
+ class SetupAccessibilityError(RuntimeError):
26
+ def __init__(self, code: str, message: str) -> None:
27
+ super().__init__(message)
28
+ self.code = code
29
+ self.message = message
30
+ self.layer = "accessibility"
31
+
32
+
33
+ def parse_enabled_services(value: str) -> tuple[str, ...]:
34
+ normalized = value.strip()
35
+ if not normalized or normalized == "null":
36
+ return ()
37
+ return tuple(
38
+ service.strip() for service in normalized.split(":") if service.strip()
39
+ )
40
+
41
+
42
+ def canonical_component_name(component: str) -> str:
43
+ package, separator, class_name = component.partition("/")
44
+ if not separator:
45
+ return component
46
+ if class_name.startswith("."):
47
+ class_name = f"{package}{class_name}"
48
+ return f"{package}/{class_name}"
49
+
50
+
51
+ def merge_enabled_services(
52
+ value: str,
53
+ *,
54
+ service: str = setup_adb.ANDROIDCTL_ACCESSIBILITY_SERVICE,
55
+ ) -> str:
56
+ services = parse_enabled_services(value)
57
+ if any(component_names_match(existing, service) for existing in services):
58
+ return ":".join(services)
59
+ return ":".join((*services, service))
60
+
61
+
62
+ def service_is_enabled(
63
+ value: str,
64
+ *,
65
+ service: str = setup_adb.ANDROIDCTL_ACCESSIBILITY_SERVICE,
66
+ ) -> bool:
67
+ return any(
68
+ component_names_match(existing, service)
69
+ for existing in parse_enabled_services(value)
70
+ )
71
+
72
+
73
+ def component_names_match(left: str, right: str) -> bool:
74
+ return canonical_component_name(left) == canonical_component_name(right)
75
+
76
+
77
+ def enable_agent_accessibility(
78
+ *,
79
+ serial: str,
80
+ adb_path: str = "adb",
81
+ attempts: int = DEFAULT_ENABLE_ATTEMPTS,
82
+ retry_delay_seconds: float = DEFAULT_ENABLE_RETRY_DELAY_SECONDS,
83
+ ) -> AccessibilityEnableResult:
84
+ normalized_attempts = max(1, attempts)
85
+ last_error: SetupAccessibilityError | None = None
86
+ for attempt_index in range(normalized_attempts):
87
+ try:
88
+ return _enable_agent_accessibility_once(serial=serial, adb_path=adb_path)
89
+ except SetupAccessibilityError as error:
90
+ last_error = error
91
+ if (
92
+ attempt_index == normalized_attempts - 1
93
+ or error.code != "ACCESSIBILITY_ENABLE_NOT_CONFIRMED"
94
+ ):
95
+ raise
96
+ time.sleep(max(0.0, retry_delay_seconds))
97
+ if last_error is not None:
98
+ raise last_error
99
+ raise SetupAccessibilityError(
100
+ "ACCESSIBILITY_ENABLE_NOT_CONFIRMED",
101
+ "Android settings did not report AndroidCtl Accessibility as enabled",
102
+ )
103
+
104
+
105
+ def _enable_agent_accessibility_once(
106
+ *,
107
+ serial: str,
108
+ adb_path: str,
109
+ ) -> AccessibilityEnableResult:
110
+ try:
111
+ current_services = setup_adb.get_secure_setting(
112
+ ENABLED_ACCESSIBILITY_SERVICES,
113
+ serial=serial,
114
+ adb_path=adb_path,
115
+ )
116
+ merged_services = merge_enabled_services(current_services)
117
+ changed_service_list = merged_services != ":".join(
118
+ parse_enabled_services(current_services)
119
+ )
120
+ if changed_service_list:
121
+ setup_adb.put_secure_setting(
122
+ ENABLED_ACCESSIBILITY_SERVICES,
123
+ merged_services,
124
+ serial=serial,
125
+ adb_path=adb_path,
126
+ )
127
+ setup_adb.put_secure_setting(
128
+ ACCESSIBILITY_ENABLED,
129
+ ACCESSIBILITY_ENABLED_VALUE,
130
+ serial=serial,
131
+ adb_path=adb_path,
132
+ )
133
+ verified_services = setup_adb.get_secure_setting(
134
+ ENABLED_ACCESSIBILITY_SERVICES,
135
+ serial=serial,
136
+ adb_path=adb_path,
137
+ )
138
+ verified_enabled = setup_adb.get_secure_setting(
139
+ ACCESSIBILITY_ENABLED,
140
+ serial=serial,
141
+ adb_path=adb_path,
142
+ )
143
+ except setup_adb.SetupAdbError as error:
144
+ raise SetupAccessibilityError(error.code, error.message) from error
145
+
146
+ if not service_is_enabled(verified_services):
147
+ raise SetupAccessibilityError(
148
+ "ACCESSIBILITY_ENABLE_NOT_CONFIRMED",
149
+ "Android settings did not report AndroidCtl Accessibility as enabled",
150
+ )
151
+ if verified_enabled != ACCESSIBILITY_ENABLED_VALUE:
152
+ raise SetupAccessibilityError(
153
+ "ACCESSIBILITY_ENABLE_NOT_CONFIRMED",
154
+ "Android settings did not report Accessibility as globally enabled",
155
+ )
156
+ return AccessibilityEnableResult(
157
+ changed_service_list=changed_service_list,
158
+ enabled_services=verified_services,
159
+ )