android-env 1.2.2__py3-none-any.whl → 1.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- android_env/__init__.py +1 -1
- android_env/components/__init__.py +1 -1
- android_env/components/a11y/__init__.py +15 -0
- android_env/components/a11y/a11y_events.py +118 -0
- android_env/components/a11y/a11y_events_test.py +173 -0
- android_env/components/a11y/a11y_forests.py +128 -0
- android_env/components/a11y/a11y_forests_test.py +237 -0
- android_env/components/a11y/a11y_servicer.py +199 -0
- android_env/components/a11y/a11y_servicer_test.py +224 -0
- android_env/components/action_fns.py +132 -0
- android_env/components/action_fns_test.py +227 -0
- android_env/components/action_type.py +26 -3
- android_env/components/adb_call_parser.py +233 -185
- android_env/components/adb_call_parser_test.py +165 -163
- android_env/components/adb_controller.py +19 -28
- android_env/components/adb_controller_test.py +100 -9
- android_env/components/adb_log_stream.py +3 -3
- android_env/components/adb_log_stream_test.py +1 -1
- android_env/components/app_screen_checker.py +15 -13
- android_env/components/app_screen_checker_test.py +1 -1
- android_env/components/config_classes.py +203 -0
- android_env/components/coordinator.py +53 -338
- android_env/components/coordinator_test.py +26 -283
- android_env/components/device_settings.py +174 -0
- android_env/components/device_settings_test.py +228 -0
- android_env/components/dumpsys_thread.py +3 -4
- android_env/components/dumpsys_thread_test.py +1 -1
- android_env/components/errors.py +2 -5
- android_env/components/errors_test.py +1 -1
- android_env/components/log_stream.py +2 -2
- android_env/components/log_stream_test.py +1 -1
- android_env/components/logcat_thread.py +9 -8
- android_env/components/logcat_thread_test.py +2 -3
- android_env/components/{utils.py → pixel_fns.py} +19 -20
- android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
- android_env/components/setup_step_interpreter.py +45 -37
- android_env/components/setup_step_interpreter_test.py +1 -1
- android_env/components/simulators/__init__.py +1 -1
- android_env/components/simulators/base_simulator.py +79 -23
- android_env/components/simulators/base_simulator_test.py +131 -9
- android_env/components/simulators/emulator/__init__.py +1 -1
- android_env/components/simulators/emulator/emulator_launcher.py +62 -81
- android_env/components/simulators/emulator/emulator_launcher_test.py +120 -43
- android_env/components/simulators/emulator/emulator_simulator.py +111 -98
- android_env/components/simulators/emulator/emulator_simulator_test.py +174 -138
- android_env/components/simulators/fake/__init__.py +1 -1
- android_env/components/simulators/fake/fake_simulator.py +9 -17
- android_env/components/simulators/fake/fake_simulator_test.py +23 -8
- android_env/components/specs.py +1 -1
- android_env/components/specs_test.py +1 -1
- android_env/components/task_manager.py +26 -31
- android_env/components/task_manager_test.py +1 -18
- android_env/env_interface.py +1 -17
- android_env/environment.py +27 -17
- android_env/environment_test.py +51 -25
- android_env/loader.py +57 -43
- android_env/loader_test.py +115 -35
- android_env/proto/__init__.py +1 -1
- android_env/proto/a11y/__init__.py +15 -0
- android_env/proto/a11y/a11y.proto +75 -0
- android_env/proto/a11y/a11y_pb2.py +54 -0
- android_env/proto/a11y/a11y_pb2.pyi +49 -0
- android_env/proto/a11y/a11y_pb2_grpc.py +202 -0
- android_env/proto/a11y/android_accessibility_action.proto +32 -0
- android_env/proto/a11y/android_accessibility_action_pb2.py +37 -0
- android_env/proto/a11y/android_accessibility_action_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_action_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_forest.proto +29 -0
- android_env/proto/a11y/android_accessibility_forest_pb2.py +38 -0
- android_env/proto/a11y/android_accessibility_forest_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_forest_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_node_info.proto +122 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto +49 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.py +39 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.pyi +28 -0
- android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2.py +42 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2.pyi +75 -0
- android_env/proto/a11y/android_accessibility_node_info_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_tree.proto +29 -0
- android_env/proto/a11y/android_accessibility_tree_pb2.py +38 -0
- android_env/proto/a11y/android_accessibility_tree_pb2.pyi +13 -0
- android_env/proto/a11y/android_accessibility_tree_pb2_grpc.py +24 -0
- android_env/proto/a11y/android_accessibility_window_info.proto +84 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2.py +41 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2.pyi +48 -0
- android_env/proto/a11y/android_accessibility_window_info_pb2_grpc.py +24 -0
- android_env/proto/a11y/rect.proto +30 -0
- android_env/proto/a11y/rect_pb2.py +37 -0
- android_env/proto/a11y/rect_pb2.pyi +17 -0
- android_env/proto/a11y/rect_pb2_grpc.py +24 -0
- android_env/proto/adb.proto +13 -1
- android_env/proto/adb_pb2.py +120 -107
- android_env/proto/adb_pb2.pyi +396 -0
- android_env/proto/adb_pb2_grpc.py +20 -0
- android_env/proto/emulator_controller.proto +1 -1
- android_env/proto/emulator_controller_pb2.py +142 -131
- android_env/proto/emulator_controller_pb2.pyi +672 -0
- android_env/proto/emulator_controller_pb2_grpc.py +497 -136
- android_env/proto/snapshot.proto +1 -1
- android_env/proto/snapshot_pb2.py +30 -19
- android_env/proto/snapshot_pb2.pyi +117 -0
- android_env/proto/snapshot_pb2_grpc.py +20 -0
- android_env/proto/snapshot_service.proto +1 -1
- android_env/proto/snapshot_service_pb2.py +36 -25
- android_env/proto/snapshot_service_pb2.pyi +86 -0
- android_env/proto/snapshot_service_pb2_grpc.py +119 -28
- android_env/proto/state.proto +1 -1
- android_env/proto/state_pb2.py +46 -35
- android_env/proto/state_pb2.pyi +85 -0
- android_env/proto/state_pb2_grpc.py +20 -0
- android_env/proto/task.proto +4 -1
- android_env/proto/task_pb2.py +41 -30
- android_env/proto/task_pb2.pyi +160 -0
- android_env/proto/task_pb2_grpc.py +20 -0
- android_env/wrappers/__init__.py +1 -1
- android_env/wrappers/a11y_grpc_wrapper.py +500 -0
- android_env/wrappers/a11y_grpc_wrapper_test.py +849 -0
- android_env/wrappers/base_wrapper.py +1 -5
- android_env/wrappers/base_wrapper_test.py +1 -7
- android_env/wrappers/discrete_action_wrapper.py +15 -14
- android_env/wrappers/discrete_action_wrapper_test.py +1 -1
- android_env/wrappers/flat_interface_wrapper.py +5 -5
- android_env/wrappers/flat_interface_wrapper_test.py +1 -1
- android_env/wrappers/float_pixels_wrapper.py +5 -4
- android_env/wrappers/float_pixels_wrapper_test.py +1 -1
- android_env/wrappers/gym_wrapper.py +1 -1
- android_env/wrappers/gym_wrapper_test.py +1 -1
- android_env/wrappers/image_rescale_wrapper.py +13 -10
- android_env/wrappers/image_rescale_wrapper_test.py +1 -1
- android_env/wrappers/last_action_wrapper.py +5 -4
- android_env/wrappers/last_action_wrapper_test.py +1 -1
- android_env/wrappers/rate_limit_wrapper.py +1 -1
- android_env/wrappers/rate_limit_wrapper_test.py +1 -1
- android_env/wrappers/tap_action_wrapper.py +12 -12
- android_env/wrappers/tap_action_wrapper_test.py +49 -14
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -16
- android_env-1.2.3.dist-info/RECORD +141 -0
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
- android_env-1.2.2.dist-info/RECORD +0 -88
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
- {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
# coding=utf-8
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2024 DeepMind Technologies Limited.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,34 +18,20 @@
|
|
18
18
|
import os
|
19
19
|
import subprocess
|
20
20
|
import time
|
21
|
-
from typing import Optional
|
22
21
|
|
23
22
|
from absl import logging
|
23
|
+
from android_env.components import config_classes
|
24
24
|
from android_env.components import errors
|
25
25
|
|
26
26
|
|
27
27
|
class AdbController:
|
28
28
|
"""Manages communication with adb."""
|
29
29
|
|
30
|
-
def __init__(self,
|
31
|
-
|
32
|
-
adb_path: str = 'adb',
|
33
|
-
adb_server_port: int = 5037,
|
34
|
-
default_timeout: float = 120.0):
|
35
|
-
"""Instantiates an AdbController object.
|
30
|
+
def __init__(self, config: config_classes.AdbControllerConfig):
|
31
|
+
"""Instantiates an AdbController object."""
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
adb_path: Path to the adb binary.
|
40
|
-
adb_server_port: Port for adb server.
|
41
|
-
default_timeout: Default timeout in seconds.
|
42
|
-
"""
|
43
|
-
|
44
|
-
self._device_name = device_name
|
45
|
-
self._adb_path = os.path.expandvars(adb_path)
|
46
|
-
self._adb_server_port = str(adb_server_port)
|
47
|
-
self._default_timeout = default_timeout
|
48
|
-
logging.info('adb_path: %r', self._adb_path)
|
33
|
+
self._config = config
|
34
|
+
logging.info('config: %r', self._config)
|
49
35
|
|
50
36
|
# Unset problematic environment variables. ADB commands will fail if these
|
51
37
|
# are set. They are normally exported by AndroidStudio.
|
@@ -63,12 +49,16 @@ class AdbController:
|
|
63
49
|
|
64
50
|
def command_prefix(self, include_device_name: bool = True) -> list[str]:
|
65
51
|
"""The command for instantiating an adb client to this server."""
|
66
|
-
command_prefix = [
|
52
|
+
command_prefix = [
|
53
|
+
self._config.adb_path,
|
54
|
+
'-P',
|
55
|
+
str(self._config.adb_server_port),
|
56
|
+
]
|
67
57
|
if include_device_name:
|
68
|
-
command_prefix.extend(['-s', self.
|
58
|
+
command_prefix.extend(['-s', self._config.device_name])
|
69
59
|
return command_prefix
|
70
60
|
|
71
|
-
def init_server(self, timeout:
|
61
|
+
def init_server(self, timeout: float | None = None):
|
72
62
|
"""Initialize the ADB server deamon on the given port.
|
73
63
|
|
74
64
|
This function should be called immediately after initializing the first
|
@@ -82,7 +72,7 @@ class AdbController:
|
|
82
72
|
self.execute_command(['devices'], timeout, device_specific=False)
|
83
73
|
time.sleep(0.2)
|
84
74
|
|
85
|
-
def _restart_server(self, timeout:
|
75
|
+
def _restart_server(self, timeout: float | None = None):
|
86
76
|
"""Kills and restarts the adb server.
|
87
77
|
|
88
78
|
Args:
|
@@ -104,7 +94,7 @@ class AdbController:
|
|
104
94
|
def execute_command(
|
105
95
|
self,
|
106
96
|
args: list[str],
|
107
|
-
timeout:
|
97
|
+
timeout: float | None = None,
|
108
98
|
device_specific: bool = True,
|
109
99
|
) -> bytes:
|
110
100
|
"""Executes an adb command.
|
@@ -119,13 +109,14 @@ class AdbController:
|
|
119
109
|
Returns:
|
120
110
|
The output of running such command as a binary string.
|
121
111
|
"""
|
122
|
-
timeout = self.
|
112
|
+
timeout = self._config.default_timeout if timeout is None else timeout
|
123
113
|
command = self.command_prefix(include_device_name=device_specific) + args
|
124
114
|
command_str = 'adb ' + ' '.join(command[1:])
|
125
115
|
|
116
|
+
n_retries = 2
|
126
117
|
n_tries = 1
|
127
118
|
latest_error = None
|
128
|
-
while n_tries
|
119
|
+
while n_tries <= n_retries:
|
129
120
|
try:
|
130
121
|
logging.info('Executing ADB command: [%s]', command_str)
|
131
122
|
cmd_output = subprocess.check_output(
|
@@ -150,7 +141,7 @@ class AdbController:
|
|
150
141
|
logging.error(' %s', line)
|
151
142
|
n_tries += 1
|
152
143
|
latest_error = e
|
153
|
-
if device_specific:
|
144
|
+
if device_specific and n_tries <= n_retries:
|
154
145
|
self._restart_server(timeout=timeout)
|
155
146
|
|
156
147
|
raise errors.AdbControllerError(
|
@@ -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.
|
@@ -22,6 +22,7 @@ from unittest import mock
|
|
22
22
|
|
23
23
|
from absl.testing import absltest
|
24
24
|
from android_env.components import adb_controller as adb_controller_lib
|
25
|
+
from android_env.components import config_classes
|
25
26
|
from android_env.components import errors
|
26
27
|
|
27
28
|
# Timeout to be used by default in tests below. Set to a small value to avoid
|
@@ -38,14 +39,24 @@ class AdbControllerTest(absltest.TestCase):
|
|
38
39
|
os.environ['HOME'] = '$MY_ENV_VAR'
|
39
40
|
self._env_before = os.environ
|
40
41
|
self._adb_controller = adb_controller_lib.AdbController(
|
41
|
-
|
42
|
+
config_classes.AdbControllerConfig(
|
43
|
+
adb_path='my_adb',
|
44
|
+
device_name='awesome_device',
|
45
|
+
adb_server_port=9999,
|
46
|
+
)
|
47
|
+
)
|
42
48
|
|
43
49
|
@mock.patch.object(subprocess, 'check_output', autospec=True)
|
44
50
|
@mock.patch.object(time, 'sleep', autospec=True)
|
45
51
|
def test_init_server(self, mock_sleep, mock_check_output):
|
46
52
|
# Arrange.
|
47
53
|
adb_controller = adb_controller_lib.AdbController(
|
48
|
-
|
54
|
+
config_classes.AdbControllerConfig(
|
55
|
+
adb_path='my_adb',
|
56
|
+
device_name='awesome_device',
|
57
|
+
adb_server_port=9999,
|
58
|
+
)
|
59
|
+
)
|
49
60
|
|
50
61
|
# Act.
|
51
62
|
adb_controller.init_server(timeout=_TIMEOUT)
|
@@ -69,7 +80,12 @@ class AdbControllerTest(absltest.TestCase):
|
|
69
80
|
subprocess.CalledProcessError(returncode=1, cmd='blah'),
|
70
81
|
] + ['fake_output'.encode('utf-8')] * 4
|
71
82
|
adb_controller = adb_controller_lib.AdbController(
|
72
|
-
|
83
|
+
config_classes.AdbControllerConfig(
|
84
|
+
adb_path='my_adb',
|
85
|
+
device_name='awesome_device',
|
86
|
+
adb_server_port=9999,
|
87
|
+
)
|
88
|
+
)
|
73
89
|
|
74
90
|
# Act.
|
75
91
|
adb_controller.execute_command(['my_command'], timeout=_TIMEOUT)
|
@@ -112,6 +128,73 @@ class AdbControllerTest(absltest.TestCase):
|
|
112
128
|
mock_sleep.assert_has_calls(
|
113
129
|
[mock.call(0.2), mock.call(2.0), mock.call(0.2)])
|
114
130
|
|
131
|
+
@mock.patch.object(subprocess, 'check_output', autospec=True)
|
132
|
+
@mock.patch.object(time, 'sleep', autospec=True)
|
133
|
+
def test_invalid_command(self, mock_sleep, mock_check_output):
|
134
|
+
# Arrange.
|
135
|
+
restart_sequence = ['fake_output'.encode('utf-8')] * 3
|
136
|
+
mock_check_output.side_effect = (
|
137
|
+
[
|
138
|
+
subprocess.CalledProcessError(returncode=1, cmd='blah'),
|
139
|
+
]
|
140
|
+
+ restart_sequence
|
141
|
+
+ [subprocess.CalledProcessError(returncode=1, cmd='blah')]
|
142
|
+
# Don't restart if last call fails.
|
143
|
+
)
|
144
|
+
adb_controller = adb_controller_lib.AdbController(
|
145
|
+
config_classes.AdbControllerConfig(
|
146
|
+
adb_path='my_adb',
|
147
|
+
device_name='awesome_device',
|
148
|
+
adb_server_port=9999,
|
149
|
+
)
|
150
|
+
)
|
151
|
+
|
152
|
+
# Act.
|
153
|
+
with self.assertRaises(errors.AdbControllerError):
|
154
|
+
adb_controller.execute_command(['my_command'], timeout=_TIMEOUT)
|
155
|
+
|
156
|
+
# Assert.
|
157
|
+
expected_env = self._env_before
|
158
|
+
expected_env['HOME'] = '/some/path/'
|
159
|
+
mock_check_output.assert_has_calls(
|
160
|
+
[
|
161
|
+
mock.call(
|
162
|
+
['my_adb', '-P', '9999', '-s', 'awesome_device', 'my_command'],
|
163
|
+
stderr=subprocess.STDOUT,
|
164
|
+
timeout=_TIMEOUT,
|
165
|
+
env=expected_env,
|
166
|
+
),
|
167
|
+
mock.call(
|
168
|
+
['my_adb', '-P', '9999', 'kill-server'],
|
169
|
+
stderr=subprocess.STDOUT,
|
170
|
+
timeout=_TIMEOUT,
|
171
|
+
env=expected_env,
|
172
|
+
),
|
173
|
+
mock.call(
|
174
|
+
['my_adb', '-P', '9999', 'start-server'],
|
175
|
+
stderr=subprocess.STDOUT,
|
176
|
+
timeout=_TIMEOUT,
|
177
|
+
env=expected_env,
|
178
|
+
),
|
179
|
+
mock.call(
|
180
|
+
['my_adb', '-P', '9999', 'devices'],
|
181
|
+
stderr=subprocess.STDOUT,
|
182
|
+
timeout=_TIMEOUT,
|
183
|
+
env=expected_env,
|
184
|
+
),
|
185
|
+
mock.call(
|
186
|
+
['my_adb', '-P', '9999', '-s', 'awesome_device', 'my_command'],
|
187
|
+
stderr=subprocess.STDOUT,
|
188
|
+
timeout=_TIMEOUT,
|
189
|
+
env=expected_env,
|
190
|
+
),
|
191
|
+
],
|
192
|
+
any_order=False,
|
193
|
+
)
|
194
|
+
mock_sleep.assert_has_calls(
|
195
|
+
[mock.call(0.2), mock.call(2.0), mock.call(0.2)]
|
196
|
+
)
|
197
|
+
|
115
198
|
@mock.patch.object(subprocess, 'check_output', autospec=True)
|
116
199
|
@mock.patch.object(time, 'sleep', autospec=True)
|
117
200
|
def test_avoid_infinite_recursion(self, mock_sleep, mock_check_output):
|
@@ -119,7 +202,12 @@ class AdbControllerTest(absltest.TestCase):
|
|
119
202
|
mock_check_output.side_effect = subprocess.CalledProcessError(
|
120
203
|
returncode=1, cmd='blah')
|
121
204
|
adb_controller = adb_controller_lib.AdbController(
|
122
|
-
|
205
|
+
config_classes.AdbControllerConfig(
|
206
|
+
adb_path='my_adb',
|
207
|
+
device_name='awesome_device',
|
208
|
+
adb_server_port=9999,
|
209
|
+
)
|
210
|
+
)
|
123
211
|
self.assertRaises(
|
124
212
|
errors.AdbControllerError,
|
125
213
|
adb_controller.execute_command, ['my_command'], timeout=_TIMEOUT)
|
@@ -131,10 +219,13 @@ class AdbControllerInitTest(absltest.TestCase):
|
|
131
219
|
os.environ['ANDROID_HOME'] = '/usr/local/Android/Sdk'
|
132
220
|
os.environ['ANDROID_ADB_SERVER_PORT'] = '1337'
|
133
221
|
adb_controller_lib.AdbController(
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
222
|
+
config_classes.AdbControllerConfig(
|
223
|
+
adb_path='my_adb',
|
224
|
+
device_name='awesome_device',
|
225
|
+
adb_server_port=9999,
|
226
|
+
default_timeout=_TIMEOUT,
|
227
|
+
)
|
228
|
+
)
|
138
229
|
self.assertNotIn('ANDROID_HOME', os.environ)
|
139
230
|
self.assertNotIn('ANDROID_ADB_SERVER_PORT', os.environ)
|
140
231
|
|
@@ -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.
|
@@ -27,8 +27,8 @@ _LOGCAT_COMMAND = ['logcat', '-v', 'epoch']
|
|
27
27
|
class AdbLogStream(log_stream.LogStream):
|
28
28
|
"""Manages adb logcat process for a locally running emulator."""
|
29
29
|
|
30
|
-
def __init__(self, adb_command_prefix: list[str],
|
31
|
-
super().__init__(
|
30
|
+
def __init__(self, adb_command_prefix: list[str], verbose: bool = False):
|
31
|
+
super().__init__(verbose=verbose)
|
32
32
|
self._adb_command_prefix = adb_command_prefix
|
33
33
|
|
34
34
|
def _get_stream_output(self):
|
@@ -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,13 +15,13 @@
|
|
15
15
|
|
16
16
|
"""Determines if the current app screen matches an expected app screen."""
|
17
17
|
|
18
|
+
from collections.abc import Callable, Sequence
|
18
19
|
import enum
|
19
20
|
import re
|
20
21
|
import time
|
21
|
-
from typing import
|
22
|
+
from typing import Self
|
22
23
|
|
23
24
|
from absl import logging
|
24
|
-
|
25
25
|
from android_env.components import adb_call_parser as adb_call_parser_lib
|
26
26
|
from android_env.components import errors
|
27
27
|
from android_env.proto import adb_pb2
|
@@ -31,7 +31,7 @@ from android_env.proto import task_pb2
|
|
31
31
|
class _DumpsysNode:
|
32
32
|
"""A node in a dumpsys tree."""
|
33
33
|
|
34
|
-
def __init__(self, data:
|
34
|
+
def __init__(self, data: str):
|
35
35
|
self._children = []
|
36
36
|
self._data = data
|
37
37
|
|
@@ -40,12 +40,12 @@ class _DumpsysNode:
|
|
40
40
|
return self._data
|
41
41
|
|
42
42
|
@property
|
43
|
-
def children(self) -> list[
|
43
|
+
def children(self) -> list[Self]:
|
44
44
|
return self._children
|
45
45
|
|
46
|
-
def find_child(
|
47
|
-
|
48
|
-
|
46
|
+
def find_child(
|
47
|
+
self, predicate: Callable[[Self], bool], max_levels: int = 0
|
48
|
+
) -> Self | None:
|
49
49
|
"""Returns the first direct child that matches `predicate`, None otherwise.
|
50
50
|
|
51
51
|
Args:
|
@@ -126,9 +126,11 @@ def build_tree_from_dumpsys_output(dumpsys_output: str) -> _DumpsysNode:
|
|
126
126
|
return root
|
127
127
|
|
128
128
|
|
129
|
-
def matches_path(
|
130
|
-
|
131
|
-
|
129
|
+
def matches_path(
|
130
|
+
dumpsys_activity_output: str,
|
131
|
+
expected_view_hierarchy_path: Sequence[re.Pattern[str]],
|
132
|
+
max_levels: int = 0,
|
133
|
+
) -> bool:
|
132
134
|
"""Returns True if the current dumpsys output matches the expected path.
|
133
135
|
|
134
136
|
Args:
|
@@ -150,7 +152,7 @@ def matches_path(dumpsys_activity_output: str,
|
|
150
152
|
'view_hierarchy is None. Dumpsys activity output: %s. tree: %r',
|
151
153
|
str(dumpsys_activity_output), root.print_tree())
|
152
154
|
logging.error('Tree root: %s', str(root))
|
153
|
-
return
|
155
|
+
return False
|
154
156
|
|
155
157
|
current_node = view_hierarchy
|
156
158
|
for i, regex in enumerate(expected_view_hierarchy_path):
|
@@ -165,7 +167,7 @@ def matches_path(dumpsys_activity_output: str,
|
|
165
167
|
regex.pattern, current_node)
|
166
168
|
logging.error('Dumpsys activity output: %s', str(dumpsys_activity_output))
|
167
169
|
logging.error('Tree root: %s', str(root))
|
168
|
-
return
|
170
|
+
return False
|
169
171
|
else:
|
170
172
|
current_node = child
|
171
173
|
return True
|
@@ -0,0 +1,203 @@
|
|
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
|
+
"""Dataclass definitions used for instantiating AndroidEnv components."""
|
17
|
+
|
18
|
+
import dataclasses
|
19
|
+
|
20
|
+
|
21
|
+
@dataclasses.dataclass
|
22
|
+
class AdbControllerConfig:
|
23
|
+
"""Settings for instatiating an `AdbController` instance."""
|
24
|
+
|
25
|
+
# Filesystem path to the `adb` binary.
|
26
|
+
# NOTE: This must be a full path and must not contain environment variables
|
27
|
+
# or user folder shorthands (e.g. `~/some/path/to/adb`) since they will not be
|
28
|
+
# expanded internally by AndroidEnv.
|
29
|
+
adb_path: str = '~/Android/Sdk/platform-tools/adb'
|
30
|
+
# Port for adb server.
|
31
|
+
adb_server_port: int = 5037
|
32
|
+
# Default timeout in seconds for internal commands.
|
33
|
+
default_timeout: float = 120.0
|
34
|
+
# Name of the device to communicate with.
|
35
|
+
device_name: str = ''
|
36
|
+
|
37
|
+
|
38
|
+
@dataclasses.dataclass
|
39
|
+
class DeviceSettingsConfig:
|
40
|
+
"""Config class for DeviceSettings."""
|
41
|
+
|
42
|
+
# Whether to show circles on the screen indicating touch position.
|
43
|
+
show_touches: bool = True
|
44
|
+
# Whether to show blue lines on the screen indicating touch position.
|
45
|
+
show_pointer_location: bool = True
|
46
|
+
# Whether or not to show the status (top) bar.
|
47
|
+
show_status_bar: bool = False
|
48
|
+
# Whether or not to show the navigation (bottom) bar.
|
49
|
+
show_navigation_bar: bool = False
|
50
|
+
|
51
|
+
|
52
|
+
@dataclasses.dataclass
|
53
|
+
class CoordinatorConfig:
|
54
|
+
"""Config class for Coordinator."""
|
55
|
+
|
56
|
+
# Number of virtual "fingers" of the agent.
|
57
|
+
num_fingers: int = 1
|
58
|
+
# Whether to enable keyboard key events.
|
59
|
+
enable_key_events: bool = False
|
60
|
+
# Time between periodic restarts in minutes. If > 0, will trigger
|
61
|
+
# a simulator restart at the beginning of the next episode once the time has
|
62
|
+
# been reached.
|
63
|
+
periodic_restart_time_min: float = 0.0
|
64
|
+
# General Android settings.
|
65
|
+
device_settings: DeviceSettingsConfig = dataclasses.field(
|
66
|
+
default_factory=DeviceSettingsConfig
|
67
|
+
)
|
68
|
+
|
69
|
+
|
70
|
+
@dataclasses.dataclass
|
71
|
+
class SimulatorConfig:
|
72
|
+
"""Base class for all simulator configs."""
|
73
|
+
|
74
|
+
# If true, the log stream of the simulator will be verbose.
|
75
|
+
verbose_logs: bool = False
|
76
|
+
# How often to (asynchronously) grab the screenshot from the simulator.
|
77
|
+
# If <= 0, stepping the environment blocks on fetching the screenshot (the
|
78
|
+
# environment is synchronous).
|
79
|
+
interaction_rate_sec: float = 0.0
|
80
|
+
|
81
|
+
|
82
|
+
@dataclasses.dataclass
|
83
|
+
class EmulatorLauncherConfig:
|
84
|
+
"""Config class for EmulatorLauncher."""
|
85
|
+
|
86
|
+
# NOTE: If `adb_port`, `emulator_console_port` and `grpc_port` are defined
|
87
|
+
# (i.e. not all equal to 0), it is assumed that the emulator they point to
|
88
|
+
# exists already and EmulatorLauncher will be skipped.
|
89
|
+
|
90
|
+
# Filesystem path to the `emulator` binary.
|
91
|
+
emulator_path: str = '~/Android/Sdk/emulator/emulator'
|
92
|
+
# Filesystem path to the Android SDK root.
|
93
|
+
android_sdk_root: str = '~/Android/Sdk'
|
94
|
+
# Name of the AVD.
|
95
|
+
avd_name: str = ''
|
96
|
+
# Local directory for AVDs.
|
97
|
+
android_avd_home: str = '~/.android/avd'
|
98
|
+
# Name of the snapshot to load.
|
99
|
+
snapshot_name: str = ''
|
100
|
+
# Path to the KVM device.
|
101
|
+
kvm_device: str = '/dev/kvm'
|
102
|
+
# Path to directory which will hold temporary files.
|
103
|
+
tmp_dir: str = '/tmp/android_env/simulator/'
|
104
|
+
# GPU mode override.
|
105
|
+
# Please see
|
106
|
+
# https://developer.android.com/studio/run/emulator-acceleration#accel-graphics.
|
107
|
+
gpu_mode: str = 'swangle_indirect' # Alternative: swiftshader_indirect, host
|
108
|
+
# Whether to run in headless mode (i.e. without a graphical window).
|
109
|
+
run_headless: bool = True
|
110
|
+
# Whether to restrict network access.
|
111
|
+
# If True, will disable networking on the device. This option is only
|
112
|
+
# available for emulator version > 31.3.9 (June 2022).
|
113
|
+
restrict_network: bool = False
|
114
|
+
# Whether to set `SHOW_PERF_STATS=1` when launching the emulator to display
|
115
|
+
# performance and memory statistics.
|
116
|
+
show_perf_stats: bool = False
|
117
|
+
|
118
|
+
# ADB port for the Android device.
|
119
|
+
adb_port: int = 0
|
120
|
+
# Port for telnet communication with the emulator.
|
121
|
+
emulator_console_port: int = 0
|
122
|
+
# Port for gRPC communication with the emulator.
|
123
|
+
grpc_port: int = 0
|
124
|
+
|
125
|
+
|
126
|
+
@dataclasses.dataclass
|
127
|
+
class EmulatorConfig(SimulatorConfig):
|
128
|
+
"""Config class for EmulatorSimulator."""
|
129
|
+
|
130
|
+
# Configuration for launching the Android Emulator.
|
131
|
+
emulator_launcher: EmulatorLauncherConfig = dataclasses.field(
|
132
|
+
default_factory=EmulatorLauncherConfig
|
133
|
+
)
|
134
|
+
# Configuration for talking to adb.
|
135
|
+
adb_controller: AdbControllerConfig = dataclasses.field(
|
136
|
+
default_factory=AdbControllerConfig
|
137
|
+
)
|
138
|
+
# Path to file which holds emulator logs. If not provided, it will be
|
139
|
+
# determined by the EmulatorLauncher.
|
140
|
+
logfile_path: str = ''
|
141
|
+
# The number of times to try launching the emulator before rebooting (reboot
|
142
|
+
# on the n+1-st try).
|
143
|
+
launch_n_times_without_reboot: int = 1
|
144
|
+
# The number of times to try launching the emulator before reinstalling
|
145
|
+
# (reinstall on the n+1-st try).
|
146
|
+
launch_n_times_without_reinstall: int = 2
|
147
|
+
|
148
|
+
|
149
|
+
@dataclasses.dataclass
|
150
|
+
class FakeSimulatorConfig(SimulatorConfig):
|
151
|
+
"""Config class for FakeSimulator."""
|
152
|
+
|
153
|
+
# The dimensions in pixels of the device screen (HxW).
|
154
|
+
screen_dimensions: tuple[int, int] = (0, 0)
|
155
|
+
|
156
|
+
|
157
|
+
@dataclasses.dataclass
|
158
|
+
class TaskManagerConfig:
|
159
|
+
"""Config class for TaskManager."""
|
160
|
+
|
161
|
+
# If max_bad_states episodes finish in a bad state in a row, restart
|
162
|
+
# the simulation.
|
163
|
+
max_bad_states: int = 3
|
164
|
+
# The frequency to check for the current activity and view hierarchy.
|
165
|
+
# The unit is raw observation (i.e. each call to AndroidEnv.step()).
|
166
|
+
dumpsys_check_frequency: int = 150
|
167
|
+
# The maximum number of tries for extracting the current activity before
|
168
|
+
# forcing the episode to restart.
|
169
|
+
max_failed_current_activity: int = 10
|
170
|
+
# The maximum number of extras elements to store. If this number is exceeded,
|
171
|
+
# elements are dropped in the order they were received.
|
172
|
+
extras_max_buffer_size: int = 100
|
173
|
+
|
174
|
+
|
175
|
+
@dataclasses.dataclass
|
176
|
+
class TaskConfig:
|
177
|
+
"""Base config class for loading tasks."""
|
178
|
+
|
179
|
+
# The directory for temporary task-related resources.
|
180
|
+
tmp_dir: str = ''
|
181
|
+
|
182
|
+
|
183
|
+
@dataclasses.dataclass
|
184
|
+
class FilesystemTaskConfig(TaskConfig):
|
185
|
+
"""Config for protobuf files stored in the local filesystem."""
|
186
|
+
|
187
|
+
# Filesystem path to `.binarypb` or `.textproto` protobuf Task.
|
188
|
+
path: str = ''
|
189
|
+
|
190
|
+
|
191
|
+
@dataclasses.dataclass
|
192
|
+
class AndroidEnvConfig:
|
193
|
+
"""Config class for AndroidEnv."""
|
194
|
+
|
195
|
+
# Configs for main components.
|
196
|
+
task: TaskConfig = dataclasses.field(default_factory=TaskConfig)
|
197
|
+
task_manager: TaskManagerConfig = dataclasses.field(
|
198
|
+
default_factory=TaskManagerConfig
|
199
|
+
)
|
200
|
+
coordinator: CoordinatorConfig = dataclasses.field(
|
201
|
+
default_factory=CoordinatorConfig
|
202
|
+
)
|
203
|
+
simulator: SimulatorConfig = dataclasses.field(default_factory=EmulatorConfig)
|