diff --git a/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml b/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml index f31c36df9a1..83c81bf2419 100644 --- a/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml +++ b/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml @@ -23,7 +23,6 @@ runs: apt-get install -y lsb-release apt-get install -y pciutils apt-get install -y apt-show-versions - apt-get install -y linux-image-arm64 linux-headers-arm64 - name: Setup base and non-free repository shell: bash run: | diff --git a/.github/actions/run-cw-sharded-e2e-test/action.yaml b/.github/actions/run-cw-sharded-e2e-test/action.yaml index 4687e55504f..5fa87cc828b 100644 --- a/.github/actions/run-cw-sharded-e2e-test/action.yaml +++ b/.github/actions/run-cw-sharded-e2e-test/action.yaml @@ -16,6 +16,8 @@ runs: with: name: android-cuttlefish-e2etest-image-tar github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Run tests shell: bash env: diff --git a/.github/actions/upgrade-crun/action.yaml b/.github/actions/upgrade-crun/action.yaml new file mode 100644 index 00000000000..53dc2120d50 --- /dev/null +++ b/.github/actions/upgrade-crun/action.yaml @@ -0,0 +1,29 @@ +name: 'Upgrade crun' +description: 'Upgrade crun to latest version to avoid OCI compatibility issues' +runs: + using: "composite" + steps: + - name: Upgrade crun + shell: bash + run: | + TARGET_VERSION="1.28" + EXPECTED_HASH="2aa6b7024a9c9f153895c0d11ae233d3758f54844011c3a039e3e89048d01d42" + CRUN_PATH=$(which crun || echo "/usr/bin/crun") + echo "Current crun path: $CRUN_PATH" + if [ -f "$CRUN_PATH" ]; then + echo "Current crun version:" + $CRUN_PATH --version + fi + + TEMP_CRUN=$(mktemp) + echo "Downloading crun $TARGET_VERSION..." + curl -L -o "$TEMP_CRUN" https://github.com/containers/crun/releases/download/${TARGET_VERSION}/crun-${TARGET_VERSION}-linux-amd64 + + echo "Verifying hash..." + echo "$EXPECTED_HASH $TEMP_CRUN" | sha256sum --check + + echo "Installing crun..." + sudo mv "$TEMP_CRUN" "$CRUN_PATH" + sudo chmod +x "$CRUN_PATH" + echo "Upgraded crun version:" + $CRUN_PATH --version diff --git a/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml b/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml index 8e52f6694db..32ef8127919 100644 --- a/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml +++ b/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml @@ -56,7 +56,7 @@ jobs: nvidia_gpu: ["true", "false"] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Check preseed after install script uses: ./.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script with: @@ -72,11 +72,11 @@ jobs: image: debian@sha256:13f29b6806e531c3ff3b565bb6eed73f2132506c8c9d41bb996065ca20fb27f2 # debian:trixie-20260223 (amd64) steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Build iso installer uses: ./.github/actions/build-gigabyte-ampere-cuttlefish-installer - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts path: gigabyte-ampere-cuttlefish-installer/preseed-mini.iso.xz @@ -93,9 +93,9 @@ jobs: TEST_DISK_SIZE: "10G" steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -137,9 +137,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -185,9 +185,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -233,9 +233,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -281,9 +281,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -301,6 +301,9 @@ jobs: run: | screen -d -m -L -Logfile console_001.log ./installer-iso-run-qemu.sh while ! egrep "[^[:space:]]+[[:space:]]login:" console_001.log; do sleep 30; done + # The login prompt appears before the system is usable, so wait for the + # boot to complete. "degraded" means booted with some unit failed. + until sshpass -p cuttlefish ssh -o "StrictHostKeyChecking no" -o "UserKnownHostsFile /dev/null" -o "ConnectTimeout 60" -p 33322 vsoc-01@localhost 'systemctl is-system-running | grep -qE "running|degraded"'; do sleep 30; done cp -f console_001.log console_001_p1.log CONSOLELINES=$(cat console_001_p1.log | wc -l) cat console_001_p1.log @@ -361,9 +364,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -381,6 +384,9 @@ jobs: run: | screen -d -m -L -Logfile console_001.log ./installer-iso-run-qemu.sh while ! egrep "[^[:space:]]+[[:space:]]login:" console_001.log; do sleep 30; done + # The login prompt appears before the system is usable, so wait for the + # boot to complete. "degraded" means booted with some unit failed. + until sshpass -p cuttlefish ssh -o "StrictHostKeyChecking no" -o "UserKnownHostsFile /dev/null" -o "ConnectTimeout 60" -p 33322 vsoc-01@localhost 'systemctl is-system-running | grep -qE "running|degraded"'; do sleep 30; done cp -f console_001.log console_001_p1.log CONSOLELINES=$(cat console_001_p1.log | wc -l) cat console_001_p1.log diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml index 86275860d06..ac5948fd54d 100644 --- a/.github/workflows/presubmit.yaml +++ b/.github/workflows/presubmit.yaml @@ -304,6 +304,8 @@ jobs: with: name: debs_amd64 github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Build image run: | tar -xvf debs_amd64.tar @@ -363,6 +365,8 @@ jobs: with: name: android-cuttlefish-e2etest-image-tar github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Run tests run: | sudo podman info @@ -509,6 +513,8 @@ jobs: uses: actions/download-artifact@v7 with: name: cuttlefish-orchestration-amd64 + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Load docker image as podman image run: | sudo sysctl -w kernel.unprivileged_userns_clone=1 diff --git a/base/cvd/cuttlefish/common/libs/utils/container.cpp b/base/cvd/cuttlefish/common/libs/utils/container.cpp index 0425edff373..91896508b7b 100644 --- a/base/cvd/cuttlefish/common/libs/utils/container.cpp +++ b/base/cvd/cuttlefish/common/libs/utils/container.cpp @@ -22,18 +22,21 @@ #include "cuttlefish/common/libs/utils/files.h" namespace cuttlefish { +namespace { -static bool IsRunningInDocker() { +bool IsRunningInDocker() { // if /.dockerenv exists, it's inside a docker container - static std::string docker_env_path("/.dockerenv"); - static bool ret = - FileExists(docker_env_path) || DirectoryExists(docker_env_path); - return ret; + std::string docker_env_path("/.dockerenv"); + return FileExists(docker_env_path) || DirectoryExists(docker_env_path); } +bool IsRunningInPodman() { return FileExists("/run/.containerenv"); } + +} // namespace + bool IsRunningInContainer() { // TODO: add more if we support other containers than docker - return IsRunningInDocker(); + return IsRunningInDocker() || IsRunningInPodman(); } } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel index 0f006b2aecb..379634a7036 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel @@ -458,6 +458,7 @@ cf_cc_library( "@abseil-cpp//absl/strings", "@fmt", "@protobuf", + "@protobuf//src/google/protobuf/io:tokenizer", ], ) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc index 84ce67848e5..12ac1df4a90 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc @@ -21,12 +21,13 @@ #include #include -#include "absl/strings/str_join.h" -#include -#include #include "absl/log/log.h" #include "absl/strings/ascii.h" +#include "absl/strings/str_join.h" #include "absl/strings/str_split.h" +#include +#include +#include #include "cuttlefish/common/libs/utils/contains.h" #include "cuttlefish/common/libs/utils/files.h" @@ -52,6 +53,23 @@ namespace cuttlefish { namespace { +struct AggregatingErrorCollector : public google::protobuf::io::ErrorCollector { + void RecordError(int /* line */, int /* column */, + const absl::string_view message) override { + if (!error_message.empty()) { + absl::StrAppend(&error_message, "; "); + } + absl::StrAppend(&error_message, message); + } + + void RecordWarning(int /* line */, int /* column */, + const absl::string_view /* message */) override { + // Ignore warnings + } + + std::string error_message; +}; + struct CommonState { const VmmMode vmm_mode; const GuestConfig& guest_config; @@ -177,18 +195,29 @@ GetGpuModeRequirementsMap() { "Consider enabling --gpu_mode=gfxstream_guest_angle_host_swiftshader " "for host software rendering which has a vetted software renderer.", }; - // TODO: separate host vulkan loader check out. - const RequirementWithReason kHostVulkanAvailable{ + const RequirementWithReason kHostVulkanLoaderAvailable{ + .func = + [](const CommonState& common) { + const auto& availability = common.graphics_availability; + return availability.vulkan_loader_available(); + }, + .success_explanation = + "The host has the Vulkan loader installed and " + "available.", + .failure_explanation = + "The host does not have the Vulkan loader installed. Please ensure " + "the Vulkan loader is installed and available.", + }; + const RequirementWithReason kHostVulkanDriverAvailable{ .func = [](const CommonState& common) { const auto& availability = common.graphics_availability; return availability.has_vulkan(); }, - .success_explanation = "The host has Vulkan support.", + .success_explanation = "The host has a Vulkan driver available.", .failure_explanation = - "The host does not have Vulkan support. Please ensure the Vulkan " - "userspace drivers and the Vulkan loader are installed and " - "available.", + "The host does not have a Vulkan driver available. Please ensure " + "a Vulkan driver is installed.", }; const RequirementWithReason kHostVulkanIsNonSoftwareRenderer{ .func = @@ -252,7 +281,8 @@ GetGpuModeRequirementsMap() { kHostGlesAvailable, kHostGlesIsNonSoftwareRenderer, kHostIsNonArm, - kHostVulkanAvailable, + kHostVulkanLoaderAvailable, + kHostVulkanDriverAvailable, kHostVulkanIsNonSoftwareRenderer, }, }, @@ -261,7 +291,8 @@ GetGpuModeRequirementsMap() { { kGuestSupportsGfxstream, kHostIsNonArm, - kHostVulkanAvailable, + kHostVulkanLoaderAvailable, + kHostVulkanDriverAvailable, kHostVulkanIsNonSoftwareRenderer, kHostVulkanMemoryCanBeMappedIntoKvm, kNotUsingHostQemu, @@ -272,7 +303,7 @@ GetGpuModeRequirementsMap() { { kGuestSupportsGfxstream, kHostIsNonArm, - kHostVulkanAvailable, + kHostVulkanLoaderAvailable, kNotUsingHostQemu, }, }, @@ -281,7 +312,7 @@ GetGpuModeRequirementsMap() { { kGuestSupportsGfxstream, kHostIsNonArm, - kHostVulkanAvailable, + kHostVulkanLoaderAvailable, kNotUsingHostQemu, }, }, @@ -620,6 +651,35 @@ Result SelectGpuVhostUserMode(const GpuMode gpu_mode, return gpu_vhost_user_mode_arg == kGpuVhostUserModeOn; } +Result SelectGuestHwuiRenderer( + const GpuMode gpu_mode, const GuestConfig& guest_config, + const std::string& guest_hwui_renderer_arg) { + if (!guest_hwui_renderer_arg.empty()) { + GuestHwuiRenderer hwui_renderer = CF_EXPECT( + ParseGuestHwuiRenderer(guest_hwui_renderer_arg), + "Failed to parse HWUI renderer flag: " << guest_hwui_renderer_arg); + VLOG(0) << "Using explicitly provided HWUI renderer: " + << ToString(hwui_renderer); + return hwui_renderer; + } + + // Only makes sense for Android guests: + if (guest_config.android_version_number.empty()) { + return GuestHwuiRenderer::kUnknown; + } + + // TODO(b/533056543): after testing Gfxstream's virtual queue support. + if (IsGfxstreamGuestAngleMode(gpu_mode) && + gpu_mode != GpuMode::GfxstreamGuestAngleHostSwiftshader) { + VLOG(0) << "Selecting SkiaVk as the HWUI renderer for " + << GpuModeString(gpu_mode) + << " GPU mode which is GfxstreamGuestAngle* based."; + return GuestHwuiRenderer::kSkiaVk; + } + + return GuestHwuiRenderer::kUnknown; +} + Result SelectGuestRendererPreload( const GpuMode gpu_mode, const GuestHwuiRenderer guest_hwui_renderer, const std::string& guest_renderer_preload_arg) { @@ -635,8 +695,8 @@ Result SelectGuestRendererPreload( if (guest_hwui_renderer == GuestHwuiRenderer::kSkiaVk && (gpu_mode == GpuMode::GfxstreamGuestAngle || gpu_mode == GpuMode::GfxstreamGuestAngleHostSwiftshader)) { - LOG(INFO) << "Disabling guest renderer preload for Gfxstream based mode " - "when running with SkiaVk."; + VLOG(0) << "Disabling guest renderer preload for Gfxstream based mode " + "when running with SkiaVk."; guest_renderer_preload = GuestRendererPreload::kDisabled; } } @@ -689,9 +749,32 @@ std::string GetGfxstreamRendererFeaturesString( return absl::StrJoin(parts, ","); } +CF_UNUSED_ON_MACOS +bool HasMultipleGraphicsQueues( + const gfxstream::proto::GraphicsAvailability& availability) { + if (!availability.has_vulkan()) { + return false; + } + const gfxstream::proto::VulkanAvailability& vulkan_availability = + availability.vulkan(); + if (vulkan_availability.physical_devices().empty()) { + return false; + } + const auto& physical_device = vulkan_availability.physical_devices(0); + for (const auto& queue_family : physical_device.queue_families()) { + if (!queue_family.has_supports_graphics()) continue; + if (!queue_family.supports_graphics()) continue; + + // HWUI seems to only check the first queue family supporting graphics: + return queue_family.has_queue_count() && queue_family.queue_count() >= 2; + } + return false; +} + CF_UNUSED_ON_MACOS Result SetGfxstreamFlags( - const GpuMode gpu_mode, const std::string& gpu_renderer_features_arg, + const GpuMode gpu_mode, const GuestHwuiRenderer hwui_renderer, + const std::string& gpu_renderer_features_arg, const GuestConfig& guest_config, const gfxstream::proto::GraphicsAvailability& availability, CuttlefishConfig::MutableInstanceSpecific& instance) { @@ -715,6 +798,23 @@ Result SetGfxstreamFlags( features["GlProgramBinaryLinkStatus"] = true; } + if (hwui_renderer == GuestHwuiRenderer::kSkiaVk) { + // SkiaVK requires a second graphics queue for AHB transfers. + const bool needs_multi_queue_emulation = + (gpu_mode == GpuMode::GfxstreamGuestAngleHostSwiftshader) + ? + // The SwiftShader driver packaged with the Cuttlefish host tools + // does not appear in `availability` and does not have multiple + // queues. + true + : !HasMultipleGraphicsQueues(availability); + ; + + if (needs_multi_queue_emulation) { + features["VulkanVirtualQueue"] = true; + } + } + // Apply feature overrides from --gpu_renderer_features. const auto feature_overrides = CF_EXPECT(ParseGfxstreamRendererFlag(gpu_renderer_features_arg)); @@ -774,15 +874,25 @@ GetGraphicsAvailabilityWithSubprocessCheck() { graphics_availability_content_result.value(); gfxstream::proto::GraphicsAvailability availability; + google::protobuf::TextFormat::Parser parser; + parser.AllowUnknownField(true); + AggregatingErrorCollector error_collector; + parser.RecordErrorsTo(&error_collector); if (!parser.ParseFromString(graphics_availability_content, &availability)) { LOG(ERROR) << "Failed to parse graphics detector output: " << graphics_availability_content + << ". Error(s): " << error_collector.error_message << ". Assuming no availability."; return {}; } - VLOG(0) << "Host Graphics Availability:" << availability.DebugString(); + VLOG(0) << "Host Graphics Availability:"; + for (absl::string_view line : + absl::StrSplit(graphics_availability_content, '\n')) { + VLOG(0) << line; + } + return availability; #endif } @@ -821,11 +931,6 @@ Result ConfigureGpuSettings( const bool enable_gpu_vhost_user = CF_EXPECT(SelectGpuVhostUserMode(gpu_mode, gpu_vhost_user_mode_arg, vmm)); - if (IsGfxstreamMode(gpu_mode)) { - CF_EXPECT(SetGfxstreamFlags(gpu_mode, gpu_renderer_features_arg, - guest_config, graphics_availability, instance)); - } - if (gpu_mode == GpuMode::Custom) { std::vector requested_types = absl::StrSplit(gpu_context_types_arg, ':'); @@ -854,18 +959,20 @@ Result ConfigureGpuSettings( instance.set_enable_gpu_system_blob(false); } - GuestHwuiRenderer hwui_renderer = GuestHwuiRenderer::kUnknown; - if (!guest_hwui_renderer_arg.empty()) { - hwui_renderer = CF_EXPECT( - ParseGuestHwuiRenderer(guest_hwui_renderer_arg), - "Failed to parse HWUI renderer flag: " << guest_hwui_renderer_arg); - } + const GuestHwuiRenderer hwui_renderer = CF_EXPECT( + SelectGuestHwuiRenderer(gpu_mode, guest_config, guest_hwui_renderer_arg)); instance.set_guest_hwui_renderer(hwui_renderer); const auto guest_renderer_preload = CF_EXPECT(SelectGuestRendererPreload( gpu_mode, hwui_renderer, guest_renderer_preload_arg)); instance.set_guest_renderer_preload(guest_renderer_preload); + if (IsGfxstreamMode(gpu_mode)) { + CF_EXPECT(SetGfxstreamFlags(gpu_mode, hwui_renderer, + gpu_renderer_features_arg, guest_config, + graphics_availability, instance)); + } + instance.set_gpu_mode(gpu_mode); instance.set_enable_gpu_vhost_user(enable_gpu_vhost_user); diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/BUILD.bazel b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/BUILD.bazel index 22da76db6bc..d21df5bf69d 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/BUILD.bazel @@ -390,6 +390,7 @@ cf_cc_library( hdrs = ["start.h"], clang_format_enabled = False, deps = [ + "//cuttlefish/common/libs/fs", "//cuttlefish/common/libs/utils:contains", "//cuttlefish/common/libs/utils:files", "//cuttlefish/common/libs/utils:json", diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp index 66b686663bf..39f5e53f3c5 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp @@ -16,6 +16,7 @@ #include "cuttlefish/host/commands/cvd/cli/commands/start.h" +#include #include // IWYU pragma: keep #include #include @@ -41,6 +42,7 @@ #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" +#include "cuttlefish/common/libs/fs/shared_fd.h" #include "cuttlefish/common/libs/utils/contains.h" #include "cuttlefish/common/libs/utils/files.h" #include "cuttlefish/flag_parser/flag.h" @@ -310,6 +312,41 @@ Result> GetCvdInternalStartFlags( return flags; } +bool CanBypassToSingleInstance(const LocalInstance& instance, + const LocalInstanceGroup& group, + const std::vector& subcmd_args) { + if (instance.State() != cvd::INSTANCE_STATE_STOPPED) { + return false; + } + if (group.StartTime() == TimeStamp{}) { + return false; + } + + std::vector args_copy = subcmd_args; + bool daemon = true; + std::vector safe_flags = { + GflagsCompatFlag("daemon", daemon), + }; + const Result res = ConsumeFlags(safe_flags, args_copy); + if (!res.ok() || !daemon || !args_copy.empty()) { + return false; + } + + const std::vector& instances = group.Instances(); + if (instances.empty()) { + return false; + } + const LocalInstance& main_instance = instances[0]; + if (instance.Id() == main_instance.Id()) { + return false; + } + if (main_instance.State() != cvd::INSTANCE_STATE_RUNNING) { + return false; + } + + return true; +} + } // namespace CvdStartCommandHandler::CvdStartCommandHandler( @@ -338,6 +375,20 @@ Result CvdStartCommandHandler::Handle(const CommandRequest& request) { return CF_ERR(NoGroupMessage(request)); } + if (request.Selectors().instance_names && + request.Selectors().instance_names->size() == 1) { + auto [instance, group] = + CF_EXPECT(selector::SelectInstance(instance_manager_, request)); + + if (CanBypassToSingleInstance(instance, group, subcmd_args)) { + CF_EXPECT(LaunchSingleInstance(instance, group, request)); + return {}; + } else { + VLOG(1) << "Cannot bypass to single instance start. Proceeding with " + "normal group start."; + } + } + CF_EXPECT(ConsumeDaemonModeFlag(subcmd_args)); subcmd_args.push_back("--daemon=true"); @@ -495,6 +546,73 @@ Result CvdStartCommandHandler::LaunchDeviceInterruptible( return {}; } +Result CvdStartCommandHandler::LaunchSingleInstance( + LocalInstance& instance, LocalInstanceGroup& group, + const CommandRequest& request) { + const std::string bin_path = group.HostArtifactsPath() + "/bin/run_cvd"; + cvd_common::Envs run_cvd_envs = request.Env(); + run_cvd_envs[kCuttlefishInstanceEnvVarName] = std::to_string(instance.Id()); + run_cvd_envs["HOME"] = group.HomeDir(); + run_cvd_envs[kAndroidHostOut] = group.HostArtifactsPath(); + run_cvd_envs[kAndroidProductOut] = group.ProductOutPath(); + run_cvd_envs[kAndroidSoongHostOut] = group.HostArtifactsPath(); + run_cvd_envs[kCvdMarkEnv] = "true"; + + ConstructCommandParam construct_cmd_param{.bin_path = bin_path, + .home = group.HomeDir(), + .args = cvd_common::Args{}, + .envs = run_cvd_envs, + .working_dir = CurrentDirectory(), + .command_name = "run_cvd"}; + + Command command = CF_EXPECT(ConstructCommand(construct_cmd_param)); + command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, + Subprocess::StdIOChannel::kStdErr); + SharedFD dev_null = SharedFD::Open("/dev/null", O_RDONLY); + if (dev_null->IsOpen()) { + command.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, dev_null); + } else { + LOG(ERROR) << "Failed to open /dev/null: " << dev_null->StrError(); + } + + const Result symlink_config_res = + SymlinkPreviousConfig(group.HomeDir()); + if (!symlink_config_res.ok()) { + LOG(ERROR) << "Failed to symlink the config file at system wide home: " + << symlink_config_res.error(); + } + + auto set_instance_state = [&group, &instance](cvd::InstanceState state) { + for (auto& inst : group.Instances()) { + if (inst.Id() == instance.Id()) { + inst.SetState(state); + break; + } + } + }; + + set_instance_state(cvd::INSTANCE_STATE_STARTING); + group.SetStartTime(CvdServerClock::now()); + CF_EXPECT(instance_manager_.UpdateInstanceGroup(group)); + + Result start_res = + LaunchDevice(std::move(command), group, run_cvd_envs, request); + + if (!start_res.ok()) { + set_instance_state(cvd::INSTANCE_STATE_BOOT_FAILED); + CF_EXPECT(instance_manager_.UpdateInstanceGroup(group)); + return start_res; + } + + set_instance_state(cvd::INSTANCE_STATE_RUNNING); + CF_EXPECT(instance_manager_.UpdateInstanceGroup(group)); + + const Json::Value group_json = CF_EXPECT(group.FetchStatus()); + std::cout << group_json.toStyledString(); + + return {}; +} + std::vector CvdStartCommandHandler::Description() const { std::vector description; description.emplace_back( diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.h b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.h index 6b836cb0078..6479ce40088 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.h +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.h @@ -43,6 +43,10 @@ class CvdStartCommandHandler : public CvdCommandHandler { bool RequiresDeviceExists() const override { return true; } private: + Result LaunchSingleInstance(LocalInstance& instance, + LocalInstanceGroup& group, + const CommandRequest& request); + Result LaunchDevice(Command command, LocalInstanceGroup& group, const cvd_common::Envs& envs, const CommandRequest& request); diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc index b0ca0d1c7b8..bc41537b144 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc @@ -374,7 +374,7 @@ TEST(FlagsParserTest, ParseMediaSplaneSingleInstance) { << "Invalid Json string"; auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_emulated_camera_splane")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_emulated_camera_splane")) << "media flag is missing or wrongly formatted"; } @@ -407,7 +407,7 @@ TEST(FlagsParserTest, ParseMediaSplaneTwoDevices) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); EXPECT_EQ(std::count(serialized_data->begin(), serialized_data->end(), - "--media=type=v4l2_emulated_camera_splane"), + "--media=v4l2_emulated_camera_splane"), 2); } @@ -437,7 +437,7 @@ TEST(FlagsParserTest, ParseMediaMplane) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_emulated_camera_mplane")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_emulated_camera_mplane")) << "media flag is missing or wrongly formatted"; } @@ -469,7 +469,7 @@ TEST(FlagsParserTest, ParseMediaV4l2Proxy) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_proxy")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_proxy")) << "media flag is missing or wrongly formatted"; } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp index 94e6994d597..71b99a0fd20 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp @@ -41,16 +41,16 @@ Result> GenerateMediaFlags( for (const auto& device : instance.media().devices()) { std::string flag = "--media="; if (device.has_v4l2_emulated_camera_splane()) { - flag += "type=v4l2_emulated_camera_splane"; + flag += "v4l2_emulated_camera_splane"; } else if (device.has_v4l2_emulated_camera_mplane()) { - flag += "type=v4l2_emulated_camera_mplane"; + flag += "v4l2_emulated_camera_mplane"; } else if (device.has_v4l2_proxy()) { // TODO(b/520114678): Use device.v4l2_proxy.device_path when // supported. - flag += "type=v4l2_proxy"; + flag += "v4l2_proxy"; } if (device.has_lens_facing()) { - flag += ",lens_facing=" + device.lens_facing(); + flag += ":lens_facing=" + device.lens_facing(); } flags.push_back(flag); } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp index 181707a67ff..fbd3cee10bf 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp @@ -49,6 +49,7 @@ inline constexpr char kFlagEnableSandbox[] = "enable_sandbox"; inline constexpr char kFlagCrosvmSimpleMediaDevice[] = "crosvm_simple_media_device"; inline constexpr char kFlagCrosvmV4l2Proxy[] = "crosvm_v4l2_proxy"; +inline constexpr char kFlagVhostUserVsock[] = "vhost_user_vsock"; inline constexpr char kFlagEnablePkvm[] = "enable_pkvm"; std::set GatherFlagNamesUsedInInstanceConfig(const Instance& ins) { @@ -83,6 +84,10 @@ std::set GatherFlagNamesUsedInInstanceConfig(const Instance& ins) { ins.vm().crosvm().has_v4l2_proxy()) { names.insert(kFlagCrosvmV4l2Proxy); } + if (ins.vm().vmm_case() == Vm::VmmCase::kCrosvm && + ins.vm().crosvm().has_vhost_user_vsock()) { + names.insert(kFlagVhostUserVsock); + } if (ins.vm().has_enable_pkvm()) { names.insert(kFlagEnablePkvm); } @@ -179,6 +184,13 @@ static std::string V4l2Proxy(const Instance& instance) { return crosvm.has_v4l2_proxy() ? crosvm.v4l2_proxy() : default_val; } +static std::string VhostUserVsock(const Instance& instance) { + const auto& crosvm = instance.vm().crosvm(); + const auto& default_val = CF_DEFAULTS_VHOST_USER_VSOCK; + return crosvm.has_vhost_user_vsock() ? crosvm.vhost_user_vsock() + : default_val; +} + static bool EnablePkvm(const Instance& instance) { const auto& vm = instance.vm(); return vm.has_enable_pkvm() ? vm.enable_pkvm() : CF_DEFAULTS_ENABLE_PKVM; @@ -257,10 +269,10 @@ Result> GenerateVmFlags( if (used_names.contains(kFlagUuid)) { flags.push_back(GenerateInstanceFlag(kFlagUuid, cfg, Uuid)); } - // TODO(b/517984573): Always pass enable_sandbox. - // if (used_names.contains(kFlagEnableSandbox)) { - flags.push_back(GenerateInstanceFlag(kFlagEnableSandbox, cfg, EnableSandbox)); - // } + if (used_names.contains(kFlagEnableSandbox)) { + flags.push_back( + GenerateInstanceFlag(kFlagEnableSandbox, cfg, EnableSandbox)); + } if (used_names.contains(kFlagCrosvmSimpleMediaDevice)) { flags.push_back(GenerateInstanceFlag(kFlagCrosvmSimpleMediaDevice, cfg, SimpleMediaDevice)); @@ -269,6 +281,10 @@ Result> GenerateVmFlags( flags.emplace_back( GenerateInstanceFlag(kFlagCrosvmV4l2Proxy, cfg, V4l2Proxy)); } + if (used_names.contains(kFlagVhostUserVsock)) { + flags.emplace_back( + GenerateInstanceFlag(kFlagVhostUserVsock, cfg, VhostUserVsock)); + } if (used_names.contains(kFlagEnablePkvm)) { flags.emplace_back(GenerateInstanceFlag(kFlagEnablePkvm, cfg, EnablePkvm)); } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/vm_configs_test.cc b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/vm_configs_test.cc index 9401a4bde5d..6d90077e89d 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/vm_configs_test.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/vm_configs_test.cc @@ -743,4 +743,71 @@ TEST(VmFlagsParserTest, ParseTwoInstancesCustomActionsFlagPartialJson) { EXPECT_THAT(ParseJson(custom_actions[0]), IsOkAndValue(expected_actions)); } +TEST(VmFlagsParserTest, ParseTwoInstancesVhostUserVsockFlagPartialJson) { + const char* test_string = R""""( +{ + "instances" : + [ + { + "vm": { + "crosvm":{ + } + } + }, + { + "vm": { + "crosvm":{ + "vhost_user_vsock": "true" + } + } + } + ] +} + )""""; + + Json::Value json_configs; + std::string json_text(test_string); + + EXPECT_TRUE(ParseJsonString(json_text, json_configs)) + << "Invalid Json string"; + auto serialized_data = LaunchCvdParserTester(json_configs); + EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); + EXPECT_TRUE(FindConfig(*serialized_data, R"(--vhost_user_vsock=auto,true)")) + << "vhost_user_vsock flag is missing or wrongly formatted"; +} + +TEST(VmFlagsParserTest, ParseTwoInstancesVhostUserVsockFlagFullJson) { + const char* test_string = R""""( +{ + "instances" : + [ + { + "vm": { + "crosvm":{ + "vhost_user_vsock": "true" + } + } + }, + { + "vm": { + "crosvm":{ + "vhost_user_vsock": "false" + } + } + } + ] +} + )""""; + + Json::Value json_configs; + std::string json_text(test_string); + + EXPECT_TRUE(ParseJsonString(json_text, json_configs)) + << "Invalid Json string"; + auto serialized_data = LaunchCvdParserTester(json_configs); + EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); + EXPECT_TRUE(FindConfig(*serialized_data, R"(--vhost_user_vsock=true,false)")) + << "vhost_user_vsock flag is missing or wrongly formatted"; +} + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto index a9177160a87..c80696c2b72 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto @@ -199,6 +199,7 @@ message Crosvm { optional bool enable_sandbox = 1; optional bool simple_media_device = 2; optional string v4l2_proxy = 3; + optional string vhost_user_vsock = 4; } message Gem5 {} diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml index 655c37a967a..25ad8495b39 100755 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml @@ -177,5 +177,9 @@ - + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806852 + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml index b4363da9308..1b65eb621a4 100755 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml @@ -203,5 +203,9 @@ - + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806852 + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml new file mode 100755 index 00000000000..f1f77b54035 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml @@ -0,0 +1,185 @@ + + + + 144,0,621A8205422100300483022F008A01058B032F0601800200C08801F0 + 144,0,61184F10A0000003431002FF86FF0389FFFFFFFF50044353494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,61184F10A0000000871002FF86FF0389FFFFFFFF50045553494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,62178202412183022FE28A01058B032F06038002000A880110 + 144,0,98683081462002318389 + + 89860318640220133898 + + + 144,0,62178202412183022F058A01058B032F060280020004880128 + 144,0,FFFFFFFF + + + + + + 144,0,62198205422100400283024F308A01058B036F0606800200808800 + 144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130 + + + 144,0,621A820542210002FA83024F098A01058B036F060E800201F4880110 + + + 144,0,621A82054221000FFA83024F118A01058B036F060E80020EA6880120 + + + + + + + + 311740123456790 + + + 144,0,621982054221001C0283026F408A01058B036F0605800200388800 + 144,0,000000000000000000000000000007915155214365F7FFFFFFFFFFFF + + + 144,0,62198205422100050183026FC98A01058B036F0602800200058800 + 144,0,0100000000 + + + 144,0,621982054221001C0283026F408A01058B036F06058002003E8800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07915155674523F1FFFFFFFFFFFF + + + 144,0,62178202412183026FAD8A01058B036F060180020004880118 + 144,0,00000003 + + + 144,0,62198205422100050183026FCA8A01058B036F060E800200058800 + 144,0,0000000000 + + + 106,130 + + + 144,0,621C8202412183026F7BA5038001718A01058B036F06038002001E880168 + 144,0,64F00064F02064F04064F07064F080FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621982054221001C0A83026F3B8A01058B036F0605800201188800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + + + + 4,6124 + 76,62228202412183025031A503C001408A01058B066F0601010001800200108102002288009000 + 36,A706300404024401A5063004040244029000 + + + + 6,019000 + 4,6b00 + + + + + + + + PINSTATE_UNKNOWN + 1234 + 12345678 + 3 + 10 + 1234 + 12345678 + 3 + 10 + + + + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + + + + + + + + + + + + + + + + + + + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806853 + + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml new file mode 100755 index 00000000000..c3e6db772bc --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml @@ -0,0 +1,211 @@ + + + + 144,0,621A8205422100300483022F008A01058B032F0601800200C08801F0 + 144,0,61184F10A0000003431002FF86FF0389FFFFFFFF50044353494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,61184F10A0000000871002FF86FF0389FFFFFFFF50045553494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,62178202412183022FE28A01058B032F06038002000A880110 + 144,0,98683081462002318389 + + 89860318640220133898 + + + 144,0,62178202412183022F058A01058B032F060280020004880128 + 144,0,FFFFFFFF + + + + + + 144,0,62198205422100400283024F308A01058B036F0606800200808800 + 144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130 + + + 144,0,621A820542210002FA83024F098A01058B036F060E800201F4880110 + + + 144,0,621A82054221000FFA83024F118A01058B036F060E80020EA6880120 + + + + + + + + 311740123456790 + + + 144,0,621982054221001C0283026F408A01058B036F0605800200388800 + 144,0,00000000000000000000000000000891688118109844F0FFFFFFFFFF + + + 144,0,62258205422100040183026FC9A503C001408A01058B066F060103000080020004810200188800 + 144,0,01000000 + + + 144,0,62178202412183026FAD8A01058B036F060180020004880118 + 144,0,00000002 + + + 144,0,62258205422100260483026FC7A503C001408A01058B066F060103000080020098810200AC8800 + 144,0 + 144,0 + 144,0 + + + 144,0,62198205422100050183026FCA8A01058B036F060E800200058800 + 144,0,0000000000 + + + 106,130 + + + 144,0,621C8202412183026F7BA5038001718A01058B036F06038002001E880168 + 144,0,64F00064F02064F04064F07064F080FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621982054221001C0A83026F3B8A01058B036F0605800201188800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + + + + + 76,62228202412183024300A503C001408A01058B066F0601010001800201DC810201EE88009000 + 516,30088200300404024310301AA0120410A000000476416E64726F696443545340300404024311301AA0120410A000000476416E64726F696443545341300404024312301AA0120410A000000476416E64726F696443545342300404024313301AA0120410A000000476416E64726F696443545343300404024314301AA0120410A000000476416E64726F696443545344300404024315301AA0120410A000000476416E64726F696443545345300404024316301AA0120410A000000476416E64726F6964435453463004040243173010A0080406FFFFFFFFFFFF300404024318301AA0120410A000000476416E64726F696443545347300404024313301AA0129000 + + + 76,62228202412183024318A503C001408A01058B066F0601010001800200188102002A88009000 + 124,3016041461ED377E85D386A8DFEE6B864BD85B0BFAA5AF8130220420CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA09000 + + + + + 6,019000 + 110,62338202782183023F00A50C80016187010183040007DBF08A01058B062F0601020002C60C90016083010183010A83010D8102FFFF9000 + 6,019000 + 4,9000 + 4,6C35 + 4,6B00 + 4,9000 + 4,6D00 + 4,6B00 + 4,6A82 + 4,6A81 + 4,6E00 + 4,9000 + 24,983311111111111111029000 + 78,622382054221004A1283022F06A503C001408A01058B062F060101000080020534810205489000 + + + + + 6,019000 + 4,6b00 + + + + + + + + PINSTATE_UNKNOWN + 1234 + 12345678 + 3 + 10 + 1234 + 12345678 + 3 + 10 + + + + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + + + + + + + + + + + + + + + + + + + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806853 + + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp index 3bfc1ec2e99..040b5cce472 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp @@ -89,7 +89,9 @@ int ModemSimulatorMain(int argc, char** argv) { NvramConfig::InitNvramConfigService(server_fds.size(), FLAGS_sim_type); // Don't get a SIGPIPE from the clients - if (sigaction(SIGPIPE, nullptr, nullptr) != 0) { + struct sigaction sa{}; + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, nullptr) != 0) { LOG(ERROR) << "Failed to set SIGPIPE to be ignored: " << strerror(errno); } diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp index 0d5f7646fd4..46f4d802781 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp @@ -313,6 +313,12 @@ std::vector SimService::InitializeCommandHandlers() { CommandHandler( "+CICCID", [this](const Client& client) { this->HandleGetIccId(client); }), + CommandHandler( + "+CEID", + [this](const Client& client) { this->HandleGetEid(client); }), + CommandHandler( + "+CATR", + [this](const Client& client) { this->HandleGetAtr(client); }), CommandHandler("+CLCK=", [this](const Client& client, std::string& cmd) { this->HandleFacilityLock(client, cmd); @@ -1323,6 +1329,58 @@ void SimService::HandleGetIccId(const Client& client) { client.SendCommandResponse(responses); } +void SimService::HandleGetEid(const Client& client) { + std::vector responses; + + XMLElement* root = sim_file_system_.GetRootElement(); + if (!root) { + client.SendCommandResponse(kCmeErrorOperationNotAllowed); + return; + } + + XMLElement* card_profile = root->FirstChildElement("CardProfile"); + if (!card_profile) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + XMLElement* final = card_profile->FirstChildElement("EID"); + if (!final) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + responses.push_back("+CEID: " + std::string(final->GetText())); + responses.push_back("OK"); + client.SendCommandResponse(responses); +} + +void SimService::HandleGetAtr(const Client& client) { + std::vector responses; + + XMLElement* root = sim_file_system_.GetRootElement(); + if (!root) { + client.SendCommandResponse(kCmeErrorOperationNotAllowed); + return; + } + + XMLElement* card_profile = root->FirstChildElement("CardProfile"); + if (!card_profile) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + XMLElement* final = card_profile->FirstChildElement("ATR"); + if (!final) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + responses.push_back("+CATR: " + std::string(final->GetText())); + responses.push_back("OK"); + client.SendCommandResponse(responses); +} + /* * AT+CLCK * Execute command is used to lock, unlock or interrogate a MT or a network diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h index 198aa697bfb..e3695f7791f 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h @@ -42,6 +42,8 @@ class SimService : public ModemService, public std::enable_shared_from_this> Commands() override { @@ -123,6 +125,11 @@ class NetsimServer : public CommandSource { // Port configuration. netsimd.AddParameter("--hci_port=", config_.rootcanal_hci_port()); + if (EnableNetsimNfc(config_)) { + netsimd.AddParameter("--grpc_uds_path=", grpc_socket_.CreateGrpcSocket( + "NetsimControlServer")); + } + // When no connector is requested, add the instance number if (config_.netsim_connector_instance_num() == config_.netsim_instance_num()) { @@ -138,8 +145,16 @@ class NetsimServer : public CommandSource { } // Add parameters from passthrough option --netsim-args. - for (auto const& arg : config_.netsim_args()) { - netsimd.AddParameter(arg); + // NETSIM_GRPC_PORT is extracted and injected as an environment variable; + // all other options are passed as command-line arguments. + for (const std::string& arg : config_.netsim_args()) { + if (arg.starts_with("NETSIM_GRPC_PORT=")) { + const std::string::size_type equals_pos = arg.find('='); + netsimd.AddEnvironmentVariable(arg.substr(0, equals_pos), + arg.substr(equals_pos + 1)); + } else { + netsimd.AddParameter(arg); + } } // Add command for forwarding the HCI port to a vsock server. @@ -223,7 +238,7 @@ class NetsimServer : public CommandSource { device.chips.emplace_back(chip); } // Add nfc chip if enabled - if (config_.enable_host_nfc() && !config_.enable_host_nfc_connector()) { + if (EnableNetsimNfc(config_)) { Chip chip("NFC"); chip.fd_in = CF_EXPECT(MakeFifo(instance, "nfc_fifo_vm.in")); chip.fd_out = CF_EXPECT(MakeFifo(instance, "nfc_fifo_vm.out")); @@ -277,12 +292,14 @@ class NetsimServer : public CommandSource { std::vector devices_; const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific instance_; + GrpcSocketCreator& grpc_socket_; }; } // namespace fruit::Component> + const CuttlefishConfig::InstanceSpecific, + GrpcSocketCreator>> NetsimServerComponent() { return fruit::createComponent() .addMultibinding() diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h index 65c0640ffcd..9dcf3e4a54b 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h @@ -17,12 +17,14 @@ #include "fruit/fruit.h" +#include "cuttlefish/host/commands/run_cvd/launch/grpc_socket_creator.h" #include "cuttlefish/host/libs/config/cuttlefish_config.h" namespace cuttlefish { fruit::Component> + const CuttlefishConfig::InstanceSpecific, + GrpcSocketCreator>> NetsimServerComponent(); } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs index 6986837e048..42f0b7af7df 100644 --- a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs +++ b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs @@ -171,7 +171,7 @@ impl Buffer { } /// Update the state of the buffer as well as its V4L2 representation. - fn set_state(&mut self, state: BufferState) { + fn set_state(&mut self, state: BufferState, width: u32, height: u32) { let mut flags = self.v4l2_buffer.flags(); match state { BufferState::New => { @@ -196,9 +196,9 @@ impl Buffer { { let planes = self.v4l2_buffer.planes_with_backing_iter_mut(); if let V4l2PlanesWithBackingMut::Mmap(mut planes) = planes { - *planes.next().unwrap().bytesused = WIDTH * HEIGHT; - *planes.next().unwrap().bytesused = WIDTH * HEIGHT / 4; - *planes.next().unwrap().bytesused = WIDTH * HEIGHT / 4; + *planes.next().unwrap().bytesused = width * height; + *planes.next().unwrap().bytesused = width * height / 4; + *planes.next().unwrap().bytesused = width * height / 4; } } self.v4l2_buffer.set_sequence(sequence); @@ -246,6 +246,8 @@ impl EmulatedCameraSession { fn write_pattern( iteration: u64, controls: &CameraControls, + width: u32, + height: u32, mut sink_y: WY, mut sink_u: WU, mut sink_v: WV, @@ -261,13 +263,13 @@ impl EmulatedCameraSession { let y = ((base_y as f32) * (controls.gain.value() as f32 / Gain::MIN as f32)).min(255.0) as u8; let u = ((iteration + 64) % 256) as u8; let v = ((iteration + 128) % 256) as u8; - for _ in 0..(WIDTH * HEIGHT) { + for _ in 0..(width * height) { writer_y.write_all(&[y]).map_err(|_| libc::EIO)?; } - for _ in 0..(WIDTH * HEIGHT / 4) { + for _ in 0..(width * height / 4) { writer_u.write_all(&[u]).map_err(|_| libc::EIO)?; } - for _ in 0..(WIDTH * HEIGHT / 4) { + for _ in 0..(width * height / 4) { writer_v.write_all(&[v]).map_err(|_| libc::EIO)?; } Ok(()) @@ -278,6 +280,8 @@ impl EmulatedCameraSession { &mut self, evt_queue: &mut Q, controls: &CameraControls, + width: u32, + height: u32, ) -> IoctlResult<()> { while let Some(buf_id) = self.queued_buffers.pop_front() { let iteration = self.iteration; @@ -294,6 +298,8 @@ impl EmulatedCameraSession { Self::write_pattern( iteration, controls, + width, + height, buffer.planes[0].fd.as_file(), buffer.planes[1].fd.as_file(), buffer.planes[2].fd.as_file(), @@ -301,7 +307,7 @@ impl EmulatedCameraSession { buffer.set_state(BufferState::Outgoing { sequence: iteration as u32, - }); + }, width, height); evt_queue.send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new( self.id, buffer.v4l2_buffer.clone(), @@ -332,6 +338,10 @@ pub struct EmulatedCamera, /// Camera controls. controls: CameraControls, + /// Width of the video. + width: u32, + /// Height of the video. + height: u32, } impl EmulatedCamera @@ -345,6 +355,8 @@ where mmap_manager: MmapMappingManager::from(mapper), active_session: None, controls: CameraControls::new(lens_facing), + width: 640, + height: 480, } } @@ -502,6 +514,12 @@ const WIDTH: u32 = 640; const HEIGHT: u32 = 480; const FRAME_RATE: u32 = 30; +const SUPPORTED_SIZES: [(u32, u32); 3] = [ + (320, 240), + (640, 480), + (1280, 720), +]; + const INPUTS: [bindings::v4l2_input; 1] = [bindings::v4l2_input { index: 0, name: *b"Default\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", @@ -518,28 +536,34 @@ fn default_fmtdesc(queue: QueueType) -> v4l2_fmtdesc { } } -fn default_fmt(queue: QueueType) -> v4l2_format { +fn session_fmt(queue: QueueType, width: u32, height: u32) -> v4l2_format { let pix_mp = bindings::v4l2_pix_format_mplane { - width: WIDTH, - height: HEIGHT, + width, + height, pixelformat: PIXELFORMAT, field: bindings::v4l2_field_V4L2_FIELD_NONE, colorspace: bindings::v4l2_colorspace_V4L2_COLORSPACE_SRGB, num_planes: 3, plane_fmt: [ bindings::v4l2_plane_pix_format { - sizeimage: WIDTH * HEIGHT, - bytesperline: WIDTH, + // Size of Y plane + sizeimage: width * height, + // Bytes per line for Y plane + bytesperline: width, ..Default::default() }, bindings::v4l2_plane_pix_format { - sizeimage: WIDTH * HEIGHT / 4, - bytesperline: WIDTH / 2, + // Size of U plane (chroma subsampled by 2 in both directions) + sizeimage: width * height / 4, + // Bytes per line for U plane + bytesperline: width / 2, ..Default::default() }, bindings::v4l2_plane_pix_format { - sizeimage: WIDTH * HEIGHT / 4, - bytesperline: WIDTH / 2, + // Size of V plane + sizeimage: width * height / 4, + // Bytes per line for V plane + bytesperline: width / 2, ..Default::default() }, Default::default(), @@ -557,6 +581,10 @@ fn default_fmt(queue: QueueType) -> v4l2_format { } } +fn default_fmt(queue: QueueType) -> v4l2_format { + session_fmt(queue, WIDTH, HEIGHT) +} + /// Implementations of the ioctls required by a v4l2 CAPTURE device. impl VirtioMediaIoctlHandler for EmulatedCamera where @@ -581,35 +609,60 @@ where Ok(default_fmtdesc(queue)) } - fn g_fmt(&mut self, _session: &Self::Session, queue: QueueType) -> IoctlResult { + fn g_fmt(&mut self, session: &Self::Session, queue: QueueType) -> IoctlResult { if queue != QueueType::VideoCaptureMplane { return Err(libc::EINVAL); } - Ok(default_fmt(queue)) + log::info!("g_fmt: returning {}x{}", self.width, self.height); + Ok(session_fmt(queue, self.width, self.height)) } fn s_fmt( &mut self, - _session: &mut Self::Session, + session: &mut Self::Session, queue: QueueType, - _format: v4l2_format, + format: v4l2_format, ) -> IoctlResult { if queue != QueueType::VideoCaptureMplane { return Err(libc::EINVAL); } - Ok(default_fmt(queue)) + + let pix_mp = unsafe { format.fmt.pix_mp }; + let req_width = pix_mp.width; + let req_height = pix_mp.height; + log::info!("s_fmt: requested {}x{}", req_width, req_height); + + if SUPPORTED_SIZES.contains(&(req_width, req_height)) { + self.width = req_width; + self.height = req_height; + log::info!("s_fmt: set resolution to {}x{}", req_width, req_height); + } else { + log::info!("s_fmt: requested resolution {}x{} not supported, keeping {}x{}", req_width, req_height, self.width, self.height); + } + + Ok(session_fmt(queue, self.width, self.height)) } fn try_fmt( &mut self, - _session: &Self::Session, + session: &Self::Session, queue: QueueType, - _format: v4l2_format, + format: v4l2_format, ) -> IoctlResult { if queue != QueueType::VideoCaptureMplane { return Err(libc::EINVAL); } - Ok(default_fmt(queue)) + + let pix_mp = unsafe { format.fmt.pix_mp }; + let req_width = pix_mp.width; + let req_height = pix_mp.height; + log::info!("try_fmt: requested {}x{}", req_width, req_height); + + if SUPPORTED_SIZES.contains(&(req_width, req_height)) { + Ok(session_fmt(queue, req_width, req_height)) + } else { + Ok(session_fmt(queue, self.width, self.height)) + } } fn g_parm( @@ -689,7 +742,7 @@ where // TODO factorize with streamoff. session.queued_buffers.clear(); for buffer in session.buffers.iter_mut() { - buffer.set_state(BufferState::New); + buffer.set_state(BufferState::New, self.width, self.height); } self.active_session = Some(session.id); } @@ -702,9 +755,9 @@ where } } - let size_y = (WIDTH * HEIGHT) as u64; - let size_u = (WIDTH * HEIGHT / 4) as u64; - let size_v = (WIDTH * HEIGHT / 4) as u64; + let size_y = (self.width * self.height) as u64; + let size_u = (self.width * self.height / 4) as u64; + let size_v = (self.width * self.height / 4) as u64; session.buffers = (0..count) .map(|i| -> std::result::Result { @@ -824,13 +877,13 @@ where return Err(libc::EINVAL); } - host_buffer.set_state(BufferState::Incoming); + host_buffer.set_state(BufferState::Incoming, self.width, self.height); session.queued_buffers.push_back(buffer.index() as usize); let buffer = host_buffer.v4l2_buffer.clone(); if session.streaming { - session.process_queued_buffers(&mut self.evt_queue, &self.controls)?; + session.process_queued_buffers(&mut self.evt_queue, &self.controls, self.width, self.height)?; } Ok(buffer) @@ -842,7 +895,7 @@ where } session.streaming = true; - session.process_queued_buffers(&mut self.evt_queue, &self.controls)?; + session.process_queued_buffers(&mut self.evt_queue, &self.controls, self.width, self.height)?; Ok(()) } @@ -854,7 +907,7 @@ where session.streaming = false; session.queued_buffers.clear(); for buffer in session.buffers.iter_mut() { - buffer.set_state(BufferState::New); + buffer.set_state(BufferState::New, self.width, self.height); } Ok(()) @@ -882,21 +935,26 @@ where index: u32, pixel_format: u32, ) -> IoctlResult { + log::info!("enum_framesizes: index {}, format {}", index, pixel_format); if pixel_format != PIXELFORMAT { + log::info!("enum_framesizes: format {} not supported", pixel_format); return Err(libc::EINVAL); } - if index > 0 { - return Err(libc::EINVAL); - } + + let &(width, height) = SUPPORTED_SIZES.get(index as usize).ok_or_else(|| { + log::info!("enum_framesizes: index {} out of bounds", index); + libc::EINVAL + })?; + log::info!("enum_framesizes: returning {}x{}", width, height); Ok(bindings::v4l2_frmsizeenum { index, pixel_format, type_: bindings::v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE, __bindgen_anon_1: bindings::v4l2_frmsizeenum__bindgen_ty_1 { discrete: bindings::v4l2_frmsize_discrete { - width: WIDTH, - height: HEIGHT, + width, + height, }, }, ..Default::default() @@ -911,13 +969,17 @@ where width: u32, height: u32, ) -> IoctlResult { + log::info!("enum_frameintervals: index {}, format {}, {}x{}", index, pixel_format, width, height); if pixel_format != PIXELFORMAT { + log::info!("enum_frameintervals: format {} not supported", pixel_format); return Err(libc::EINVAL); } - if width != WIDTH || height != HEIGHT { + if !SUPPORTED_SIZES.contains(&(width, height)) { + log::info!("enum_frameintervals: size {}x{} not supported", width, height); return Err(libc::EINVAL); } if index > 0 { + log::info!("enum_frameintervals: index {} > 0 not supported", index); return Err(libc::EINVAL); } @@ -994,7 +1056,7 @@ where // Ensure all requested controls belong to the selected class. if let CtrlWhich::Class(class_id) = which { - for (idx, ctrl) in ctrl_array.iter().enumerate() { + for (_idx, ctrl) in ctrl_array.iter().enumerate() { if v4l2_ctrl_id2which(ctrl.id) != class_id { ctrls.error_idx = ctrls.count; return Err(libc::EINVAL); @@ -1003,7 +1065,7 @@ where } // Process controls. Class controls are write-only headers and must fail on read. - for (idx, ctrl) in ctrl_array.iter_mut().enumerate() { + for (_idx, ctrl) in ctrl_array.iter_mut().enumerate() { match ctrl.id { bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { ctrls.error_idx = ctrls.count; @@ -1114,7 +1176,7 @@ where // Ensure all requested controls belong to the selected class. if let CtrlWhich::Class(class_id) = which { - for (idx, ctrl) in ctrl_array.iter().enumerate() { + for (_idx, ctrl) in ctrl_array.iter().enumerate() { if v4l2_ctrl_id2which(ctrl.id) != class_id { ctrls.error_idx = ctrls.count; return Err(libc::EINVAL); @@ -1123,7 +1185,7 @@ where } // Apply control values. Class controls are read-only headers and must fail on write/try. - for (idx, ctrl) in ctrl_array.iter_mut().enumerate() { + for (_idx, ctrl) in ctrl_array.iter_mut().enumerate() { match ctrl.id { bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { ctrls.error_idx = ctrls.count; diff --git a/base/cvd/cuttlefish/host/graphics_detector/BUILD.bazel b/base/cvd/cuttlefish/host/graphics_detector/BUILD.bazel index 871bf41f1cf..08a88bc0b46 100644 --- a/base/cvd/cuttlefish/host/graphics_detector/BUILD.bazel +++ b/base/cvd/cuttlefish/host/graphics_detector/BUILD.bazel @@ -35,6 +35,8 @@ cf_cc_binary( "graphics_detector_vk.h", "graphics_detector_vk_external_memory_host.cpp", "graphics_detector_vk_external_memory_host.h", + "graphics_detector_vk_loader.cpp", + "graphics_detector_vk_loader.h", "graphics_detector_vk_precision_qualifiers_on_yuv_samplers.cpp", "graphics_detector_vk_precision_qualifiers_on_yuv_samplers.h", "image.cpp", diff --git a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.cpp b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.cpp index c4849ece40d..b6dd36c1114 100644 --- a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.cpp +++ b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.cpp @@ -19,6 +19,7 @@ #include "cuttlefish/host/graphics_detector/graphics_detector_gl.h" #include "cuttlefish/host/graphics_detector/graphics_detector_vk.h" #include "cuttlefish/host/graphics_detector/graphics_detector_vk_external_memory_host.h" +#include "cuttlefish/host/graphics_detector/graphics_detector_vk_loader.h" #include "cuttlefish/host/graphics_detector/graphics_detector_vk_precision_qualifiers_on_yuv_samplers.h" #include "cuttlefish/host/graphics_detector/subprocess.h" @@ -32,6 +33,7 @@ ::gfxstream::proto::GraphicsAvailability DetectGraphicsAvailability() { const std::vector> checks = { {"PopulateEglAndGlesAvailability", PopulateEglAndGlesAvailability}, {"PopulateVulkanAvailability", PopulateVulkanAvailability}, + {"PopulateVulkanLoaderAvailability", PopulateVulkanLoaderAvailability}, {"PopulateVulkanExternalMemoryHostQuirk", PopulateVulkanExternalMemoryHostQuirk}, {"PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirk", diff --git a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.proto b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.proto index 0858b58c25d..e434f3e438e 100644 --- a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.proto +++ b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector.proto @@ -53,6 +53,13 @@ message VulkanQuirks { optional VulkanExternalMemoryHostQuirks external_memory_host_quirks = 2; } +message VulkanQueueFamily { + optional bool supports_compute = 1; + optional bool supports_graphics = 2; + optional bool supports_transfer = 3; + optional uint32 queue_count = 4; +} + message VulkanPhysicalDevice { optional string name = 1; repeated string extensions = 2; @@ -63,6 +70,8 @@ message VulkanPhysicalDevice { optional Type type = 3; optional VulkanQuirks quirks = 4; + + repeated VulkanQueueFamily queue_families = 5; } message VulkanAvailability { @@ -76,4 +85,6 @@ message GraphicsAvailability { optional VulkanAvailability vulkan = 2; repeated string errors = 3; + + optional bool vulkan_loader_available = 4; } diff --git a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk.cpp b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk.cpp index f62950f871f..bf0a5b57211 100644 --- a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk.cpp +++ b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk.cpp @@ -45,6 +45,18 @@ gfxstream::expected PopulateVulkanAvailabilityImpl( for (const auto& ext : exts) { outPhysicalDevice->add_extensions(std::string(ext.extensionName)); } + + const auto queueFamilies = physicalDevice.getQueueFamilyProperties(); + for (const auto& queueFamily : queueFamilies) { + auto* outQueueFamily = outPhysicalDevice->add_queue_families(); + outQueueFamily->set_supports_compute(static_cast( + queueFamily.queueFlags & vk::QueueFlagBits::eCompute)); + outQueueFamily->set_supports_graphics(static_cast( + queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)); + outQueueFamily->set_supports_transfer(static_cast( + queueFamily.queueFlags & vk::QueueFlagBits::eTransfer)); + outQueueFamily->set_queue_count(queueFamily.queueCount); + } } return Ok{}; diff --git a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.cpp b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.cpp new file mode 100644 index 00000000000..9175c586bf2 --- /dev/null +++ b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * 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. + */ + +#include "cuttlefish/host/graphics_detector/graphics_detector_vk_loader.h" + +#include + +#include "cuttlefish/host/graphics_detector/expected.h" + +namespace gfxstream { + +gfxstream::expected PopulateVulkanLoaderAvailability( + ::gfxstream::proto::GraphicsAvailability* availability) { + void* libvulkan = dlopen("libvulkan.so.1", RTLD_LAZY | RTLD_LOCAL); + availability->set_vulkan_loader_available(libvulkan != nullptr); + if (libvulkan) { + dlclose(libvulkan); + } + return Ok{}; +} + +} // namespace gfxstream diff --git a/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.h b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.h new file mode 100644 index 00000000000..586a41dcc85 --- /dev/null +++ b/base/cvd/cuttlefish/host/graphics_detector/graphics_detector_vk_loader.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * 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. + */ +#pragma once + +#include "cuttlefish/host/graphics_detector/expected.h" +#include "cuttlefish/host/graphics_detector/graphics_detector.pb.h" + +namespace gfxstream { + +gfxstream::expected PopulateVulkanLoaderAvailability( + ::gfxstream::proto::GraphicsAvailability* availability); + +} // namespace gfxstream diff --git a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp index b4b02251775..cb868aff4e4 100644 --- a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp +++ b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp @@ -692,6 +692,10 @@ std::vector CuttlefishConfig::environment_dirs() const { return result; } +bool EnableNetsimNfc(const CuttlefishConfig& config) { + return config.enable_host_nfc() && !config.enable_host_nfc_connector(); +} + bool VmManagerIsCrosvm(const CuttlefishConfig& config) { return VmManagerIsCrosvm(config.vm_manager()); } diff --git a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h index 218d1df0d44..fd5f4f91c99 100644 --- a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h +++ b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h @@ -942,4 +942,6 @@ bool VmManagerIsCrosvm(const CuttlefishConfig&); bool VmManagerIsQemu(const CuttlefishConfig&); bool VmManagerIsGem5(const CuttlefishConfig&); +bool EnableNetsimNfc(const CuttlefishConfig& config); + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/config/media.cpp b/base/cvd/cuttlefish/host/libs/config/media.cpp index 7adbadaf61c..5ac4103b2e8 100644 --- a/base/cvd/cuttlefish/host/libs/config/media.cpp +++ b/base/cvd/cuttlefish/host/libs/config/media.cpp @@ -16,6 +16,7 @@ #include "cuttlefish/host/libs/config/media.h" +#include #include #include #include @@ -38,30 +39,30 @@ static constexpr char kMediaTypeV4l2Proxy[] = "v4l2_proxy"; Result> ParseMediaConfig( const std::string& flag) { - std::unordered_map props; - if (!flag.empty()) { - const std::vector pairs = absl::StrSplit(flag, ","); - for (const std::string& pair : pairs) { - const std::vector keyvalue = absl::StrSplit(pair, "="); - CF_EXPECT_EQ(keyvalue.size(), 2, - "Invalid media flag key-value: \"" << flag << "\""); - const std::string& prop_key = keyvalue[0]; - const std::string& prop_val = keyvalue[1]; - props[prop_key] = prop_val; - } - } + const std::vector parts = absl::StrSplit(flag, ":"); + CF_EXPECT(!parts.empty(), "Invalid media flag: \"" << flag << "\""); - auto type_it = props.find("type"); - CF_EXPECT(type_it != props.end(), "Missing media type"); + const std::string& type_str = parts[0]; CuttlefishConfig::MediaType type{CuttlefishConfig::MediaType::kUnknown}; - if (type_it->second == kMediaTypeV4l2EmulatedCameraSPlane) { + if (type_str == kMediaTypeV4l2EmulatedCameraSPlane) { type = CuttlefishConfig::MediaType::kV4l2EmulatedCameraSPlane; - } else if (type_it->second == kMediaTypeV4l2EmulatedCameraMPlane) { + } else if (type_str == kMediaTypeV4l2EmulatedCameraMPlane) { type = CuttlefishConfig::MediaType::kV4l2EmulatedCameraMPlane; - } else if (type_it->second == kMediaTypeV4l2Proxy) { + } else if (type_str == kMediaTypeV4l2Proxy) { type = CuttlefishConfig::MediaType::kV4l2Proxy; } else { - return CF_ERRF("Unknown media type value: \"{}\"", type_it->second); + return CF_ERRF("Unknown media type value: \"{}\"", type_str); + } + + std::unordered_map props; + for (size_t i = 1; i < parts.size(); ++i) { + const std::vector keyvalue = absl::StrSplit(parts[i], "="); + CF_EXPECT_EQ(keyvalue.size(), 2, + "Invalid media flag key-value: \"" << parts[i] << "\" in \"" + << flag << "\""); + const std::string& prop_key = keyvalue[0]; + const std::string& prop_val = keyvalue[1]; + props[prop_key] = prop_val; } std::string lens_facing = ""; diff --git a/base/cvd/cuttlefish/host/libs/config/media.h b/base/cvd/cuttlefish/host/libs/config/media.h index 946cebf64fc..fd329ce2021 100644 --- a/base/cvd/cuttlefish/host/libs/config/media.h +++ b/base/cvd/cuttlefish/host/libs/config/media.h @@ -24,15 +24,18 @@ namespace cuttlefish { constexpr const char kMediaFlag[] = "media"; constexpr const char kMediaHelp[] = - "Comma separated key=value pairs of media device properties. Supported " - "properties:\n" - " 'type': optional, defaults to 'v4l2_emulated_camera_splane', supported values:\n" - " 'v4l2_emulated_camera_splane': emulated media capture device (single-plane)\n" - " 'v4l2_emulated_camera_mplane': emulated media capture device (multi-plane)\n" + "Colon separated media device properties: " + "\"[type]:[key1]=[val1]:[key2]=[val2]\". " + "Supported types:\n" + " 'v4l2_emulated_camera_splane': emulated media capture device " + "(single-plane)\n" + " 'v4l2_emulated_camera_mplane': emulated media capture device " + "(multi-plane)\n" " 'v4l2_proxy': proxy a host V4L2 device into the guest\n" + "Supported keys:\n" " 'lens_facing': optional, supported values: 'FRONT', 'BACK', 'EXTERNAL'\n" "Example usage:\n" - " --media=type=v4l2_emulated_camera_splane,lens_facing=BACK\n"; + " --media=v4l2_emulated_camera_mplane:lens_facing=BACK\n"; Result> ParseMediaConfig( const std::string& flag); diff --git a/base/cvd/cuttlefish/package/BUILD.bazel b/base/cvd/cuttlefish/package/BUILD.bazel index 27de4a1cff1..384d90d0042 100644 --- a/base/cvd/cuttlefish/package/BUILD.bazel +++ b/base/cvd/cuttlefish/package/BUILD.bazel @@ -117,6 +117,8 @@ package_files( "cuttlefish-common/etc/cvd_custom_action_config/cuttlefish_example_action_config.json": "//cuttlefish/host/example_custom_actions:custom_action_config.json", "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml", "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim0.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim0.xml", + "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml", + "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim1.xml", "cuttlefish-common/etc/modem_simulator/files/numeric_operator.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/numeric_operator.xml", # "cuttlefish-common/bin/crosvm": "@crosvm_bin//:crosvm__crosvm", # TODO: b/402274999 - currently requires --enable_sandbox=false "cuttlefish-common/usr/share/webrtc/assets/client.html": "//cuttlefish/host/frontend/webrtc/html_client:client.html", diff --git a/base/debian/rules b/base/debian/rules index 76868178722..c85518c9c3a 100755 --- a/base/debian/rules +++ b/base/debian/rules @@ -115,3 +115,9 @@ override_dh_fixperms: dh_fixperms chmod -x ${cuttlefish_common}/bin/*.json find ${cuttlefish_common}/etc -type f -exec chmod -x '{}' ';' + +# Override udev rule priority (default is 60) +.PHONY: override_dh_installudev +override_dh_installudev: + dh_installudev --package=cuttlefish-base --priority=88 + dh_installudev --remaining-packages diff --git a/container/image/Containerfile b/container/image/Containerfile index 6b058d9d2ef..bcc74670df1 100644 --- a/container/image/Containerfile +++ b/container/image/Containerfile @@ -5,7 +5,7 @@ ARG BUILD_OPTION=prod -FROM mirror.gcr.io/library/debian:12 AS runner-base +FROM mirror.gcr.io/library/debian:13 AS runner-base # Expose Operator Port (HTTP:1080, HTTPS:1443) EXPOSE 1080 1443 @@ -26,6 +26,7 @@ RUN apt update RUN apt install -y --no-install-recommends \ ca-certificates \ curl \ + libvulkan1 \ mesa-utils \ nginx \ sudo @@ -59,10 +60,11 @@ RUN apt install -y --no-install-recommends -f \ FROM runner-base AS runner-prod ARG REPO -RUN apt install -y --no-install-recommends gnupg -RUN curl https://us-apt.pkg.dev/doc/repo-signing-key.gpg | apt-key add - -RUN echo "deb https://us-apt.pkg.dev/projects/android-cuttlefish-artifacts $REPO main" \ - | tee -a /etc/apt/sources.list.d/artifact-registry.list +RUN install -m 0755 -d /etc/apt/keyrings +RUN curl -fsSL https://us-apt.pkg.dev/doc/repo-signing-key.gpg -o /etc/apt/keyrings/android-cuttlefish-artifacts.asc +RUN chmod a+r /etc/apt/keyrings/android-cuttlefish-artifacts.asc +RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/android-cuttlefish-artifacts.asc] https://us-apt.pkg.dev/projects/android-cuttlefish-artifacts $REPO main" \ + | tee /etc/apt/sources.list.d/android-cuttlefish-artifacts.list > /dev/null RUN apt update RUN apt install -y --no-install-recommends \ cuttlefish-base \ diff --git a/e2etests/cvd/bugreport_tests/main_test.go b/e2etests/cvd/bugreport_tests/main_test.go index a1d9c5d02c0..170b397d8bb 100644 --- a/e2etests/cvd/bugreport_tests/main_test.go +++ b/e2etests/cvd/bugreport_tests/main_test.go @@ -29,13 +29,13 @@ func TestTakeBugreport(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/common/common.go b/e2etests/cvd/common/common.go index c827da9a8e9..dd8af8b41c9 100644 --- a/e2etests/cvd/common/common.go +++ b/e2etests/cvd/common/common.go @@ -166,7 +166,7 @@ type FetchAndCreateArgs struct { } // Performs `cvd fetch `. -func (tc *TestContext) CVDFetch(args FetchArgs) error { +func (tc *TestContext) CVDFetch(args FetchArgs) (CommandOutput, error) { log.Printf("Fetching...") fetchCmd := []string{ tc.TargetBin(), @@ -182,9 +182,10 @@ func (tc *TestContext) CVDFetch(args FetchArgs) error { if credentialArg != "" { fetchCmd = append(fetchCmd, fmt.Sprintf("--credential_source=%s", credentialArg)) } - if _, err := tc.RunCmd(fetchCmd...); err != nil { + res, err := tc.RunCmd(fetchCmd...); + if err != nil { log.Printf("Failed to fetch: %w", err) - return err + return res, err } // Android CTS includes some files with a `kernel` suffix which confuses the @@ -195,11 +196,11 @@ func (tc *TestContext) CVDFetch(args FetchArgs) error { log.Printf("Fetch completed!") - return nil + return res, nil } // Performs `cvd create `. -func (tc *TestContext) CVDCreate(args CreateArgs) error { +func (tc *TestContext) CVDCreate(args CreateArgs) (CommandOutput, error) { tempdirEnv := map[string]string{ "HOME": tc.tempdir, } @@ -213,13 +214,14 @@ func (tc *TestContext) CVDCreate(args CreateArgs) error { if len(args.Args) > 0 { createCmd = append(createCmd, args.Args...) } - if _, err := tc.RunCmdWithEnv(createCmd, tempdirEnv); err != nil { + res, err := tc.RunCmdWithEnv(createCmd, tempdirEnv) + if err != nil { log.Printf("Failed to create instance(s): %w", err) - return err + return res, err } tc.Cleanup(func() { tc.CVDStop() }) - return nil + return res, nil } // Performs `cvd stop`. @@ -577,11 +579,11 @@ func RunXts(t *testing.T, cuttlefishArgs FetchAndCreateArgs, xtsArgs XtsArgs) { log.Printf("Failed to find existing XTS, will fetch.") } - if err := tc.CVDFetch(cuttlefishArgs.Fetch); err != nil { + if _, err := tc.CVDFetch(cuttlefishArgs.Fetch); err != nil { t.Fatal(err) } - if err := tc.CVDCreate(cuttlefishArgs.Create); err != nil { + if _, err := tc.CVDCreate(cuttlefishArgs.Create); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/cvd_create_tests/BUILD.bazel b/e2etests/cvd/cvd_create_tests/BUILD.bazel index cac31215791..5d9544addfa 100644 --- a/e2etests/cvd/cvd_create_tests/BUILD.bazel +++ b/e2etests/cvd/cvd_create_tests/BUILD.bazel @@ -22,6 +22,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], deps = [ @@ -39,6 +40,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], deps = [ diff --git a/e2etests/cvd/cvd_create_tests/main_test.go b/e2etests/cvd/cvd_create_tests/main_test.go index 36ad9da854b..08685ff9757 100644 --- a/e2etests/cvd/cvd_create_tests/main_test.go +++ b/e2etests/cvd/cvd_create_tests/main_test.go @@ -38,6 +38,14 @@ func TestCvdCreate(t *testing.T) { branch: "git_main-throttled-nightly", target: "aosp_cf_x86_64_auto-trunk_staging-userdebug", }, + { + branch: "git_main-swcar-dev", + target: "aosp_cf_x86_64_sdv_core-trunk_staging-userdebug", + }, + { + branch: "git_main-swcar-dev", + target: "aosp_cf_x86_64_sdv_media-trunk_staging-userdebug", + }, } c := e2etests.TestContext{} for _, tc := range testcases { @@ -45,14 +53,14 @@ func TestCvdCreate(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: tc.branch, DefaultBuildTarget: tc.target, }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/cvd_load_tests/BUILD.bazel b/e2etests/cvd/cvd_load_tests/BUILD.bazel index a9343065be5..b08ed7faad1 100644 --- a/e2etests/cvd/cvd_load_tests/BUILD.bazel +++ b/e2etests/cvd/cvd_load_tests/BUILD.bazel @@ -25,6 +25,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], ) diff --git a/e2etests/cvd/cvd_load_tests/main_test.go b/e2etests/cvd/cvd_load_tests/main_test.go index 7ca46517d49..69612f98721 100644 --- a/e2etests/cvd/cvd_load_tests/main_test.go +++ b/e2etests/cvd/cvd_load_tests/main_test.go @@ -110,28 +110,61 @@ func TestCvdLoad(t *testing.T) { }`, }, { - name: "AospMainX64PhoneX2", + name: "GitSwCarDevSdv", loadconfig: ` { "instances": [ { "name": "ins-1", + "vm": { + "cpus": 2, + "memory_mb": 2048 + }, + "boot": { + "extra_bootconfig_args": "androidboot.sdv.instance_name=instance1 androidboot.virt.address=3 androidboot.sdv.boot_mode=unlocked" + }, + "security": { + "guest_enforce_security": false + }, "disk": { - "default_build": "@ab\/aosp-android-latest-release\/aosp_cf_x86_64_only_phone-userdebug" + "default_build": "@ab\/git_main-swcar-dev\/aosp_cf_x86_64_sdv_core-trunk_staging-userdebug" + }, + "graphics": { + "gpu_mode": "none" } }, { "name": "ins-2", + "vm": { + "cpus": 4, + "memory_mb": 4096 + }, + "boot": { + "extra_bootconfig_args": "androidboot.sdv.instance_name=instance2 androidboot.virt.address=4 androidboot.sdv.boot_mode=unlocked" + }, + "security": { + "guest_enforce_security": false + }, "disk": { - "default_build": "@ab\/aosp-android-latest-release\/aosp_cf_x86_64_only_phone-userdebug" + "default_build": "@ab\/git_main-swcar-dev\/aosp_cf_x86_64_sdv_media-trunk_staging-userdebug" + }, + "graphics": { + "displays": [ + { + "width": 1920, + "height": 1080 + } + ], + "gpu_mode": "gfxstream_guest_angle_host_swiftshader" } } ], + "netsim_bt": false, "metrics": { "enable": true }, "common": { - "host_package": "@ab\/aosp-android-latest-release\/aosp_cf_x86_64_only_phone-userdebug" + "host_package": "@ab\/git_main-swcar-dev\/aosp_cf_x86_64_sdv_media-trunk_staging-userdebug" } }`, }, diff --git a/e2etests/cvd/cvd_powerwash_tests/main_test.go b/e2etests/cvd/cvd_powerwash_tests/main_test.go index e5161ab00ed..634b4e03a8b 100644 --- a/e2etests/cvd/cvd_powerwash_tests/main_test.go +++ b/e2etests/cvd/cvd_powerwash_tests/main_test.go @@ -37,14 +37,14 @@ func TestCvdPowerwash(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: tc.branch, DefaultBuildTarget: tc.target, }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/display_tests/main_test.go b/e2etests/cvd/display_tests/main_test.go index be15878fa4f..d86b19a44ed 100644 --- a/e2etests/cvd/display_tests/main_test.go +++ b/e2etests/cvd/display_tests/main_test.go @@ -24,14 +24,14 @@ func addDisplay(c e2etests.TestContext, t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if res, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { - t.Fatal(err) + t.Fatalf("cvd fetch failed with %v, stderr:%s", err, res.Stderr) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } @@ -44,14 +44,14 @@ func listDisplays(c e2etests.TestContext, t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/env_tests/main_test.go b/e2etests/cvd/env_tests/main_test.go index d304e256b90..0d292466dcb 100644 --- a/e2etests/cvd/env_tests/main_test.go +++ b/e2etests/cvd/env_tests/main_test.go @@ -25,14 +25,14 @@ func TestListEnvServices(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/graphics_detector_tests/main_test.go b/e2etests/cvd/graphics_detector_tests/main_test.go index 3cffec9a4b6..c1eb2f3eb2d 100644 --- a/e2etests/cvd/graphics_detector_tests/main_test.go +++ b/e2etests/cvd/graphics_detector_tests/main_test.go @@ -25,14 +25,14 @@ func TestLaunchingWithAutoEnablesGfxstream(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/graphics_tests/gfxstream/BUILD.bazel b/e2etests/cvd/graphics_tests/gfxstream/BUILD.bazel index e538b962439..f883f688c17 100644 --- a/e2etests/cvd/graphics_tests/gfxstream/BUILD.bazel +++ b/e2etests/cvd/graphics_tests/gfxstream/BUILD.bazel @@ -25,6 +25,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "requires_gpu", "supports-graceful-termination", ], diff --git a/e2etests/cvd/graphics_tests/gfxstream_guest_angle/BUILD.bazel b/e2etests/cvd/graphics_tests/gfxstream_guest_angle/BUILD.bazel index 6c8dfec34ca..989db29d357 100644 --- a/e2etests/cvd/graphics_tests/gfxstream_guest_angle/BUILD.bazel +++ b/e2etests/cvd/graphics_tests/gfxstream_guest_angle/BUILD.bazel @@ -25,6 +25,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", # This does not actually require a GPU but currently only the GPU image # has the XTS requirements preinstalled. "requires_gpu", diff --git a/e2etests/cvd/graphics_tests/gfxstream_guest_angle_host_swiftshader/BUILD.bazel b/e2etests/cvd/graphics_tests/gfxstream_guest_angle_host_swiftshader/BUILD.bazel index c77acb18d9e..52d976174b5 100644 --- a/e2etests/cvd/graphics_tests/gfxstream_guest_angle_host_swiftshader/BUILD.bazel +++ b/e2etests/cvd/graphics_tests/gfxstream_guest_angle_host_swiftshader/BUILD.bazel @@ -25,6 +25,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], ) diff --git a/e2etests/cvd/launch_cvd_tests/BUILD.bazel b/e2etests/cvd/launch_cvd_tests/BUILD.bazel index 4a38cc20ca2..2b95748fdf0 100644 --- a/e2etests/cvd/launch_cvd_tests/BUILD.bazel +++ b/e2etests/cvd/launch_cvd_tests/BUILD.bazel @@ -25,6 +25,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], ) diff --git a/e2etests/cvd/launch_cvd_tests/main_test.go b/e2etests/cvd/launch_cvd_tests/main_test.go index d2d0e6c5bec..2df6625b79b 100644 --- a/e2etests/cvd/launch_cvd_tests/main_test.go +++ b/e2etests/cvd/launch_cvd_tests/main_test.go @@ -61,6 +61,26 @@ func TestLaunchCvd(t *testing.T) { branch: "git_android16-car-release", target: "aosp_cf_x86_64_auto-userdebug", }, + { + name: "Car17Auto", + branch: "git_android17-car-release", + target: "aosp_cf_x86_64_auto-userdebug", + }, + { + name: "GitSwCarDevSdvCore", + branch: "git_main-swcar-dev", + target: "aosp_cf_x86_64_sdv_core-trunk_staging-userdebug", + }, + { + name: "GitSwCarDevSdvMedia", + branch: "git_main-swcar-dev", + target: "aosp_cf_x86_64_sdv_media-trunk_staging-userdebug", + }, + { + name: "Aosp11GsiPhone", + branch: "aosp-android11-gsi", + target: "aosp_cf_x86_64_only_phone-userdebug", + }, } c := e2etests.TestContext{} for _, tc := range testcases { @@ -68,7 +88,7 @@ func TestLaunchCvd(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: tc.branch, DefaultBuildTarget: tc.target, }); err != nil { diff --git a/e2etests/cvd/logs_tests/BUILD.bazel b/e2etests/cvd/logs_tests/BUILD.bazel index 1bf7c8b0653..880b0135d42 100644 --- a/e2etests/cvd/logs_tests/BUILD.bazel +++ b/e2etests/cvd/logs_tests/BUILD.bazel @@ -10,6 +10,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], deps = [ diff --git a/e2etests/cvd/media_tests/BUILD.bazel b/e2etests/cvd/media_tests/BUILD.bazel index 26581384dcb..9703a09f39c 100644 --- a/e2etests/cvd/media_tests/BUILD.bazel +++ b/e2etests/cvd/media_tests/BUILD.bazel @@ -11,22 +11,3 @@ # 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. - -load("@rules_go//go:def.bzl", "go_test") - -go_test( - name = "media_tests", - size = "large", - srcs = ["main_test.go"], - data = ["//:debian_substitution_marker"], - env = {"LOCAL_DEBIAN_SUBSTITUTION_MARKER_FILE": "$(rlocationpath //:debian_substitution_marker)"}, - tags = [ - "exclusive", - "external", - "no-sandbox", - "supports-graceful-termination", - ], - deps = [ - "//cvd/common", - ], -) diff --git a/e2etests/cvd/media_tests/cts/BUILD.bazel b/e2etests/cvd/media_tests/cts/BUILD.bazel new file mode 100644 index 00000000000..9babc7e7c95 --- /dev/null +++ b/e2etests/cvd/media_tests/cts/BUILD.bazel @@ -0,0 +1,31 @@ +# Copyright (C) 2026 The Android Open Source Project +# +# 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. + +load("@rules_go//go:def.bzl", "go_test") + +go_test( + name = "media_tests", + srcs = ["main_test.go"], + deps = [ + "//cvd/common:common", + ], + size = "large", + tags = [ + "exclusive", + "external", + "no-sandbox", + "requires_ab", + "supports-graceful-termination", + ], +) diff --git a/e2etests/cvd/media_tests/cts/main_test.go b/e2etests/cvd/media_tests/cts/main_test.go new file mode 100644 index 00000000000..bffd2bf451c --- /dev/null +++ b/e2etests/cvd/media_tests/cts/main_test.go @@ -0,0 +1,48 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// 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. + +package main + +import ( + "testing" + + "github.com/google/android-cuttlefish/e2etests/cvd/common" +) + +func TestEmulatedCamera(t *testing.T) { + e2etests.RunXts(t, + e2etests.FetchAndCreateArgs{ + Fetch: e2etests.FetchArgs{ + DefaultBuildBranch: "git_main", + DefaultBuildTarget: "aosp_cf_x86_64_only_phone-trunk_staging-userdebug", + TestSuiteBuildBranch: "aosp-android15-tests-release", + TestSuiteBuildTarget: "test_suites_x86_64", + }, + Create: e2etests.CreateArgs{ + Args: []string{ + "--media=v4l2_emulated_camera_mplane:lens_facing=BACK", + "--media=v4l2_emulated_camera_mplane:lens_facing=FRONT", + }, + }, + }, + e2etests.XtsArgs{ + XtsType: "cts", + XtsArgs: []string{ + "--include-filter=CtsAppTestCases android.app.cts.SystemFeaturesTest#testCameraFeatures", + "--include-filter=CtsCameraTestCases android.hardware.camera2.cts.CameraManagerTest", + "--include-filter=CtsCameraTestCases android.hardware.camera2.cts.CaptureResultTest", + "--include-filter=CtsCameraTestCases android.hardware.camera2.cts.FastBasicsTest", + }, + }) +} diff --git a/e2etests/cvd/media_tests/v4l2compliance/BUILD.bazel b/e2etests/cvd/media_tests/v4l2compliance/BUILD.bazel new file mode 100644 index 00000000000..8ae8f33fabf --- /dev/null +++ b/e2etests/cvd/media_tests/v4l2compliance/BUILD.bazel @@ -0,0 +1,33 @@ +# Copyright (C) 2026 The Android Open Source Project +# +# 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. + +load("@rules_go//go:def.bzl", "go_test") + +go_test( + name = "media_tests", + size = "large", + srcs = ["main_test.go"], + data = ["//:debian_substitution_marker"], + env = {"LOCAL_DEBIAN_SUBSTITUTION_MARKER_FILE": "$(rlocationpath //:debian_substitution_marker)"}, + tags = [ + "exclusive", + "external", + "no-sandbox", + "requires_ab", + "supports-graceful-termination", + ], + deps = [ + "//cvd/common", + ], +) diff --git a/e2etests/cvd/media_tests/main_test.go b/e2etests/cvd/media_tests/v4l2compliance/main_test.go similarity index 74% rename from e2etests/cvd/media_tests/main_test.go rename to e2etests/cvd/media_tests/v4l2compliance/main_test.go index eb14d63fb39..3af9d484d08 100644 --- a/e2etests/cvd/media_tests/main_test.go +++ b/e2etests/cvd/media_tests/v4l2compliance/main_test.go @@ -23,12 +23,19 @@ import ( func TestEmulatedCameraV4l2Compliance(t *testing.T) { testcases := []struct { - branch string - target string + branch string + target string + mediaType string }{ { - branch: "git_main", - target: "aosp_cf_x86_64_only_phone-trunk_staging-userdebug", + branch: "git_main", + target: "aosp_cf_x86_64_only_phone-trunk_staging-userdebug", + mediaType: "v4l2_emulated_camera_splane", + }, + { + branch: "git_main-throttled-nightly", + target: "aosp_cf_x86_64_auto-trunk_staging-userdebug", + mediaType: "v4l2_emulated_camera_mplane", }, } c := e2etests.TestContext{} @@ -37,14 +44,18 @@ func TestEmulatedCameraV4l2Compliance(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: tc.branch, DefaultBuildTarget: tc.target, }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{Args: []string{"--media=type=v4l2_emulated_camera_splane"}}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{ + Args: []string{ + fmt.Sprintf("--media=%s", tc.mediaType), + }, + }); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/metrics_tests/main_test.go b/e2etests/cvd/metrics_tests/main_test.go index 59b6741a824..e07770e3a0b 100644 --- a/e2etests/cvd/metrics_tests/main_test.go +++ b/e2etests/cvd/metrics_tests/main_test.go @@ -37,14 +37,14 @@ func TestMetrics(t *testing.T) { c.SetUp(t) defer c.TearDown() - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: "aosp-android-latest-release", DefaultBuildTarget: "aosp_cf_x86_64_only_phone-userdebug", }); err != nil { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { t.Fatal(err) } diff --git a/e2etests/cvd/networking_tests/BUILD.bazel b/e2etests/cvd/networking_tests/BUILD.bazel index 016558d0542..07f6b17475c 100644 --- a/e2etests/cvd/networking_tests/BUILD.bazel +++ b/e2etests/cvd/networking_tests/BUILD.bazel @@ -13,6 +13,7 @@ go_test( "exclusive", "external", "no-sandbox", + "requires_ab", "supports-graceful-termination", ], deps = [ diff --git a/e2etests/cvd/networking_tests/main_test.go b/e2etests/cvd/networking_tests/main_test.go index 29923ed3668..cfe1e0df640 100644 --- a/e2etests/cvd/networking_tests/main_test.go +++ b/e2etests/cvd/networking_tests/main_test.go @@ -25,8 +25,10 @@ import ( func TestDeviceNetworking(t *testing.T) { testcases := []struct { - branch string - target string + name string + branch string + target string + createArgs e2etests.CreateArgs }{ { branch: "aosp-android-latest-release", @@ -36,15 +38,27 @@ func TestDeviceNetworking(t *testing.T) { branch: "git_main", target: "aosp_cf_x86_64_only_phone-trunk_staging-userdebug", }, + { + name: "cvdalloc", + branch: "git_main", + target: "aosp_cf_x86_64_only_phone-trunk_staging-userdebug", + createArgs: e2etests.CreateArgs{ + Args: []string{"--use_cvdalloc=true"}, + }, + }, } c := e2etests.TestContext{} for _, tc := range testcases { - t.Run(fmt.Sprintf("BUILD=%s/%s", tc.branch, tc.target), func(t *testing.T) { + testName := fmt.Sprintf("BUILD=%s/%s", tc.branch, tc.target) + if tc.name != "" { + testName = fmt.Sprintf("%s_CONFIG=%s", testName, tc.name) + } + t.Run(testName, func(t *testing.T) { c.SetUp(t) defer c.TearDown() t.Log("Fetching remote build...") - if err := c.CVDFetch(e2etests.FetchArgs{ + if _, err := c.CVDFetch(e2etests.FetchArgs{ DefaultBuildBranch: tc.branch, DefaultBuildTarget: tc.target, }); err != nil { @@ -52,7 +66,7 @@ func TestDeviceNetworking(t *testing.T) { } t.Log("Launching Cuttlefish...") - if err := c.CVDCreate(e2etests.CreateArgs{}); err != nil { + if _, err := c.CVDCreate(tc.createArgs); err != nil { t.Fatal(err) } diff --git a/e2etests/debian_substitution_marker b/e2etests/debian_substitution_marker index 83c485fcbfb..0557c7d65b0 100644 --- a/e2etests/debian_substitution_marker +++ b/e2etests/debian_substitution_marker @@ -220,6 +220,11 @@ symlinks: { link_name: "bin/vhu_media_emulated_camera_splane" } +symlinks: { + target: "/usr/lib/cuttlefish-common/bin/vhu_media_emulated_camera_mplane" + link_name: "bin/vhu_media_emulated_camera_mplane" +} + symlinks: { target: "/usr/lib/cuttlefish-common/bin/webRTC" link_name: "bin/webRTC" @@ -275,6 +280,16 @@ symlinks: { link_name: "etc/modem_simulator/files/iccprofile_for_sim0.xml" } +symlinks: { + target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml" + link_name: "etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml" +} + +symlinks: { + target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1.xml" + link_name: "etc/modem_simulator/files/iccprofile_for_sim1.xml" +} + symlinks: { target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/numeric_operator.xml" link_name: "etc/modem_simulator/files/numeric_operator.xml" diff --git a/e2etests/orchestration/create_from_images_zip_test/main_test.go b/e2etests/orchestration/create_from_images_zip_test/main_test.go index d15b6243234..72fb5fc637a 100644 --- a/e2etests/orchestration/create_from_images_zip_test/main_test.go +++ b/e2etests/orchestration/create_from_images_zip_test/main_test.go @@ -90,7 +90,7 @@ func TestCreateInstance(t *testing.T) { Group: "foo", Name: "1", Status: "Running", - Displays: []string{"720 x 1280 ( 320 )"}, + Displays: []string{"720 x 1348 ( 280 )"}, WebRTCDeviceID: "cvd-1", ADBSerial: "127.0.0.1:6520", ADBPort: 6520, diff --git a/e2etests/orchestration/create_local_image_test/main_test.go b/e2etests/orchestration/create_local_image_test/main_test.go index 625effd8347..231c045c62a 100644 --- a/e2etests/orchestration/create_local_image_test/main_test.go +++ b/e2etests/orchestration/create_local_image_test/main_test.go @@ -98,7 +98,7 @@ func TestInstance(t *testing.T) { Group: group_name, Name: "1", Status: "Running", - Displays: []string{"720 x 1280 ( 320 )"}, + Displays: []string{"720 x 1348 ( 280 )"}, WebRTCDeviceID: "cvd-1", ADBSerial: "127.0.0.1:6520", ADBPort: 6520, diff --git a/e2etests/orchestration/stop_start_test/main_test.go b/e2etests/orchestration/stop_start_test/main_test.go index ed4ade7d152..ad1e6116858 100644 --- a/e2etests/orchestration/stop_start_test/main_test.go +++ b/e2etests/orchestration/stop_start_test/main_test.go @@ -104,17 +104,16 @@ func TestStopStart(t *testing.T) { t.Fatalf("status mismatch (-want +got):\n%s", diff) } - // TODO(b/523379344): Failing to boot. - // if err := srv.StartGroup("cvd_1", &hoapi.StartCVDRequest{}); err != nil { - // t.Fatal(err) - // } - // cvds, err = srv.ListCVDs() - // if err != nil { - // t.Fatal(err) - // } - // if diff := cmp.Diff(cvdStatus(cvds), []string{"Running", "Running"}); diff != "" { - // t.Fatalf("status mismatch (-want +got):\n%s", diff) - // } + if err := srv.StartGroup("cvd_1", &hoapi.StartCVDRequest{}); err != nil { + t.Fatal(err) + } + cvds, err = srv.ListCVDs() + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(cvdStatus(cvds), []string{"Running", "Running"}); diff != "" { + t.Fatalf("status mismatch (-want +got):\n%s", diff) + } } func cvdStatus(cvds []*hoapi.CVD) []string { diff --git a/frontend/src/host_orchestrator/orchestrator/listcvdsaction_test.go b/frontend/src/host_orchestrator/orchestrator/listcvdsaction_test.go index 06da3f28e3c..ffd0e447459 100644 --- a/frontend/src/host_orchestrator/orchestrator/listcvdsaction_test.go +++ b/frontend/src/host_orchestrator/orchestrator/listcvdsaction_test.go @@ -41,7 +41,7 @@ func TestListCVDsSucceeds(t *testing.T) { "adb_serial": "0.0.0.0:6520", "assembly_dir": "/var/lib/cuttlefish-common/runtimes/cuttlefish/assembly", "displays": [ - "720 x 1280 ( 320 )" + "720 x 1348 ( 280 )" ], "instance_dir": "/var/lib/cuttlefish-common/runtimes/cuttlefish/instances/cvd-1", "instance_name": "1", @@ -60,7 +60,7 @@ func TestListCVDsSucceeds(t *testing.T) { "adb_serial": "0.0.0.0:6520", "assembly_dir": "/var/lib/cuttlefish-common/runtimes/cuttlefish/assembly", "displays": [ - "720 x 1280 ( 320 )" + "720 x 1348 ( 280 )" ], "instance_dir": "/var/lib/cuttlefish-common/runtimes/cuttlefish/instances/cvd-1", "instance_name": "1", @@ -87,7 +87,7 @@ func TestListCVDsSucceeds(t *testing.T) { Group: "foo", Name: "1", Status: "Running", - Displays: []string{"720 x 1280 ( 320 )"}, + Displays: []string{"720 x 1348 ( 280 )"}, WebRTCDeviceID: "cvd-1", ADBSerial: "0.0.0.0:6520", ADBPort: 6520, @@ -96,7 +96,7 @@ func TestListCVDsSucceeds(t *testing.T) { Group: "bar", Name: "1", Status: "Running", - Displays: []string{"720 x 1280 ( 320 )"}, + Displays: []string{"720 x 1348 ( 280 )"}, WebRTCDeviceID: "cvd-1", ADBSerial: "0.0.0.0:6520", ADBPort: 6520, diff --git a/frontend/src/libhoclient/fake_host_orchestrator_client.go b/frontend/src/libhoclient/fake_host_orchestrator_client.go index e6d217a6e6b..dc361786261 100644 --- a/frontend/src/libhoclient/fake_host_orchestrator_client.go +++ b/frontend/src/libhoclient/fake_host_orchestrator_client.go @@ -182,7 +182,7 @@ func (c *FakeHostOrchestratorClient) createFakeCVDs(total int) ([]*hoapi.CVD, er Group: fmt.Sprintf("cvd-%d", id), Name: fmt.Sprintf("%d", id), Status: "Running", - Displays: []string{"720 x 1280 (320)"}, + Displays: []string{"720 x 1348 ( 280 )"}, WebRTCDeviceID: fmt.Sprintf("cvd-%d-%d", id, id), ADBSerial: fmt.Sprintf("0.0.0.0:%d", 6520+id-1), } diff --git a/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh b/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh index 3872c447c5a..62d1594b175 100755 --- a/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh +++ b/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh @@ -4,11 +4,11 @@ set -o errexit -URL=https://ci.android.com/builds/latest/branches/aosp-android-latest-release/targets/aosp_cf_arm64_only_phone-userdebug/view/BUILD_INFO -RURL=$(curl -Ls -o /dev/null -w %{url_effective} ${URL}) -echo "RURL = ${RURL}" - -BUILD_ID=$(echo "${RURL}" | sed -n 's/.*\/builds\/submitted\/\([^\/]*\)\/.*/\1/p') +BRANCH=aosp-android-latest-release +TARGET=aosp_cf_arm64_only_phone-userdebug +BUILD_ID=$(curl -fsS \ + "https://ci.android.com/builds/branches/${BRANCH}/targets/${TARGET}/status.json" \ + | sed -n 's/.*[{,[:space:]]"last_known_good_build"[[:space:]]*:[[:space:]]*"\([0-9][0-9]*\)".*/\1/p') echo "BUILD_ID = ${BUILD_ID}" if [[ -z "${BUILD_ID}" ]]; then @@ -19,12 +19,9 @@ fi FILENAME="aosp_cf_arm64_only_phone-img-${BUILD_ID}.zip" echo "FILENAME = ${FILENAME}" -if [[ -z "${FILENAME}" ]]; then - echo "Error: FILENAME empty." - exit 1 -fi +RAWURL="https://ci.android.com/builds/submitted/${BUILD_ID}/${TARGET}/latest/raw" -wget -nv -c ${RURL%/view/BUILD_INFO}/raw/${FILENAME} -wget -nv -c ${RURL%/view/BUILD_INFO}/raw/cvd-host_package.tar.gz +wget -nv -c ${RAWURL}/${FILENAME} +wget -nv -c ${RAWURL}/cvd-host_package.tar.gz exit 0 diff --git a/tools/baseimage/pkg/gce/gce.go b/tools/baseimage/pkg/gce/gce.go index 9093424dc6f..cd26abed16d 100644 --- a/tools/baseimage/pkg/gce/gce.go +++ b/tools/baseimage/pkg/gce/gce.go @@ -295,6 +295,12 @@ func (h *GceHelper) BuildImage(project, zone string, opts BuildImageOpts) error defer h.cleanupDetachDisk(insName, attachedDiskName) log.Println("disk attached") + log.Println("waiting for instance to become responsive over SSH...") + if err := WaitForInstance(project, zone, insName); err != nil { + return fmt.Errorf("instance failed to become responsive over SSH: %w", err) + } + log.Println("instance is responsive over SSH") + if err := UploadBashScript(project, zone, insName, "fill_available_disk_space.sh", scripts.FillAvailableDiskSpace); err != nil { return fmt.Errorf("error uploading script: %v", err) } diff --git a/tools/baseimage/pkg/gce/scripts/install_kernel_main.sh b/tools/baseimage/pkg/gce/scripts/install_kernel_main.sh index 0bfa854c367..363fe9513ac 100644 --- a/tools/baseimage/pkg/gce/scripts/install_kernel_main.sh +++ b/tools/baseimage/pkg/gce/scripts/install_kernel_main.sh @@ -52,6 +52,20 @@ if [ "${version}" != "${linux_image_deb}" ]; then exit 1 fi +# Remove old kernel packages, keeping only the target kernel and the +# linux-image-cloud-${arch} meta-package. +old_kernels=$(sudo chroot /mnt/image /usr/bin/dpkg -l | grep '^ii' | awk '{print $2}' | \ + grep '^linux-image-' | grep -v "^${linux_image_deb}$" | grep -v "^linux-image-cloud-${arch}$" || true) +if [ -n "${old_kernels}" ]; then + echo "Removing old kernel packages: ${old_kernels}" + sudo chroot /mnt/image /usr/bin/apt-get purge -y ${old_kernels} + # update-grub may fail in a chroot; the grub config will be rebuilt + # when the image boots, so this is non-fatal. + sudo chroot /mnt/image /bin/sh -c 'command -v update-grub >/dev/null && update-grub || true' +else + echo "No old kernel packages to remove" +fi + # Skip unmounting: # Sometimes systemd starts, making it hard to unmount # In any case we'll unmount cleanly when the instance shuts down diff --git a/tools/buildutils/cw/Containerfile b/tools/buildutils/cw/Containerfile index 7a75e0f7693..7827a94cf9f 100644 --- a/tools/buildutils/cw/Containerfile +++ b/tools/buildutils/cw/Containerfile @@ -1,16 +1,10 @@ -FROM mirror.gcr.io/library/debian:bookworm-20250811 AS base +FROM mirror.gcr.io/library/debian:13 AS base ENV DEBIAN_FRONTEND=noninteractive RUN apt update -y && apt upgrade -y RUN apt install -y sudo devscripts -# Download newer version of golang with keep using bookworm for building debian -# packages. -RUN echo "deb http://deb.debian.org/debian bookworm-backports main" > /etc/apt/sources.list.d/backports.list -RUN apt update -RUN apt install -t bookworm-backports -y golang - COPY ./tools/buildutils/installbazel.sh /installbazel.sh RUN /installbazel.sh && rm /installbazel.sh diff --git a/tools/testutils/cw/Containerfile b/tools/testutils/cw/Containerfile index 3dee19c77d2..99b8dfad03d 100644 --- a/tools/testutils/cw/Containerfile +++ b/tools/testutils/cw/Containerfile @@ -1,4 +1,4 @@ -FROM mirror.gcr.io/library/debian:bookworm-20250811 AS base +FROM mirror.gcr.io/library/debian:13 AS base ENV DEBIAN_FRONTEND=noninteractive ENV OVERRIDE_BAZEL_WRAPPER_DOWNLOAD_DIR=/tmp/cw_bazel @@ -6,6 +6,9 @@ ENV OVERRIDE_BAZEL_WRAPPER_DOWNLOAD_DIR=/tmp/cw_bazel RUN apt-get update && apt-get upgrade -y RUN apt-get install -y sudo systemd init systemd-journal-remote nginx jq adb +# Packages needed to run CTS +RUN apt-get install -y aapt + RUN groupadd kvm COPY tools/testutils/cw/setup.service /etc/systemd/system/setup.service RUN systemctl enable setup