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.
- android_env/__init__.py +1 -1
- android_env/components/__init__.py +1 -1
- android_env/components/a11y/__init__.py +15 -0
- android_env/components/a11y/a11y_events.py +118 -0
- android_env/components/a11y/a11y_events_test.py +173 -0
- android_env/components/a11y/a11y_forests.py +128 -0
- android_env/components/a11y/a11y_forests_test.py +237 -0
- android_env/components/a11y/a11y_servicer.py +199 -0
- android_env/components/a11y/a11y_servicer_test.py +224 -0
- android_env/components/action_fns.py +132 -0
- android_env/components/action_fns_test.py +227 -0
- android_env/components/action_type.py +26 -3
- android_env/components/adb_call_parser.py +233 -185
- android_env/components/adb_call_parser_test.py +165 -163
- android_env/components/adb_controller.py +19 -28
- android_env/components/adb_controller_test.py +100 -9
- android_env/components/adb_log_stream.py +3 -3
- android_env/components/adb_log_stream_test.py +1 -1
- android_env/components/app_screen_checker.py +15 -13
- android_env/components/app_screen_checker_test.py +1 -1
- android_env/components/config_classes.py +203 -0
- android_env/components/coordinator.py +53 -338
- android_env/components/coordinator_test.py +26 -283
- android_env/components/device_settings.py +174 -0
- android_env/components/device_settings_test.py +228 -0
- android_env/components/dumpsys_thread.py +3 -4
- android_env/components/dumpsys_thread_test.py +1 -1
- android_env/components/errors.py +2 -5
- android_env/components/errors_test.py +1 -1
- android_env/components/log_stream.py +2 -2
- android_env/components/log_stream_test.py +1 -1
- android_env/components/logcat_thread.py +9 -8
- android_env/components/logcat_thread_test.py +2 -3
- android_env/components/{utils.py → pixel_fns.py} +19 -20
- android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
- android_env/components/setup_step_interpreter.py +45 -37
- android_env/components/setup_step_interpreter_test.py +1 -1
- android_env/components/simulators/__init__.py +1 -1
- android_env/components/simulators/base_simulator.py +79 -23
- android_env/components/simulators/base_simulator_test.py +131 -9
- android_env/components/simulators/emulator/__init__.py +1 -1
- android_env/components/simulators/emulator/emulator_launcher.py +62 -81
- android_env/components/simulators/emulator/emulator_launcher_test.py +120 -43
- android_env/components/simulators/emulator/emulator_simulator.py +111 -98
- android_env/components/simulators/emulator/emulator_simulator_test.py +174 -138
- android_env/components/simulators/fake/__init__.py +1 -1
- android_env/components/simulators/fake/fake_simulator.py +9 -17
- android_env/components/simulators/fake/fake_simulator_test.py +23 -8
- android_env/components/specs.py +1 -1
- android_env/components/specs_test.py +1 -1
- android_env/components/task_manager.py +26 -31
- android_env/components/task_manager_test.py +1 -18
- android_env/env_interface.py +1 -17
- android_env/environment.py +27 -17
- android_env/environment_test.py +51 -25
- android_env/loader.py +57 -43
- android_env/loader_test.py +115 -35
- android_env/proto/__init__.py +1 -1
- android_env/proto/a11y/__init__.py +15 -0
- android_env/proto/a11y/a11y.proto +75 -0
- android_env/proto/a11y/a11y_pb2.py +54 -0
- android_env/proto/a11y/a11y_pb2.pyi +49 -0
- android_env/proto/a11y/a11y_pb2_grpc.py +202 -0
- android_env/proto/a11y/android_accessibility_action.proto +32 -0
- android_env/proto/a11y/android_accessibility_action_pb2.py +37 -0
- android_env/proto/a11y/android_accessibility_action_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_action_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_forest.proto +29 -0
- android_env/proto/a11y/android_accessibility_forest_pb2.py +38 -0
- android_env/proto/a11y/android_accessibility_forest_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_forest_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_node_info.proto +122 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto +49 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.py +39 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.pyi +28 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2.py +42 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2.pyi +75 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_tree.proto +29 -0
- android_env/proto/a11y/android_accessibility_tree_pb2.py +38 -0
- android_env/proto/a11y/android_accessibility_tree_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_tree_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_window_info.proto +84 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2.py +41 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2.pyi +48 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2_grpc.py +24 -0
- android_env/proto/a11y/rect.proto +30 -0
- android_env/proto/a11y/rect_pb2.py +37 -0
- android_env/proto/a11y/rect_pb2.pyi +17 -0
- android_env/proto/a11y/rect_pb2_grpc.py +24 -0
- android_env/proto/adb.proto +13 -1
- android_env/proto/adb_pb2.py +120 -107
- android_env/proto/adb_pb2.pyi +396 -0
- android_env/proto/adb_pb2_grpc.py +20 -0
- android_env/proto/emulator_controller.proto +1 -1
- android_env/proto/emulator_controller_pb2.py +142 -131
- android_env/proto/emulator_controller_pb2.pyi +672 -0
- android_env/proto/emulator_controller_pb2_grpc.py +497 -136
- android_env/proto/snapshot.proto +1 -1
- android_env/proto/snapshot_pb2.py +30 -19
- android_env/proto/snapshot_pb2.pyi +117 -0
- android_env/proto/snapshot_pb2_grpc.py +20 -0
- android_env/proto/snapshot_service.proto +1 -1
- android_env/proto/snapshot_service_pb2.py +36 -25
- android_env/proto/snapshot_service_pb2.pyi +86 -0
- android_env/proto/snapshot_service_pb2_grpc.py +119 -28
- android_env/proto/state.proto +1 -1
- android_env/proto/state_pb2.py +46 -35
- android_env/proto/state_pb2.pyi +85 -0
- android_env/proto/state_pb2_grpc.py +20 -0
- android_env/proto/task.proto +4 -1
- android_env/proto/task_pb2.py +41 -30
- android_env/proto/task_pb2.pyi +160 -0
- android_env/proto/task_pb2_grpc.py +20 -0
- android_env/wrappers/__init__.py +1 -1
- android_env/wrappers/a11y_grpc_wrapper.py +500 -0
- android_env/wrappers/a11y_grpc_wrapper_test.py +849 -0
- android_env/wrappers/base_wrapper.py +1 -5
- android_env/wrappers/base_wrapper_test.py +1 -7
- android_env/wrappers/discrete_action_wrapper.py +15 -14
- android_env/wrappers/discrete_action_wrapper_test.py +1 -1
- android_env/wrappers/flat_interface_wrapper.py +5 -5
- android_env/wrappers/flat_interface_wrapper_test.py +1 -1
- android_env/wrappers/float_pixels_wrapper.py +5 -4
- android_env/wrappers/float_pixels_wrapper_test.py +1 -1
- android_env/wrappers/gym_wrapper.py +1 -1
- android_env/wrappers/gym_wrapper_test.py +1 -1
- android_env/wrappers/image_rescale_wrapper.py +13 -10
- android_env/wrappers/image_rescale_wrapper_test.py +1 -1
- android_env/wrappers/last_action_wrapper.py +5 -4
- android_env/wrappers/last_action_wrapper_test.py +1 -1
- android_env/wrappers/rate_limit_wrapper.py +1 -1
- android_env/wrappers/rate_limit_wrapper_test.py +1 -1
- android_env/wrappers/tap_action_wrapper.py +12 -12
- android_env/wrappers/tap_action_wrapper_test.py +49 -14
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -16
- android_env-1.2.3.dist-info/RECORD +141 -0
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
- android_env-1.2.2.dist-info/RECORD +0 -88
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
- {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
|
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
|
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
|
-
|
48
|
-
|
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
|
-
|
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.
|
85
|
-
self.
|
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.
|
125
|
-
enable_key_events=self.
|
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.
|
130
|
-
|
131
|
-
|
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.
|
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.
|
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.'
|
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.
|
146
|
+
self._device_settings.update(self._config.device_settings)
|
230
147
|
except errors.AdbControllerError as e:
|
231
|
-
logging.exception('
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
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 = (
|
384
|
-
|
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':
|
395
|
-
'orientation': self.
|
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
|
-
|
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
|