diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 00000000..19915887 --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +android_env/apps diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000..ba7f754d --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +7.4.0 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5bb5509f..e4650170 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: TEST_TMPDIR: '/tmp' strategy: matrix: - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@v6 @@ -27,13 +27,20 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install dependencies + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.19.0 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + + - name: Configure MODULE.bazel python version run: | - pip install --upgrade pip setuptools - pip install .[testing] + MATRIX_VER="${{ matrix.python-version }}" + VER_UNDERSCORE="${MATRIX_VER//./_}" + sed -i "s/python_version = \"3.11\"/python_version = \"$MATRIX_VER\"/g" MODULE.bazel + sed -i "s/python_3_11/python_$VER_UNDERSCORE/g" MODULE.bazel - name: Run tests run: | - # Find all test files, print their names and execute them in parallel - # with a maximum of 20 proccesses. - find . -type f -name "*_test.py" -print0 | xargs -t -0 -n1 -P 20 python3 + bazel test //... diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 00000000..73cccce9 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1 @@ +exports_files(["requirements.txt"]) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000..8abd1918 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,26 @@ +module( + name = "android_env", + version = "1.3.0", +) + +# Core rules +bazel_dep(name = "rules_python", version = "0.34.0") +bazel_dep(name = "rules_proto_grpc_python", version = "5.0.0") +bazel_dep(name = "protobuf", version = "27.2") +bazel_dep(name = "rules_proto", version = "6.0.2") + +# Hermetic Python Toolchain +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + python_version = "3.11", +) +use_repo(python, "python_3_11") + +# Pip dependencies +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + hub_name = "pip", + python_version = "3.11", + requirements_lock = "//:requirements.txt", +) +use_repo(pip, "pip") diff --git a/android_env/BUILD.bazel b/android_env/BUILD.bazel new file mode 100644 index 00000000..9aefb593 --- /dev/null +++ b/android_env/BUILD.bazel @@ -0,0 +1,127 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") +load("@rules_python//python:packaging.bzl", "py_package", "py_wheel") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "env_interface", + srcs = ["env_interface.py"], + deps = [ + "//android_env/proto:adb_py_pb2", + "//android_env/proto:state_py_pb2", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "environment", + srcs = ["environment.py"], + deps = [ + ":env_interface", + "//android_env/components:adb_call_parser", + "//android_env/components:coordinator", + "//android_env/components:task_manager", + "//android_env/components/simulators:base_simulator", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "environment_test", + srcs = ["environment_test.py"], + deps = [ + ":environment", + "//android_env/components:config_classes", + "//android_env/components:coordinator", + "//android_env/components:task_manager", + "//android_env/components/simulators:base_simulator", + "//android_env/components/simulators/fake:fake_simulator", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "loader", + srcs = ["loader.py"], + deps = [ + ":environment", + "//android_env/components:config_classes", + "//android_env/components:coordinator", + "//android_env/components:device_settings", + "//android_env/components:task_manager", + "//android_env/components/simulators/emulator:emulator_simulator", + "//android_env/components/simulators/emulator:tcp_ports", + "//android_env/components/simulators/fake:fake_simulator", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//protobuf", + ], +) + +py_test( + name = "loader_test", + srcs = ["loader_test.py"], + deps = [ + ":env_interface", + ":loader", + "//android_env/components:config_classes", + "//android_env/components:coordinator", + "//android_env/components:device_settings", + "//android_env/components:task_manager", + "//android_env/components/simulators/emulator:emulator_simulator", + "//android_env/components/simulators/emulator:tcp_ports", + "//android_env/components/simulators/fake:fake_simulator", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + ], +) + +# Packaging rules for PyPI release + +py_package( + name = "android_env_pkg", + packages = ["android_env"], + deps = [ + ":loader", + "//android_env/wrappers:a11y_grpc_wrapper", + "//android_env/wrappers:discrete_action_wrapper", + "//android_env/wrappers:flat_interface_wrapper", + "//android_env/wrappers:float_pixels_wrapper", + "//android_env/wrappers:gym_wrapper", + "//android_env/wrappers:image_rescale_wrapper", + "//android_env/wrappers:last_action_wrapper", + "//android_env/wrappers:rate_limit_wrapper", + "//android_env/wrappers:swipe_action_wrapper", + "//android_env/wrappers:tap_action_wrapper", + ], +) + +py_wheel( + name = "android_env_wheel", + author = "DeepMind", + distribution = "android_env", + license = "Apache License, Version 2.0", + requires = [ + "absl-py>=0.1.0", + "dm-env>=1.6", + "numpy>=1.26.0", + "portpicker>=1.6.0", + "pygame>=2.6.0", + "pillow>=10.4.0", + "gymnasium>=0.29.1", + "protobuf>=5.27.2", + "grpcio>=1.64.2", + ], + summary = "AndroidEnv: A Gym/Gymnasium Environment for Android", + version = "1.3.0", + deps = [":android_env_pkg"], +) diff --git a/android_env/components/BUILD.bazel b/android_env/components/BUILD.bazel new file mode 100644 index 00000000..cf890e7d --- /dev/null +++ b/android_env/components/BUILD.bazel @@ -0,0 +1,373 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "action_fns", + srcs = ["action_fns.py"], + deps = [ + ":action_type", + ":errors", + ":pixel_fns", + "//android_env/components/simulators:base_simulator", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_test( + name = "action_fns_test", + srcs = ["action_fns_test.py"], + deps = [ + ":action_fns", + ":action_type", + ":errors", + ":pixel_fns", + "//android_env/components/simulators:base_simulator", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_library( + name = "action_type", + srcs = ["action_type.py"], +) + +py_library( + name = "adb_call_parser", + srcs = ["adb_call_parser.py"], + deps = [ + ":adb_controller", + "//android_env/proto:adb_py_pb2", + "@pip//absl_py", + ], +) + +py_test( + name = "adb_call_parser_test", + srcs = ["adb_call_parser_test.py"], + deps = [ + ":adb_call_parser", + ":adb_controller", + "//android_env/proto:adb_py_pb2", + "@pip//absl_py", + "@pip//protobuf", + ], +) + +py_library( + name = "adb_controller", + srcs = ["adb_controller.py"], + deps = [ + ":config_classes", + ":errors", + "@pip//absl_py", + ], +) + +py_test( + name = "adb_controller_test", + srcs = ["adb_controller_test.py"], + deps = [ + ":adb_controller", + ":config_classes", + ":errors", + "@pip//absl_py", + ], +) + +py_library( + name = "adb_log_stream", + srcs = ["adb_log_stream.py"], + deps = [ + ":log_stream", + "@pip//absl_py", + ], +) + +py_test( + name = "adb_log_stream_test", + srcs = ["adb_log_stream_test.py"], + deps = [ + ":adb_log_stream", + "@pip//absl_py", + ], +) + +py_library( + name = "app_screen_checker", + srcs = ["app_screen_checker.py"], + deps = [ + ":adb_call_parser", + ":errors", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + ], +) + +py_test( + name = "app_screen_checker_test", + srcs = ["app_screen_checker_test.py"], + deps = [ + ":adb_call_parser", + ":app_screen_checker", + ":errors", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + ], +) + +py_library( + name = "config_classes", + srcs = ["config_classes.py"], +) + +py_library( + name = "coordinator", + srcs = ["coordinator.py"], + deps = [ + ":action_fns", + ":adb_call_parser", + ":config_classes", + ":device_settings", + ":errors", + ":specs", + ":task_manager", + "//android_env/components/simulators:base_simulator", + "//android_env/proto:adb_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "coordinator_test", + srcs = ["coordinator_test.py"], + deps = [ + ":action_type", + ":adb_call_parser", + ":config_classes", + ":coordinator", + ":device_settings", + ":errors", + ":task_manager", + "//android_env/components/simulators:base_simulator", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:state_py_pb2", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "device_settings", + srcs = ["device_settings.py"], + deps = [ + ":adb_call_parser", + ":config_classes", + "//android_env/components/simulators:base_simulator", + "//android_env/proto:adb_py_pb2", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_test( + name = "device_settings_test", + srcs = ["device_settings_test.py"], + deps = [ + ":config_classes", + ":device_settings", + "//android_env/components/simulators:base_simulator", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_library( + name = "dumpsys_thread", + srcs = ["dumpsys_thread.py"], + deps = [ + ":app_screen_checker", + "@pip//absl_py", + ], +) + +py_test( + name = "dumpsys_thread_test", + srcs = ["dumpsys_thread_test.py"], + deps = [ + ":app_screen_checker", + ":dumpsys_thread", + "@pip//absl_py", + ], +) + +py_library( + name = "errors", + srcs = ["errors.py"], +) + +py_test( + name = "errors_test", + srcs = ["errors_test.py"], + deps = [ + ":errors", + "@pip//absl_py", + ], +) + +py_library( + name = "logcat_thread", + srcs = ["logcat_thread.py"], + deps = [ + ":log_stream", + "@pip//absl_py", + ], +) + +py_test( + name = "logcat_thread_test", + srcs = ["logcat_thread_test.py"], + deps = [ + ":fake_log_stream", + ":logcat_thread", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + ], +) + +py_library( + name = "log_stream", + srcs = ["log_stream.py"], + deps = [ + "@pip//absl_py", + ], +) + +py_library( + name = "fake_log_stream", + srcs = ["fake_log_stream.py"], + deps = [ + ":log_stream", + ], +) + +py_test( + name = "log_stream_test", + srcs = ["log_stream_test.py"], + deps = [ + ":fake_log_stream", + "@pip//absl_py", + ], +) + +py_library( + name = "setup_step_interpreter", + srcs = ["setup_step_interpreter.py"], + deps = [ + ":adb_call_parser", + ":app_screen_checker", + ":errors", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + ], +) + +py_test( + name = "setup_step_interpreter_test", + srcs = ["setup_step_interpreter_test.py"], + deps = [ + ":adb_call_parser", + ":errors", + ":setup_step_interpreter", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//protobuf", + ], +) + +py_library( + name = "specs", + srcs = ["specs.py"], + deps = [ + ":action_type", + "//android_env/proto:task_py_pb2", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "specs_test", + srcs = ["specs_test.py"], + deps = [ + ":specs", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_library( + name = "task_manager", + srcs = ["task_manager.py"], + deps = [ + ":adb_call_parser", + ":app_screen_checker", + ":config_classes", + ":dumpsys_thread", + ":log_stream", + ":logcat_thread", + ":setup_step_interpreter", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "task_manager_test", + srcs = ["task_manager_test.py"], + deps = [ + ":adb_call_parser", + ":config_classes", + ":dumpsys_thread", + ":log_stream", + ":logcat_thread", + ":setup_step_interpreter", + ":task_manager", + "//android_env/proto:task_py_pb2", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_library( + name = "pixel_fns", + srcs = ["pixel_fns.py"], + deps = [ + "@pip//dm_env", # Replaced dm_env:specs with dm_env + "@pip//numpy", + ], +) + +py_test( + name = "pixel_fns_test", + srcs = ["pixel_fns_test.py"], + deps = [ + ":pixel_fns", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) diff --git a/android_env/components/simulators/BUILD.bazel b/android_env/components/simulators/BUILD.bazel new file mode 100644 index 00000000..f608fee1 --- /dev/null +++ b/android_env/components/simulators/BUILD.bazel @@ -0,0 +1,30 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "base_simulator", + srcs = ["base_simulator.py"], + deps = [ + "//android_env/components:adb_controller", + "//android_env/components:config_classes", + "//android_env/components:errors", + "//android_env/components:log_stream", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_test( + name = "base_simulator_test", + srcs = ["base_simulator_test.py"], + deps = [ + ":base_simulator", + "//android_env/components:config_classes", + "//android_env/components:errors", + "//android_env/components/simulators/fake:fake_simulator", + "@pip//absl_py", + "@pip//numpy", + ], +) diff --git a/android_env/components/simulators/emulator/BUILD.bazel b/android_env/components/simulators/emulator/BUILD.bazel new file mode 100644 index 00000000..43ddc5f7 --- /dev/null +++ b/android_env/components/simulators/emulator/BUILD.bazel @@ -0,0 +1,87 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "emulator_launcher", + srcs = ["emulator_launcher.py"], + deps = [ + "//android_env/components:config_classes", + "@pip//absl_py", + ], +) + +py_test( + name = "emulator_launcher_test", + srcs = ["emulator_launcher_test.py"], + deps = [ + ":emulator_launcher", + "//android_env/components:config_classes", + "@pip//absl_py", + ], +) + +py_library( + name = "tcp_ports", + srcs = ["tcp_ports.py"], + deps = [ + "//android_env/components:config_classes", + "@pip//absl_py", + "@pip//portpicker", + ], +) + +py_test( + name = "tcp_ports_test", + srcs = ["tcp_ports_test.py"], + deps = [ + ":tcp_ports", + "//android_env/components:config_classes", + "@pip//absl_py", + "@pip//portpicker", + ], +) + +py_library( + name = "emulator_simulator", + srcs = ["emulator_simulator.py"], + deps = [ + ":emulator_launcher", + "//android_env/components:adb_controller", + "//android_env/components:adb_log_stream", + "//android_env/components:config_classes", + "//android_env/components:errors", + "//android_env/components:log_stream", + "//android_env/components/simulators:base_simulator", + "//android_env/proto:emulator_controller_py_pb2", + "//android_env/proto:emulator_controller_py_pb2_grpc", + "//android_env/proto:snapshot_service_py_pb2", + "//android_env/proto:snapshot_service_py_pb2_grpc", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//grpcio", + "@pip//numpy", + "@pip//portpicker", + "@pip//protobuf", + ], +) + +py_test( + name = "emulator_simulator_test", + srcs = ["emulator_simulator_test.py"], + deps = [ + ":emulator_launcher", + ":emulator_simulator", + "//android_env/components:adb_call_parser", + "//android_env/components:adb_controller", + "//android_env/components:config_classes", + "//android_env/proto:emulator_controller_py_pb2", + "//android_env/proto:emulator_controller_py_pb2_grpc", + "//android_env/proto:snapshot_service_py_pb2", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//grpcio", + "@pip//pillow", # Replaced PIL:pil with pillow + "@pip//portpicker", + ], +) diff --git a/android_env/components/simulators/fake/BUILD.bazel b/android_env/components/simulators/fake/BUILD.bazel new file mode 100644 index 00000000..5a61a0bc --- /dev/null +++ b/android_env/components/simulators/fake/BUILD.bazel @@ -0,0 +1,28 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "fake_simulator", + srcs = ["fake_simulator.py"], + deps = [ + "//android_env/components:adb_controller", + "//android_env/components:config_classes", + "//android_env/components:fake_log_stream", + "//android_env/components:log_stream", + "//android_env/components/simulators:base_simulator", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_test( + name = "fake_simulator_test", + srcs = ["fake_simulator_test.py"], + deps = [ + ":fake_simulator", + "//android_env/components:config_classes", + "@pip//absl_py", + "@pip//numpy", + ], +) diff --git a/android_env/proto/BUILD.bazel b/android_env/proto/BUILD.bazel new file mode 100644 index 00000000..a97f545c --- /dev/null +++ b/android_env/proto/BUILD.bazel @@ -0,0 +1,75 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_proto_grpc_python//:defs.bzl", "python_grpc_library", "python_proto_library") + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "adb_proto", + srcs = ["adb.proto"], +) + +python_proto_library( + name = "adb_py_pb2", + protos = [":adb_proto"], +) + +proto_library( + name = "emulator_controller_proto", + srcs = ["emulator_controller.proto"], + deps = ["@protobuf//:empty_proto"], +) + +python_grpc_library( + name = "emulator_controller_py_pb2_grpc", + protos = [":emulator_controller_proto"], +) + +python_proto_library( + name = "emulator_controller_py_pb2", + protos = [":emulator_controller_proto"], +) + +proto_library( + name = "snapshot_proto", + srcs = ["snapshot.proto"], +) + +proto_library( + name = "snapshot_service_proto", + srcs = ["snapshot_service.proto"], + deps = [ + ":snapshot_proto", + "@protobuf//:empty_proto", + ], +) + +python_grpc_library( + name = "snapshot_service_py_pb2_grpc", + protos = [":snapshot_service_proto"], +) + +python_proto_library( + name = "snapshot_service_py_pb2", + protos = [":snapshot_service_proto"], +) + +proto_library( + name = "task_proto", + srcs = ["task.proto"], + deps = [":adb_proto"], +) + +python_proto_library( + name = "task_py_pb2", + protos = [":task_proto"], +) + +proto_library( + name = "state_proto", + srcs = ["state.proto"], +) + +python_proto_library( + name = "state_py_pb2", + protos = [":state_proto"], +) diff --git a/android_env/proto/a11y/BUILD.bazel b/android_env/proto/a11y/BUILD.bazel new file mode 100644 index 00000000..e8b64d78 --- /dev/null +++ b/android_env/proto/a11y/BUILD.bazel @@ -0,0 +1,100 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_proto_grpc_python//:defs.bzl", "python_grpc_library", "python_proto_library") + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "a11y_proto", + srcs = ["a11y.proto"], + deps = [ + ":android_accessibility_forest_proto", + ], +) + +python_grpc_library( + name = "a11y_py_pb2_grpc", + protos = [":a11y_proto"], +) + +# We also define the individual message-only python proto libraries for granularity. + +proto_library( + name = "android_accessibility_action_proto", + srcs = ["android_accessibility_action.proto"], +) + +python_proto_library( + name = "android_accessibility_action_py_pb2", + protos = [":android_accessibility_action_proto"], +) + +proto_library( + name = "android_accessibility_forest_proto", + srcs = ["android_accessibility_forest.proto"], + deps = [ + ":android_accessibility_window_info_proto", + ], +) + +python_proto_library( + name = "android_accessibility_forest_py_pb2", + protos = [":android_accessibility_forest_proto"], +) + +proto_library( + name = "android_accessibility_node_info_proto", + srcs = ["android_accessibility_node_info.proto"], + deps = [ + ":android_accessibility_action_proto", + ":android_accessibility_node_info_clickable_span_proto", + ":rect_proto", + ], +) + +python_proto_library( + name = "android_accessibility_node_info_py_pb2", + protos = [":android_accessibility_node_info_proto"], +) + +proto_library( + name = "android_accessibility_node_info_clickable_span_proto", + srcs = ["android_accessibility_node_info_clickable_span.proto"], +) + +# clickable_span is only used by node_info, so we don't strictly need a py_proto_library +# for it unless referenced elsewhere. We'll skip it to reduce duplication. + +proto_library( + name = "android_accessibility_tree_proto", + srcs = ["android_accessibility_tree.proto"], + deps = [":android_accessibility_node_info_proto"], +) + +python_proto_library( + name = "android_accessibility_tree_py_pb2", + protos = [":android_accessibility_tree_proto"], +) + +proto_library( + name = "android_accessibility_window_info_proto", + srcs = ["android_accessibility_window_info.proto"], + deps = [ + ":android_accessibility_tree_proto", + ":rect_proto", + ], +) + +python_proto_library( + name = "android_accessibility_window_info_py_pb2", + protos = [":android_accessibility_window_info_proto"], +) + +proto_library( + name = "rect_proto", + srcs = ["rect.proto"], +) + +python_proto_library( + name = "rect_py_pb2", + protos = [":rect_proto"], +) diff --git a/android_env/wrappers/BUILD.bazel b/android_env/wrappers/BUILD.bazel new file mode 100644 index 00000000..a44f1caf --- /dev/null +++ b/android_env/wrappers/BUILD.bazel @@ -0,0 +1,281 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "a11y_grpc_wrapper", + srcs = ["a11y_grpc_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "//android_env/proto:adb_py_pb2", + "//android_env/proto/a11y:a11y_py_pb2_grpc", + "//android_env/wrappers/a11y:a11y_events", + "//android_env/wrappers/a11y:a11y_forests", + "//android_env/wrappers/a11y:a11y_servicer", + "@pip//absl_py", + "@pip//dm_env", + "@pip//grpcio", + "@pip//numpy", + "@pip//portpicker", + ], +) + +py_test( + name = "a11y_grpc_wrapper_test", + srcs = ["a11y_grpc_wrapper_test.py"], + deps = [ + ":a11y_grpc_wrapper", + "//android_env:env_interface", + "//android_env/proto:adb_py_pb2", + "//android_env/proto/a11y:a11y_py_pb2_grpc", + "//android_env/proto/a11y:android_accessibility_forest_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//grpcio", + "@pip//numpy", + "@pip//portpicker", + ], +) + +py_library( + name = "base_wrapper", + srcs = ["base_wrapper.py"], + deps = [ + "//android_env:env_interface", + "//android_env/proto:adb_py_pb2", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "base_wrapper_test", + srcs = ["base_wrapper_test.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/proto:state_py_pb2", + "@pip//absl_py", + ], +) + +py_library( + name = "discrete_action_wrapper", + srcs = ["discrete_action_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "discrete_action_wrapper_test", + srcs = ["discrete_action_wrapper_test.py"], + deps = [ + ":discrete_action_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//absl_py", + "@pip//numpy", + ], +) + +py_library( + name = "rate_limit_wrapper", + srcs = ["rate_limit_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "rate_limit_wrapper_test", + srcs = ["rate_limit_wrapper_test.py"], + deps = [ + ":rate_limit_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "flat_interface_wrapper", + srcs = ["flat_interface_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "flat_interface_wrapper_test", + srcs = ["flat_interface_wrapper_test.py"], + deps = [ + ":flat_interface_wrapper", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "float_pixels_wrapper", + srcs = ["float_pixels_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:pixel_fns", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "float_pixels_wrapper_test", + srcs = ["float_pixels_wrapper_test.py"], + deps = [ + ":float_pixels_wrapper", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "gym_wrapper", + srcs = ["gym_wrapper.py"], + deps = [ + "@pip//dm_env", + "@pip//gymnasium", + "@pip//numpy", + ], +) + +py_test( + name = "gym_wrapper_test", + srcs = ["gym_wrapper_test.py"], + deps = [ + ":gym_wrapper", + "//android_env:env_interface", + "@pip//absl_py", + "@pip//dm_env", + "@pip//gymnasium", + "@pip//numpy", + ], +) + +py_library( + name = "image_rescale_wrapper", + srcs = ["image_rescale_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "@pip//dm_env", + "@pip//numpy", + "@pip//pillow", # Replaced PIL:pil with pillow + ], +) + +py_test( + name = "image_rescale_wrapper_test", + srcs = ["image_rescale_wrapper_test.py"], + deps = [ + ":image_rescale_wrapper", + "//android_env:env_interface", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "last_action_wrapper", + srcs = ["last_action_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "//android_env/components:pixel_fns", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "last_action_wrapper_test", + srcs = ["last_action_wrapper_test.py"], + deps = [ + ":last_action_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "tap_action_wrapper", + srcs = ["tap_action_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "tap_action_wrapper_test", + srcs = ["tap_action_wrapper_test.py"], + deps = [ + ":tap_action_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_library( + name = "swipe_action_wrapper", + srcs = ["swipe_action_wrapper.py"], + deps = [ + ":base_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//dm_env", + "@pip//numpy", + ], +) + +py_test( + name = "swipe_action_wrapper_test", + srcs = ["swipe_action_wrapper_test.py"], + deps = [ + ":swipe_action_wrapper", + "//android_env:env_interface", + "//android_env/components:action_type", + "@pip//absl_py", + "@pip//dm_env", + "@pip//numpy", + ], +) diff --git a/android_env/wrappers/a11y/BUILD.bazel b/android_env/wrappers/a11y/BUILD.bazel new file mode 100644 index 00000000..63c84a2b --- /dev/null +++ b/android_env/wrappers/a11y/BUILD.bazel @@ -0,0 +1,71 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +package(default_visibility = ["//android_env/wrappers:__subpackages__"]) + +py_library( + name = "a11y_events", + srcs = ["a11y_events.py"], + deps = [ + "//android_env/proto/a11y:a11y_py_pb2_grpc", # Redirected from a11y_py_pb2 + "@pip//absl_py", + "@pip//numpy", + "@pip//protobuf", # For any_py_pb2 + ], +) + +py_test( + name = "a11y_events_test", + srcs = ["a11y_events_test.py"], + deps = [ + ":a11y_events", + "//android_env/proto/a11y:a11y_py_pb2_grpc", + "@pip//absl_py", + "@pip//numpy", + "@pip//protobuf", + ], +) + +py_library( + name = "a11y_forests", + srcs = ["a11y_forests.py"], + deps = [ + "//android_env/proto/a11y:android_accessibility_forest_py_pb2", + "@pip//numpy", + "@pip//protobuf", + ], +) + +py_test( + name = "a11y_forests_test", + srcs = ["a11y_forests_test.py"], + deps = [ + ":a11y_forests", + "//android_env/proto/a11y:android_accessibility_forest_py_pb2", + "@pip//absl_py", + "@pip//numpy", + "@pip//protobuf", + ], +) + +py_library( + name = "a11y_servicer", + srcs = ["a11y_servicer.py"], + deps = [ + "//android_env/proto/a11y:a11y_py_pb2_grpc", + "//android_env/proto/a11y:android_accessibility_forest_py_pb2", + "@pip//absl_py", + "@pip//grpcio", + ], +) + +py_test( + name = "a11y_servicer_test", + srcs = ["a11y_servicer_test.py"], + deps = [ + ":a11y_servicer", + "//android_env/proto/a11y:a11y_py_pb2_grpc", + "//android_env/proto/a11y:android_accessibility_forest_py_pb2", + "@pip//absl_py", + "@pip//grpcio", + ], +) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..4487deac --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +absl-py==2.1.0 +dm-env==1.6 +numpy==1.26.4 +portpicker==1.6.0 +pygame==2.6.0 +pillow==10.4.0 +gymnasium==0.29.1 +protobuf==5.27.2 +grpcio==1.64.3 +cloudpickle==3.0.0 +farama-notifications==0.0.4 +typing-extensions==4.11.0 +psutil==5.9.8 diff --git a/setup.py b/setup.py deleted file mode 100644 index 8d05dc1b..00000000 --- a/setup.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2026 DeepMind Technologies Limited. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Simple package definition for using with `pip`.""" - -import importlib -import os - -import setuptools -from setuptools import find_packages -from setuptools import setup -from setuptools.command.build_ext import build_ext -from setuptools.command.build_py import build_py - -_ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) - -# Tuple of proto message definitions to build Python bindings for. Paths must -# be relative to root directory. -_ANDROID_ENV_PROTOS = ( - 'android_env/proto/adb.proto', - 'android_env/proto/emulator_controller.proto', - 'android_env/proto/snapshot.proto', - 'android_env/proto/snapshot_service.proto', - 'android_env/proto/state.proto', - 'android_env/proto/task.proto', - 'android_env/proto/a11y/a11y.proto', - 'android_env/proto/a11y/android_accessibility_action.proto', - 'android_env/proto/a11y/android_accessibility_forest.proto', - 'android_env/proto/a11y/android_accessibility_node_info.proto', - 'android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto', - 'android_env/proto/a11y/android_accessibility_tree.proto', - 'android_env/proto/a11y/android_accessibility_window_info.proto', - 'android_env/proto/a11y/rect.proto', -) - - -class _GenerateProtoFiles(setuptools.Command): - """Command to generate protobuf bindings for AndroidEnv protos.""" - - descriptions = 'Generates Python protobuf bindings for AndroidEnv protos.' - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - # Import grpc_tools here, after setuptools has installed setup_requires - # dependencies. - from grpc_tools import protoc # pylint: disable=g-import-not-at-top - - with importlib.resources.as_file( - importlib.resources.files('grpc_tools').joinpath('_proto') - ) as path: - grpc_protos_include = str(path) - - for proto_path in _ANDROID_ENV_PROTOS: - proto_args = [ - 'grpc_tools.protoc', - '--proto_path={}'.format(grpc_protos_include), - '--proto_path={}'.format(_ROOT_DIR), - '--python_out={}'.format(_ROOT_DIR), - '--pyi_out={}'.format(_ROOT_DIR), - '--grpc_python_out={}'.format(_ROOT_DIR), - os.path.join(_ROOT_DIR, proto_path), - ] - if protoc.main(proto_args) != 0: - raise RuntimeError('ERROR: {}'.format(proto_args)) - - -class _BuildExt(build_ext): - """Generate protobuf bindings in build_ext stage.""" - - def run(self): - self.run_command('generate_protos') - build_ext.run(self) - - -class _BuildPy(build_py): - """Generate protobuf bindings in build_py stage.""" - - def run(self): - self.run_command('generate_protos') - build_py.run(self) - -setup( - packages=find_packages(exclude=['examples']), - package_data={'android_env': ['proto/*.proto', 'proto/a11y/*.proto']}, - include_package_data=True, - setup_requires=['grpcio-tools'], - cmdclass={ - 'build_ext': _BuildExt, - 'build_py': _BuildPy, - 'generate_protos': _GenerateProtoFiles, - }, -)