android-env 1.2.2__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 (142) 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 +233 -185
  14. android_env/components/adb_call_parser_test.py +165 -163
  15. android_env/components/adb_controller.py +19 -28
  16. android_env/components/adb_controller_test.py +100 -9
  17. android_env/components/adb_log_stream.py +3 -3
  18. android_env/components/adb_log_stream_test.py +1 -1
  19. android_env/components/app_screen_checker.py +15 -13
  20. android_env/components/app_screen_checker_test.py +1 -1
  21. android_env/components/config_classes.py +203 -0
  22. android_env/components/coordinator.py +53 -338
  23. android_env/components/coordinator_test.py +26 -283
  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 +2 -5
  29. android_env/components/errors_test.py +1 -1
  30. android_env/components/log_stream.py +2 -2
  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 +2 -3
  34. android_env/components/{utils.py → pixel_fns.py} +19 -20
  35. android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
  36. android_env/components/setup_step_interpreter.py +45 -37
  37. android_env/components/setup_step_interpreter_test.py +1 -1
  38. android_env/components/simulators/__init__.py +1 -1
  39. android_env/components/simulators/base_simulator.py +79 -23
  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 +62 -81
  43. android_env/components/simulators/emulator/emulator_launcher_test.py +120 -43
  44. android_env/components/simulators/emulator/emulator_simulator.py +111 -98
  45. android_env/components/simulators/emulator/emulator_simulator_test.py +174 -138
  46. android_env/components/simulators/fake/__init__.py +1 -1
  47. android_env/components/simulators/fake/fake_simulator.py +9 -17
  48. android_env/components/simulators/fake/fake_simulator_test.py +23 -8
  49. android_env/components/specs.py +1 -1
  50. android_env/components/specs_test.py +1 -1
  51. android_env/components/task_manager.py +26 -31
  52. android_env/components/task_manager_test.py +1 -18
  53. android_env/env_interface.py +1 -17
  54. android_env/environment.py +27 -17
  55. android_env/environment_test.py +51 -25
  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 +13 -1
  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 +1 -1
  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 +497 -136
  100. android_env/proto/snapshot.proto +1 -1
  101. android_env/proto/snapshot_pb2.py +30 -19
  102. android_env/proto/snapshot_pb2.pyi +117 -0
  103. android_env/proto/snapshot_pb2_grpc.py +20 -0
  104. android_env/proto/snapshot_service.proto +1 -1
  105. android_env/proto/snapshot_service_pb2.py +36 -25
  106. android_env/proto/snapshot_service_pb2.pyi +86 -0
  107. android_env/proto/snapshot_service_pb2_grpc.py +119 -28
  108. android_env/proto/state.proto +1 -1
  109. android_env/proto/state_pb2.py +46 -35
  110. android_env/proto/state_pb2.pyi +85 -0
  111. android_env/proto/state_pb2_grpc.py +20 -0
  112. android_env/proto/task.proto +4 -1
  113. android_env/proto/task_pb2.py +41 -30
  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 +1 -5
  120. android_env/wrappers/base_wrapper_test.py +1 -7
  121. android_env/wrappers/discrete_action_wrapper.py +15 -14
  122. android_env/wrappers/discrete_action_wrapper_test.py +1 -1
  123. android_env/wrappers/flat_interface_wrapper.py +5 -5
  124. android_env/wrappers/flat_interface_wrapper_test.py +1 -1
  125. android_env/wrappers/float_pixels_wrapper.py +5 -4
  126. android_env/wrappers/float_pixels_wrapper_test.py +1 -1
  127. android_env/wrappers/gym_wrapper.py +1 -1
  128. android_env/wrappers/gym_wrapper_test.py +1 -1
  129. android_env/wrappers/image_rescale_wrapper.py +13 -10
  130. android_env/wrappers/image_rescale_wrapper_test.py +1 -1
  131. android_env/wrappers/last_action_wrapper.py +5 -4
  132. android_env/wrappers/last_action_wrapper_test.py +1 -1
  133. android_env/wrappers/rate_limit_wrapper.py +1 -1
  134. android_env/wrappers/rate_limit_wrapper_test.py +1 -1
  135. android_env/wrappers/tap_action_wrapper.py +12 -12
  136. android_env/wrappers/tap_action_wrapper_test.py +49 -14
  137. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -16
  138. android_env-1.2.3.dist-info/RECORD +141 -0
  139. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
  140. android_env-1.2.2.dist-info/RECORD +0 -88
  141. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
  142. {android_env-1.2.2.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 2023 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,23 +16,21 @@
16
16
  """Coordinator handles interaction between internal components of AndroidEnv."""
17
17
 
18
18
  import copy
19
- import socket
20
- import tempfile
21
- import threading
22
19
  import time
23
- from typing import Any, Optional
20
+ from typing import Any
24
21
 
25
22
  from absl import logging
23
+ from android_env.components import action_fns
26
24
  from android_env.components import action_type as action_type_lib
27
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
28
28
  from android_env.components import errors
29
+ from android_env.components import pixel_fns
29
30
  from android_env.components import specs
30
31
  from android_env.components import task_manager as task_manager_lib
31
- from android_env.components import utils
32
32
  from android_env.components.simulators import base_simulator
33
33
  from android_env.proto import adb_pb2
34
- from android_env.proto import state_pb2
35
- from android_env.proto import task_pb2
36
34
  import dm_env
37
35
  import numpy as np
38
36
 
@@ -44,58 +42,21 @@ class Coordinator:
44
42
  self,
45
43
  simulator: base_simulator.BaseSimulator,
46
44
  task_manager: task_manager_lib.TaskManager,
47
- num_fingers: int = 1,
48
- interaction_rate_sec: float = 0.0,
49
- enable_key_events: bool = False,
50
- show_touches: bool = True,
51
- show_pointer_location: bool = True,
52
- show_status_bar: bool = False,
53
- show_navigation_bar: bool = False,
54
- periodic_restart_time_min: float = 0.0,
55
- tmp_dir: Optional[str] = None,
45
+ device_settings: device_settings_lib.DeviceSettings,
46
+ config: config_classes.CoordinatorConfig | None = None,
56
47
  ):
57
48
  """Handles communication between AndroidEnv and its components.
58
49
 
59
50
  Args:
60
51
  simulator: A BaseSimulator instance.
61
52
  task_manager: The TaskManager, responsible for coordinating RL tasks.
62
- num_fingers: Number of virtual fingers of the agent.
63
- interaction_rate_sec: How often (in seconds) to fetch the screenshot from
64
- the simulator (asynchronously). If <= 0, stepping the environment blocks
65
- on fetching the screenshot (the environment is synchronous). If > 0,
66
- screenshots are grabbed in a separate thread at this rate; stepping
67
- returns the most recently grabbed screenshot.
68
- enable_key_events: Whether keyboard key events are enabled.
69
- show_touches: Whether to show circles on the screen indicating the
70
- position of the current touch.
71
- show_pointer_location: Whether to show blue lines on the screen indicating
72
- the position of the current touch.
73
- show_status_bar: Whether or not to show the status bar (at the top of the
74
- screen, displays battery life, time, notifications etc.).
75
- show_navigation_bar: Whether or not to show the navigation bar (at the
76
- bottom of the screen, displayes BACK and HOME buttons, etc.)
77
- periodic_restart_time_min: Time between periodic restarts in minutes. If >
78
- 0.0, will trigger a simulator restart at the end of the next episode
79
- once the time has been reached.
80
- tmp_dir: Temporary directory to write transient data.
53
+ config: Settings to customize this Coordinator.
81
54
  """
82
55
  self._simulator = simulator
83
56
  self._task_manager = task_manager
84
- self._num_fingers = num_fingers
85
- self._enable_key_events = enable_key_events
86
- self._show_touches = show_touches
87
- self._show_pointer_location = show_pointer_location
88
- self._show_status_bar = show_status_bar
89
- self._show_navigation_bar = show_navigation_bar
57
+ self._config = config or config_classes.CoordinatorConfig()
58
+ self._device_settings = device_settings
90
59
  self._adb_call_parser: adb_call_parser.AdbCallParser = None
91
- self._periodic_restart_time_min = periodic_restart_time_min
92
- self._tmp_dir = tmp_dir or tempfile.gettempdir()
93
- self._orientation = np.zeros(4, dtype=np.uint8)
94
- self._interaction_rate_sec = interaction_rate_sec
95
- self._interaction_thread = None
96
-
97
- # The size of the device screen in pixels (H x W).
98
- self._screen_size = np.array([0, 0], dtype=np.int32)
99
60
 
100
61
  # Initialize stats.
101
62
  self._stats = {
@@ -121,55 +82,15 @@ class Coordinator:
121
82
 
122
83
  def action_spec(self) -> dict[str, dm_env.specs.Array]:
123
84
  return specs.base_action_spec(
124
- num_fingers=self._num_fingers,
125
- enable_key_events=self._enable_key_events)
85
+ num_fingers=self._config.num_fingers,
86
+ enable_key_events=self._config.enable_key_events,
87
+ )
126
88
 
127
89
  def observation_spec(self) -> dict[str, dm_env.specs.Array]:
128
90
  return specs.base_observation_spec(
129
- height=self._screen_size[0], width=self._screen_size[1])
130
-
131
- def _update_screen_size(self) -> None:
132
- """Sets the screen size from a screenshot ignoring the color channel."""
133
- screenshot = self._simulator.get_screenshot()
134
- self._screen_size = np.array(screenshot.shape[:2], dtype=np.int32)
135
-
136
- def _update_device_orientation(self) -> None:
137
- """Updates the current device orientation."""
138
-
139
- # Skip fetching the orientation if we already have it.
140
- if not np.all(self._orientation == np.zeros(4)):
141
- logging.info('self._orientation already set, not setting it again')
142
- return
143
-
144
- orientation_response = self._adb_call_parser.parse(
145
- adb_pb2.AdbRequest(
146
- get_orientation=adb_pb2.AdbRequest.GetOrientationRequest()))
147
- if orientation_response.status != adb_pb2.AdbResponse.Status.OK:
148
- logging.error('Got bad orientation: %r', orientation_response)
149
- return
150
-
151
- orientation = orientation_response.get_orientation.orientation
152
- if orientation not in {0, 1, 2, 3}:
153
- logging.error('Got bad orientation: %r', orientation_response)
154
- return
155
-
156
- # Transform into one-hot format.
157
- orientation_onehot = np.zeros([4], dtype=np.uint8)
158
- orientation_onehot[orientation] = 1
159
- self._orientation = orientation_onehot
160
-
161
- def _lift_all_fingers(self) -> None:
162
- """Performs a lift action with every finger."""
163
- lift_action = {
164
- 'action_type': np.array(action_type_lib.ActionType.LIFT),
165
- 'touch_position': np.array([0, 0]),
166
- }
167
- for i in range(2, self._num_fingers + 1):
168
- lift_action.update({
169
- f'action_type_{i}': np.array(action_type_lib.ActionType.LIFT),
170
- f'touch_position_{i}': np.array([0, 0]),
171
- })
172
- self._send_action_to_simulator(lift_action)
91
+ height=self._device_settings.screen_height(),
92
+ width=self._device_settings.screen_width(),
93
+ )
173
94
 
174
95
  def _should_periodic_relaunch(self) -> bool:
175
96
  """Checks if it is time to restart the simulator.
@@ -183,10 +104,10 @@ class Coordinator:
183
104
  Boolean indicating if it is time to restart the simulator.
184
105
  """
185
106
 
186
- if self._periodic_restart_time_min and self._simulator_start_time:
107
+ if self._config.periodic_restart_time_min and self._simulator_start_time:
187
108
  sim_alive_time = (time.time() - self._simulator_start_time) / 60.0
188
109
  logging.info('Simulator has been running for %f mins', sim_alive_time)
189
- if sim_alive_time > self._periodic_restart_time_min:
110
+ if sim_alive_time > self._config.periodic_restart_time_min:
190
111
  logging.info('Maximum alive time reached. Restarting simulator.')
191
112
  self._stats['relaunch_count_periodic'] += 1
192
113
  return True
@@ -203,18 +124,14 @@ class Coordinator:
203
124
 
204
125
  self._simulator_healthy = False
205
126
 
206
- # Stop screenshot thread.
207
- if self._interaction_thread is not None:
208
- self._interaction_thread.stop()
209
- self._interaction_thread.join()
210
-
211
127
  # Attempt to restart the system a given number of times.
212
128
  num_tries = 1
213
129
  latest_error = None
214
130
  while True:
215
131
  if num_tries > max_retries:
216
132
  raise errors.TooManyRestartsError(
217
- 'Maximum number of restart attempts reached.') from latest_error
133
+ 'Maximum number of restart attempts reached.'
134
+ ) from latest_error
218
135
  logging.info('Simulator launch attempt %d of %d', num_tries, max_retries)
219
136
 
220
137
  self._task_manager.stop()
@@ -226,9 +143,9 @@ class Coordinator:
226
143
  # From here on, the simulator is assumed to be up and running.
227
144
  self._adb_call_parser = self._create_adb_call_parser()
228
145
  try:
229
- self._update_settings()
146
+ self._device_settings.update(self._config.device_settings)
230
147
  except errors.AdbControllerError as e:
231
- logging.exception('_update_settings() failed.')
148
+ logging.exception('device_settings.update() failed.')
232
149
  self._stats['relaunch_count_update_settings'] += 1
233
150
  self._latest_error = e
234
151
  num_tries += 1
@@ -252,77 +169,16 @@ class Coordinator:
252
169
  self._simulator_healthy = True
253
170
  self._stats['relaunch_count'] += 1
254
171
  break
255
- if self._interaction_rate_sec > 0:
256
- self._interaction_thread = InteractionThread(self._simulator,
257
- self._interaction_rate_sec)
258
- self._interaction_thread.start()
259
-
260
- def _update_settings(self) -> None:
261
- """Updates some internal state and preferences given in the constructor."""
262
-
263
- self._update_screen_size()
264
- self._adb_call_parser.parse(
265
- adb_pb2.AdbRequest(
266
- settings=adb_pb2.AdbRequest.SettingsRequest(
267
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.SYSTEM,
268
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
269
- key='show_touches',
270
- value='1' if self._show_touches else '0'))))
271
- self._adb_call_parser.parse(
272
- adb_pb2.AdbRequest(
273
- settings=adb_pb2.AdbRequest.SettingsRequest(
274
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.SYSTEM,
275
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
276
- key='pointer_location',
277
- value='1' if self._show_pointer_location else '0'))))
278
- if self._show_navigation_bar and self._show_status_bar:
279
- policy_control_value = 'null*'
280
- elif self._show_navigation_bar and not self._show_status_bar:
281
- policy_control_value = 'immersive.status=*'
282
- elif not self._show_navigation_bar and self._show_status_bar:
283
- policy_control_value = 'immersive.navigation=*'
284
- else:
285
- policy_control_value = 'immersive.full=*'
286
- self._adb_call_parser.parse(
287
- adb_pb2.AdbRequest(
288
- settings=adb_pb2.AdbRequest.SettingsRequest(
289
- name_space=adb_pb2.AdbRequest.SettingsRequest.Namespace.GLOBAL,
290
- put=adb_pb2.AdbRequest.SettingsRequest.Put(
291
- key='policy_control', value=policy_control_value))))
292
172
 
293
173
  def _create_adb_call_parser(self):
294
174
  """Creates a new AdbCallParser instance."""
295
175
  return adb_call_parser.AdbCallParser(
296
- adb_controller=self._simulator.create_adb_controller(),
297
- tmp_dir=self._tmp_dir)
176
+ adb_controller=self._simulator.create_adb_controller()
177
+ )
298
178
 
299
179
  def execute_adb_call(self, call: adb_pb2.AdbRequest) -> adb_pb2.AdbResponse:
300
180
  return self._adb_call_parser.parse(call)
301
181
 
302
- def update_task(self, task: task_pb2.Task) -> bool:
303
- """Replaces the current task with a new task.
304
-
305
- Args:
306
- task: A new task to replace the current one.
307
-
308
- Returns:
309
- A bool indicating the success of the task setup.
310
- """
311
- self._task_manager.stop()
312
- self._task_manager.update_task(task)
313
-
314
- self._task_manager.start(
315
- adb_call_parser_factory=self._create_adb_call_parser,
316
- log_stream=self._simulator.create_log_stream(),
317
- )
318
- try:
319
- self._task_manager.setup_task()
320
- return True
321
- except errors.StepCommandError:
322
- logging.error('Failed to set up the task.')
323
- self._stats['failed_task_updates'] += 1
324
- return False
325
-
326
182
  def rl_reset(self) -> dm_env.TimeStep:
327
183
  """Resets the RL episode."""
328
184
 
@@ -337,11 +193,19 @@ class Coordinator:
337
193
  self._stats[key] = 0.0
338
194
 
339
195
  # Execute a lift action before resetting the task.
340
- 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
341
205
 
342
206
  # Reset the task.
343
207
  self._task_manager.reset_task()
344
- self._update_device_orientation()
208
+ self._device_settings.get_orientation()
345
209
 
346
210
  # Get data from the simulator.
347
211
  simulator_signals = self._gather_simulator_signals()
@@ -360,12 +224,20 @@ class Coordinator:
360
224
  An RL timestep.
361
225
  """
362
226
 
363
- 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
364
236
 
365
237
  # Get data from the simulator.
366
238
  try:
367
239
  simulator_signals = self._gather_simulator_signals()
368
- except (errors.ReadObservationError, socket.error):
240
+ except errors.ReadObservationError:
369
241
  logging.exception('Unable to fetch observation. Restarting simulator.')
370
242
  self._stats['relaunch_count_fetch_observation'] += 1
371
243
  self._simulator_healthy = False
@@ -380,188 +252,31 @@ class Coordinator:
380
252
 
381
253
  # Get current timestamp and update the delta.
382
254
  now = time.time()
383
- timestamp_delta = (0 if self._latest_observation_time == 0 else
384
- (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
+ )
385
260
  self._latest_observation_time = now
386
261
 
387
- # Grab pixels.
388
- if self._interaction_rate_sec > 0:
389
- pixels = self._interaction_thread.screenshot() # Async mode.
390
- else:
391
- pixels = self._simulator.get_screenshot() # Sync mode.
392
-
393
262
  return {
394
- 'pixels': pixels,
395
- 'orientation': self._orientation,
263
+ 'pixels': self._simulator.get_screenshot(),
264
+ 'orientation': self._device_settings.get_orientation(),
396
265
  'timedelta': np.array(timestamp_delta, dtype=np.int64),
397
266
  }
398
267
 
399
268
  def __del__(self):
400
269
  self.close()
401
270
 
402
- def _send_action_to_simulator(self, action: dict[str, np.ndarray]) -> None:
403
- """Sends the selected action to the simulator.
404
-
405
- The simulator will interpret the action as a touchscreen event and perform
406
- it accordingly. The effect this action triggers in the Android OS will be
407
- determined by the currently running application.
408
-
409
- Args:
410
- action: action which will get interpreted as a touchscreen event.
411
- """
412
-
413
- try:
414
- # If the action is a TOUCH or LIFT, send a touch event to the simulator.
415
- if (action['action_type'] == action_type_lib.ActionType.TOUCH or
416
- action['action_type'] == action_type_lib.ActionType.LIFT):
417
- prepared_action = self._prepare_touch_action(action)
418
- self._simulator.send_touch(prepared_action)
419
- # If the action is a key event, send a key event to the simulator.
420
- elif action['action_type'] == action_type_lib.ActionType.KEYDOWN:
421
- self._simulator.send_key(
422
- action['keycode'].item(0), event_type='keydown'
423
- )
424
- elif action['action_type'] == action_type_lib.ActionType.KEYUP:
425
- self._simulator.send_key(action['keycode'].item(0), event_type='keyup')
426
- elif action['action_type'] == action_type_lib.ActionType.KEYPRESS:
427
- self._simulator.send_key(action['keycode'].item(0), event_type='keypress')
428
- except (socket.error, errors.SendActionError):
429
- logging.exception('Unable to execute action. Restarting simulator.')
430
- self._stats['relaunch_count_execute_action'] += 1
431
- self._simulator_healthy = False
432
-
433
- def _prepare_touch_action(
434
- self, action: dict[str, np.ndarray]
435
- ) -> list[tuple[int, int, bool, int]]:
436
- """Turns an AndroidEnv action into values that the simulator can interpret.
437
-
438
- Converts float-valued 'touch_position' to integer coordinates corresponding
439
- to specific pixels, and 'action_type' to booleans indicating whether the
440
- screen is touched at said location or not. The result of this function can
441
- be sent directly to the underlying simulator (e.g. the Android Emulator,
442
- virtual machine, or a phone).
443
-
444
- Args:
445
- action: An action containing 'action_type' and 'touch_position'.
446
-
447
- Returns:
448
- A tuple with the format (x: int, y: int, down/up: bool).
449
- """
450
-
451
- touch_events = []
452
- width_height = self._screen_size[::-1]
453
- for i, finger_action in enumerate(self._split_touch_action(action)):
454
- is_touch = (
455
- finger_action['action_type'] == action_type_lib.ActionType.TOUCH)
456
- touch_position = finger_action['touch_position']
457
- touch_pixels = utils.touch_position_to_pixel_position(
458
- touch_position, width_height=width_height)
459
- touch_events.append((touch_pixels[0], touch_pixels[1], is_touch, i))
460
- return touch_events
461
-
462
- def _split_touch_action(
463
- self, action: dict[str, np.ndarray]
464
- ) -> list[dict[str, np.ndarray]]:
465
- """Splits a multitouch action into a list of single-touch actions."""
466
-
467
- single_touch_actions = [{
468
- 'action_type': action['action_type'],
469
- 'touch_position': action['touch_position'],
470
- }]
471
- for i in range(2, self._num_fingers + 1):
472
- single_touch_actions.append({
473
- 'action_type': action[f'action_type_{i}'],
474
- 'touch_position': action[f'touch_position_{i}'],
475
- })
476
- return single_touch_actions
477
-
478
- def _get_time_since_last_observation(self) -> float:
479
- """Computes time passed since the last observation was fetched."""
480
-
481
- return time.time() - self._latest_observation_time
482
-
483
271
  def stats(self) -> dict[str, Any]:
484
272
  """Returns various statistics."""
485
273
 
486
- output = copy.deepcopy(self._stats)
487
- output.update(self._task_manager.stats())
488
- return output
489
-
490
- def load_state(
491
- self, request: state_pb2.LoadStateRequest
492
- ) -> state_pb2.LoadStateResponse:
493
- """Loads a state.
494
-
495
- Args:
496
- request: A `LoadStateRequest` containing any parameters necessary to
497
- specify how/what state to load.
498
-
499
- Returns:
500
- A `LoadStateResponse` containing the status, error message (if
501
- applicable), and any other relevant information.
502
- """
503
- self._task_manager.stop()
504
- response = self._simulator.load_state(request)
505
- self._task_manager.start(
506
- adb_call_parser_factory=self._create_adb_call_parser,
507
- log_stream=self._simulator.create_log_stream(),
508
- )
509
- return response
510
-
511
- def save_state(
512
- self, request: state_pb2.SaveStateRequest
513
- ) -> state_pb2.SaveStateResponse:
514
- """Saves a state.
515
-
516
- Args:
517
- request: A `SaveStateRequest` containing any parameters necessary to
518
- specify how/what state to save.
519
-
520
- Returns:
521
- A `SaveStateResponse` containing the status, error message (if
522
- applicable), and any other relevant information.
523
- """
524
- return self._simulator.save_state(request)
274
+ return copy.deepcopy(self._stats)
525
275
 
526
276
  def close(self):
527
277
  """Cleans up the state of this Coordinator."""
528
- if self._interaction_thread is not None:
529
- self._interaction_thread.stop()
530
- self._interaction_thread.join()
531
278
 
532
279
  if hasattr(self, '_task_manager'):
533
280
  self._task_manager.stop()
534
281
  if hasattr(self, '_simulator'):
535
282
  self._simulator.close()
536
-
537
-
538
- class InteractionThread(threading.Thread):
539
- """A thread that interacts with a simulator."""
540
-
541
- def __init__(self, simulator: base_simulator.BaseSimulator,
542
- interaction_rate_sec: float):
543
- super().__init__()
544
- self._simulator = simulator
545
- self._interaction_rate_sec = interaction_rate_sec
546
- self._should_stop = threading.Event()
547
- self._screenshot = self._simulator.get_screenshot()
548
-
549
- def run(self):
550
- last_read = time.time()
551
- while not self._should_stop.is_set():
552
- self._screenshot = self._simulator.get_screenshot()
553
-
554
- now = time.time()
555
- elapsed = now - last_read
556
- last_read = now
557
- sleep_time = self._interaction_rate_sec - elapsed
558
- if sleep_time > 0.0:
559
- time.sleep(sleep_time)
560
- logging.info('InteractionThread.run() finished.')
561
-
562
- def stop(self):
563
- logging.info('Stopping InteractionThread.')
564
- self._should_stop.set()
565
-
566
- def screenshot(self):
567
- return self._screenshot