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.
Files changed (142) hide show
  1. android_env/__init__.py +1 -1
  2. android_env/components/__init__.py +1 -1
  3. android_env/components/a11y/__init__.py +15 -0
  4. android_env/components/a11y/a11y_events.py +118 -0
  5. android_env/components/a11y/a11y_events_test.py +173 -0
  6. android_env/components/a11y/a11y_forests.py +128 -0
  7. android_env/components/a11y/a11y_forests_test.py +237 -0
  8. android_env/components/a11y/a11y_servicer.py +199 -0
  9. android_env/components/a11y/a11y_servicer_test.py +224 -0
  10. android_env/components/action_fns.py +132 -0
  11. android_env/components/action_fns_test.py +227 -0
  12. android_env/components/action_type.py +26 -3
  13. android_env/components/adb_call_parser.py +233 -185
  14. android_env/components/adb_call_parser_test.py +165 -163
  15. android_env/components/adb_controller.py +19 -28
  16. android_env/components/adb_controller_test.py +100 -9
  17. android_env/components/adb_log_stream.py +3 -3
  18. android_env/components/adb_log_stream_test.py +1 -1
  19. android_env/components/app_screen_checker.py +15 -13
  20. android_env/components/app_screen_checker_test.py +1 -1
  21. android_env/components/config_classes.py +203 -0
  22. android_env/components/coordinator.py +53 -338
  23. android_env/components/coordinator_test.py +26 -283
  24. android_env/components/device_settings.py +174 -0
  25. android_env/components/device_settings_test.py +228 -0
  26. android_env/components/dumpsys_thread.py +3 -4
  27. android_env/components/dumpsys_thread_test.py +1 -1
  28. android_env/components/errors.py +2 -5
  29. android_env/components/errors_test.py +1 -1
  30. android_env/components/log_stream.py +2 -2
  31. android_env/components/log_stream_test.py +1 -1
  32. android_env/components/logcat_thread.py +9 -8
  33. android_env/components/logcat_thread_test.py +2 -3
  34. android_env/components/{utils.py → pixel_fns.py} +19 -20
  35. android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
  36. android_env/components/setup_step_interpreter.py +45 -37
  37. android_env/components/setup_step_interpreter_test.py +1 -1
  38. android_env/components/simulators/__init__.py +1 -1
  39. android_env/components/simulators/base_simulator.py +79 -23
  40. android_env/components/simulators/base_simulator_test.py +131 -9
  41. android_env/components/simulators/emulator/__init__.py +1 -1
  42. android_env/components/simulators/emulator/emulator_launcher.py +62 -81
  43. android_env/components/simulators/emulator/emulator_launcher_test.py +120 -43
  44. android_env/components/simulators/emulator/emulator_simulator.py +111 -98
  45. android_env/components/simulators/emulator/emulator_simulator_test.py +174 -138
  46. android_env/components/simulators/fake/__init__.py +1 -1
  47. android_env/components/simulators/fake/fake_simulator.py +9 -17
  48. android_env/components/simulators/fake/fake_simulator_test.py +23 -8
  49. android_env/components/specs.py +1 -1
  50. android_env/components/specs_test.py +1 -1
  51. android_env/components/task_manager.py +26 -31
  52. android_env/components/task_manager_test.py +1 -18
  53. android_env/env_interface.py +1 -17
  54. android_env/environment.py +27 -17
  55. android_env/environment_test.py +51 -25
  56. android_env/loader.py +57 -43
  57. android_env/loader_test.py +115 -35
  58. android_env/proto/__init__.py +1 -1
  59. android_env/proto/a11y/__init__.py +15 -0
  60. android_env/proto/a11y/a11y.proto +75 -0
  61. android_env/proto/a11y/a11y_pb2.py +54 -0
  62. android_env/proto/a11y/a11y_pb2.pyi +49 -0
  63. android_env/proto/a11y/a11y_pb2_grpc.py +202 -0
  64. android_env/proto/a11y/android_accessibility_action.proto +32 -0
  65. android_env/proto/a11y/android_accessibility_action_pb2.py +37 -0
  66. android_env/proto/a11y/android_accessibility_action_pb2.pyi +13 -0
  67. android_env/proto/a11y/android_accessibility_action_pb2_grpc.py +24 -0
  68. android_env/proto/a11y/android_accessibility_forest.proto +29 -0
  69. android_env/proto/a11y/android_accessibility_forest_pb2.py +38 -0
  70. android_env/proto/a11y/android_accessibility_forest_pb2.pyi +13 -0
  71. android_env/proto/a11y/android_accessibility_forest_pb2_grpc.py +24 -0
  72. android_env/proto/a11y/android_accessibility_node_info.proto +122 -0
  73. android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto +49 -0
  74. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.py +39 -0
  75. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.pyi +28 -0
  76. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2_grpc.py +24 -0
  77. android_env/proto/a11y/android_accessibility_node_info_pb2.py +42 -0
  78. android_env/proto/a11y/android_accessibility_node_info_pb2.pyi +75 -0
  79. android_env/proto/a11y/android_accessibility_node_info_pb2_grpc.py +24 -0
  80. android_env/proto/a11y/android_accessibility_tree.proto +29 -0
  81. android_env/proto/a11y/android_accessibility_tree_pb2.py +38 -0
  82. android_env/proto/a11y/android_accessibility_tree_pb2.pyi +13 -0
  83. android_env/proto/a11y/android_accessibility_tree_pb2_grpc.py +24 -0
  84. android_env/proto/a11y/android_accessibility_window_info.proto +84 -0
  85. android_env/proto/a11y/android_accessibility_window_info_pb2.py +41 -0
  86. android_env/proto/a11y/android_accessibility_window_info_pb2.pyi +48 -0
  87. android_env/proto/a11y/android_accessibility_window_info_pb2_grpc.py +24 -0
  88. android_env/proto/a11y/rect.proto +30 -0
  89. android_env/proto/a11y/rect_pb2.py +37 -0
  90. android_env/proto/a11y/rect_pb2.pyi +17 -0
  91. android_env/proto/a11y/rect_pb2_grpc.py +24 -0
  92. android_env/proto/adb.proto +13 -1
  93. android_env/proto/adb_pb2.py +120 -107
  94. android_env/proto/adb_pb2.pyi +396 -0
  95. android_env/proto/adb_pb2_grpc.py +20 -0
  96. android_env/proto/emulator_controller.proto +1 -1
  97. android_env/proto/emulator_controller_pb2.py +142 -131
  98. android_env/proto/emulator_controller_pb2.pyi +672 -0
  99. android_env/proto/emulator_controller_pb2_grpc.py +497 -136
  100. android_env/proto/snapshot.proto +1 -1
  101. android_env/proto/snapshot_pb2.py +30 -19
  102. android_env/proto/snapshot_pb2.pyi +117 -0
  103. android_env/proto/snapshot_pb2_grpc.py +20 -0
  104. android_env/proto/snapshot_service.proto +1 -1
  105. android_env/proto/snapshot_service_pb2.py +36 -25
  106. android_env/proto/snapshot_service_pb2.pyi +86 -0
  107. android_env/proto/snapshot_service_pb2_grpc.py +119 -28
  108. android_env/proto/state.proto +1 -1
  109. android_env/proto/state_pb2.py +46 -35
  110. android_env/proto/state_pb2.pyi +85 -0
  111. android_env/proto/state_pb2_grpc.py +20 -0
  112. android_env/proto/task.proto +4 -1
  113. android_env/proto/task_pb2.py +41 -30
  114. android_env/proto/task_pb2.pyi +160 -0
  115. android_env/proto/task_pb2_grpc.py +20 -0
  116. android_env/wrappers/__init__.py +1 -1
  117. android_env/wrappers/a11y_grpc_wrapper.py +500 -0
  118. android_env/wrappers/a11y_grpc_wrapper_test.py +849 -0
  119. android_env/wrappers/base_wrapper.py +1 -5
  120. android_env/wrappers/base_wrapper_test.py +1 -7
  121. android_env/wrappers/discrete_action_wrapper.py +15 -14
  122. android_env/wrappers/discrete_action_wrapper_test.py +1 -1
  123. android_env/wrappers/flat_interface_wrapper.py +5 -5
  124. android_env/wrappers/flat_interface_wrapper_test.py +1 -1
  125. android_env/wrappers/float_pixels_wrapper.py +5 -4
  126. android_env/wrappers/float_pixels_wrapper_test.py +1 -1
  127. android_env/wrappers/gym_wrapper.py +1 -1
  128. android_env/wrappers/gym_wrapper_test.py +1 -1
  129. android_env/wrappers/image_rescale_wrapper.py +13 -10
  130. android_env/wrappers/image_rescale_wrapper_test.py +1 -1
  131. android_env/wrappers/last_action_wrapper.py +5 -4
  132. android_env/wrappers/last_action_wrapper_test.py +1 -1
  133. android_env/wrappers/rate_limit_wrapper.py +1 -1
  134. android_env/wrappers/rate_limit_wrapper_test.py +1 -1
  135. android_env/wrappers/tap_action_wrapper.py +12 -12
  136. android_env/wrappers/tap_action_wrapper_test.py +49 -14
  137. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -16
  138. android_env-1.2.3.dist-info/RECORD +141 -0
  139. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
  140. android_env-1.2.2.dist-info/RECORD +0 -88
  141. {android_env-1.2.2.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
  142. {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 2023 DeepMind Technologies Limited.
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
- device_name: str = '',
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
- Args:
38
- device_name: Name of the device to communicate with.
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 = [self._adb_path, '-P', self._adb_server_port]
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._device_name])
58
+ command_prefix.extend(['-s', self._config.device_name])
69
59
  return command_prefix
70
60
 
71
- def init_server(self, timeout: Optional[float] = None):
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: Optional[float] = None):
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: Optional[float] = None,
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._default_timeout if timeout is None else timeout
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 < 3:
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 2023 DeepMind Technologies Limited.
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
- adb_path='my_adb', device_name='awesome_device', adb_server_port=9999)
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
- adb_path='my_adb', device_name='awesome_device', adb_server_port=9999)
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
- adb_path='my_adb', device_name='awesome_device', adb_server_port=9999)
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
- adb_path='my_adb', device_name='awesome_device', adb_server_port=9999)
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
- adb_path='my_adb',
135
- device_name='awesome_device',
136
- adb_server_port=9999,
137
- default_timeout=_TIMEOUT)
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 2023 DeepMind Technologies Limited.
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], *args, **kwargs):
31
- super().__init__(*args, **kwargs)
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 2023 DeepMind Technologies Limited.
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.
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2023 DeepMind Technologies Limited.
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 Callable, Optional, Sequence, Pattern
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: Optional[str] = None):
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['_DumpsysNode']:
43
+ def children(self) -> list[Self]:
44
44
  return self._children
45
45
 
46
- def find_child(self,
47
- predicate: Callable[['_DumpsysNode'], bool],
48
- max_levels: int = 0) -> Optional['_DumpsysNode']:
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(dumpsys_activity_output: str,
130
- expected_view_hierarchy_path: Sequence[Pattern[str]],
131
- max_levels: int = 0) -> bool:
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 None
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 None
170
+ return False
169
171
  else:
170
172
  current_node = child
171
173
  return True
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2023 DeepMind Technologies Limited.
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.
@@ -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)