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.
@@ -20,7 +20,6 @@ import re
20
20
  import subprocess
21
21
  import sys
22
22
  import tempfile
23
- from typing import Optional
24
23
 
25
24
  from absl import logging
26
25
  from android_env.components import adb_controller as adb_control
@@ -41,9 +40,8 @@ _BUTTON_TO_KEYCODE = {
41
40
  class AdbCallParser:
42
41
  """Parses AdbRequest messages and executes corresponding adb commands."""
43
42
 
44
- def __init__(self, adb_controller: adb_control.AdbController, tmp_dir: str):
43
+ def __init__(self, adb_controller: adb_control.AdbController):
45
44
  self._adb_controller = adb_controller
46
- self._tmp_dir = tmp_dir
47
45
  self._handlers = {
48
46
  'install_apk': self._install_apk,
49
47
  'start_activity': self._start_activity,
@@ -65,7 +63,7 @@ class AdbCallParser:
65
63
  }
66
64
 
67
65
  def _execute_command(
68
- self, command_args: list[str], timeout: Optional[float]
66
+ self, command_args: list[str], timeout: float | None
69
67
  ) -> tuple[adb_pb2.AdbResponse, bytes]:
70
68
  """Executes the command, catches errors and populates the response status.
71
69
 
@@ -109,12 +107,12 @@ class AdbCallParser:
109
107
  f'Got: {request.timeout_sec}')
110
108
  return response
111
109
 
112
- timeout: Optional[float] = request.timeout_sec or None
110
+ timeout: float | None = request.timeout_sec or None
113
111
  return self._handlers[command_type](request, timeout)
114
112
 
115
- def _force_stop(self,
116
- request: adb_pb2.AdbRequest,
117
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
113
+ def _force_stop(
114
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
115
+ ) -> adb_pb2.AdbResponse:
118
116
  """Stops an application.
119
117
 
120
118
  Args:
@@ -137,9 +135,9 @@ class AdbCallParser:
137
135
 
138
136
  return response
139
137
 
140
- def _fetch_current_task_id(self,
141
- full_activity_name: str,
142
- timeout: Optional[float] = None) -> int:
138
+ def _fetch_current_task_id(
139
+ self, full_activity_name: str, timeout: float | None = None
140
+ ) -> int:
143
141
  """Returns the task ID of the given `full_activity_name`.
144
142
 
145
143
  Args:
@@ -185,9 +183,8 @@ class AdbCallParser:
185
183
  return -1
186
184
 
187
185
  def _start_screen_pinning(
188
- self,
189
- request: adb_pb2.AdbRequest,
190
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
186
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
187
+ ) -> adb_pb2.AdbResponse:
191
188
  """Pins an application.
192
189
 
193
190
  Args:
@@ -219,9 +216,9 @@ class AdbCallParser:
219
216
 
220
217
  return response
221
218
 
222
- def _send_broadcast(self,
223
- request: adb_pb2.AdbRequest,
224
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
219
+ def _send_broadcast(
220
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
221
+ ) -> adb_pb2.AdbResponse:
225
222
  """Sends a broadcast.
226
223
 
227
224
  Args:
@@ -232,21 +229,29 @@ class AdbCallParser:
232
229
  An AdbResponse.
233
230
  """
234
231
 
235
- send_brodcast = request.send_broadcast
232
+ send_broadcast = request.send_broadcast
236
233
  response = adb_pb2.AdbResponse(status=adb_pb2.AdbResponse.Status.OK)
237
- if not send_brodcast.action:
234
+ if not send_broadcast.action:
238
235
  response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
239
236
  response.error_message = ('`send_broadcast.{action}` cannot be empty.')
240
237
  return response
241
238
 
239
+ if send_broadcast.component:
240
+ component_args = ['-n', send_broadcast.component]
241
+ else:
242
+ component_args = []
243
+
242
244
  response, _ = self._execute_command(
243
- ['shell', 'am', 'broadcast', '-a', send_brodcast.action],
244
- timeout=timeout)
245
+ ['shell', 'am', 'broadcast', '-a', send_broadcast.action]
246
+ + component_args,
247
+ timeout=timeout,
248
+ )
249
+
245
250
  return response
246
251
 
247
- def _install_apk(self,
248
- request: adb_pb2.AdbRequest,
249
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
252
+ def _install_apk(
253
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
254
+ ) -> adb_pb2.AdbResponse:
250
255
  """Installs an app given its local path in the filesystem.
251
256
 
252
257
  Args:
@@ -262,25 +267,46 @@ class AdbCallParser:
262
267
  response = adb_pb2.AdbResponse()
263
268
  location_type = install_apk.WhichOneof('location')
264
269
  logging.info('location_type: %s', location_type)
265
- if location_type != 'filesystem':
266
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
267
- response.error_message = (
268
- f'Unsupported `install_apk.location` type: {location_type}')
269
- return response
270
270
 
271
- fpath = install_apk.filesystem.path
272
- if not os.path.exists(fpath):
273
- response.status = adb_pb2.AdbResponse.Status.INTERNAL_ERROR
274
- response.error_message = f'Could not find local_apk_path: {fpath}'
275
- return response
271
+ match location_type:
272
+ case 'filesystem':
273
+ fpath = install_apk.filesystem.path
274
+ if not os.path.exists(fpath):
275
+ response.status = adb_pb2.AdbResponse.Status.INTERNAL_ERROR
276
+ response.error_message = f'Could not find local_apk_path: {fpath}'
277
+ return response
278
+
279
+ response, _ = self._execute_command(
280
+ ['install', '-r', '-t', '-g', fpath], timeout=timeout
281
+ )
282
+ case 'blob':
283
+
284
+ # `delete_on_close` was only added in Python 3.12 so we add a switch
285
+ # here to still support previous Python versions.
286
+ if sys.version_info >= (3, 12):
287
+ kwargs = {'suffix': '.apk', 'delete_on_close': False}
288
+ else:
289
+ kwargs = {'suffix': '.apk'}
290
+
291
+ with tempfile.NamedTemporaryFile(**kwargs) as f:
292
+ fpath = f.name
293
+ f.write(install_apk.blob.contents)
294
+
295
+ response, _ = self._execute_command(
296
+ ['install', '-r', '-t', '-g', fpath], timeout=timeout
297
+ )
298
+ case _:
299
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
300
+ response.error_message = (
301
+ f'Unsupported `install_apk.location` type: {location_type}'
302
+ )
303
+ return response
276
304
 
277
- response, _ = self._execute_command(['install', '-r', '-t', '-g', fpath],
278
- timeout=timeout)
279
305
  return response
280
306
 
281
- def _start_activity(self,
282
- request: adb_pb2.AdbRequest,
283
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
307
+ def _start_activity(
308
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
309
+ ) -> adb_pb2.AdbResponse:
284
310
  """Starts a given activity.
285
311
 
286
312
  Options for `start_activity`:
@@ -326,9 +352,9 @@ class AdbCallParser:
326
352
  response.start_activity.output = command_output
327
353
  return response
328
354
 
329
- def _press_button(self,
330
- request: adb_pb2.AdbRequest,
331
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
355
+ def _press_button(
356
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
357
+ ) -> adb_pb2.AdbResponse:
332
358
  """Presses a keyboard key.
333
359
 
334
360
  Args:
@@ -354,9 +380,8 @@ class AdbCallParser:
354
380
  return response
355
381
 
356
382
  def _handle_uninstall_package(
357
- self,
358
- request: adb_pb2.AdbRequest,
359
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
383
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
384
+ ) -> adb_pb2.AdbResponse:
360
385
  """Handles UninstallPackage messages.
361
386
 
362
387
  Args:
@@ -394,9 +419,8 @@ class AdbCallParser:
394
419
  return response
395
420
 
396
421
  def _get_current_activity(
397
- self,
398
- request: adb_pb2.AdbRequest,
399
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
422
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
423
+ ) -> adb_pb2.AdbResponse:
400
424
  """Fetches current activity.
401
425
 
402
426
  Args:
@@ -448,9 +472,9 @@ class AdbCallParser:
448
472
  response.get_current_activity.full_activity = matches.group(1)
449
473
  return response
450
474
 
451
- def _get_orientation(self,
452
- request: adb_pb2.AdbRequest,
453
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
475
+ def _get_orientation(
476
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
477
+ ) -> adb_pb2.AdbResponse:
454
478
  """Fetches current device orientation.
455
479
 
456
480
  Args:
@@ -486,23 +510,30 @@ class AdbCallParser:
486
510
  if physical_width:
487
511
  skip_next = int(physical_width.group(1)) < 0
488
512
 
489
- surface_orientation = re.match(r'\s+SurfaceOrientation:\s+(\d)', line)
513
+ surface_orientation = re.match(
514
+ r'\s+(SurfaceOrientation|InputDeviceOrientation):\s+(\d)', line
515
+ )
516
+
490
517
  if surface_orientation is not None:
491
518
  if skip_next:
492
519
  continue
493
- orientation = surface_orientation.group(1)
520
+ if surface_orientation.re.groups < 2:
521
+ continue
522
+ orientation = surface_orientation.group(2)
494
523
  logging.info('Done getting orientation: %r', orientation)
495
524
  response.get_orientation.orientation = int(orientation)
496
525
  return response
497
526
 
498
527
  response.status = adb_pb2.AdbResponse.Status.INTERNAL_ERROR
499
- response.error_message = ('Could not find SurfaceOrientation in dumpsys '
500
- 'output')
528
+ response.error_message = (
529
+ 'Could not find SurfaceOrientation/InputDeviceOrientation in dumpsys '
530
+ 'output'
531
+ )
501
532
  return response
502
533
 
503
- def _push(self,
504
- request: adb_pb2.AdbRequest,
505
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
534
+ def _push(
535
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
536
+ ) -> adb_pb2.AdbResponse:
506
537
  """Uploads contents to the device.
507
538
 
508
539
  Args:
@@ -520,7 +551,7 @@ class AdbCallParser:
520
551
  error_message='Push.path is empty.')
521
552
 
522
553
  # Create temporary file with `push` contents.
523
- with tempfile.NamedTemporaryFile(dir=self._tmp_dir, delete=False) as f:
554
+ with tempfile.NamedTemporaryFile(delete=False) as f:
524
555
  fname = f.name
525
556
  f.write(request.push.content)
526
557
  # Issue `adb push` command to upload file.
@@ -531,9 +562,9 @@ class AdbCallParser:
531
562
 
532
563
  return response
533
564
 
534
- def _pull(self,
535
- request: adb_pb2.AdbRequest,
536
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
565
+ def _pull(
566
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
567
+ ) -> adb_pb2.AdbResponse:
537
568
  """Downloads file content from the device.
538
569
 
539
570
  Args:
@@ -551,7 +582,7 @@ class AdbCallParser:
551
582
  error_message='Pull.path is empty.')
552
583
 
553
584
  # Issue `adb pull` command to copy it to a temporary file.
554
- with tempfile.NamedTemporaryFile(dir=self._tmp_dir, delete=False) as f:
585
+ with tempfile.NamedTemporaryFile(delete=False) as f:
555
586
  fname = f.name
556
587
  logging.info('Downloading %r to %r.', path, fname)
557
588
  response, _ = self._execute_command(['pull', path, fname],
@@ -564,9 +595,9 @@ class AdbCallParser:
564
595
 
565
596
  return response
566
597
 
567
- def _input_text(self,
568
- request: adb_pb2.AdbRequest,
569
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
598
+ def _input_text(
599
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
600
+ ) -> adb_pb2.AdbResponse:
570
601
  """Inserts text as keyboard events.
571
602
 
572
603
  Args:
@@ -587,9 +618,9 @@ class AdbCallParser:
587
618
  timeout=timeout)
588
619
  return response
589
620
 
590
- def _tap(self,
591
- request: adb_pb2.AdbRequest,
592
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
621
+ def _tap(
622
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
623
+ ) -> adb_pb2.AdbResponse:
593
624
  """Taps the device screen.
594
625
 
595
626
  Args:
@@ -617,9 +648,9 @@ class AdbCallParser:
617
648
 
618
649
  return response
619
650
 
620
- def _handle_settings(self,
621
- request: adb_pb2.AdbRequest,
622
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
651
+ def _handle_settings(
652
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
653
+ ) -> adb_pb2.AdbResponse:
623
654
  """Handles SettingsRequest messages.
624
655
 
625
656
  Args:
@@ -642,70 +673,84 @@ class AdbCallParser:
642
673
  namespace = adb_pb2.AdbRequest.SettingsRequest.Namespace.Name(
643
674
  request.name_space).lower()
644
675
 
645
- verb = request.WhichOneof('verb')
646
- if verb == 'get':
647
- get = request.get
648
- if not get.key:
649
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
650
- response.error_message = (
651
- f'Empty SettingsRequest.get.key. Got: {request}.')
652
- return response
653
- response, command_output = self._execute_command(
654
- ['shell', 'settings', 'get', namespace, get.key], timeout=timeout)
655
- response.settings.output = command_output
656
- elif verb == 'put':
657
- put = request.put
658
- if not put.key or not put.value:
659
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
660
- response.error_message = (
661
- f'Empty SettingsRequest.put key or value. Got: {request}.')
662
- return response
663
- response, command_output = self._execute_command(
664
- ['shell', 'settings', 'put', namespace, put.key, put.value],
665
- timeout=timeout)
666
- response.settings.output = command_output
667
- elif verb == 'delete_key':
668
- delete = request.delete_key
669
- if not delete.key:
676
+ match request.WhichOneof('verb'):
677
+ case 'get':
678
+ get = request.get
679
+ if not get.key:
680
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
681
+ response.error_message = (
682
+ f'Empty SettingsRequest.get.key. Got: {request}.'
683
+ )
684
+ return response
685
+ response, command_output = self._execute_command(
686
+ ['shell', 'settings', 'get', namespace, get.key], timeout=timeout
687
+ )
688
+ response.settings.output = command_output
689
+ case 'put':
690
+ put = request.put
691
+ if not put.key or not put.value:
692
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
693
+ response.error_message = (
694
+ f'Empty SettingsRequest.put key or value. Got: {request}.'
695
+ )
696
+ return response
697
+ response, command_output = self._execute_command(
698
+ ['shell', 'settings', 'put', namespace, put.key, put.value],
699
+ timeout=timeout,
700
+ )
701
+ response.settings.output = command_output
702
+ case 'delete_key':
703
+ delete = request.delete_key
704
+ if not delete.key:
705
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
706
+ response.error_message = (
707
+ f'Empty SettingsRequest.delete_key.key. Got: {request}.'
708
+ )
709
+ return response
710
+ response, command_output = self._execute_command(
711
+ ['shell', 'settings', 'delete', namespace, delete.key],
712
+ timeout=timeout,
713
+ )
714
+ response.settings.output = command_output
715
+ case 'reset':
716
+ reset = request.reset
717
+ # At least one of `package_name` or `mode` should be given.
718
+ if (
719
+ not reset.package_name
720
+ and reset.mode
721
+ == adb_pb2.AdbRequest.SettingsRequest.Reset.Mode.UNKNOWN
722
+ ):
723
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
724
+ response.error_message = (
725
+ 'At least one of SettingsRequest.reset package_name or mode'
726
+ f' should be given. Got: {request}.'
727
+ )
728
+ return response
729
+
730
+ mode = adb_pb2.AdbRequest.SettingsRequest.Reset.Mode.Name(
731
+ reset.mode
732
+ ).lower()
733
+ arg = reset.package_name or mode
734
+ response, command_output = self._execute_command(
735
+ ['shell', 'settings', 'reset', namespace, arg], timeout=timeout
736
+ )
737
+ response.settings.output = command_output
738
+ case 'list':
739
+ response, command_output = self._execute_command(
740
+ ['shell', 'settings', 'list', namespace], timeout=timeout
741
+ )
742
+ response.settings.output = command_output
743
+ case _:
670
744
  response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
671
745
  response.error_message = (
672
- f'Empty SettingsRequest.delete_key.key. Got: {request}.')
673
- return response
674
- response, command_output = self._execute_command(
675
- ['shell', 'settings', 'delete', namespace, delete.key],
676
- timeout=timeout)
677
- response.settings.output = command_output
678
- elif verb == 'reset':
679
- reset = request.reset
680
- # At least one of `package_name` or `mode` should be given.
681
- if (not reset.package_name and
682
- reset.mode == adb_pb2.AdbRequest.SettingsRequest.Reset.Mode.UNKNOWN):
683
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
684
- response.error_message = (
685
- 'At least one of SettingsRequest.reset package_name or mode should '
686
- f'be given. Got: {request}.')
687
- return response
688
-
689
- mode = adb_pb2.AdbRequest.SettingsRequest.Reset.Mode.Name(
690
- reset.mode).lower()
691
- arg = reset.package_name or mode
692
- response, command_output = self._execute_command(
693
- ['shell', 'settings', 'reset', namespace, arg], timeout=timeout)
694
- response.settings.output = command_output
695
- elif verb == 'list':
696
- response, command_output = self._execute_command(
697
- ['shell', 'settings', 'list', namespace], timeout=timeout)
698
- response.settings.output = command_output
699
- else:
700
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
701
- response.error_message = (
702
- f'Unknown SettingsRequest.verb. Got: {request}.')
746
+ f'Unknown SettingsRequest.verb. Got: {request}.'
747
+ )
703
748
 
704
749
  return response
705
750
 
706
- def _handle_generic(self,
707
- request: adb_pb2.AdbRequest,
708
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
751
+ def _handle_generic(
752
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
753
+ ) -> adb_pb2.AdbResponse:
709
754
  """Handles GenericRequest messages.
710
755
 
711
756
  Args:
@@ -723,9 +768,8 @@ class AdbCallParser:
723
768
  return response
724
769
 
725
770
  def _handle_package_manager(
726
- self,
727
- request: adb_pb2.AdbRequest,
728
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
771
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
772
+ ) -> adb_pb2.AdbResponse:
729
773
  """Handles PackageManagerRequest messages.
730
774
 
731
775
  Args:
@@ -740,60 +784,64 @@ class AdbCallParser:
740
784
  request = request.package_manager
741
785
  response = adb_pb2.AdbResponse()
742
786
 
743
- verb = request.WhichOneof('verb')
744
- if verb == 'list':
745
- what = request.list.WhichOneof('what')
746
- response, output = self._execute_command(['shell', 'pm', 'list', what],
747
- timeout=timeout)
748
-
749
- if output:
750
- items = output.decode('utf-8').split()
751
- # Remove prefix for each item.
752
- prefix = {
753
- 'features': 'feature:',
754
- 'libraries': 'library:',
755
- 'packages': 'package:',
756
- }[what]
757
- items = [x[len(prefix):] for x in items if x.startswith(prefix)]
758
- response.package_manager.list.items.extend(items)
759
- response.package_manager.output = output
760
- elif verb == 'clear':
761
- package_name = request.clear.package_name
762
- if not package_name:
763
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
764
- response.error_message = (
765
- f'Empty PackageManagerRequest.clear.package_name. Got: {request}.')
766
- return response
767
-
768
- args = ['shell', 'pm', 'clear', package_name]
769
- if request.clear.user_id:
770
- args.insert(3, '-f')
771
- args.insert(4, request.clear.user_id)
772
- response, response.package_manager.output = self._execute_command(
773
- args, timeout=timeout)
774
- elif verb == 'grant':
775
- grant = request.grant
776
- if not grant.package_name:
777
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
778
- response.error_message = ('`grant.package_name` cannot be empty.')
779
- return response
780
-
781
- if not grant.permissions:
782
- response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
783
- response.error_message = ('`grant.permissions` cannot be empty.')
784
- return response
785
-
786
- for permission in grant.permissions:
787
- logging.info('Granting permission: %r', permission)
787
+ match request.WhichOneof('verb'):
788
+ case 'list':
789
+ what = request.list.WhichOneof('what')
790
+ response, output = self._execute_command(
791
+ ['shell', 'pm', 'list', what], timeout=timeout
792
+ )
793
+
794
+ if output:
795
+ items = output.decode('utf-8').split()
796
+ # Remove prefix for each item.
797
+ prefix = {
798
+ 'features': 'feature:',
799
+ 'libraries': 'library:',
800
+ 'packages': 'package:',
801
+ }[what]
802
+ items = [x[len(prefix) :] for x in items if x.startswith(prefix)]
803
+ response.package_manager.list.items.extend(items)
804
+ response.package_manager.output = output
805
+ case 'clear':
806
+ package_name = request.clear.package_name
807
+ if not package_name:
808
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
809
+ response.error_message = (
810
+ f'Empty PackageManagerRequest.clear.package_name. Got: {request}.'
811
+ )
812
+ return response
813
+
814
+ args = ['shell', 'pm', 'clear', package_name]
815
+ if request.clear.user_id:
816
+ args.insert(3, '-f')
817
+ args.insert(4, request.clear.user_id)
788
818
  response, response.package_manager.output = self._execute_command(
789
- ['shell', 'pm', 'grant', grant.package_name, permission],
790
- timeout=timeout)
819
+ args, timeout=timeout
820
+ )
821
+ case 'grant':
822
+ grant = request.grant
823
+ if not grant.package_name:
824
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
825
+ response.error_message = '`grant.package_name` cannot be empty.'
826
+ return response
827
+
828
+ if not grant.permissions:
829
+ response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
830
+ response.error_message = '`grant.permissions` cannot be empty.'
831
+ return response
832
+
833
+ for permission in grant.permissions:
834
+ logging.info('Granting permission: %r', permission)
835
+ response, response.package_manager.output = self._execute_command(
836
+ ['shell', 'pm', 'grant', grant.package_name, permission],
837
+ timeout=timeout,
838
+ )
791
839
 
792
840
  return response
793
841
 
794
- def _handle_dumpsys(self,
795
- request: adb_pb2.AdbRequest,
796
- timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
842
+ def _handle_dumpsys(
843
+ self, request: adb_pb2.AdbRequest, timeout: float | None = None
844
+ ) -> adb_pb2.AdbResponse:
797
845
  """Handles DumpsysRequest messages.
798
846
 
799
847
  Args: