aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--host/commands/assemble_cvd/disk/disk.h10
-rw-r--r--host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp157
-rw-r--r--host/commands/assemble_cvd/disk/generate_persistent_vbmeta.cpp6
-rw-r--r--host/commands/assemble_cvd/disk_flags.cc2
-rw-r--r--host/libs/audio_connector/buffers.cpp24
-rw-r--r--host/libs/audio_connector/buffers.h39
-rw-r--r--host/libs/audio_connector/server.cpp41
-rw-r--r--host/libs/audio_connector/server.h4
-rw-r--r--shared/bluetooth/device_vendor.mk6
-rw-r--r--shared/device.mk2
10 files changed, 133 insertions, 158 deletions
diff --git a/host/commands/assemble_cvd/disk/disk.h b/host/commands/assemble_cvd/disk/disk.h
index a32ab0377..548ab71b2 100644
--- a/host/commands/assemble_cvd/disk/disk.h
+++ b/host/commands/assemble_cvd/disk/disk.h
@@ -33,12 +33,8 @@ fruit::Component<fruit::Required<const CuttlefishConfig,
KernelRamdiskRepacker>
KernelRamdiskRepackerComponent();
-class GeneratePersistentBootconfig : public SetupFeature {};
-
-fruit::Component<fruit::Required<const CuttlefishConfig,
- const CuttlefishConfig::InstanceSpecific>,
- GeneratePersistentBootconfig>
-GeneratePersistentBootconfigComponent();
+Result<void> GeneratePersistentBootconfig(
+ const CuttlefishConfig&, const CuttlefishConfig::InstanceSpecific&);
fruit::Component<fruit::Required<const CuttlefishConfig, KernelRamdiskRepacker>>
Gem5ImageUnpackerComponent();
@@ -47,7 +43,7 @@ class GeneratePersistentVbmeta : public SetupFeature {};
fruit::Component<fruit::Required<const CuttlefishConfig::InstanceSpecific,
AutoSetup<InitBootloaderEnvPartition>::Type,
- GeneratePersistentBootconfig>,
+ AutoSetup<GeneratePersistentBootconfig>::Type>,
GeneratePersistentVbmeta>
GeneratePersistentVbmetaComponent();
diff --git a/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp b/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
index 568220594..d0a841fec 100644
--- a/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
+++ b/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
@@ -37,98 +37,77 @@
namespace cuttlefish {
-class GeneratePersistentBootconfigImpl : public GeneratePersistentBootconfig {
- public:
- INJECT(GeneratePersistentBootconfigImpl(
- const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance))
- : config_(config), instance_(instance) {}
-
- // SetupFeature
- std::string Name() const override { return "GeneratePersistentBootconfig"; }
- bool Enabled() const override { return (!instance_.protected_vm()); }
-
- private:
- std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
- Result<void> ResultSetup() override {
- // Cuttlefish for the time being won't be able to support OTA from a
- // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the
- // device is stopped (via stop_cvd). This is rarely an issue since OTA
- // testing run on cuttlefish is done within one launch cycle of the device.
- // If this ever becomes an issue, this code will have to be rewritten.
- if (!instance_.bootconfig_supported()) {
- return {};
- }
- const auto bootconfig_path = instance_.persistent_bootconfig_path();
- if (!FileExists(bootconfig_path)) {
- CF_EXPECT(CreateBlankImage(bootconfig_path, 1 /* mb */, "none"),
- "Failed to create image at " << bootconfig_path);
- }
-
- auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
- CF_EXPECT(bootconfig_fd->IsOpen(),
- "Unable to open bootconfig file: " << bootconfig_fd->StrError());
-
- const auto bootconfig_args =
- CF_EXPECT(BootconfigArgsFromConfig(config_, instance_));
- const auto bootconfig =
- CF_EXPECT(BootconfigArgsString(bootconfig_args, "\n")) + "\n";
-
- LOG(DEBUG) << "bootconfig size is " << bootconfig.size();
- ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
- CF_EXPECT(WriteAll(bootconfig_fd, bootconfig) == bootconfig.size(),
- "Failed to write bootconfig to \"" << bootconfig_path << "\"");
- LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
- << ReadFile(bootconfig_path);
-
- CF_EXPECT(bootconfig_fd->Truncate(bootconfig.size()) == 0,
- "`truncate --size=" << bootconfig.size() << " bytes "
- << bootconfig_path
- << "` failed:" << bootconfig_fd->StrError());
-
- if (config_.vm_manager() == vm_manager::Gem5Manager::name()) {
- const off_t bootconfig_size_bytes_gem5 =
- AlignToPowerOf2(bytesWritten, PARTITION_SIZE_SHIFT);
- CF_EXPECT(bootconfig_fd->Truncate(bootconfig_size_bytes_gem5) == 0);
- bootconfig_fd->Close();
- } else {
- bootconfig_fd->Close();
- const off_t bootconfig_size_bytes = AlignToPowerOf2(
- MAX_AVB_METADATA_SIZE + bootconfig.size(), PARTITION_SIZE_SHIFT);
-
- auto avbtool_path = HostBinaryPath("avbtool");
- Command bootconfig_hash_footer_cmd(avbtool_path);
- bootconfig_hash_footer_cmd.AddParameter("add_hash_footer");
- bootconfig_hash_footer_cmd.AddParameter("--image");
- bootconfig_hash_footer_cmd.AddParameter(bootconfig_path);
- bootconfig_hash_footer_cmd.AddParameter("--partition_size");
- bootconfig_hash_footer_cmd.AddParameter(bootconfig_size_bytes);
- bootconfig_hash_footer_cmd.AddParameter("--partition_name");
- bootconfig_hash_footer_cmd.AddParameter("bootconfig");
- bootconfig_hash_footer_cmd.AddParameter("--key");
- bootconfig_hash_footer_cmd.AddParameter(
- DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
- bootconfig_hash_footer_cmd.AddParameter("--algorithm");
- bootconfig_hash_footer_cmd.AddParameter("SHA256_RSA4096");
- int success = bootconfig_hash_footer_cmd.Start().Wait();
- CF_EXPECT(
- success == 0,
- "Unable to run append hash footer. Exited with status " << success);
- }
+Result<void> GeneratePersistentBootconfig(
+ const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance) {
+ if (instance.protected_vm()) {
return {};
}
+ // Cuttlefish for the time being won't be able to support OTA from a
+ // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the
+ // device is stopped (via stop_cvd). This is rarely an issue since OTA
+ // testing run on cuttlefish is done within one launch cycle of the device.
+ // If this ever becomes an issue, this code will have to be rewritten.
+ if (!instance.bootconfig_supported()) {
+ return {};
+ }
+ const auto bootconfig_path = instance.persistent_bootconfig_path();
+ if (!FileExists(bootconfig_path)) {
+ CF_EXPECT(CreateBlankImage(bootconfig_path, 1 /* mb */, "none"),
+ "Failed to create image at " << bootconfig_path);
+ }
- const CuttlefishConfig& config_;
- const CuttlefishConfig::InstanceSpecific& instance_;
-};
-
-fruit::Component<fruit::Required<const CuttlefishConfig,
- const CuttlefishConfig::InstanceSpecific>,
- GeneratePersistentBootconfig>
-GeneratePersistentBootconfigComponent() {
- return fruit::createComponent()
- .addMultibinding<SetupFeature, GeneratePersistentBootconfigImpl>()
- .bind<GeneratePersistentBootconfig, GeneratePersistentBootconfigImpl>();
+ auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
+ CF_EXPECT(bootconfig_fd->IsOpen(),
+ "Unable to open bootconfig file: " << bootconfig_fd->StrError());
+
+ const auto bootconfig_args =
+ CF_EXPECT(BootconfigArgsFromConfig(config, instance));
+ const auto bootconfig =
+ CF_EXPECT(BootconfigArgsString(bootconfig_args, "\n")) + "\n";
+
+ LOG(DEBUG) << "bootconfig size is " << bootconfig.size();
+ ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
+ CF_EXPECT(WriteAll(bootconfig_fd, bootconfig) == bootconfig.size(),
+ "Failed to write bootconfig to \"" << bootconfig_path << "\"");
+ LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
+ << ReadFile(bootconfig_path);
+
+ CF_EXPECT(bootconfig_fd->Truncate(bootconfig.size()) == 0,
+ "`truncate --size=" << bootconfig.size() << " bytes "
+ << bootconfig_path
+ << "` failed:" << bootconfig_fd->StrError());
+
+ if (config.vm_manager() == vm_manager::Gem5Manager::name()) {
+ const off_t bootconfig_size_bytes_gem5 =
+ AlignToPowerOf2(bytesWritten, PARTITION_SIZE_SHIFT);
+ CF_EXPECT(bootconfig_fd->Truncate(bootconfig_size_bytes_gem5) == 0);
+ bootconfig_fd->Close();
+ } else {
+ bootconfig_fd->Close();
+ const off_t bootconfig_size_bytes = AlignToPowerOf2(
+ MAX_AVB_METADATA_SIZE + bootconfig.size(), PARTITION_SIZE_SHIFT);
+
+ auto avbtool_path = HostBinaryPath("avbtool");
+ Command bootconfig_hash_footer_cmd(avbtool_path);
+ bootconfig_hash_footer_cmd.AddParameter("add_hash_footer");
+ bootconfig_hash_footer_cmd.AddParameter("--image");
+ bootconfig_hash_footer_cmd.AddParameter(bootconfig_path);
+ bootconfig_hash_footer_cmd.AddParameter("--partition_size");
+ bootconfig_hash_footer_cmd.AddParameter(bootconfig_size_bytes);
+ bootconfig_hash_footer_cmd.AddParameter("--partition_name");
+ bootconfig_hash_footer_cmd.AddParameter("bootconfig");
+ bootconfig_hash_footer_cmd.AddParameter("--key");
+ bootconfig_hash_footer_cmd.AddParameter(
+ DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
+ bootconfig_hash_footer_cmd.AddParameter("--algorithm");
+ bootconfig_hash_footer_cmd.AddParameter("SHA256_RSA4096");
+ int success = bootconfig_hash_footer_cmd.Start().Wait();
+ CF_EXPECT(
+ success == 0,
+ "Unable to run append hash footer. Exited with status " << success);
+ }
+ return {};
}
} // namespace cuttlefish
diff --git a/host/commands/assemble_cvd/disk/generate_persistent_vbmeta.cpp b/host/commands/assemble_cvd/disk/generate_persistent_vbmeta.cpp
index a1c4cf2b3..6c3560c00 100644
--- a/host/commands/assemble_cvd/disk/generate_persistent_vbmeta.cpp
+++ b/host/commands/assemble_cvd/disk/generate_persistent_vbmeta.cpp
@@ -35,7 +35,7 @@ class GeneratePersistentVbmetaImpl : public GeneratePersistentVbmeta {
INJECT(GeneratePersistentVbmetaImpl(
const CuttlefishConfig::InstanceSpecific& instance,
AutoSetup<InitBootloaderEnvPartition>::Type& bootloader_env,
- GeneratePersistentBootconfig& bootconfig))
+ AutoSetup<GeneratePersistentBootconfig>::Type& bootconfig))
: instance_(instance),
bootloader_env_(bootloader_env),
bootconfig_(bootconfig) {}
@@ -112,12 +112,12 @@ class GeneratePersistentVbmetaImpl : public GeneratePersistentVbmeta {
const CuttlefishConfig::InstanceSpecific& instance_;
AutoSetup<InitBootloaderEnvPartition>::Type& bootloader_env_;
- GeneratePersistentBootconfig& bootconfig_;
+ AutoSetup<GeneratePersistentBootconfig>::Type& bootconfig_;
};
fruit::Component<fruit::Required<const CuttlefishConfig::InstanceSpecific,
AutoSetup<InitBootloaderEnvPartition>::Type,
- GeneratePersistentBootconfig>,
+ AutoSetup<GeneratePersistentBootconfig>::Type>,
GeneratePersistentVbmeta>
GeneratePersistentVbmetaComponent() {
return fruit::createComponent()
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index 57bfa555d..07b17be2e 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -689,7 +689,7 @@ static fruit::Component<> DiskChangesPerInstanceComponent(
.install(AutoSetup<InitializeHwcomposerPmemImage>::Component)
.install(AutoSetup<InitializePstore>::Component)
.install(AutoSetup<InitializeSdCard>::Component)
- .install(GeneratePersistentBootconfigComponent)
+ .install(AutoSetup<GeneratePersistentBootconfig>::Component)
.install(GeneratePersistentVbmetaComponent)
.install(AutoSetup<InitializeInstanceCompositeDisk>::Component)
.install(InitializeDataImageComponent);
diff --git a/host/libs/audio_connector/buffers.cpp b/host/libs/audio_connector/buffers.cpp
index 29804cadf..100599acd 100644
--- a/host/libs/audio_connector/buffers.cpp
+++ b/host/libs/audio_connector/buffers.cpp
@@ -19,11 +19,23 @@
namespace cuttlefish {
+ShmBuffer::ShmBuffer(const virtio_snd_pcm_xfer& header,
+ volatile uint8_t* buffer, uint32_t len,
+ OnConsumedCb on_consumed)
+ : header_(header),
+ len_(len),
+ on_consumed_(on_consumed),
+ // Cast away the volatile qualifier: No one else will touch this buffer
+ // until SendStatus is called, at which point a memory fence will be used
+ // to ensure reads and writes are completed before the status is sent.
+ buffer_(const_cast<uint8_t*>(buffer)) {}
+
ShmBuffer::ShmBuffer(ShmBuffer&& other)
: header_(std::move(other.header_)),
len_(std::move(other.len_)),
on_consumed_(std::move(other.on_consumed_)),
- status_sent_(other.status_sent_) {
+ status_sent_(other.status_sent_.load()),
+ buffer_(other.buffer_) {
// It's now this buffer's responsibility to send the status.
other.status_sent_ = true;
}
@@ -32,12 +44,16 @@ ShmBuffer::~ShmBuffer() {
CHECK(status_sent_) << "Disposing of ShmBuffer before setting status";
}
-uint32_t ShmBuffer::stream_id() const { return header_.stream_id.as_uint32_t(); }
+uint32_t ShmBuffer::stream_id() const {
+ return header_.stream_id.as_uint32_t();
+}
void ShmBuffer::SendStatus(AudioStatus status, uint32_t latency_bytes,
- uint32_t consumed_len) {
+ uint32_t consumed_len) {
+ // Memory order is seq_cst to provide memory fence. It ensures all accesses
+ // are completed before the status is sent and the buffer is released.
+ CHECK(!status_sent_.exchange(true)) << "Status should only be sent once";
on_consumed_(status, latency_bytes, consumed_len);
- status_sent_ = true;
}
} // namespace cuttlefish
diff --git a/host/libs/audio_connector/buffers.h b/host/libs/audio_connector/buffers.h
index a92fd83f9..5dd589e2c 100644
--- a/host/libs/audio_connector/buffers.h
+++ b/host/libs/audio_connector/buffers.h
@@ -14,6 +14,7 @@
// limitations under the License.
#pragma once
+#include <atomic>
#include <cinttypes>
#include <functional>
@@ -35,11 +36,11 @@ using OnConsumedCb = std::function<void(AudioStatus, uint32_t /*latency*/,
// Objects of this class can only be moved, not copied. Destroying a buffer
// without sending the status to the client is a bug so the program aborts in
// those cases.
+// This class is NOT thread safe despite its use of atomic variables.
class ShmBuffer {
public:
- ShmBuffer(const virtio_snd_pcm_xfer& header, uint32_t len,
- OnConsumedCb on_consumed)
- : header_(header), len_(len), on_consumed_(on_consumed) {}
+ ShmBuffer(const virtio_snd_pcm_xfer& header, volatile uint8_t* buffer,
+ uint32_t len, OnConsumedCb on_consumed);
ShmBuffer(const ShmBuffer& other) = delete;
ShmBuffer(ShmBuffer&& other);
ShmBuffer& operator=(const ShmBuffer& other) = delete;
@@ -52,41 +53,27 @@ class ShmBuffer {
void SendStatus(AudioStatus status, uint32_t latency_bytes,
uint32_t consumed_len);
+ const uint8_t* get() const { return buffer_; }
+
private:
const virtio_snd_pcm_xfer header_;
const uint32_t len_;
OnConsumedCb on_consumed_;
- bool status_sent_ = false;
-};
-
-class TxBuffer : public ShmBuffer {
- public:
- TxBuffer(const virtio_snd_pcm_xfer& header, const volatile uint8_t* buffer,
- uint32_t len, OnConsumedCb on_consumed)
- : ShmBuffer(header, len, on_consumed), buffer_(buffer) {}
- TxBuffer(const TxBuffer& other) = delete;
- TxBuffer(TxBuffer&& other) = default;
- TxBuffer& operator=(const TxBuffer& other) = delete;
+ std::atomic<bool> status_sent_ = false;
- const volatile uint8_t* get() const { return buffer_; }
-
- private:
- const volatile uint8_t* const buffer_;
+ protected:
+ uint8_t* buffer_;
};
+using TxBuffer = ShmBuffer;
+// Only RxBuffer can be written to
class RxBuffer : public ShmBuffer {
public:
RxBuffer(const virtio_snd_pcm_xfer& header, volatile uint8_t* buffer,
uint32_t len, OnConsumedCb on_consumed)
- : ShmBuffer(header, len, on_consumed), buffer_(buffer) {}
- RxBuffer(const RxBuffer& other) = delete;
- RxBuffer(RxBuffer&& other) = default;
- RxBuffer& operator=(const RxBuffer& other) = delete;
+ : ShmBuffer(header, buffer, len, on_consumed) {}
- volatile uint8_t* get() const { return buffer_; }
-
- private:
- volatile uint8_t* const buffer_;
+ uint8_t* get() { return buffer_; }
};
} // namespace cuttlefish
diff --git a/host/libs/audio_connector/server.cpp b/host/libs/audio_connector/server.cpp
index ab74daa02..f98621c3b 100644
--- a/host/libs/audio_connector/server.cpp
+++ b/host/libs/audio_connector/server.cpp
@@ -55,6 +55,13 @@ ScopedMMap AllocateShm(size_t size, const std::string& name, SharedFD* shm_fd) {
return mmap_res;
}
+volatile uint8_t* BufferAt(ScopedMMap& shm, size_t offset, size_t len) {
+ CHECK(shm.WithinBounds(offset, len))
+ << "Tx buffer bounds outside the buffer area: " << offset << " " << len;
+ void* ptr = shm.get();
+ return &reinterpret_cast<volatile uint8_t*>(ptr)[offset];
+}
+
bool CreateSocketPair(SharedFD* local, SharedFD* remote) {
auto ret = SharedFD::SocketPair(AF_UNIX, SOCK_SEQPACKET, 0, local, remote);
if (!ret) {
@@ -301,10 +308,11 @@ bool AudioClientConnection::ReceivePlayback(AudioServerExecutor& executor) {
LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
return false;
}
- TxBuffer buffer(msg_hdr->io_xfer,
- TxBufferAt(msg_hdr->buffer_offset, msg_hdr->buffer_len),
- msg_hdr->buffer_len,
- SendStatusCallback(msg_hdr->buffer_offset, tx_socket_));
+ TxBuffer buffer(
+ msg_hdr->io_xfer,
+ BufferAt(tx_shm_, msg_hdr->buffer_offset, msg_hdr->buffer_len),
+ msg_hdr->buffer_len,
+ SendStatusCallback(msg_hdr->buffer_offset, tx_socket_));
executor.OnPlaybackBuffer(std::move(buffer));
return true;
}
@@ -320,10 +328,11 @@ bool AudioClientConnection::ReceiveCapture(AudioServerExecutor& executor) {
LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
return false;
}
- RxBuffer buffer(msg_hdr->io_xfer,
- RxBufferAt(msg_hdr->buffer_offset, msg_hdr->buffer_len),
- msg_hdr->buffer_len,
- SendStatusCallback(msg_hdr->buffer_offset, rx_socket_));
+ RxBuffer buffer(
+ msg_hdr->io_xfer,
+ BufferAt(rx_shm_, msg_hdr->buffer_offset, msg_hdr->buffer_len),
+ msg_hdr->buffer_len,
+ SendStatusCallback(msg_hdr->buffer_offset, rx_socket_));
executor.OnCaptureBuffer(std::move(buffer));
return true;
}
@@ -347,22 +356,6 @@ bool AudioClientConnection::CmdReply(AudioStatus status, const void* data,
return true;
}
-const volatile uint8_t* AudioClientConnection::TxBufferAt(size_t offset,
- size_t len) const {
- CHECK(tx_shm_.WithinBounds(offset, len))
- << "Tx buffer bounds outside the buffer area: " << offset << " " << len;
- const void* ptr = tx_shm_.get();
- return &reinterpret_cast<const volatile uint8_t*>(ptr)[offset];
-}
-
-volatile uint8_t* AudioClientConnection::RxBufferAt(size_t offset,
- size_t len) {
- CHECK(rx_shm_.WithinBounds(offset, len))
- << "Rx buffer bounds outside the buffer area: " << offset << " " << len;
- void* ptr = rx_shm_.get();
- return &reinterpret_cast<volatile uint8_t*>(ptr)[offset];
-}
-
bool AudioClientConnection::SendEvent(/*TODO*/) { return false; }
ssize_t AudioClientConnection::ReceiveMsg(SharedFD socket, void* buffer,
diff --git a/host/libs/audio_connector/server.h b/host/libs/audio_connector/server.h
index 6fa14d936..fec36dcb2 100644
--- a/host/libs/audio_connector/server.h
+++ b/host/libs/audio_connector/server.h
@@ -87,10 +87,8 @@ class AudioClientConnection {
AudioServerExecutor& executor);
ssize_t ReceiveMsg(SharedFD socket, void* buffer, size_t size);
- const volatile uint8_t* TxBufferAt(size_t offset, size_t len) const;
- volatile uint8_t* RxBufferAt(size_t offset, size_t len);
- const ScopedMMap tx_shm_;
+ ScopedMMap tx_shm_;
ScopedMMap rx_shm_;
SharedFD control_socket_;
SharedFD event_socket_;
diff --git a/shared/bluetooth/device_vendor.mk b/shared/bluetooth/device_vendor.mk
index 60c090adb..451f30063 100644
--- a/shared/bluetooth/device_vendor.mk
+++ b/shared/bluetooth/device_vendor.mk
@@ -32,6 +32,12 @@ PRODUCT_COPY_FILES += \
PRODUCT_PACKAGES += com.google.cf.bt
+#
+# Bluetooth Audio AIDL HAL
+#
+PRODUCT_PACKAGES += \
+ android.hardware.bluetooth.audio-impl \
+
else # BOARD_HAVE_BLUETOOTH == true
PRODUCT_COPY_FILES += \
diff --git a/shared/device.mk b/shared/device.mk
index 4dd1de652..4e9ea97ac 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -55,7 +55,7 @@ TARGET_USERDATAIMAGE_PARTITION_SIZE ?= 8589934592
TARGET_VULKAN_SUPPORT ?= true
# Enable Virtual A/B
-$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/android_t_baseline.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk)
PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := lz4
PRODUCT_VIRTUAL_AB_COW_VERSION := 3