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.
- 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 +239 -196
- android_env/components/adb_call_parser_test.py +179 -209
- android_env/components/adb_controller.py +90 -52
- android_env/components/adb_controller_test.py +187 -16
- android_env/components/adb_log_stream.py +17 -5
- android_env/components/adb_log_stream_test.py +17 -3
- android_env/components/app_screen_checker.py +17 -15
- android_env/components/app_screen_checker_test.py +7 -8
- android_env/components/config_classes.py +203 -0
- android_env/components/coordinator.py +102 -338
- android_env/components/coordinator_test.py +59 -199
- 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 +52 -10
- android_env/components/errors_test.py +110 -0
- android_env/components/log_stream.py +7 -5
- android_env/components/log_stream_test.py +1 -1
- android_env/components/logcat_thread.py +9 -8
- android_env/components/logcat_thread_test.py +3 -4
- android_env/components/{utils.py → pixel_fns.py} +20 -20
- android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
- android_env/components/setup_step_interpreter.py +47 -39
- android_env/components/setup_step_interpreter_test.py +4 -4
- android_env/components/simulators/__init__.py +1 -1
- android_env/components/simulators/base_simulator.py +116 -44
- 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 +67 -77
- android_env/components/simulators/emulator/emulator_launcher_test.py +153 -49
- android_env/components/simulators/emulator/emulator_simulator.py +276 -95
- android_env/components/simulators/emulator/emulator_simulator_test.py +314 -89
- android_env/components/simulators/fake/__init__.py +1 -1
- android_env/components/simulators/fake/fake_simulator.py +17 -25
- android_env/components/simulators/fake/fake_simulator_test.py +29 -12
- android_env/components/specs.py +18 -28
- android_env/components/specs_test.py +1 -44
- android_env/components/task_manager.py +48 -48
- android_env/components/task_manager_test.py +71 -60
- android_env/env_interface.py +37 -23
- android_env/environment.py +83 -51
- android_env/environment_test.py +68 -29
- 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 +17 -6
- 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 +68 -63
- 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 +505 -142
- android_env/proto/snapshot.proto +169 -0
- android_env/proto/snapshot_pb2.py +47 -0
- android_env/proto/snapshot_pb2.pyi +117 -0
- android_env/proto/snapshot_pb2_grpc.py +24 -0
- android_env/proto/snapshot_service.proto +289 -0
- android_env/proto/snapshot_service_pb2.py +54 -0
- android_env/proto/snapshot_service_pb2.pyi +86 -0
- android_env/proto/snapshot_service_pb2_grpc.py +487 -0
- android_env/proto/state.proto +63 -0
- android_env/proto/state_pb2.py +63 -0
- android_env/proto/state_pb2.pyi +85 -0
- android_env/proto/state_pb2_grpc.py +24 -0
- android_env/proto/task.proto +5 -1
- android_env/proto/task_pb2.py +42 -31
- 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 +34 -13
- android_env/wrappers/base_wrapper_test.py +22 -16
- android_env/wrappers/discrete_action_wrapper.py +18 -17
- android_env/wrappers/discrete_action_wrapper_test.py +4 -4
- android_env/wrappers/flat_interface_wrapper.py +5 -5
- android_env/wrappers/flat_interface_wrapper_test.py +7 -11
- android_env/wrappers/float_pixels_wrapper.py +9 -10
- android_env/wrappers/float_pixels_wrapper_test.py +3 -3
- android_env/wrappers/gym_wrapper.py +19 -13
- android_env/wrappers/gym_wrapper_test.py +3 -5
- android_env/wrappers/image_rescale_wrapper.py +18 -21
- android_env/wrappers/image_rescale_wrapper_test.py +25 -37
- android_env/wrappers/last_action_wrapper.py +16 -13
- android_env/wrappers/last_action_wrapper_test.py +44 -51
- android_env/wrappers/rate_limit_wrapper.py +6 -3
- android_env/wrappers/rate_limit_wrapper_test.py +22 -1
- android_env/wrappers/tap_action_wrapper.py +16 -17
- android_env/wrappers/tap_action_wrapper_test.py +51 -16
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -18
- android_env-1.2.3.dist-info/RECORD +141 -0
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
- android_env/proto/raw_observation.proto +0 -39
- android_env/proto/raw_observation_pb2.py +0 -27
- android_env/proto/raw_observation_pb2_grpc.py +0 -4
- android_env-1.2.1.dist-info/RECORD +0 -81
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
- {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
|
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
|
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
|
-
|
46
|
-
|
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
|
-
|
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.
|
84
|
-
self.
|
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
|
-
'
|
102
|
-
'
|
103
|
-
'
|
104
|
-
'
|
105
|
-
'
|
106
|
-
'
|
107
|
-
'
|
108
|
-
'
|
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.
|
81
|
+
self._launch_simulator()
|
119
82
|
|
120
|
-
def action_spec(self) ->
|
83
|
+
def action_spec(self) -> dict[str, dm_env.specs.Array]:
|
121
84
|
return specs.base_action_spec(
|
122
|
-
num_fingers=self.
|
123
|
-
enable_key_events=self.
|
85
|
+
num_fingers=self._config.num_fingers,
|
86
|
+
enable_key_events=self._config.enable_key_events,
|
87
|
+
)
|
124
88
|
|
125
|
-
def observation_spec(self) ->
|
89
|
+
def observation_spec(self) -> dict[str, dm_env.specs.Array]:
|
126
90
|
return specs.base_observation_spec(
|
127
|
-
height=self.
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
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.
|
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.
|
110
|
+
if sim_alive_time > self._config.periodic_restart_time_min:
|
191
111
|
logging.info('Maximum alive time reached. Restarting simulator.')
|
192
|
-
self._stats['
|
112
|
+
self._stats['relaunch_count_periodic'] += 1
|
193
113
|
return True
|
194
114
|
return False
|
195
115
|
|
196
|
-
def
|
197
|
-
"""
|
116
|
+
def _launch_simulator(self, max_retries: int = 3):
|
117
|
+
"""Launches the simulator.
|
198
118
|
|
199
|
-
|
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.'
|
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
|
-
|
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
|
-
|
225
|
-
|
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['
|
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['
|
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
|
-
|
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
|
-
#
|
312
|
-
if not self._simulator_healthy or self.
|
313
|
-
self.
|
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
|
-
|
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.
|
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:
|
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
|
-
|
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
|
240
|
+
except errors.ReadObservationError:
|
351
241
|
logging.exception('Unable to fetch observation. Restarting simulator.')
|
352
|
-
self._stats['
|
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) ->
|
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 = (
|
366
|
-
|
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.
|
372
|
-
'timedelta': np.
|
264
|
+
'orientation': self._device_settings.get_orientation(),
|
265
|
+
'timedelta': np.array(timestamp_delta, dtype=np.int64),
|
373
266
|
}
|
374
267
|
|
375
|
-
def
|
376
|
-
|
268
|
+
def __del__(self):
|
269
|
+
self.close()
|
377
270
|
|
378
|
-
|
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
|
-
|
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.
|
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}')
|