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
@@ -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.
|
@@ -15,8 +15,6 @@
|
|
15
15
|
|
16
16
|
"""Definitions of exceptions used by AndroidEnv."""
|
17
17
|
|
18
|
-
from typing import Optional
|
19
|
-
|
20
18
|
|
21
19
|
class AndroidEnvError(Exception):
|
22
20
|
"""Base class for all known errors generated by AndroidEnv."""
|
@@ -80,7 +78,7 @@ class CheckInstallError(StepCommandError):
|
|
80
78
|
ERROR_CODE = 9
|
81
79
|
|
82
80
|
|
83
|
-
def from_code(code: int, msg: str = '') ->
|
81
|
+
def from_code(code: int, msg: str = '') -> AndroidEnvError | None:
|
84
82
|
"""Returns an AndroidEnvError instance from the given arguments."""
|
85
83
|
|
86
84
|
code_to_error = {
|
@@ -98,4 +96,3 @@ def from_code(code: int, msg: str = '') -> Optional[AndroidEnvError]:
|
|
98
96
|
|
99
97
|
if code in code_to_error:
|
100
98
|
return code_to_error[code](msg)
|
101
|
-
|
@@ -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, Sequence
|
21
21
|
from absl import logging
|
22
22
|
|
23
23
|
|
@@ -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
|
@@ -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,11 +15,10 @@
|
|
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
|
22
|
-
import numpy.typing as np_typing
|
23
22
|
|
24
23
|
|
25
24
|
def touch_position_to_pixel_position(
|
@@ -39,27 +38,27 @@ def transpose_pixels(frame: np.ndarray) -> np.ndarray:
|
|
39
38
|
|
40
39
|
def orient_pixels(frame: np.ndarray, orientation: int) -> np.ndarray:
|
41
40
|
"""Rotates screen pixels according to the given orientation."""
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
+
)
|
53
55
|
|
54
56
|
|
55
|
-
def convert_int_to_float(data: np.ndarray,
|
56
|
-
data_spec: specs.Array,
|
57
|
-
float_type: np_typing.DTypeLike = np.float32):
|
57
|
+
def convert_int_to_float(data: np.ndarray, data_spec: specs.Array):
|
58
58
|
"""Converts an array of int values to floats between 0 and 1."""
|
59
|
+
|
59
60
|
if not np.issubdtype(data.dtype, np.integer):
|
60
61
|
raise TypeError(f'{data.dtype} is not an integer type')
|
61
|
-
if not np.issubdtype(float_type, np.floating):
|
62
|
-
raise TypeError(f'{float_type} is not a floating-point type')
|
63
62
|
if isinstance(data_spec, specs.BoundedArray):
|
64
63
|
value_min = data_spec.minimum
|
65
64
|
value_max = data_spec.maximum
|
@@ -68,4 +67,4 @@ def convert_int_to_float(data: np.ndarray,
|
|
68
67
|
iinfo = np.iinfo(data_spec.dtype)
|
69
68
|
value_min = iinfo.min
|
70
69
|
value_max = iinfo.max
|
71
|
-
return
|
70
|
+
return np.float32(1.0 * (data - value_min) / (value_max - value_min))
|
@@ -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.
|
@@ -13,11 +13,11 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
"""Tests for
|
16
|
+
"""Tests for pixel_fns."""
|
17
17
|
|
18
18
|
from absl.testing import absltest
|
19
19
|
from absl.testing import parameterized
|
20
|
-
from android_env.components import
|
20
|
+
from android_env.components import pixel_fns
|
21
21
|
from dm_env import specs
|
22
22
|
import numpy as np
|
23
23
|
|
@@ -32,38 +32,42 @@ class UtilsTest(parameterized.TestCase):
|
|
32
32
|
)
|
33
33
|
def test_touch_position_to_pixel_position(
|
34
34
|
self, touch_pos, width_height, pixel_pos):
|
35
|
-
self.assertEqual(
|
36
|
-
|
35
|
+
self.assertEqual(
|
36
|
+
pixel_fns.touch_position_to_pixel_position(
|
37
|
+
np.array(touch_pos), width_height
|
38
|
+
),
|
39
|
+
pixel_pos,
|
40
|
+
)
|
37
41
|
|
38
42
|
def test_transpose_pixels(self):
|
39
43
|
image = np.reshape(np.array(range(12)), (3, 2, 2))
|
40
44
|
expected = [[[0, 1], [4, 5], [8, 9]], [[2, 3], [6, 7], [10, 11]]]
|
41
|
-
self.assertEqual(
|
42
|
-
self.assertTrue((
|
45
|
+
self.assertEqual(pixel_fns.transpose_pixels(image).shape, (2, 3, 2))
|
46
|
+
self.assertTrue((pixel_fns.transpose_pixels(image) == expected).all())
|
43
47
|
|
44
48
|
def test_orient_pixels(self):
|
45
49
|
image = np.reshape(np.array(range(12)), (3, 2, 2))
|
46
50
|
|
47
51
|
expected_90 = [[[8, 9], [4, 5], [0, 1]], [[10, 11], [6, 7], [2, 3]]]
|
48
52
|
rot_90 = 1 # LANDSCAPE_90
|
49
|
-
rotated =
|
53
|
+
rotated = pixel_fns.orient_pixels(image, rot_90)
|
50
54
|
self.assertEqual(rotated.shape, (2, 3, 2))
|
51
55
|
self.assertTrue((rotated == expected_90).all())
|
52
56
|
|
53
57
|
expected_180 = [[[10, 11], [8, 9]], [[6, 7], [4, 5]], [[2, 3], [0, 1]]]
|
54
58
|
rot_180 = 2 # PORTRAIT_180
|
55
|
-
rotated =
|
59
|
+
rotated = pixel_fns.orient_pixels(image, rot_180)
|
56
60
|
self.assertEqual(rotated.shape, (3, 2, 2))
|
57
61
|
self.assertTrue((rotated == expected_180).all())
|
58
62
|
|
59
63
|
expected_270 = [[[2, 3], [6, 7], [10, 11]], [[0, 1], [4, 5], [8, 9]]]
|
60
64
|
rot_270 = 3 # LANDSCAPE_270
|
61
|
-
rotated =
|
65
|
+
rotated = pixel_fns.orient_pixels(image, rot_270)
|
62
66
|
self.assertEqual(rotated.shape, (2, 3, 2))
|
63
67
|
self.assertTrue((rotated == expected_270).all())
|
64
68
|
|
65
69
|
rot_0 = 0 # PORTRAIT_0
|
66
|
-
rotated =
|
70
|
+
rotated = pixel_fns.orient_pixels(image, rot_0)
|
67
71
|
self.assertEqual(rotated.shape, (3, 2, 2))
|
68
72
|
self.assertTrue((rotated == image).all())
|
69
73
|
|
@@ -75,15 +79,16 @@ class UtilsTest(parameterized.TestCase):
|
|
75
79
|
maximum=[5, 5, 20, 2],
|
76
80
|
name='bounded_array')
|
77
81
|
data = np.array([2, 2, 10, 0], dtype=np.int32)
|
78
|
-
float_data =
|
82
|
+
float_data = pixel_fns.convert_int_to_float(data, spec)
|
79
83
|
np.testing.assert_equal(
|
80
|
-
np.array([2. / 5
|
84
|
+
np.array([2.0 / 5.0, 1.0 / 4.0, 0.0, 0.5], dtype=np.float32), float_data
|
85
|
+
)
|
81
86
|
|
82
87
|
def test_convert_int_to_float_bounded_array_broadcast(self):
|
83
88
|
spec = specs.BoundedArray(
|
84
89
|
shape=(3,), dtype=np.int16, minimum=2, maximum=4, name='bounded_array')
|
85
90
|
data = np.array([2, 3, 4], dtype=np.int16)
|
86
|
-
float_data =
|
91
|
+
float_data = pixel_fns.convert_int_to_float(data, spec)
|
87
92
|
np.testing.assert_equal(
|
88
93
|
np.array([0.0, 0.5, 1.0], dtype=np.float32), float_data)
|
89
94
|
|
@@ -93,7 +98,7 @@ class UtilsTest(parameterized.TestCase):
|
|
93
98
|
dtype=np.int8, # int8 implies min=-128, max=127
|
94
99
|
name='bounded_array')
|
95
100
|
data = np.array([-128, 0, 127], dtype=np.int16)
|
96
|
-
float_data =
|
101
|
+
float_data = pixel_fns.convert_int_to_float(data, spec)
|
97
102
|
np.testing.assert_equal(
|
98
103
|
np.array([0.0, 128. / 255., 1.0], dtype=np.float32), float_data)
|
99
104
|
|
@@ -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,9 +15,10 @@
|
|
15
15
|
|
16
16
|
"""A component that parses and processes SetupSteps."""
|
17
17
|
|
18
|
+
from collections.abc import Sequence
|
18
19
|
import copy
|
19
20
|
import time
|
20
|
-
from typing import Any
|
21
|
+
from typing import Any
|
21
22
|
|
22
23
|
from absl import logging
|
23
24
|
from android_env.components import adb_call_parser as adb_call_parser_lib
|
@@ -110,45 +111,52 @@ class SetupStepInterpreter:
|
|
110
111
|
f'Step failed: [{step_cmd}]') from latest_error
|
111
112
|
|
112
113
|
def _execute_step_cmd(
|
113
|
-
self, step_cmd: task_pb2.SetupStep,
|
114
|
-
|
114
|
+
self, step_cmd: task_pb2.SetupStep, step_type: str | None
|
115
|
+
) -> adb_pb2.AdbResponse | None:
|
115
116
|
"""Executes a step command of given type."""
|
116
|
-
if not step_type:
|
117
|
-
return
|
118
117
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
118
|
+
match step_type:
|
119
|
+
case None:
|
120
|
+
return None
|
121
|
+
case 'sleep':
|
122
|
+
time.sleep(step_cmd.sleep.time_sec)
|
123
|
+
return None
|
124
|
+
case 'adb_request':
|
125
|
+
response = self._adb_call_parser.parse(step_cmd.adb_request)
|
126
|
+
if response.status != adb_pb2.AdbResponse.Status.OK:
|
127
|
+
raise errors.AdbControllerError(
|
128
|
+
f'Failed to execute AdbRequest [{step_cmd.adb_request}].\n'
|
129
|
+
f'Status: {response.status}\n'
|
130
|
+
f'Error: {response.error_message}'
|
131
|
+
)
|
132
|
+
return response
|
133
|
+
case _:
|
134
|
+
raise NotImplementedError(f'No step command of type [{step_type}].')
|
135
|
+
|
136
|
+
def _check_success(
|
137
|
+
self,
|
138
|
+
success_check: str | None,
|
139
|
+
success_condition: task_pb2.SuccessCondition,
|
140
|
+
) -> None:
|
135
141
|
"""Checks whether the given success condition was met."""
|
136
142
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
143
|
+
match success_check:
|
144
|
+
case None:
|
145
|
+
return None
|
146
|
+
case 'wait_for_app_screen':
|
147
|
+
wait_for_app_screen = success_condition.wait_for_app_screen
|
148
|
+
screen_checker = app_screen_checker.AppScreenChecker(
|
149
|
+
adb_call_parser=self._adb_call_parser,
|
150
|
+
expected_app_screen=wait_for_app_screen.app_screen,
|
151
|
+
)
|
152
|
+
wait_time = screen_checker.wait_for_app_screen(
|
153
|
+
timeout_sec=wait_for_app_screen.timeout_sec
|
154
|
+
)
|
155
|
+
self._stats['total_time_waiting_for_app_screen'] += wait_time
|
156
|
+
case 'check_install':
|
157
|
+
self._check_install(success_condition.check_install)
|
158
|
+
case _:
|
159
|
+
raise NotImplementedError(f'No success check called [{success_check}].')
|
152
160
|
|
153
161
|
def _check_install(self, check_install: task_pb2.CheckInstall) -> None:
|
154
162
|
"""Checks that the given package is installed."""
|