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
@@ -0,0 +1,228 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
# Copyright 2024 DeepMind Technologies Limited.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from unittest import mock
|
17
|
+
|
18
|
+
from absl.testing import absltest
|
19
|
+
from absl.testing import parameterized
|
20
|
+
from android_env.components import config_classes
|
21
|
+
from android_env.components import device_settings as device_settings_lib
|
22
|
+
from android_env.components.simulators import base_simulator
|
23
|
+
import numpy as np
|
24
|
+
|
25
|
+
|
26
|
+
class DeviceSettingsTest(parameterized.TestCase):
|
27
|
+
|
28
|
+
def test_screen_size_before_update(self):
|
29
|
+
"""The screen size should be 0x0 without calling `update()`."""
|
30
|
+
|
31
|
+
# Arrange.
|
32
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
33
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
34
|
+
|
35
|
+
# Act.
|
36
|
+
height = device_settings.screen_height()
|
37
|
+
width = device_settings.screen_width()
|
38
|
+
|
39
|
+
# Assert.
|
40
|
+
self.assertEqual(height, 0)
|
41
|
+
self.assertEqual(width, 0)
|
42
|
+
|
43
|
+
def test_screen_size_after_update(self):
|
44
|
+
"""The screen size should be set after calling `update()`."""
|
45
|
+
|
46
|
+
# Arrange.
|
47
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
48
|
+
simulator.get_screenshot.return_value = np.random.randint(
|
49
|
+
low=0, high=255, size=(123, 456, 3), dtype=np.uint8
|
50
|
+
)
|
51
|
+
adb_controller = simulator.create_adb_controller.return_value
|
52
|
+
adb_controller.execute_command.return_value = b''
|
53
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
54
|
+
|
55
|
+
# Act.
|
56
|
+
device_settings.update(config_classes.DeviceSettingsConfig())
|
57
|
+
height = device_settings.screen_height()
|
58
|
+
width = device_settings.screen_width()
|
59
|
+
|
60
|
+
# Assert.
|
61
|
+
self.assertEqual(height, 123)
|
62
|
+
self.assertEqual(width, 456)
|
63
|
+
|
64
|
+
@parameterized.named_parameters(
|
65
|
+
(
|
66
|
+
'show_touches',
|
67
|
+
config_classes.DeviceSettingsConfig(show_touches=True),
|
68
|
+
mock.call(
|
69
|
+
['shell', 'settings', 'put', 'system', 'show_touches', '1'],
|
70
|
+
timeout=None,
|
71
|
+
),
|
72
|
+
),
|
73
|
+
(
|
74
|
+
'show_touches_false',
|
75
|
+
config_classes.DeviceSettingsConfig(show_touches=False),
|
76
|
+
mock.call(
|
77
|
+
['shell', 'settings', 'put', 'system', 'show_touches', '0'],
|
78
|
+
timeout=None,
|
79
|
+
),
|
80
|
+
),
|
81
|
+
(
|
82
|
+
'show_pointer_location',
|
83
|
+
config_classes.DeviceSettingsConfig(show_pointer_location=True),
|
84
|
+
mock.call(
|
85
|
+
['shell', 'settings', 'put', 'system', 'pointer_location', '1'],
|
86
|
+
timeout=None,
|
87
|
+
),
|
88
|
+
),
|
89
|
+
(
|
90
|
+
'show_pointer_location_false',
|
91
|
+
config_classes.DeviceSettingsConfig(show_pointer_location=False),
|
92
|
+
mock.call(
|
93
|
+
['shell', 'settings', 'put', 'system', 'pointer_location', '0'],
|
94
|
+
timeout=None,
|
95
|
+
),
|
96
|
+
),
|
97
|
+
(
|
98
|
+
'show_navigation_and_status',
|
99
|
+
config_classes.DeviceSettingsConfig(
|
100
|
+
show_navigation_bar=True, show_status_bar=True
|
101
|
+
),
|
102
|
+
mock.call(
|
103
|
+
['shell', 'settings', 'put', 'global', 'policy_control', 'null*'],
|
104
|
+
timeout=None,
|
105
|
+
),
|
106
|
+
),
|
107
|
+
(
|
108
|
+
'show_navigation_and_no_status',
|
109
|
+
config_classes.DeviceSettingsConfig(
|
110
|
+
show_navigation_bar=True, show_status_bar=False
|
111
|
+
),
|
112
|
+
mock.call(
|
113
|
+
[
|
114
|
+
'shell',
|
115
|
+
'settings',
|
116
|
+
'put',
|
117
|
+
'global',
|
118
|
+
'policy_control',
|
119
|
+
'immersive.status=*',
|
120
|
+
],
|
121
|
+
timeout=None,
|
122
|
+
),
|
123
|
+
),
|
124
|
+
(
|
125
|
+
'show_no_navigation_and_status',
|
126
|
+
config_classes.DeviceSettingsConfig(
|
127
|
+
show_navigation_bar=False, show_status_bar=True
|
128
|
+
),
|
129
|
+
mock.call(
|
130
|
+
[
|
131
|
+
'shell',
|
132
|
+
'settings',
|
133
|
+
'put',
|
134
|
+
'global',
|
135
|
+
'policy_control',
|
136
|
+
'immersive.navigation=*',
|
137
|
+
],
|
138
|
+
timeout=None,
|
139
|
+
),
|
140
|
+
),
|
141
|
+
(
|
142
|
+
'show_no_navigation_and_no_status',
|
143
|
+
config_classes.DeviceSettingsConfig(
|
144
|
+
show_navigation_bar=False, show_status_bar=False
|
145
|
+
),
|
146
|
+
mock.call(
|
147
|
+
[
|
148
|
+
'shell',
|
149
|
+
'settings',
|
150
|
+
'put',
|
151
|
+
'global',
|
152
|
+
'policy_control',
|
153
|
+
'immersive.full=*',
|
154
|
+
],
|
155
|
+
timeout=None,
|
156
|
+
),
|
157
|
+
),
|
158
|
+
)
|
159
|
+
def test_update(
|
160
|
+
self, settings: config_classes.DeviceSettingsConfig, expected_call
|
161
|
+
):
|
162
|
+
"""We expect the right call for each setting."""
|
163
|
+
|
164
|
+
# Arrange.
|
165
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
166
|
+
adb_controller = simulator.create_adb_controller.return_value
|
167
|
+
adb_controller.execute_command.return_value = b''
|
168
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
169
|
+
|
170
|
+
# Act.
|
171
|
+
device_settings.update(settings)
|
172
|
+
|
173
|
+
# Assert.
|
174
|
+
adb_controller.execute_command.assert_has_calls(
|
175
|
+
[expected_call], any_order=True
|
176
|
+
)
|
177
|
+
|
178
|
+
def test_get_orientation_bad_response(self):
|
179
|
+
"""The orientation should be unset if the underlying response is bad."""
|
180
|
+
|
181
|
+
# Arrange.
|
182
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
183
|
+
adb_controller = simulator.create_adb_controller.return_value
|
184
|
+
adb_controller.execute_command.return_value = b''
|
185
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
186
|
+
|
187
|
+
# Act.
|
188
|
+
orientation = device_settings.get_orientation()
|
189
|
+
|
190
|
+
# Assert.
|
191
|
+
np.testing.assert_array_equal(orientation, np.zeros(4))
|
192
|
+
|
193
|
+
def test_get_orientation_bad_orientation(self):
|
194
|
+
"""The orientation should be unset if the underlying orientation is bad."""
|
195
|
+
|
196
|
+
# Arrange.
|
197
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
198
|
+
adb_controller = simulator.create_adb_controller.return_value
|
199
|
+
adb_controller.execute_command.return_value = b' InputDeviceOrientation: 9'
|
200
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
201
|
+
|
202
|
+
# Act.
|
203
|
+
orientation = device_settings.get_orientation()
|
204
|
+
|
205
|
+
# Assert.
|
206
|
+
np.testing.assert_array_equal(orientation, np.zeros(4))
|
207
|
+
|
208
|
+
def test_get_orientation_success(self):
|
209
|
+
"""Checks that the orientation comes back as expected."""
|
210
|
+
|
211
|
+
# Arrange.
|
212
|
+
simulator = mock.create_autospec(base_simulator.BaseSimulator)
|
213
|
+
adb_controller = simulator.create_adb_controller.return_value
|
214
|
+
adb_controller.execute_command.return_value = b' InputDeviceOrientation: 3'
|
215
|
+
device_settings = device_settings_lib.DeviceSettings(simulator)
|
216
|
+
|
217
|
+
# Act.
|
218
|
+
orientation = device_settings.get_orientation()
|
219
|
+
# The output should be idempotent if the underlying system did not change.
|
220
|
+
orientation_again = device_settings.get_orientation()
|
221
|
+
|
222
|
+
# Assert.
|
223
|
+
np.testing.assert_array_equal(orientation, np.array([0, 0, 0, 1]))
|
224
|
+
np.testing.assert_array_equal(orientation, orientation_again)
|
225
|
+
|
226
|
+
|
227
|
+
if __name__ == '__main__':
|
228
|
+
absltest.main()
|
@@ -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,7 +16,6 @@
|
|
16
16
|
"""A ThreadFunction that runs and parses adb dumpsys."""
|
17
17
|
|
18
18
|
import concurrent.futures
|
19
|
-
from typing import Optional
|
20
19
|
|
21
20
|
from absl import logging
|
22
21
|
from android_env.components import app_screen_checker as app_screen_checker_lib
|
@@ -56,9 +55,9 @@ class DumpsysThread:
|
|
56
55
|
self._check_frequency = check_frequency
|
57
56
|
self._max_failed_activity_extraction = max_failed_current_activity
|
58
57
|
self._num_failed_activity_extraction = 0
|
59
|
-
self._latest_check:
|
58
|
+
self._latest_check: concurrent.futures.Future | None = None
|
60
59
|
|
61
|
-
def check_user_exited(self, timeout:
|
60
|
+
def check_user_exited(self, timeout: float | None = None) -> bool:
|
62
61
|
"""Returns True if the user is not in the expected screen.
|
63
62
|
|
64
63
|
Args:
|
android_env/components/errors.py
CHANGED
@@ -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,41 +16,83 @@
|
|
16
16
|
"""Definitions of exceptions used by AndroidEnv."""
|
17
17
|
|
18
18
|
|
19
|
-
class
|
19
|
+
class AndroidEnvError(Exception):
|
20
|
+
"""Base class for all known errors generated by AndroidEnv."""
|
21
|
+
|
22
|
+
# An integer that identifies this class of error.
|
23
|
+
# Subclasses should use a different value.
|
24
|
+
ERROR_CODE: int = 0
|
25
|
+
|
26
|
+
|
27
|
+
class ReadObservationError(AndroidEnvError):
|
20
28
|
"""When the environment is unable to obtain an observation from a simulator."""
|
21
29
|
|
30
|
+
ERROR_CODE = 1
|
31
|
+
|
22
32
|
|
23
|
-
class CoordinatorError(
|
33
|
+
class CoordinatorError(AndroidEnvError):
|
24
34
|
"""Error raised by the Coordinator."""
|
25
35
|
|
36
|
+
ERROR_CODE = 2
|
37
|
+
|
26
38
|
|
27
39
|
class TooManyRestartsError(CoordinatorError):
|
28
40
|
"""The number of restarts has exceeded _MAX_RESTART_TRIES."""
|
29
41
|
|
42
|
+
ERROR_CODE = 3
|
30
43
|
|
31
|
-
class AdbControllerError(Exception):
|
32
|
-
"""Errors that can be raised by ADBController."""
|
33
44
|
|
45
|
+
class AdbControllerError(AndroidEnvError):
|
46
|
+
"""Errors that can be raised by ADBController."""
|
34
47
|
|
35
|
-
|
36
|
-
"""Raised when a device takes too long to respond."""
|
48
|
+
ERROR_CODE = 4
|
37
49
|
|
38
50
|
|
39
|
-
class SimulatorError(
|
51
|
+
class SimulatorError(AndroidEnvError):
|
40
52
|
"""Errors that can be raised by a simulator."""
|
41
53
|
|
54
|
+
ERROR_CODE = 5
|
55
|
+
|
42
56
|
|
43
|
-
class SendActionError(
|
57
|
+
class SendActionError(AndroidEnvError):
|
44
58
|
"""Raised when action couldn't be sent successfully."""
|
45
59
|
|
60
|
+
ERROR_CODE = 6
|
46
61
|
|
47
|
-
|
62
|
+
|
63
|
+
class StepCommandError(AndroidEnvError):
|
48
64
|
"""Raised when setup step interpreter cannot process a command."""
|
49
65
|
|
66
|
+
ERROR_CODE = 7
|
67
|
+
|
50
68
|
|
51
69
|
class WaitForAppScreenError(StepCommandError):
|
52
70
|
"""Raised when the wait_for_app_screen success check is not met."""
|
53
71
|
|
72
|
+
ERROR_CODE = 8
|
73
|
+
|
54
74
|
|
55
75
|
class CheckInstallError(StepCommandError):
|
56
76
|
"""Raised when the check_install success check is not met."""
|
77
|
+
|
78
|
+
ERROR_CODE = 9
|
79
|
+
|
80
|
+
|
81
|
+
def from_code(code: int, msg: str = '') -> AndroidEnvError | None:
|
82
|
+
"""Returns an AndroidEnvError instance from the given arguments."""
|
83
|
+
|
84
|
+
code_to_error = {
|
85
|
+
0: AndroidEnvError,
|
86
|
+
1: ReadObservationError,
|
87
|
+
2: CoordinatorError,
|
88
|
+
3: TooManyRestartsError,
|
89
|
+
4: AdbControllerError,
|
90
|
+
5: SimulatorError,
|
91
|
+
6: SendActionError,
|
92
|
+
7: StepCommandError,
|
93
|
+
8: WaitForAppScreenError,
|
94
|
+
9: CheckInstallError,
|
95
|
+
}
|
96
|
+
|
97
|
+
if code in code_to_error:
|
98
|
+
return code_to_error[code](msg)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
# Copyright 2024 DeepMind Technologies Limited.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
"""Tests for errors.py."""
|
17
|
+
|
18
|
+
from absl.testing import absltest
|
19
|
+
from absl.testing import parameterized
|
20
|
+
from android_env.components import errors
|
21
|
+
|
22
|
+
|
23
|
+
class ErrorsTest(parameterized.TestCase):
|
24
|
+
|
25
|
+
@parameterized.parameters(
|
26
|
+
(errors.ReadObservationError, 1),
|
27
|
+
(errors.CoordinatorError, 2),
|
28
|
+
(errors.TooManyRestartsError, 3),
|
29
|
+
(errors.AdbControllerError, 4),
|
30
|
+
(errors.SimulatorError, 5),
|
31
|
+
(errors.SendActionError, 6),
|
32
|
+
(errors.StepCommandError, 7),
|
33
|
+
(errors.WaitForAppScreenError, 8),
|
34
|
+
(errors.CheckInstallError, 9),
|
35
|
+
)
|
36
|
+
def test_error_codes(self, error, expected_error_code):
|
37
|
+
with self.assertRaises(error) as context:
|
38
|
+
raise error()
|
39
|
+
self.assertEqual(context.exception.ERROR_CODE, expected_error_code)
|
40
|
+
|
41
|
+
def test_error_codes_unique(self):
|
42
|
+
error_codes = set()
|
43
|
+
errors_list = (
|
44
|
+
errors.ReadObservationError,
|
45
|
+
errors.CoordinatorError,
|
46
|
+
errors.TooManyRestartsError,
|
47
|
+
errors.AdbControllerError,
|
48
|
+
errors.SimulatorError,
|
49
|
+
errors.SendActionError,
|
50
|
+
errors.StepCommandError,
|
51
|
+
errors.WaitForAppScreenError,
|
52
|
+
errors.CheckInstallError,
|
53
|
+
)
|
54
|
+
for error in errors_list:
|
55
|
+
self.assertNotIn(error.ERROR_CODE, error_codes)
|
56
|
+
error_codes.add(error.ERROR_CODE)
|
57
|
+
|
58
|
+
@parameterized.parameters([
|
59
|
+
errors.ReadObservationError(),
|
60
|
+
errors.CoordinatorError(),
|
61
|
+
errors.TooManyRestartsError(),
|
62
|
+
errors.AdbControllerError(),
|
63
|
+
errors.SimulatorError(),
|
64
|
+
errors.SendActionError(),
|
65
|
+
errors.StepCommandError(),
|
66
|
+
errors.WaitForAppScreenError(),
|
67
|
+
errors.CheckInstallError(),
|
68
|
+
])
|
69
|
+
def test_all_errors_are_androidenv_errors(self, error):
|
70
|
+
self.assertIsInstance(error, errors.AndroidEnvError)
|
71
|
+
|
72
|
+
@parameterized.named_parameters([
|
73
|
+
('less_than_zero', -1),
|
74
|
+
# The largest `ERROR_CODE` is currently `CheckInstallError == 10`.
|
75
|
+
('greater_than_all_errors', 10 + 1),
|
76
|
+
('less_than_zero_float', -3.14159265),
|
77
|
+
('greater_than_all_errors_float', 123.456),
|
78
|
+
])
|
79
|
+
def test_from_code_unsupported_code(self, code: int):
|
80
|
+
"""Unsupported errors should raise `RuntimeError`."""
|
81
|
+
|
82
|
+
self.assertIsNone(errors.from_code(code))
|
83
|
+
|
84
|
+
@parameterized.parameters([
|
85
|
+
(-1, None, 'No such error code.'),
|
86
|
+
(0, errors.AndroidEnvError, 'hello'),
|
87
|
+
(0, errors.AndroidEnvError, ''),
|
88
|
+
(1, errors.ReadObservationError, 'Could not read obs.'),
|
89
|
+
(2, errors.CoordinatorError, 'Some error'),
|
90
|
+
(3, errors.TooManyRestartsError, 'Too many already...'),
|
91
|
+
(4, errors.AdbControllerError, 'Some adb error...'),
|
92
|
+
(5, errors.SimulatorError, 'Simulator is not coping.'),
|
93
|
+
(6, errors.SendActionError, 'Could not send action.'),
|
94
|
+
(7, errors.StepCommandError, 'Some issue setting up the task.'),
|
95
|
+
(8, errors.WaitForAppScreenError, 'Waited for too long!'),
|
96
|
+
(9, errors.CheckInstallError, 'App did not install correctly.'),
|
97
|
+
])
|
98
|
+
def test_from_code(self, code: int, expected_class: errors.AndroidEnvError,
|
99
|
+
msg: str):
|
100
|
+
"""`from_code` should produce consistent outputs for known errors."""
|
101
|
+
|
102
|
+
error = errors.from_code(code, msg)
|
103
|
+
if error is not None:
|
104
|
+
self.assertIsInstance(error, expected_class)
|
105
|
+
self.assertEqual(error.ERROR_CODE, code)
|
106
|
+
self.assertEqual(str(error), msg)
|
107
|
+
|
108
|
+
|
109
|
+
if __name__ == '__main__':
|
110
|
+
absltest.main()
|
@@ -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,8 +16,8 @@
|
|
16
16
|
"""Abstract class for handling a stream of logs from a simulator."""
|
17
17
|
|
18
18
|
import abc
|
19
|
+
from collections.abc import Generator, Sequence
|
19
20
|
import threading
|
20
|
-
from typing import Generator, List
|
21
21
|
from absl import logging
|
22
22
|
|
23
23
|
|
@@ -44,8 +44,10 @@ class LogStream(metaclass=abc.ABCMeta):
|
|
44
44
|
|
45
45
|
@abc.abstractmethod
|
46
46
|
def stop_stream(self) -> None:
|
47
|
-
"""Terminates log stream
|
48
|
-
|
47
|
+
"""Terminates the log stream.
|
48
|
+
|
49
|
+
NOTE: This should only be called _after_ `get_stream_output()`.
|
50
|
+
"""
|
49
51
|
|
50
52
|
def pause_stream(self) -> None:
|
51
53
|
"""No lines are yielded while the event is not set."""
|
@@ -57,6 +59,6 @@ class LogStream(metaclass=abc.ABCMeta):
|
|
57
59
|
logging.info('Resuming LogStream.')
|
58
60
|
self._should_stream.set()
|
59
61
|
|
60
|
-
def set_log_filters(self, log_filters:
|
62
|
+
def set_log_filters(self, log_filters: Sequence[str]):
|
61
63
|
"""Sets the filters for the log stream."""
|
62
64
|
self._filters = list(log_filters) + ['*:S']
|
@@ -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.
|
@@ -15,20 +15,21 @@
|
|
15
15
|
|
16
16
|
"""A class that launches a thread to read Android log outputs."""
|
17
17
|
|
18
|
+
from collections.abc import Callable
|
19
|
+
import dataclasses
|
18
20
|
import re
|
19
21
|
import threading
|
20
|
-
# `typing.Pattern` has been deprecated in Python 3.9 in favor of `re.Pattern`,
|
21
|
-
# but it is not available even in slightly older Python versions.
|
22
|
-
# Please see https://www.python.org/dev/peps/pep-0585/
|
23
|
-
from typing import Callable, Match, NamedTuple, Pattern
|
24
22
|
|
25
23
|
from absl import logging
|
26
24
|
from android_env.components import log_stream as log_stream_lib
|
27
25
|
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
@dataclasses.dataclass
|
28
|
+
class EventListener:
|
29
|
+
"""A function that's called when an event is triggered."""
|
30
|
+
|
31
|
+
regexp: re.Pattern[str]
|
32
|
+
handler_fn: Callable[[re.Pattern[str], re.Match[str]], None]
|
32
33
|
|
33
34
|
|
34
35
|
class LogcatThread:
|
@@ -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.
|
@@ -17,7 +17,6 @@
|
|
17
17
|
|
18
18
|
import re
|
19
19
|
import threading
|
20
|
-
from typing import Match, Pattern
|
21
20
|
|
22
21
|
from absl.testing import absltest
|
23
22
|
from android_env.components import log_stream
|
@@ -25,7 +24,7 @@ from android_env.components import logcat_thread
|
|
25
24
|
from android_env.proto import task_pb2
|
26
25
|
|
27
26
|
|
28
|
-
class FakeStream
|
27
|
+
class FakeStream:
|
29
28
|
"""This class simulates the logs coming from ADB."""
|
30
29
|
|
31
30
|
def __init__(self):
|
@@ -108,7 +107,7 @@ class LogcatThreadTest(absltest.TestCase):
|
|
108
107
|
# Set up a listener that modifies an arbitrary state.
|
109
108
|
some_state = threading.Event()
|
110
109
|
|
111
|
-
def my_handler(event: Pattern[str], match: Match[str]):
|
110
|
+
def my_handler(event: re.Pattern[str], match: re.Match[str]):
|
112
111
|
del event, match
|
113
112
|
nonlocal some_state
|
114
113
|
some_state.set()
|
@@ -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.
|
@@ -15,7 +15,7 @@
|
|
15
15
|
|
16
16
|
"""Utils for AndroidEnv."""
|
17
17
|
|
18
|
-
from
|
18
|
+
from collections.abc import Sequence
|
19
19
|
|
20
20
|
from dm_env import specs
|
21
21
|
import numpy as np
|
@@ -24,7 +24,7 @@ import numpy as np
|
|
24
24
|
def touch_position_to_pixel_position(
|
25
25
|
touch_position: np.ndarray,
|
26
26
|
width_height: Sequence[int],
|
27
|
-
) ->
|
27
|
+
) -> tuple[int, int]:
|
28
28
|
"""Maps touch position in [0,1] to the corresponding pixel on the screen."""
|
29
29
|
touch_pixels = (touch_position * width_height).astype(np.int32)
|
30
30
|
cap_idx = lambda v, idx_len: min(v, idx_len - 1)
|
@@ -38,27 +38,27 @@ def transpose_pixels(frame: np.ndarray) -> np.ndarray:
|
|
38
38
|
|
39
39
|
def orient_pixels(frame: np.ndarray, orientation: int) -> np.ndarray:
|
40
40
|
"""Rotates screen pixels according to the given orientation."""
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
|
42
|
+
match orientation:
|
43
|
+
case 0: # PORTRAIT_90
|
44
|
+
return frame
|
45
|
+
case 1: # LANDSCAPE_90
|
46
|
+
return np.rot90(frame, k=3, axes=(0, 1))
|
47
|
+
case 2: # PORTRAIT_180
|
48
|
+
return np.rot90(frame, k=2, axes=(0, 1))
|
49
|
+
case 3: # LANDSCAPE_270
|
50
|
+
return np.rot90(frame, k=1, axes=(0, 1))
|
51
|
+
case _:
|
52
|
+
raise ValueError(
|
53
|
+
'Orientation must be an integer in [0, 3] but is %r' % orientation
|
54
|
+
)
|
52
55
|
|
53
56
|
|
54
|
-
def convert_int_to_float(data: np.ndarray,
|
55
|
-
data_spec: specs.Array,
|
56
|
-
float_type: np.dtype = np.float32):
|
57
|
+
def convert_int_to_float(data: np.ndarray, data_spec: specs.Array):
|
57
58
|
"""Converts an array of int values to floats between 0 and 1."""
|
59
|
+
|
58
60
|
if not np.issubdtype(data.dtype, np.integer):
|
59
61
|
raise TypeError(f'{data.dtype} is not an integer type')
|
60
|
-
if not np.issubdtype(float_type, np.floating):
|
61
|
-
raise TypeError(f'{float_type} is not a floating-point type')
|
62
62
|
if isinstance(data_spec, specs.BoundedArray):
|
63
63
|
value_min = data_spec.minimum
|
64
64
|
value_max = data_spec.maximum
|
@@ -67,4 +67,4 @@ def convert_int_to_float(data: np.ndarray,
|
|
67
67
|
iinfo = np.iinfo(data_spec.dtype)
|
68
68
|
value_min = iinfo.min
|
69
69
|
value_max = iinfo.max
|
70
|
-
return
|
70
|
+
return np.float32(1.0 * (data - value_min) / (value_max - value_min))
|