android-env 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. android_env/__init__.py +1 -1
  2. android_env/components/__init__.py +1 -1
  3. android_env/components/a11y/__init__.py +15 -0
  4. android_env/components/a11y/a11y_events.py +118 -0
  5. android_env/components/a11y/a11y_events_test.py +173 -0
  6. android_env/components/a11y/a11y_forests.py +128 -0
  7. android_env/components/a11y/a11y_forests_test.py +237 -0
  8. android_env/components/a11y/a11y_servicer.py +199 -0
  9. android_env/components/a11y/a11y_servicer_test.py +224 -0
  10. android_env/components/action_fns.py +132 -0
  11. android_env/components/action_fns_test.py +227 -0
  12. android_env/components/action_type.py +26 -3
  13. android_env/components/adb_call_parser.py +239 -196
  14. android_env/components/adb_call_parser_test.py +179 -209
  15. android_env/components/adb_controller.py +90 -52
  16. android_env/components/adb_controller_test.py +187 -16
  17. android_env/components/adb_log_stream.py +17 -5
  18. android_env/components/adb_log_stream_test.py +17 -3
  19. android_env/components/app_screen_checker.py +17 -15
  20. android_env/components/app_screen_checker_test.py +7 -8
  21. android_env/components/config_classes.py +203 -0
  22. android_env/components/coordinator.py +102 -338
  23. android_env/components/coordinator_test.py +59 -199
  24. android_env/components/device_settings.py +174 -0
  25. android_env/components/device_settings_test.py +228 -0
  26. android_env/components/dumpsys_thread.py +3 -4
  27. android_env/components/dumpsys_thread_test.py +1 -1
  28. android_env/components/errors.py +52 -10
  29. android_env/components/errors_test.py +110 -0
  30. android_env/components/log_stream.py +7 -5
  31. android_env/components/log_stream_test.py +1 -1
  32. android_env/components/logcat_thread.py +9 -8
  33. android_env/components/logcat_thread_test.py +3 -4
  34. android_env/components/{utils.py → pixel_fns.py} +20 -20
  35. android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
  36. android_env/components/setup_step_interpreter.py +47 -39
  37. android_env/components/setup_step_interpreter_test.py +4 -4
  38. android_env/components/simulators/__init__.py +1 -1
  39. android_env/components/simulators/base_simulator.py +116 -44
  40. android_env/components/simulators/base_simulator_test.py +131 -9
  41. android_env/components/simulators/emulator/__init__.py +1 -1
  42. android_env/components/simulators/emulator/emulator_launcher.py +67 -77
  43. android_env/components/simulators/emulator/emulator_launcher_test.py +153 -49
  44. android_env/components/simulators/emulator/emulator_simulator.py +276 -95
  45. android_env/components/simulators/emulator/emulator_simulator_test.py +314 -89
  46. android_env/components/simulators/fake/__init__.py +1 -1
  47. android_env/components/simulators/fake/fake_simulator.py +17 -25
  48. android_env/components/simulators/fake/fake_simulator_test.py +29 -12
  49. android_env/components/specs.py +18 -28
  50. android_env/components/specs_test.py +1 -44
  51. android_env/components/task_manager.py +48 -48
  52. android_env/components/task_manager_test.py +71 -60
  53. android_env/env_interface.py +37 -23
  54. android_env/environment.py +83 -51
  55. android_env/environment_test.py +68 -29
  56. android_env/loader.py +57 -43
  57. android_env/loader_test.py +115 -35
  58. android_env/proto/__init__.py +1 -1
  59. android_env/proto/a11y/__init__.py +15 -0
  60. android_env/proto/a11y/a11y.proto +75 -0
  61. android_env/proto/a11y/a11y_pb2.py +54 -0
  62. android_env/proto/a11y/a11y_pb2.pyi +49 -0
  63. android_env/proto/a11y/a11y_pb2_grpc.py +202 -0
  64. android_env/proto/a11y/android_accessibility_action.proto +32 -0
  65. android_env/proto/a11y/android_accessibility_action_pb2.py +37 -0
  66. android_env/proto/a11y/android_accessibility_action_pb2.pyi +13 -0
  67. android_env/proto/a11y/android_accessibility_action_pb2_grpc.py +24 -0
  68. android_env/proto/a11y/android_accessibility_forest.proto +29 -0
  69. android_env/proto/a11y/android_accessibility_forest_pb2.py +38 -0
  70. android_env/proto/a11y/android_accessibility_forest_pb2.pyi +13 -0
  71. android_env/proto/a11y/android_accessibility_forest_pb2_grpc.py +24 -0
  72. android_env/proto/a11y/android_accessibility_node_info.proto +122 -0
  73. android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto +49 -0
  74. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.py +39 -0
  75. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.pyi +28 -0
  76. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2_grpc.py +24 -0
  77. android_env/proto/a11y/android_accessibility_node_info_pb2.py +42 -0
  78. android_env/proto/a11y/android_accessibility_node_info_pb2.pyi +75 -0
  79. android_env/proto/a11y/android_accessibility_node_info_pb2_grpc.py +24 -0
  80. android_env/proto/a11y/android_accessibility_tree.proto +29 -0
  81. android_env/proto/a11y/android_accessibility_tree_pb2.py +38 -0
  82. android_env/proto/a11y/android_accessibility_tree_pb2.pyi +13 -0
  83. android_env/proto/a11y/android_accessibility_tree_pb2_grpc.py +24 -0
  84. android_env/proto/a11y/android_accessibility_window_info.proto +84 -0
  85. android_env/proto/a11y/android_accessibility_window_info_pb2.py +41 -0
  86. android_env/proto/a11y/android_accessibility_window_info_pb2.pyi +48 -0
  87. android_env/proto/a11y/android_accessibility_window_info_pb2_grpc.py +24 -0
  88. android_env/proto/a11y/rect.proto +30 -0
  89. android_env/proto/a11y/rect_pb2.py +37 -0
  90. android_env/proto/a11y/rect_pb2.pyi +17 -0
  91. android_env/proto/a11y/rect_pb2_grpc.py +24 -0
  92. android_env/proto/adb.proto +17 -6
  93. android_env/proto/adb_pb2.py +120 -107
  94. android_env/proto/adb_pb2.pyi +396 -0
  95. android_env/proto/adb_pb2_grpc.py +20 -0
  96. android_env/proto/emulator_controller.proto +68 -63
  97. android_env/proto/emulator_controller_pb2.py +142 -131
  98. android_env/proto/emulator_controller_pb2.pyi +672 -0
  99. android_env/proto/emulator_controller_pb2_grpc.py +505 -142
  100. android_env/proto/snapshot.proto +169 -0
  101. android_env/proto/snapshot_pb2.py +47 -0
  102. android_env/proto/snapshot_pb2.pyi +117 -0
  103. android_env/proto/snapshot_pb2_grpc.py +24 -0
  104. android_env/proto/snapshot_service.proto +289 -0
  105. android_env/proto/snapshot_service_pb2.py +54 -0
  106. android_env/proto/snapshot_service_pb2.pyi +86 -0
  107. android_env/proto/snapshot_service_pb2_grpc.py +487 -0
  108. android_env/proto/state.proto +63 -0
  109. android_env/proto/state_pb2.py +63 -0
  110. android_env/proto/state_pb2.pyi +85 -0
  111. android_env/proto/state_pb2_grpc.py +24 -0
  112. android_env/proto/task.proto +5 -1
  113. android_env/proto/task_pb2.py +42 -31
  114. android_env/proto/task_pb2.pyi +160 -0
  115. android_env/proto/task_pb2_grpc.py +20 -0
  116. android_env/wrappers/__init__.py +1 -1
  117. android_env/wrappers/a11y_grpc_wrapper.py +500 -0
  118. android_env/wrappers/a11y_grpc_wrapper_test.py +849 -0
  119. android_env/wrappers/base_wrapper.py +34 -13
  120. android_env/wrappers/base_wrapper_test.py +22 -16
  121. android_env/wrappers/discrete_action_wrapper.py +18 -17
  122. android_env/wrappers/discrete_action_wrapper_test.py +4 -4
  123. android_env/wrappers/flat_interface_wrapper.py +5 -5
  124. android_env/wrappers/flat_interface_wrapper_test.py +7 -11
  125. android_env/wrappers/float_pixels_wrapper.py +9 -10
  126. android_env/wrappers/float_pixels_wrapper_test.py +3 -3
  127. android_env/wrappers/gym_wrapper.py +19 -13
  128. android_env/wrappers/gym_wrapper_test.py +3 -5
  129. android_env/wrappers/image_rescale_wrapper.py +18 -21
  130. android_env/wrappers/image_rescale_wrapper_test.py +25 -37
  131. android_env/wrappers/last_action_wrapper.py +16 -13
  132. android_env/wrappers/last_action_wrapper_test.py +44 -51
  133. android_env/wrappers/rate_limit_wrapper.py +6 -3
  134. android_env/wrappers/rate_limit_wrapper_test.py +22 -1
  135. android_env/wrappers/tap_action_wrapper.py +16 -17
  136. android_env/wrappers/tap_action_wrapper_test.py +51 -16
  137. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -18
  138. android_env-1.2.3.dist-info/RECORD +141 -0
  139. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
  140. android_env/proto/raw_observation.proto +0 -39
  141. android_env/proto/raw_observation_pb2.py +0 -27
  142. android_env/proto/raw_observation_pb2_grpc.py +0 -4
  143. android_env-1.2.1.dist-info/RECORD +0 -81
  144. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
  145. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2022 DeepMind Technologies Limited.
2
+ # Copyright 2024 DeepMind Technologies Limited.
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -16,96 +16,59 @@
16
16
  """Coordinator handles interaction between internal components of AndroidEnv."""
17
17
 
18
18
  import copy
19
- import socket
20
- import tempfile
21
19
  import time
22
- from typing import Any, Dict, List, Optional, Tuple
20
+ from typing import Any
23
21
 
24
22
  from absl import logging
23
+ from android_env.components import action_fns
25
24
  from android_env.components import action_type as action_type_lib
26
25
  from android_env.components import adb_call_parser
26
+ from android_env.components import config_classes
27
+ from android_env.components import device_settings as device_settings_lib
27
28
  from android_env.components import errors
29
+ from android_env.components import pixel_fns
28
30
  from android_env.components import specs
29
31
  from android_env.components import task_manager as task_manager_lib
30
- from android_env.components import utils
31
32
  from android_env.components.simulators import base_simulator
32
33
  from android_env.proto import adb_pb2
33
- from android_env.proto import task_pb2
34
34
  import dm_env
35
35
  import numpy as np
36
36
 
37
37
 
38
- class Coordinator():
38
+ class Coordinator:
39
39
  """Handles interaction between internal components of AndroidEnv."""
40
40
 
41
41
  def __init__(
42
42
  self,
43
43
  simulator: base_simulator.BaseSimulator,
44
44
  task_manager: task_manager_lib.TaskManager,
45
- num_fingers: int = 1,
46
- enable_key_events: bool = False,
47
- show_touches: bool = True,
48
- show_pointer_location: bool = True,
49
- show_status_bar: bool = False,
50
- show_navigation_bar: bool = False,
51
- periodic_restart_time_min: float = 0.0,
52
- force_simulator_launch: bool = True,
53
- check_services_max_tries: int = 20,
54
- tmp_dir: Optional[str] = None,
45
+ device_settings: device_settings_lib.DeviceSettings,
46
+ config: config_classes.CoordinatorConfig | None = None,
55
47
  ):
56
48
  """Handles communication between AndroidEnv and its components.
57
49
 
58
50
  Args:
59
51
  simulator: A BaseSimulator instance.
60
52
  task_manager: The TaskManager, responsible for coordinating RL tasks.
61
- num_fingers: Number of virtual fingers of the agent.
62
- enable_key_events: Whether keyboard key events are enabled.
63
- show_touches: Whether to show circles on the screen indicating the
64
- position of the current touch.
65
- show_pointer_location: Whether to show blue lines on the screen indicating
66
- the position of the current touch.
67
- show_status_bar: Whether or not to show the status bar (at the top of the
68
- screen, displays battery life, time, notifications etc.).
69
- show_navigation_bar: Whether or not to show the navigation bar (at the
70
- bottom of the screen, displayes BACK and HOME buttons, etc.)
71
- periodic_restart_time_min: Time between periodic restarts in minutes. If >
72
- 0.0, will trigger a simulator restart at the end of the next episode
73
- once the time has been reached.
74
- force_simulator_launch: Forces the simulator to relaunch even if it is
75
- already launched.
76
- check_services_max_tries: The maximum number of tries to check for a few
77
- Android services like the display or package management to be up and
78
- running at __init__. If <=0, nothing is checked.
79
- tmp_dir: Temporary directory to write transient data.
53
+ config: Settings to customize this Coordinator.
80
54
  """
81
55
  self._simulator = simulator
82
56
  self._task_manager = task_manager
83
- self._num_fingers = num_fingers
84
- self._enable_key_events = enable_key_events
85
- self._show_touches = show_touches
86
- self._show_pointer_location = show_pointer_location
87
- self._show_status_bar = show_status_bar
88
- self._show_navigation_bar = show_navigation_bar
57
+ self._config = config or config_classes.CoordinatorConfig()
58
+ self._device_settings = device_settings
89
59
  self._adb_call_parser: adb_call_parser.AdbCallParser = None
90
- self._periodic_restart_time_min = periodic_restart_time_min
91
- self._force_simulator_launch = force_simulator_launch
92
- self._check_services_max_tries = check_services_max_tries
93
- self._tmp_dir = tmp_dir or tempfile.gettempdir()
94
- self._orientation = np.zeros(4, dtype=np.uint8)
95
-
96
- # The size of the device screen in pixels (H x W).
97
- self._screen_size = np.array([0, 0], dtype=np.int32)
98
60
 
99
61
  # Initialize stats.
100
62
  self._stats = {
101
- 'restart_count': 0,
102
- 'restart_count_periodic': 0,
103
- 'restart_count_setup_steps': 0,
104
- 'restart_count_reset_steps': 0,
105
- 'restart_count_simulator_launch': 0,
106
- 'restart_count_simulator_reset': 0,
107
- 'restart_count_execute_action': 0,
108
- 'restart_count_fetch_observation': 0,
63
+ 'relaunch_count': 0,
64
+ 'relaunch_count_periodic': 0,
65
+ 'relaunch_count_setup_steps': 0,
66
+ 'relaunch_count_reset_steps': 0,
67
+ 'relaunch_count_simulator_launch': 0,
68
+ 'relaunch_count_simulator_reset': 0,
69
+ 'relaunch_count_execute_action': 0,
70
+ 'relaunch_count_fetch_observation': 0,
71
+ 'relaunch_count_update_settings': 0,
109
72
  'failed_task_updates': 0,
110
73
  }
111
74
 
@@ -115,89 +78,45 @@ class Coordinator():
115
78
  self._simulator_start_time = None
116
79
 
117
80
  logging.info('Starting the simulator...')
118
- self._restart_simulator()
81
+ self._launch_simulator()
119
82
 
120
- def action_spec(self) -> Dict[str, dm_env.specs.Array]:
83
+ def action_spec(self) -> dict[str, dm_env.specs.Array]:
121
84
  return specs.base_action_spec(
122
- num_fingers=self._num_fingers,
123
- enable_key_events=self._enable_key_events)
85
+ num_fingers=self._config.num_fingers,
86
+ enable_key_events=self._config.enable_key_events,
87
+ )
124
88
 
125
- def observation_spec(self) -> Dict[str, dm_env.specs.Array]:
89
+ def observation_spec(self) -> dict[str, dm_env.specs.Array]:
126
90
  return specs.base_observation_spec(
127
- height=self._screen_size[0], width=self._screen_size[1])
128
-
129
- def task_extras_spec(self) -> Dict[str, dm_env.specs.Array]:
130
- return specs.base_task_extras_spec(task=self._task_manager.task())
131
-
132
- def _update_screen_size(self) -> None:
133
- """Sets the screen size from a screenshot ignoring the color channel."""
134
- screenshot = self._simulator.get_screenshot()
135
- self._screen_size = np.array(screenshot.shape[:2], dtype=np.int32)
136
-
137
- def _update_device_orientation(self) -> None:
138
- """Updates the current device orientation."""
139
-
140
- # Skip fetching the orientation if we already have it.
141
- if not np.all(self._orientation == np.zeros(4)):
142
- logging.info('self._orientation already set, not setting it again')
143
- return
144
-
145
- orientation_response = self._adb_call_parser.parse(
146
- adb_pb2.AdbRequest(
147
- get_orientation=adb_pb2.AdbRequest.GetOrientationRequest()))
148
- if orientation_response.status != adb_pb2.AdbResponse.Status.OK:
149
- logging.error('Got bad orientation: %r', orientation_response)
150
- return
151
-
152
- orientation = orientation_response.get_orientation.orientation
153
- if orientation not in {0, 1, 2, 3}:
154
- logging.error('Got bad orientation: %r', orientation_response)
155
- return
156
-
157
- # Transform into one-hot format.
158
- orientation_onehot = np.zeros([4], dtype=np.uint8)
159
- orientation_onehot[orientation] = 1
160
- self._orientation = orientation_onehot
161
-
162
- def _lift_all_fingers(self) -> None:
163
- """Performs a lift action with every finger."""
164
- lift_action = {
165
- 'action_type': np.array(action_type_lib.ActionType.LIFT),
166
- 'touch_position': np.array([0, 0]),
167
- }
168
- for i in range(2, self._num_fingers + 1):
169
- lift_action.update({
170
- f'action_type_{i}': np.array(action_type_lib.ActionType.LIFT),
171
- f'touch_position_{i}': np.array([0, 0]),
172
- })
173
- self._send_action_to_simulator(lift_action)
174
-
175
- def _should_periodic_restart(self) -> bool:
91
+ height=self._device_settings.screen_height(),
92
+ width=self._device_settings.screen_width(),
93
+ )
94
+
95
+ def _should_periodic_relaunch(self) -> bool:
176
96
  """Checks if it is time to restart the simulator.
177
97
 
178
98
  If a periodic restart time was specified, the Coordinator will re-launch
179
99
  the simulator at regular time intervals. This helps to make sure that the
180
- simulator is not is a stale state even if the environment has been running
100
+ simulator is not in a stale state even if the environment has been running
181
101
  for a significant amount of time.
182
102
 
183
103
  Returns:
184
104
  Boolean indicating if it is time to restart the simulator.
185
105
  """
186
106
 
187
- if self._periodic_restart_time_min and self._simulator_start_time:
107
+ if self._config.periodic_restart_time_min and self._simulator_start_time:
188
108
  sim_alive_time = (time.time() - self._simulator_start_time) / 60.0
189
109
  logging.info('Simulator has been running for %f mins', sim_alive_time)
190
- if sim_alive_time > self._periodic_restart_time_min:
110
+ if sim_alive_time > self._config.periodic_restart_time_min:
191
111
  logging.info('Maximum alive time reached. Restarting simulator.')
192
- self._stats['restart_count_periodic'] += 1
112
+ self._stats['relaunch_count_periodic'] += 1
193
113
  return True
194
114
  return False
195
115
 
196
- def _restart_simulator(self, max_retries: int = 3):
197
- """Restarts the simulation.
116
+ def _launch_simulator(self, max_retries: int = 3):
117
+ """Launches the simulator.
198
118
 
199
- Closes and re-launches the system, restarting the simulator process and
200
- reinitializing the task in the newly started simulator.
119
+ Sets up the simulator and other task-related settings.
201
120
 
202
121
  Args:
203
122
  max_retries: Number of times to attempt a restart before raising an error.
@@ -211,106 +130,61 @@ class Coordinator():
211
130
  while True:
212
131
  if num_tries > max_retries:
213
132
  raise errors.TooManyRestartsError(
214
- 'Maximum number of restart attempts reached.') from latest_error
133
+ 'Maximum number of restart attempts reached.'
134
+ ) from latest_error
215
135
  logging.info('Simulator launch attempt %d of %d', num_tries, max_retries)
216
136
 
217
- # Launch the simulator (will restart if already launched).
218
- if self._force_simulator_launch or not self._simulator.is_launched():
219
- self._task_manager.stop_task()
220
- self._simulator.launch()
221
- self._simulator_start_time = time.time()
137
+ self._task_manager.stop()
222
138
 
139
+ # Launch the simulator.
140
+ self._simulator.launch()
141
+ self._simulator_start_time = time.time()
142
+
143
+ # From here on, the simulator is assumed to be up and running.
223
144
  self._adb_call_parser = self._create_adb_call_parser()
224
- self._wait_for_device(self._check_services_max_tries)
225
- self._update_settings()
145
+ try:
146
+ self._device_settings.update(self._config.device_settings)
147
+ except errors.AdbControllerError as e:
148
+ logging.exception('device_settings.update() failed.')
149
+ self._stats['relaunch_count_update_settings'] += 1
150
+ self._latest_error = e
151
+ num_tries += 1
152
+ continue
226
153
 
227
154
  # Start the task.
155
+ self._task_manager.start(
156
+ adb_call_parser_factory=self._create_adb_call_parser,
157
+ log_stream=self._simulator.create_log_stream(),
158
+ )
228
159
  try:
229
- self._task_manager.setup_task(
230
- adb_call_parser_factory=self._create_adb_call_parser,
231
- log_stream=self._simulator.create_log_stream())
160
+ self._task_manager.setup_task()
232
161
  except errors.StepCommandError as error:
233
162
  logging.exception('Failed to set up the task. Restarting simulator.')
234
- self._stats['restart_count_setup_steps'] += 1
163
+ self._stats['relaunch_count_setup_steps'] += 1
235
164
  latest_error = error
236
165
  num_tries += 1
237
166
  continue
238
167
 
239
168
  # Restart was successful.
240
169
  self._simulator_healthy = True
241
- self._stats['restart_count'] += 1
170
+ self._stats['relaunch_count'] += 1
242
171
  break
243
172
 
244
- def _update_settings(self) -> None:
245
- """Updates some internal state and preferences given in the constructor."""
246
-
247
- self._update_screen_size()
248
- self._adb_call_parser.parse(
249
- adb_pb2.AdbRequest(
250
- settings=adb_pb2.AdbRequest.SettingsRequest(
251
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.SYSTEM,
252
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
253
- key='show_touches',
254
- value='1' if self._show_touches else '0'))))
255
- self._adb_call_parser.parse(
256
- adb_pb2.AdbRequest(
257
- settings=adb_pb2.AdbRequest.SettingsRequest(
258
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.SYSTEM,
259
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
260
- key='pointer_location',
261
- value='1' if self._show_pointer_location else '0'))))
262
- if self._show_navigation_bar and self._show_status_bar:
263
- policy_control_value = 'null*'
264
- elif self._show_navigation_bar and not self._show_status_bar:
265
- policy_control_value = 'immersive.status=*'
266
- elif not self._show_navigation_bar and self._show_status_bar:
267
- policy_control_value = 'immersive.navigation=*'
268
- else:
269
- policy_control_value = 'immersive.full=*'
270
- self._adb_call_parser.parse(
271
- adb_pb2.AdbRequest(
272
- settings=adb_pb2.AdbRequest.SettingsRequest(
273
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.GLOBAL,
274
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
275
- key='policy_control', value=policy_control_value))))
276
-
277
173
  def _create_adb_call_parser(self):
278
174
  """Creates a new AdbCallParser instance."""
279
175
  return adb_call_parser.AdbCallParser(
280
- adb_controller=self._simulator.create_adb_controller(),
281
- tmp_dir=self._tmp_dir)
176
+ adb_controller=self._simulator.create_adb_controller()
177
+ )
282
178
 
283
179
  def execute_adb_call(self, call: adb_pb2.AdbRequest) -> adb_pb2.AdbResponse:
284
180
  return self._adb_call_parser.parse(call)
285
181
 
286
- def update_task(self, task: task_pb2.Task) -> bool:
287
- """Replaces the current task with a new task.
288
-
289
- Args:
290
- task: A new task to replace the current one.
291
-
292
- Returns:
293
- A bool indicating the success of the task setup.
294
- """
295
- self._task_manager.stop_task()
296
- self._task_manager.update_task(task)
297
-
298
- try:
299
- self._task_manager.setup_task(
300
- adb_call_parser_factory=self._create_adb_call_parser,
301
- log_stream=self._simulator.create_log_stream())
302
- return True
303
- except errors.StepCommandError:
304
- logging.error('Failed to set up the task.')
305
- self._stats['failed_task_updates'] += 1
306
- return False
307
-
308
182
  def rl_reset(self) -> dm_env.TimeStep:
309
183
  """Resets the RL episode."""
310
184
 
311
- # Restart the simulation if neccessary.
312
- if not self._simulator_healthy or self._should_periodic_restart():
313
- self._restart_simulator()
185
+ # Relaunch the simulator if necessary.
186
+ if not self._simulator_healthy or self._should_periodic_relaunch():
187
+ self._launch_simulator()
314
188
 
315
189
  # Reset counters.
316
190
  self._latest_observation_time = 0
@@ -319,18 +193,26 @@ class Coordinator():
319
193
  self._stats[key] = 0.0
320
194
 
321
195
  # Execute a lift action before resetting the task.
322
- self._lift_all_fingers()
196
+ if not action_fns.send_action_to_simulator(
197
+ action_fns.lift_all_fingers_action(self._config.num_fingers),
198
+ self._simulator,
199
+ self._device_settings.screen_width(),
200
+ self._device_settings.screen_height(),
201
+ self._config.num_fingers,
202
+ ):
203
+ self._stats['relaunch_count_execute_action'] += 1
204
+ self._simulator_healthy = False
323
205
 
324
206
  # Reset the task.
325
207
  self._task_manager.reset_task()
326
- self._update_device_orientation()
208
+ self._device_settings.get_orientation()
327
209
 
328
210
  # Get data from the simulator.
329
211
  simulator_signals = self._gather_simulator_signals()
330
212
 
331
213
  return self._task_manager.rl_reset(simulator_signals)
332
214
 
333
- def rl_step(self, agent_action: Dict[str, np.ndarray]) -> dm_env.TimeStep:
215
+ def rl_step(self, agent_action: dict[str, np.ndarray]) -> dm_env.TimeStep:
334
216
  """Executes the selected action and returns a timestep.
335
217
 
336
218
  Args:
@@ -342,14 +224,22 @@ class Coordinator():
342
224
  An RL timestep.
343
225
  """
344
226
 
345
- self._send_action_to_simulator(agent_action)
227
+ if not action_fns.send_action_to_simulator(
228
+ agent_action,
229
+ self._simulator,
230
+ self._device_settings.screen_width(),
231
+ self._device_settings.screen_height(),
232
+ self._config.num_fingers,
233
+ ):
234
+ self._stats['relaunch_count_execute_action'] += 1
235
+ self._simulator_healthy = False
346
236
 
347
237
  # Get data from the simulator.
348
238
  try:
349
239
  simulator_signals = self._gather_simulator_signals()
350
- except (errors.ReadObservationError, socket.error):
240
+ except errors.ReadObservationError:
351
241
  logging.exception('Unable to fetch observation. Restarting simulator.')
352
- self._stats['restart_count_fetch_observation'] += 1
242
+ self._stats['relaunch_count_fetch_observation'] += 1
353
243
  self._simulator_healthy = False
354
244
 
355
245
  if not self._simulator_healthy:
@@ -357,162 +247,36 @@ class Coordinator():
357
247
 
358
248
  return self._task_manager.rl_step(simulator_signals)
359
249
 
360
- def _gather_simulator_signals(self) -> Dict[str, np.ndarray]:
250
+ def _gather_simulator_signals(self) -> dict[str, np.ndarray]:
361
251
  """Gathers data from various sources to assemble the RL observation."""
362
252
 
363
253
  # Get current timestamp and update the delta.
364
254
  now = time.time()
365
- timestamp_delta = (0 if self._latest_observation_time == 0 else
366
- (now - self._latest_observation_time) * 1e6)
255
+ timestamp_delta = (
256
+ 0
257
+ if self._latest_observation_time == 0
258
+ else (now - self._latest_observation_time) * 1e6
259
+ )
367
260
  self._latest_observation_time = now
368
261
 
369
262
  return {
370
263
  'pixels': self._simulator.get_screenshot(),
371
- 'orientation': self._orientation,
372
- 'timedelta': np.int64(timestamp_delta),
264
+ 'orientation': self._device_settings.get_orientation(),
265
+ 'timedelta': np.array(timestamp_delta, dtype=np.int64),
373
266
  }
374
267
 
375
- def _send_action_to_simulator(self, action: Dict[str, np.ndarray]) -> None:
376
- """Sends the selected action to the simulator.
268
+ def __del__(self):
269
+ self.close()
377
270
 
378
- The simulator will interpret the action as a touchscreen event and perform
379
- it accordingly. The effect this action triggers in the Android OS will be
380
- determined by the currently running application.
381
-
382
- Args:
383
- action: action which will get interpreted as a touchscreen event.
384
- """
385
-
386
- try:
387
- # If the action is a TOUCH or LIFT, send a touch event to the simulator.
388
- if (action['action_type'] == action_type_lib.ActionType.TOUCH or
389
- action['action_type'] == action_type_lib.ActionType.LIFT):
390
- prepared_action = self._prepare_touch_action(action)
391
- self._simulator.send_touch(prepared_action)
392
- # If the action is a key event, send a key event to the simulator.
393
- elif action['action_type'] == action_type_lib.ActionType.KEYDOWN:
394
- self._simulator.send_key(action['keycode'], event_type='keydown')
395
- elif action['action_type'] == action_type_lib.ActionType.KEYUP:
396
- self._simulator.send_key(action['keycode'], event_type='keyup')
397
- elif action['action_type'] == action_type_lib.ActionType.KEYPRESS:
398
- self._simulator.send_key(action['keycode'], event_type='keypress')
399
- except (socket.error, errors.SendActionError):
400
- logging.exception('Unable to execute action. Restarting simulator.')
401
- self._stats['restart_count_execute_action'] += 1
402
- self._simulator_healthy = False
403
-
404
- def _prepare_touch_action(
405
- self, action: Dict[str, np.ndarray]) -> List[Tuple[int, int, bool, int]]:
406
- """Turns an AndroidEnv action into values that the simulator can interpret.
407
-
408
- Converts float-valued 'touch_position' to integer coordinates corresponding
409
- to specific pixels, and 'action_type' to booleans indicating whether the
410
- screen is touched at said location or not. The result of this function can
411
- be sent directly to the underlying simulator (e.g. the Android Emulator,
412
- virtual machine, or a phone).
413
-
414
- Args:
415
- action: An action containing 'action_type' and 'touch_position'.
416
-
417
- Returns:
418
- A tuple with the format (x: int, y: int, down/up: bool).
419
- """
420
-
421
- touch_events = []
422
- width_height = self._screen_size[::-1]
423
- for i, finger_action in enumerate(self._split_touch_action(action)):
424
- is_touch = (
425
- finger_action['action_type'] == action_type_lib.ActionType.TOUCH)
426
- touch_position = finger_action['touch_position']
427
- touch_pixels = utils.touch_position_to_pixel_position(
428
- touch_position, width_height=width_height)
429
- touch_events.append((touch_pixels[0], touch_pixels[1], is_touch, i))
430
- return touch_events
431
-
432
- def _split_touch_action(
433
- self, action: Dict[str, np.ndarray]) -> List[Dict[str, np.ndarray]]:
434
- """Splits a multitouch action into a list of single-touch actions."""
435
-
436
- single_touch_actions = [{
437
- 'action_type': action['action_type'],
438
- 'touch_position': action['touch_position'],
439
- }]
440
- for i in range(2, self._num_fingers + 1):
441
- single_touch_actions.append({
442
- 'action_type': action[f'action_type_{i}'],
443
- 'touch_position': action[f'touch_position_{i}'],
444
- })
445
- return single_touch_actions
446
-
447
- def _get_time_since_last_observation(self) -> float:
448
- """Computes time passed since the last observation was fetched."""
449
-
450
- return time.time() - self._latest_observation_time
451
-
452
- def stats(self) -> Dict[str, Any]:
271
+ def stats(self) -> dict[str, Any]:
453
272
  """Returns various statistics."""
454
273
 
455
- output = copy.deepcopy(self._stats)
456
- output.update(self._task_manager.stats())
457
- return output
274
+ return copy.deepcopy(self._stats)
458
275
 
459
276
  def close(self):
460
277
  """Cleans up the state of this Coordinator."""
461
278
 
462
279
  if hasattr(self, '_task_manager'):
463
- self._task_manager.stop_task()
280
+ self._task_manager.stop()
464
281
  if hasattr(self, '_simulator'):
465
282
  self._simulator.close()
466
-
467
- def _wait_for_device(self, max_tries: int) -> None:
468
- """Waits for the device to be ready.
469
-
470
- Args:
471
- max_tries: Maximum number of times to check whether required services are
472
- ready. Returns immediately without checking anything if <= 0.
473
-
474
- Raises:
475
- errors.AdbControllerDeviceTimeoutError when the device is not ready after
476
- `max_tries`.
477
- """
478
-
479
- if max_tries <= 0:
480
- return
481
-
482
- # Dictionary to hold adb output for each service.
483
- service_adb_outputs = {
484
- 'window': None,
485
- 'package': None,
486
- 'input': None,
487
- 'display': None,
488
- }
489
-
490
- def _check_device_is_ready(services: List[str]) -> bool:
491
- """Checks if all required services are ready."""
492
- for service in services:
493
- adb_response = self._adb_call_parser.parse(
494
- adb_pb2.AdbRequest(
495
- generic=adb_pb2.AdbRequest.GenericRequest(
496
- args=['shell', 'service', 'check', service])))
497
- if (adb_response.status != adb_pb2.AdbResponse.Status.OK or
498
- not adb_response.generic.output):
499
- logging.error('Check for service "%s" failed. error_message: %r',
500
- service, adb_response.error_message)
501
- return False
502
- output = adb_response.generic.output.decode('utf-8').strip()
503
- service_adb_outputs[service] = output
504
- if output != f'Service {service}: found':
505
- logging.error(output)
506
- return False
507
- return True
508
-
509
- for _ in range(max_tries):
510
- ready = _check_device_is_ready(list(service_adb_outputs.keys()))
511
- if ready:
512
- logging.info('Device is ready.')
513
- return
514
- time.sleep(1.0)
515
- logging.error('Device is not ready.')
516
- raise errors.AdbControllerDeviceTimeoutError(
517
- 'One or more services are not ready yet. '
518
- f'ADB output: {service_adb_outputs}')