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.
@@ -17,11 +17,12 @@
17
17
 
18
18
  import os
19
19
  import time
20
- from typing import Any, Optional
20
+ from typing import Any
21
21
 
22
22
  from absl import logging
23
23
  from android_env.components import adb_controller
24
24
  from android_env.components import adb_log_stream
25
+ from android_env.components import config_classes
25
26
  from android_env.components import errors
26
27
  from android_env.components import log_stream
27
28
  from android_env.components.simulators import base_simulator
@@ -38,15 +39,19 @@ from android_env.proto import snapshot_service_pb2_grpc
38
39
  from google.protobuf import empty_pb2
39
40
 
40
41
 
41
- _DEFAULT_SNAPSHOT_NAME = 'default_snapshot'
42
+ _DEFAULT_SNAPSHOT_NAME: str = 'default_snapshot'
42
43
 
43
44
 
44
- def is_existing_emulator_provided(launcher_args: dict[str, Any]) -> bool:
45
+ def _is_existing_emulator_provided(
46
+ launcher_config: config_classes.EmulatorLauncherConfig,
47
+ ) -> bool:
45
48
  """Returns true if all necessary args were provided."""
49
+
46
50
  return bool(
47
- launcher_args.get('adb_port') and
48
- launcher_args.get('emulator_console_port') and
49
- launcher_args.get('grpc_port'))
51
+ launcher_config.adb_port
52
+ and launcher_config.emulator_console_port
53
+ and launcher_config.grpc_port
54
+ )
50
55
 
51
56
 
52
57
  def _pick_adb_port() -> int:
@@ -58,6 +63,7 @@ def _pick_adb_port() -> int:
58
63
  Returns:
59
64
  port: an available port for adb.
60
65
  """
66
+
61
67
  for p in range(5555, 5587, 2):
62
68
  if portpicker.is_port_free(p):
63
69
  return p
@@ -73,29 +79,13 @@ def _pick_emulator_grpc_port() -> int:
73
79
  Returns:
74
80
  port: an available port for emulator grpc.
75
81
  """
82
+
76
83
  if portpicker.is_port_free(8554):
77
84
  return 8554
78
85
  else:
79
86
  return portpicker.pick_unused_port()
80
87
 
81
88
 
82
- def _reconnect_on_grpc_error(func):
83
- """Decorator function for reconnecting to emulator upon grpc errors."""
84
-
85
- def wrapper(*args, **kwargs):
86
- try:
87
- return func(*args, **kwargs) # pytype: disable=missing-parameter # always-use-return-annotations
88
- except grpc.RpcError:
89
- logging.exception('RpcError caught. Reconnecting to emulator...')
90
- emu = args[0] # The first arg of the function is "self"
91
- emu._emulator_stub, emu._snapshot_stub = emu._connect_to_emulator( # pylint: disable=protected-access
92
- emu._grpc_port # pylint: disable=protected-access
93
- )
94
- return func(*args, **kwargs) # pytype: disable=missing-parameter # always-use-return-annotations
95
-
96
- return wrapper
97
-
98
-
99
89
  class EmulatorBootError(errors.SimulatorError):
100
90
  """Raised when an emulator failed to boot."""
101
91
 
@@ -107,90 +97,86 @@ class EmulatorCrashError(errors.SimulatorError):
107
97
  class EmulatorSimulator(base_simulator.BaseSimulator):
108
98
  """Controls an Android Emulator."""
109
99
 
110
- def __init__(
111
- self,
112
- emulator_launcher_args: dict[str, Any],
113
- adb_controller_args: dict[str, Any],
114
- tmp_dir: str = '/tmp/android_env/simulator',
115
- logfile_path: Optional[str] = None,
116
- launch_n_times_without_reboot: int = 1,
117
- launch_n_times_without_reinstall: int = 2,
118
- **kwargs,
119
- ):
120
- """Instantiates an EmulatorSimulator.
100
+ def __init__(self, config: config_classes.EmulatorConfig):
101
+ """Instantiates an EmulatorSimulator."""
121
102
 
122
- Args:
123
- emulator_launcher_args: Arguments for EmulatorLauncher.
124
- adb_controller_args: Arguments for AdbController.
125
- tmp_dir: Temporary directory to hold simulator files.
126
- logfile_path: Path to file which holds emulator logs. If not provided, it
127
- will be determined by the EmulatorLauncher.
128
- launch_n_times_without_reboot: The number of times to try launching the
129
- emulator before rebooting (reboot on the n+1-st try).
130
- launch_n_times_without_reinstall: The number of times to try launching the
131
- emulator before reinstalling (reinstall on the n+1-st try).
132
- **kwargs: keyword arguments for base class.
133
- """
103
+ super().__init__(config)
104
+ self._config = config
134
105
 
135
106
  # If adb_port, console_port and grpc_port are all already provided,
136
107
  # we assume the emulator already exists and there's no need to launch.
137
- if is_existing_emulator_provided(emulator_launcher_args):
108
+ if _is_existing_emulator_provided(self._config.emulator_launcher):
138
109
  self._existing_emulator_provided = True
139
- self._adb_port = emulator_launcher_args['adb_port']
140
- self._console_port = emulator_launcher_args['emulator_console_port']
141
- self._grpc_port = emulator_launcher_args['grpc_port']
142
110
  logging.info('Connecting to existing emulator "%r"',
143
111
  self.adb_device_name())
144
112
  else:
145
113
  self._existing_emulator_provided = False
146
- self._adb_port = _pick_adb_port()
147
- self._console_port = portpicker.pick_unused_port()
148
- self._grpc_port = _pick_emulator_grpc_port()
114
+ self._config.emulator_launcher.adb_port = _pick_adb_port()
115
+ self._config.emulator_launcher.emulator_console_port = (
116
+ portpicker.pick_unused_port()
117
+ )
118
+ self._config.emulator_launcher.grpc_port = _pick_emulator_grpc_port()
149
119
 
150
120
  self._channel = None
151
- self._emulator_stub = None
121
+ self._emulator_stub: emulator_controller_pb2_grpc.EmulatorControllerStub | None = (
122
+ None
123
+ )
152
124
  self._snapshot_stub = None
153
125
  # Set the image format to RGBA. The width and height of the returned
154
126
  # screenshots will use the device's width and height.
155
127
  self._image_format = emulator_controller_pb2.ImageFormat(
156
128
  format=emulator_controller_pb2.ImageFormat.ImgFormat.RGBA8888)
157
129
 
158
- if launch_n_times_without_reboot > launch_n_times_without_reinstall:
130
+ if (
131
+ self._config.launch_n_times_without_reboot
132
+ > self._config.launch_n_times_without_reinstall
133
+ ):
159
134
  raise ValueError(
160
- f'Number of launch attempts before reboot '
161
- f'({launch_n_times_without_reboot}) should not be greater than '
162
- f'number of launch attempts before reinstall '
163
- f'({launch_n_times_without_reinstall})')
164
- self._launch_n_times_without_reboot = launch_n_times_without_reboot
165
- self._launch_n_times_without_reinstall = launch_n_times_without_reinstall
166
-
167
- super().__init__(**kwargs)
135
+ 'Number of launch attempts before reboot'
136
+ f' ({self._config.launch_n_times_without_reboot}) should not be'
137
+ ' greater than number of launch attempts before reinstall'
138
+ f' ({self._config.launch_n_times_without_reinstall})'
139
+ )
168
140
 
169
141
  # Initialize own ADB controller.
170
- self._adb_controller_args = adb_controller_args
142
+ self._config.adb_controller.device_name = self.adb_device_name()
171
143
  self._adb_controller = self.create_adb_controller()
172
144
  self._adb_controller.init_server()
173
- logging.info('Initialized simulator with ADB server port %r.',
174
- self._adb_controller_args['adb_server_port'])
145
+ logging.info(
146
+ 'Initialized simulator with ADB server port %r.',
147
+ self._config.adb_controller.adb_server_port,
148
+ )
175
149
 
176
150
  # If necessary, create EmulatorLauncher.
177
151
  if self._existing_emulator_provided:
178
- self._logfile_path = logfile_path or None
152
+ self._logfile_path = self._config.logfile_path or None
179
153
  self._launcher = None
180
154
  else:
181
- emulator_launcher_args.update({
182
- 'adb_path': self._adb_controller_args['adb_path'],
183
- 'adb_port': self._adb_port,
184
- 'adb_server_port': self._adb_controller_args['adb_server_port'],
185
- 'emulator_console_port': self._console_port,
186
- 'grpc_port': self._grpc_port,
187
- 'tmp_dir': tmp_dir,
188
- })
189
- self._emulator_launcher_args = emulator_launcher_args
190
- logging.info('emulator_launcher_args: %r', self._emulator_launcher_args)
155
+ logging.info(
156
+ 'emulator_launcher config: %r', self._config.emulator_launcher
157
+ )
191
158
  self._launcher = emulator_launcher.EmulatorLauncher(
192
- **self._emulator_launcher_args)
193
- self._logfile_path = logfile_path or self._launcher.logfile_path()
159
+ config=self._config.emulator_launcher,
160
+ adb_controller_config=self._config.adb_controller,
161
+ )
162
+ self._logfile_path = (
163
+ self._config.logfile_path or self._launcher.logfile_path()
164
+ )
165
+
166
+ def _reconnect_on_grpc_error(func):
167
+ """Decorator function for reconnecting to emulator upon grpc errors."""
168
+
169
+ def wrapper(self, *args, **kwargs):
170
+ try:
171
+ return func(self, *args, **kwargs)
172
+ except grpc.RpcError:
173
+ logging.exception('RpcError caught. Reconnecting to emulator...')
174
+ self._emulator_stub, self._snapshot_stub = self._connect_to_emulator(
175
+ self._config.emulator_launcher.grpc_port
176
+ )
177
+ return func(self, *args, **kwargs)
178
+
179
+ return wrapper
194
180
 
195
181
  def get_logs(self) -> str:
196
182
  """Returns logs recorded by the emulator."""
@@ -201,28 +187,29 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
201
187
  return f'Logfile does not exist: {self._logfile_path}.'
202
188
 
203
189
  def adb_device_name(self) -> str:
204
- return 'emulator-%s' % (self._adb_port - 1)
190
+ return 'emulator-%s' % (self._config.emulator_launcher.adb_port - 1)
205
191
 
206
192
  def create_adb_controller(self):
207
193
  """Returns an ADB controller which can communicate with this simulator."""
208
- return adb_controller.AdbController(
209
- device_name=self.adb_device_name(), **self._adb_controller_args)
194
+ return adb_controller.AdbController(self._config.adb_controller)
210
195
 
211
196
  def create_log_stream(self) -> log_stream.LogStream:
212
197
  return adb_log_stream.AdbLogStream(
213
198
  adb_command_prefix=self._adb_controller.command_prefix(),
214
- verbose=self._verbose_logs)
199
+ verbose=self._config.verbose_logs,
200
+ )
215
201
 
216
202
  def _launch_impl(self) -> None:
217
203
  """Prepares an Android Emulator for RL interaction.
218
204
 
219
205
  The behavior depends on `self._num_launch_attempts`'s value:
220
- * <= self._launch_n_times_without_reboot -> Normal boot behavior.
221
- * > self._launch_n_times_without_reboot but <=
222
- self._launch_n_times_without_reinstall -> reboot (i.e. process is
223
- killed and started again).
224
- * > self._launch_n_times_without_reinstall -> reinstall (i.e. process is
225
- killed, emulator files are deleted and the process started again).
206
+ * <= self._config.launch_n_times_without_reboot -> Normal boot behavior.
207
+ * > self._config.launch_n_times_without_reboot but <=
208
+ self._config.launch_n_times_without_reinstall -> reboot (i.e. process
209
+ is killed and started again).
210
+ * > self._config.launch_n_times_without_reinstall -> reinstall (i.e.
211
+ process is killed, emulator files are deleted and the process started
212
+ again).
226
213
  """
227
214
 
228
215
  logging.info('Attempt %r at launching the Android Emulator (%r)',
@@ -230,19 +217,27 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
230
217
 
231
218
  if self._launcher is not None:
232
219
  # If not the first time, then shutdown the emulator first.
233
- if (self._emulator_stub is not None and
234
- self._num_launch_attempts > self._launch_n_times_without_reboot):
220
+ if (
221
+ self._emulator_stub is not None
222
+ and self._num_launch_attempts
223
+ > self._config.launch_n_times_without_reboot
224
+ ):
235
225
  self._shutdown_emulator()
236
226
  # Subsequent attempts cause the emulator files to be reinstalled.
237
- if self._num_launch_attempts > self._launch_n_times_without_reinstall:
227
+ if (
228
+ self._num_launch_attempts
229
+ > self._config.launch_n_times_without_reinstall
230
+ ):
238
231
  logging.info('Closing emulator (%r)', self.adb_device_name())
239
232
  self._launcher.close()
240
233
  self._launcher = emulator_launcher.EmulatorLauncher(
241
- **self._emulator_launcher_args)
234
+ config=self._config.emulator_launcher,
235
+ adb_controller_config=self._config.adb_controller,
236
+ )
242
237
  self._launcher.launch_emulator_process()
243
238
  # Establish grpc connection to emulator process.
244
239
  self._emulator_stub, self._snapshot_stub = self._connect_to_emulator(
245
- self._grpc_port
240
+ self._config.emulator_launcher.grpc_port
246
241
  )
247
242
 
248
243
  # Confirm booted status.
@@ -272,6 +267,7 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
272
267
  * If an error occurred during the snapshot loading process, the status
273
268
  will be `ERROR` and the `error_message` field will be filled.
274
269
  """
270
+ assert self._snapshot_stub is not None
275
271
  snapshot_name = request.args.get('snapshot_name', _DEFAULT_SNAPSHOT_NAME)
276
272
  snapshot_list = self._snapshot_stub.ListSnapshots(
277
273
  snapshot_service_pb2.SnapshotFilter(
@@ -317,6 +313,7 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
317
313
  * If an error occurred during the snapshot saving process, the status
318
314
  will be `ERROR` and the `error_message` field will be filled.
319
315
  """
316
+ assert self._snapshot_stub is not None
320
317
  snapshot_name = request.args.get('snapshot_name', _DEFAULT_SNAPSHOT_NAME)
321
318
  snapshot_result = self._snapshot_stub.SaveSnapshot(
322
319
  snapshot_service_pb2.SnapshotPackage(snapshot_id=snapshot_name)
@@ -366,6 +363,9 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
366
363
  def _confirm_booted(self, startup_wait_time_sec: int = 300):
367
364
  """Waits until the emulator is fully booted."""
368
365
 
366
+ assert (
367
+ self._emulator_stub is not None
368
+ ), 'Emulator stub has not been initialized yet.'
369
369
  start_time = time.time()
370
370
  deadline = start_time + startup_wait_time_sec
371
371
  success = False
@@ -403,7 +403,9 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
403
403
  3 identifier: Identifies a particular finger in a multitouch event.
404
404
  """
405
405
 
406
- assert self._emulator_stub, 'Emulator stub has not been initialized yet.'
406
+ assert (
407
+ self._emulator_stub is not None
408
+ ), 'Emulator stub has not been initialized yet.'
407
409
  touch_events = [
408
410
  emulator_controller_pb2.Touch(
409
411
  x=t[0], y=t[1], pressure=int(t[2]), identifier=t[3])
@@ -421,11 +423,15 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
421
423
  See the emulator_controller_pb2 for details.
422
424
  event_type: Type of key event to be sent.
423
425
  """
426
+
424
427
  event_types = emulator_controller_pb2.KeyboardEvent.KeyEventType.keys()
425
428
  if event_type not in event_types:
426
429
  raise ValueError(
427
430
  f'Event type must be one of {event_types} but is {event_type}.')
428
431
 
432
+ assert (
433
+ self._emulator_stub is not None
434
+ ), 'Emulator stub has not been initialized yet.'
429
435
  self._emulator_stub.sendKey(
430
436
  emulator_controller_pb2.KeyboardEvent(
431
437
  codeType=emulator_controller_pb2.KeyboardEvent.KeyCodeType.XKB,
@@ -437,9 +443,12 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
437
443
  )
438
444
 
439
445
  @_reconnect_on_grpc_error
440
- def get_screenshot(self) -> np.ndarray:
446
+ def _get_screenshot_impl(self) -> np.ndarray:
441
447
  """Fetches the latest screenshot from the emulator."""
442
- assert self._emulator_stub, 'Emulator stub has not been initialized yet.'
448
+
449
+ assert (
450
+ self._emulator_stub is not None
451
+ ), 'Emulator stub has not been initialized yet.'
443
452
  assert self._image_format, 'ImageFormat has not been initialized yet.'
444
453
  image_proto = self._emulator_stub.getScreenshot(self._image_format)
445
454
  h, w = image_proto.format.height, image_proto.format.width
@@ -455,6 +464,8 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
455
464
  logging.info('Emulator (%r) is not up.', self.adb_device_name())
456
465
  return
457
466
 
467
+ assert self._launcher is not None, 'Launcher is already down.'
468
+
458
469
  logging.info('Shutting down the emulator (%r)...', self.adb_device_name())
459
470
  self._emulator_stub.setVmState(
460
471
  emulator_controller_pb2.VmRunState(
@@ -462,6 +473,8 @@ class EmulatorSimulator(base_simulator.BaseSimulator):
462
473
  self._launcher.confirm_shutdown()
463
474
 
464
475
  def close(self):
476
+ super().close()
477
+
465
478
  if self._launcher is not None:
466
479
  self._shutdown_emulator()
467
480
  logging.info('Closing emulator (%r)', self.adb_device_name())