android-env 1.2.1__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 +239 -196
- android_env/components/adb_call_parser_test.py +179 -209
- android_env/components/adb_controller.py +90 -52
- android_env/components/adb_controller_test.py +187 -16
- android_env/components/adb_log_stream.py +17 -5
- android_env/components/adb_log_stream_test.py +17 -3
- android_env/components/app_screen_checker.py +17 -15
- android_env/components/app_screen_checker_test.py +7 -8
- android_env/components/config_classes.py +203 -0
- android_env/components/coordinator.py +102 -338
- android_env/components/coordinator_test.py +59 -199
- 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 +52 -10
- android_env/components/errors_test.py +110 -0
- android_env/components/log_stream.py +7 -5
- android_env/components/log_stream_test.py +1 -1
- android_env/components/logcat_thread.py +9 -8
- android_env/components/logcat_thread_test.py +3 -4
- android_env/components/{utils.py → pixel_fns.py} +20 -20
- android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
- android_env/components/setup_step_interpreter.py +47 -39
- android_env/components/setup_step_interpreter_test.py +4 -4
- android_env/components/simulators/__init__.py +1 -1
- android_env/components/simulators/base_simulator.py +116 -44
- 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 +67 -77
- android_env/components/simulators/emulator/emulator_launcher_test.py +153 -49
- android_env/components/simulators/emulator/emulator_simulator.py +276 -95
- android_env/components/simulators/emulator/emulator_simulator_test.py +314 -89
- android_env/components/simulators/fake/__init__.py +1 -1
- android_env/components/simulators/fake/fake_simulator.py +17 -25
- android_env/components/simulators/fake/fake_simulator_test.py +29 -12
- android_env/components/specs.py +18 -28
- android_env/components/specs_test.py +1 -44
- android_env/components/task_manager.py +48 -48
- android_env/components/task_manager_test.py +71 -60
- android_env/env_interface.py +37 -23
- android_env/environment.py +83 -51
- android_env/environment_test.py +68 -29
- 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 +17 -6
- 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 +68 -63
- 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 +505 -142
- android_env/proto/snapshot.proto +169 -0
- android_env/proto/snapshot_pb2.py +47 -0
- android_env/proto/snapshot_pb2.pyi +117 -0
- android_env/proto/snapshot_pb2_grpc.py +24 -0
- android_env/proto/snapshot_service.proto +289 -0
- android_env/proto/snapshot_service_pb2.py +54 -0
- android_env/proto/snapshot_service_pb2.pyi +86 -0
- android_env/proto/snapshot_service_pb2_grpc.py +487 -0
- android_env/proto/state.proto +63 -0
- android_env/proto/state_pb2.py +63 -0
- android_env/proto/state_pb2.pyi +85 -0
- android_env/proto/state_pb2_grpc.py +24 -0
- android_env/proto/task.proto +5 -1
- android_env/proto/task_pb2.py +42 -31
- 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 +34 -13
- android_env/wrappers/base_wrapper_test.py +22 -16
- android_env/wrappers/discrete_action_wrapper.py +18 -17
- android_env/wrappers/discrete_action_wrapper_test.py +4 -4
- android_env/wrappers/flat_interface_wrapper.py +5 -5
- android_env/wrappers/flat_interface_wrapper_test.py +7 -11
- android_env/wrappers/float_pixels_wrapper.py +9 -10
- android_env/wrappers/float_pixels_wrapper_test.py +3 -3
- android_env/wrappers/gym_wrapper.py +19 -13
- android_env/wrappers/gym_wrapper_test.py +3 -5
- android_env/wrappers/image_rescale_wrapper.py +18 -21
- android_env/wrappers/image_rescale_wrapper_test.py +25 -37
- android_env/wrappers/last_action_wrapper.py +16 -13
- android_env/wrappers/last_action_wrapper_test.py +44 -51
- android_env/wrappers/rate_limit_wrapper.py +6 -3
- android_env/wrappers/rate_limit_wrapper_test.py +22 -1
- android_env/wrappers/tap_action_wrapper.py +16 -17
- android_env/wrappers/tap_action_wrapper_test.py +51 -16
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -18
- android_env-1.2.3.dist-info/RECORD +141 -0
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
- android_env/proto/raw_observation.proto +0 -39
- android_env/proto/raw_observation_pb2.py +0 -27
- android_env/proto/raw_observation_pb2_grpc.py +0 -4
- android_env-1.2.1.dist-info/RECORD +0 -81
- {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
- {android_env-1.2.1.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.
|
@@ -20,7 +20,6 @@ import re
|
|
20
20
|
import subprocess
|
21
21
|
import sys
|
22
22
|
import tempfile
|
23
|
-
from typing import List, Optional, Tuple
|
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
|
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,
|
@@ -51,7 +49,7 @@ class AdbCallParser:
|
|
51
49
|
'tap': self._tap,
|
52
50
|
'press_button': self._press_button,
|
53
51
|
'start_screen_pinning': self._start_screen_pinning,
|
54
|
-
'
|
52
|
+
'send_broadcast': self._send_broadcast,
|
55
53
|
'uninstall_package': self._handle_uninstall_package,
|
56
54
|
'get_current_activity': self._get_current_activity,
|
57
55
|
'get_orientation': self._get_orientation,
|
@@ -65,8 +63,8 @@ class AdbCallParser:
|
|
65
63
|
}
|
66
64
|
|
67
65
|
def _execute_command(
|
68
|
-
self, command_args:
|
69
|
-
|
66
|
+
self, command_args: list[str], timeout: float | None
|
67
|
+
) -> tuple[adb_pb2.AdbResponse, bytes]:
|
70
68
|
"""Executes the command, catches errors and populates the response status.
|
71
69
|
|
72
70
|
Args:
|
@@ -74,7 +72,7 @@ class AdbCallParser:
|
|
74
72
|
timeout: Timeout in seconds.
|
75
73
|
|
76
74
|
Returns:
|
77
|
-
A
|
75
|
+
A tuple of the AdbResponse with the status populated, and the output
|
78
76
|
bytes from the command.
|
79
77
|
"""
|
80
78
|
response = adb_pb2.AdbResponse(status=adb_pb2.AdbResponse.Status.OK)
|
@@ -109,12 +107,12 @@ class AdbCallParser:
|
|
109
107
|
f'Got: {request.timeout_sec}')
|
110
108
|
return response
|
111
109
|
|
112
|
-
timeout:
|
110
|
+
timeout: float | None = request.timeout_sec or None
|
113
111
|
return self._handlers[command_type](request, timeout)
|
114
112
|
|
115
|
-
def _force_stop(
|
116
|
-
|
117
|
-
|
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(
|
141
|
-
|
142
|
-
|
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
|
-
|
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,39 +216,42 @@ class AdbCallParser:
|
|
219
216
|
|
220
217
|
return response
|
221
218
|
|
222
|
-
def
|
223
|
-
|
224
|
-
|
225
|
-
"""
|
219
|
+
def _send_broadcast(
|
220
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
221
|
+
) -> adb_pb2.AdbResponse:
|
222
|
+
"""Sends a broadcast.
|
226
223
|
|
227
224
|
Args:
|
228
|
-
request: The request with information
|
225
|
+
request: The request with the information for the broadcast event.
|
229
226
|
timeout: Optional time limit in seconds.
|
230
227
|
|
231
228
|
Returns:
|
232
229
|
An AdbResponse.
|
233
230
|
"""
|
234
231
|
|
235
|
-
|
232
|
+
send_broadcast = request.send_broadcast
|
236
233
|
response = adb_pb2.AdbResponse(status=adb_pb2.AdbResponse.Status.OK)
|
237
|
-
if
|
238
|
-
not start_intent.package_name):
|
234
|
+
if not send_broadcast.action:
|
239
235
|
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
240
|
-
response.error_message = (
|
241
|
-
'`start_intent.{data_uri, action, package_name}` cannot be empty.')
|
236
|
+
response.error_message = ('`send_broadcast.{action}` cannot be empty.')
|
242
237
|
return response
|
243
238
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
239
|
+
if send_broadcast.component:
|
240
|
+
component_args = ['-n', send_broadcast.component]
|
241
|
+
else:
|
242
|
+
component_args = []
|
243
|
+
|
244
|
+
response, _ = self._execute_command(
|
245
|
+
['shell', 'am', 'broadcast', '-a', send_broadcast.action]
|
246
|
+
+ component_args,
|
247
|
+
timeout=timeout,
|
248
|
+
)
|
249
249
|
|
250
250
|
return response
|
251
251
|
|
252
|
-
def _install_apk(
|
253
|
-
|
254
|
-
|
252
|
+
def _install_apk(
|
253
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
254
|
+
) -> adb_pb2.AdbResponse:
|
255
255
|
"""Installs an app given its local path in the filesystem.
|
256
256
|
|
257
257
|
Args:
|
@@ -267,25 +267,46 @@ class AdbCallParser:
|
|
267
267
|
response = adb_pb2.AdbResponse()
|
268
268
|
location_type = install_apk.WhichOneof('location')
|
269
269
|
logging.info('location_type: %s', location_type)
|
270
|
-
if location_type != 'filesystem':
|
271
|
-
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
272
|
-
response.error_message = (
|
273
|
-
f'Unsupported `install_apk.location` type: {location_type}')
|
274
|
-
return response
|
275
270
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
281
304
|
|
282
|
-
response, _ = self._execute_command(['install', '-r', '-t', '-g', fpath],
|
283
|
-
timeout=timeout)
|
284
305
|
return response
|
285
306
|
|
286
|
-
def _start_activity(
|
287
|
-
|
288
|
-
|
307
|
+
def _start_activity(
|
308
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
309
|
+
) -> adb_pb2.AdbResponse:
|
289
310
|
"""Starts a given activity.
|
290
311
|
|
291
312
|
Options for `start_activity`:
|
@@ -331,9 +352,9 @@ class AdbCallParser:
|
|
331
352
|
response.start_activity.output = command_output
|
332
353
|
return response
|
333
354
|
|
334
|
-
def _press_button(
|
335
|
-
|
336
|
-
|
355
|
+
def _press_button(
|
356
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
357
|
+
) -> adb_pb2.AdbResponse:
|
337
358
|
"""Presses a keyboard key.
|
338
359
|
|
339
360
|
Args:
|
@@ -359,9 +380,8 @@ class AdbCallParser:
|
|
359
380
|
return response
|
360
381
|
|
361
382
|
def _handle_uninstall_package(
|
362
|
-
self,
|
363
|
-
|
364
|
-
timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
|
383
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
384
|
+
) -> adb_pb2.AdbResponse:
|
365
385
|
"""Handles UninstallPackage messages.
|
366
386
|
|
367
387
|
Args:
|
@@ -399,9 +419,8 @@ class AdbCallParser:
|
|
399
419
|
return response
|
400
420
|
|
401
421
|
def _get_current_activity(
|
402
|
-
self,
|
403
|
-
|
404
|
-
timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
|
422
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
423
|
+
) -> adb_pb2.AdbResponse:
|
405
424
|
"""Fetches current activity.
|
406
425
|
|
407
426
|
Args:
|
@@ -453,9 +472,9 @@ class AdbCallParser:
|
|
453
472
|
response.get_current_activity.full_activity = matches.group(1)
|
454
473
|
return response
|
455
474
|
|
456
|
-
def _get_orientation(
|
457
|
-
|
458
|
-
|
475
|
+
def _get_orientation(
|
476
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
477
|
+
) -> adb_pb2.AdbResponse:
|
459
478
|
"""Fetches current device orientation.
|
460
479
|
|
461
480
|
Args:
|
@@ -491,23 +510,30 @@ class AdbCallParser:
|
|
491
510
|
if physical_width:
|
492
511
|
skip_next = int(physical_width.group(1)) < 0
|
493
512
|
|
494
|
-
surface_orientation = re.match(
|
513
|
+
surface_orientation = re.match(
|
514
|
+
r'\s+(SurfaceOrientation|InputDeviceOrientation):\s+(\d)', line
|
515
|
+
)
|
516
|
+
|
495
517
|
if surface_orientation is not None:
|
496
518
|
if skip_next:
|
497
519
|
continue
|
498
|
-
|
520
|
+
if surface_orientation.re.groups < 2:
|
521
|
+
continue
|
522
|
+
orientation = surface_orientation.group(2)
|
499
523
|
logging.info('Done getting orientation: %r', orientation)
|
500
524
|
response.get_orientation.orientation = int(orientation)
|
501
525
|
return response
|
502
526
|
|
503
527
|
response.status = adb_pb2.AdbResponse.Status.INTERNAL_ERROR
|
504
|
-
response.error_message = (
|
505
|
-
|
528
|
+
response.error_message = (
|
529
|
+
'Could not find SurfaceOrientation/InputDeviceOrientation in dumpsys '
|
530
|
+
'output'
|
531
|
+
)
|
506
532
|
return response
|
507
533
|
|
508
|
-
def _push(
|
509
|
-
|
510
|
-
|
534
|
+
def _push(
|
535
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
536
|
+
) -> adb_pb2.AdbResponse:
|
511
537
|
"""Uploads contents to the device.
|
512
538
|
|
513
539
|
Args:
|
@@ -525,7 +551,7 @@ class AdbCallParser:
|
|
525
551
|
error_message='Push.path is empty.')
|
526
552
|
|
527
553
|
# Create temporary file with `push` contents.
|
528
|
-
with tempfile.NamedTemporaryFile(
|
554
|
+
with tempfile.NamedTemporaryFile(delete=False) as f:
|
529
555
|
fname = f.name
|
530
556
|
f.write(request.push.content)
|
531
557
|
# Issue `adb push` command to upload file.
|
@@ -536,9 +562,9 @@ class AdbCallParser:
|
|
536
562
|
|
537
563
|
return response
|
538
564
|
|
539
|
-
def _pull(
|
540
|
-
|
541
|
-
|
565
|
+
def _pull(
|
566
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
567
|
+
) -> adb_pb2.AdbResponse:
|
542
568
|
"""Downloads file content from the device.
|
543
569
|
|
544
570
|
Args:
|
@@ -556,7 +582,7 @@ class AdbCallParser:
|
|
556
582
|
error_message='Pull.path is empty.')
|
557
583
|
|
558
584
|
# Issue `adb pull` command to copy it to a temporary file.
|
559
|
-
with tempfile.NamedTemporaryFile(
|
585
|
+
with tempfile.NamedTemporaryFile(delete=False) as f:
|
560
586
|
fname = f.name
|
561
587
|
logging.info('Downloading %r to %r.', path, fname)
|
562
588
|
response, _ = self._execute_command(['pull', path, fname],
|
@@ -569,9 +595,9 @@ class AdbCallParser:
|
|
569
595
|
|
570
596
|
return response
|
571
597
|
|
572
|
-
def _input_text(
|
573
|
-
|
574
|
-
|
598
|
+
def _input_text(
|
599
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
600
|
+
) -> adb_pb2.AdbResponse:
|
575
601
|
"""Inserts text as keyboard events.
|
576
602
|
|
577
603
|
Args:
|
@@ -592,9 +618,9 @@ class AdbCallParser:
|
|
592
618
|
timeout=timeout)
|
593
619
|
return response
|
594
620
|
|
595
|
-
def _tap(
|
596
|
-
|
597
|
-
|
621
|
+
def _tap(
|
622
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
623
|
+
) -> adb_pb2.AdbResponse:
|
598
624
|
"""Taps the device screen.
|
599
625
|
|
600
626
|
Args:
|
@@ -622,9 +648,9 @@ class AdbCallParser:
|
|
622
648
|
|
623
649
|
return response
|
624
650
|
|
625
|
-
def _handle_settings(
|
626
|
-
|
627
|
-
|
651
|
+
def _handle_settings(
|
652
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
653
|
+
) -> adb_pb2.AdbResponse:
|
628
654
|
"""Handles SettingsRequest messages.
|
629
655
|
|
630
656
|
Args:
|
@@ -647,70 +673,84 @@ class AdbCallParser:
|
|
647
673
|
namespace = adb_pb2.AdbRequest.SettingsRequest.Namespace.Name(
|
648
674
|
request.name_space).lower()
|
649
675
|
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
response.
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
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 _:
|
688
744
|
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
689
745
|
response.error_message = (
|
690
|
-
'
|
691
|
-
|
692
|
-
return response
|
693
|
-
|
694
|
-
mode = adb_pb2.AdbRequest.SettingsRequest.Reset.Mode.Name(
|
695
|
-
reset.mode).lower()
|
696
|
-
arg = reset.package_name or mode
|
697
|
-
response, command_output = self._execute_command(
|
698
|
-
['shell', 'settings', 'reset', namespace, arg], timeout=timeout)
|
699
|
-
response.settings.output = command_output
|
700
|
-
elif verb == 'list':
|
701
|
-
response, command_output = self._execute_command(
|
702
|
-
['shell', 'settings', 'list', namespace], timeout=timeout)
|
703
|
-
response.settings.output = command_output
|
704
|
-
else:
|
705
|
-
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
706
|
-
response.error_message = (
|
707
|
-
f'Unknown SettingsRequest.verb. Got: {request}.')
|
746
|
+
f'Unknown SettingsRequest.verb. Got: {request}.'
|
747
|
+
)
|
708
748
|
|
709
749
|
return response
|
710
750
|
|
711
|
-
def _handle_generic(
|
712
|
-
|
713
|
-
|
751
|
+
def _handle_generic(
|
752
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
753
|
+
) -> adb_pb2.AdbResponse:
|
714
754
|
"""Handles GenericRequest messages.
|
715
755
|
|
716
756
|
Args:
|
@@ -728,9 +768,8 @@ class AdbCallParser:
|
|
728
768
|
return response
|
729
769
|
|
730
770
|
def _handle_package_manager(
|
731
|
-
self,
|
732
|
-
|
733
|
-
timeout: Optional[float] = None) -> adb_pb2.AdbResponse:
|
771
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
772
|
+
) -> adb_pb2.AdbResponse:
|
734
773
|
"""Handles PackageManagerRequest messages.
|
735
774
|
|
736
775
|
Args:
|
@@ -745,60 +784,64 @@ class AdbCallParser:
|
|
745
784
|
request = request.package_manager
|
746
785
|
response = adb_pb2.AdbResponse()
|
747
786
|
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
args
|
776
|
-
|
777
|
-
|
778
|
-
args,
|
779
|
-
elif verb == 'grant':
|
780
|
-
grant = request.grant
|
781
|
-
if not grant.package_name:
|
782
|
-
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
783
|
-
response.error_message = ('`grant.package_name` cannot be empty.')
|
784
|
-
return response
|
785
|
-
|
786
|
-
if not grant.permissions:
|
787
|
-
response.status = adb_pb2.AdbResponse.Status.FAILED_PRECONDITION
|
788
|
-
response.error_message = ('`grant.permissions` cannot be empty.')
|
789
|
-
return response
|
790
|
-
|
791
|
-
for permission in grant.permissions:
|
792
|
-
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)
|
793
818
|
response, response.package_manager.output = self._execute_command(
|
794
|
-
|
795
|
-
|
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
|
+
)
|
796
839
|
|
797
840
|
return response
|
798
841
|
|
799
|
-
def _handle_dumpsys(
|
800
|
-
|
801
|
-
|
842
|
+
def _handle_dumpsys(
|
843
|
+
self, request: adb_pb2.AdbRequest, timeout: float | None = None
|
844
|
+
) -> adb_pb2.AdbResponse:
|
802
845
|
"""Handles DumpsysRequest messages.
|
803
846
|
|
804
847
|
Args:
|