Merge 25Q1 (ab/12770256) to aosp-main-future

Bug: 385190204
Merged-In: I2a904a81130ec40602ac7e44e1b1435cd1d54d8f
Change-Id: Ie61d5554cdf28aabc4e2d366e9c5681e9b0fdf21
diff --git a/Android.bp b/Android.bp
index ba3267c..f1221eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,6 +43,7 @@
         "libfs_mgr",
         "libsquashfs_utils",
         "libvold_binder",
+        "android.system.vold-V1-cpp",
     ],
     shared_libs: [
         "android.hardware.boot@1.0",
@@ -103,6 +104,11 @@
     export_include_dirs: ["."],
 }
 
+vintf_fragment {
+    name: "android.system.vold-service.xml",
+    src: "android.system.vold-service.xml",
+}
+
 // Static library factored out to support testing
 cc_library_static {
     name: "libvold",
@@ -132,10 +138,12 @@
         "NetlinkManager.cpp",
         "Process.cpp",
         "Utils.cpp",
+        "VendorVoldNativeService.cpp",
         "VoldNativeService.cpp",
         "VoldNativeServiceValidation.cpp",
         "VoldUtil.cpp",
         "VolumeManager.cpp",
+        "WriteBooster.cpp",
         "cryptfs.cpp",
         "fs/Exfat.cpp",
         "fs/Ext4.cpp",
@@ -198,6 +206,7 @@
             ],
         },
     },
+    vintf_fragment_modules: ["android.system.vold-service.xml"],
 }
 
 cc_binary {
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 598a87b..76e46fb 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -159,6 +159,21 @@
 // Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
 // of isCheckpointing
 std::mutex isCheckpointingLock;
+
+std::mutex listenersLock;
+std::vector<android::sp<android::system::vold::IVoldCheckpointListener>> listeners;
+}  // namespace
+
+void notifyCheckpointListeners() {
+    std::lock_guard<std::mutex> lock(listenersLock);
+
+    for (auto& listener : listeners) {
+        listener->onCheckpointingComplete();
+        listener = nullptr;
+    }
+
+    // Reclaim vector memory; we likely won't need it again.
+    listeners = std::vector<android::sp<android::system::vold::IVoldCheckpointListener>>();
 }
 
 Status cp_commitChanges() {
@@ -221,6 +236,8 @@
     if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
         return error(err_str.c_str());
 
+    notifyCheckpointListeners();
+
     std::thread(DoCheckpointCommittedWork).detach();
     return Status::ok();
 }
@@ -290,18 +307,21 @@
     std::string content;
     auto module = BootControlClient::WaitForService();
 
-    if (isCheckpointing) return isCheckpointing;
+    if (isCheckpointing) return true;
+
     // In case of INVALID slot or other failures, we do not perform checkpoint.
     if (module && !module->IsSlotMarkedSuccessful(module->GetCurrentSlot()).value_or(true)) {
         isCheckpointing = true;
         return true;
     }
     ret = android::base::ReadFileToString(kMetadataCPFile, &content);
-    if (ret) {
-        ret = content != "0";
-        isCheckpointing = ret;
-        return ret;
+    if (ret && content != "0") {
+        isCheckpointing = true;
+        return true;
     }
+
+    // Leave isCheckpointing false and notify listeners now that we know we don't need one
+    notifyCheckpointListeners();
     return false;
 }
 
@@ -801,5 +821,20 @@
     needsCheckpointWasCalled = false;
 }
 
+bool cp_registerCheckpointListener(
+        android::sp<android::system::vold::IVoldCheckpointListener> listener) {
+    std::lock_guard<std::mutex> checkpointGuard(isCheckpointingLock);
+    if (needsCheckpointWasCalled && !isCheckpointing) {
+        // Either checkpoint already committed or we didn't need one
+        return false;
+    }
+
+    // Either we don't know whether we need a checkpoint or we're already checkpointing,
+    // so we need to save this listener to notify later.
+    std::lock_guard<std::mutex> listenersGuard(listenersLock);
+    listeners.push_back(std::move(listener));
+    return true;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/Checkpoint.h b/Checkpoint.h
index 6f3acac..7625310 100644
--- a/Checkpoint.h
+++ b/Checkpoint.h
@@ -17,6 +17,7 @@
 #ifndef _CHECKPOINT_H
 #define _CHECKPOINT_H
 
+#include <android/system/vold/IVold.h>
 #include <binder/Status.h>
 #include <string>
 
@@ -48,6 +49,9 @@
 android::binder::Status cp_markBootAttempt();
 
 void cp_resetCheckpoint();
+
+bool cp_registerCheckpointListener(
+        android::sp<android::system::vold::IVoldCheckpointListener> listener);
 }  // namespace vold
 }  // namespace android
 
diff --git a/VendorVoldNativeService.cpp b/VendorVoldNativeService.cpp
new file mode 100644
index 0000000..6d13a14
--- /dev/null
+++ b/VendorVoldNativeService.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 "VendorVoldNativeService.h"
+
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Trace.h>
+
+#include "Checkpoint.h"
+#include "VoldNativeServiceValidation.h"
+#include "VolumeManager.h"
+
+#define ENFORCE_SYSTEM_OR_ROOT                              \
+    {                                                       \
+        binder::Status status = CheckUidOrRoot(AID_SYSTEM); \
+        if (!status.isOk()) {                               \
+            return status;                                  \
+        }                                                   \
+    }
+
+#define ACQUIRE_LOCK                                                        \
+    std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); \
+    ATRACE_CALL();
+
+namespace android::vold {
+
+status_t VendorVoldNativeService::try_start() {
+    auto service_name = String16("android.system.vold.IVold/default");
+    if (!defaultServiceManager()->isDeclared(service_name)) {
+        LOG(DEBUG) << "Service for VendorVoldNativeService (" << service_name << ") not declared.";
+        return OK;
+    }
+    return defaultServiceManager()->addService(std::move(service_name),
+                                               new VendorVoldNativeService());
+}
+
+binder::Status VendorVoldNativeService::registerCheckpointListener(
+        const sp<android::system::vold::IVoldCheckpointListener>& listener,
+        android::system::vold::CheckpointingState* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    bool possible_checkpointing = cp_registerCheckpointListener(listener);
+    *_aidl_return = possible_checkpointing
+                            ? android::system::vold::CheckpointingState::POSSIBLE_CHECKPOINTING
+                            : android::system::vold::CheckpointingState::CHECKPOINTING_COMPLETE;
+    return binder::Status::ok();
+}
+
+}  // namespace android::vold
diff --git a/VendorVoldNativeService.h b/VendorVoldNativeService.h
new file mode 100644
index 0000000..884ccb0
--- /dev/null
+++ b/VendorVoldNativeService.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef _VENDOR_VOLD_NATIVE_SERVICE_H_
+#define _VENDOR_VOLD_NATIVE_SERVICE_H_
+
+#include <android/system/vold/BnVold.h>
+#include <android/system/vold/CheckpointingState.h>
+#include <android/system/vold/IVoldCheckpointListener.h>
+
+namespace android::vold {
+
+class VendorVoldNativeService : public android::system::vold::BnVold {
+  public:
+    /** Start the service, but if it's not declared, give up and return OK. */
+    static status_t try_start();
+
+    binder::Status registerCheckpointListener(
+            const sp<android::system::vold::IVoldCheckpointListener>& listener,
+            android::system::vold::CheckpointingState* _aidl_return) final;
+};
+
+}  // namespace android::vold
+
+#endif  // _VENDOR_VOLD_NATIVE_SERVICE_H_
\ No newline at end of file
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 3784487..aa9d842 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -40,6 +40,7 @@
 #include "VoldNativeServiceValidation.h"
 #include "VoldUtil.h"
 #include "VolumeManager.h"
+#include "WriteBooster.h"
 #include "cryptfs.h"
 #include "incfs.h"
 
@@ -512,6 +513,45 @@
     return Ok();
 }
 
+binder::Status VoldNativeService::getWriteBoosterBufferSize(int32_t* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+
+    *_aidl_return = GetWriteBoosterBufferSize();
+    return Ok();
+}
+
+binder::Status VoldNativeService::getWriteBoosterBufferAvailablePercent(int32_t* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    *_aidl_return = GetWriteBoosterBufferAvailablePercent();
+    return Ok();
+}
+
+binder::Status VoldNativeService::setWriteBoosterBufferFlush(bool enable, bool* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    *_aidl_return = SetWriteBoosterBufferFlush(enable);
+    return Ok();
+}
+
+binder::Status VoldNativeService::setWriteBoosterBufferOn(bool enable, bool* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    *_aidl_return = SetWriteBoosterBufferOn(enable);
+    return Ok();
+}
+
+binder::Status VoldNativeService::getWriteBoosterLifeTimeEstimate(int32_t* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    *_aidl_return = GetWriteBoosterLifeTimeEstimate();
+    return Ok();
+}
+
 binder::Status VoldNativeService::setGCUrgentPace(int32_t neededSegments,
                                                   int32_t minSegmentThreshold,
                                                   float dirtyReclaimRate, float reclaimWeight,
diff --git a/VoldNativeService.h b/VoldNativeService.h
index a5253c0..2d0613c 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -165,6 +165,12 @@
     binder::Status destroyDsuMetadataKey(const std::string& dsuSlot) override;
 
     binder::Status getStorageSize(int64_t* storageSize) override;
+
+    binder::Status getWriteBoosterBufferSize(int32_t* _aidl_return);
+    binder::Status getWriteBoosterBufferAvailablePercent(int32_t* _aidl_return);
+    binder::Status setWriteBoosterBufferFlush(bool enable, bool* _aidl_return);
+    binder::Status setWriteBoosterBufferOn(bool enable, bool* _aidl_return);
+    binder::Status getWriteBoosterLifeTimeEstimate(int32_t* _aidl_return);
 };
 
 }  // namespace vold
diff --git a/VoldUtil.cpp b/VoldUtil.cpp
index 082f743..c87b41d 100644
--- a/VoldUtil.cpp
+++ b/VoldUtil.cpp
@@ -15,5 +15,73 @@
  */
 
 #include "VoldUtil.h"
+#include "Utils.h"
+
+#include <libdm/dm.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+using android::base::Basename;
+using android::base::Realpath;
+using namespace android::dm;
 
 android::fs_mgr::Fstab fstab_default;
+
+static std::string GetUfsHostControllerSysfsPathOnce() {
+    android::fs_mgr::FstabEntry* entry =
+            android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
+    if (entry == nullptr) {
+        LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
+        return "";
+    }
+
+    // Handle symlinks.
+    std::string real_path;
+    if (!Realpath(entry->blk_device, &real_path)) {
+        real_path = entry->blk_device;
+    }
+
+    // Handle logical volumes.
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    for (;;) {
+        std::optional<std::string> parent = dm.GetParentBlockDeviceByPath(real_path);
+        if (!parent.has_value()) break;
+        real_path = *parent;
+    }
+
+    std::string path = "/sys/class/block/" + Basename(real_path);
+
+    // Walk up the sysfs directory tree from the partition (e.g, /sys/class/block/sda34)
+    // or from the disk (e.g., /sys/class/block/sda) to reach the UFS host controller's directory
+    // (e.g., /sys/class/block/sda34/../device/../../.. --> /sys/devices/platform/00000000.ufs).
+    if (android::vold::pathExists(path + "/../device")) {
+        path += "/../device/../../..";
+    } else if (android::vold::pathExists(path + "/device")) {
+        path += "/device/../../..";
+    } else {
+        LOG(WARNING) << "Failed to get sysfs_path for user data partition";
+        return "";
+    }
+
+    // Verify the block device is UFS by checking for the presence of "uic_link_state",
+    // which is UFS interconnect layer link state.
+    // If not UFS, return an empty path.
+    if (!android::vold::pathExists(path + "/uic_link_state")) {
+        LOG(ERROR) << "The block device (" << Basename(real_path) << ") of " << DATA_MNT_POINT
+                   << " is not UFS.";
+        return "";
+    }
+
+    LOG(DEBUG) << "The sysfs directory for the ufs host controller is found at " << path;
+    return path;
+}
+
+// get sysfs directory for the ufs host controller containing userdata
+// returns an empty string on failures.
+std::string GetUfsHostControllerSysfsPath() {
+    static std::string ufshc_sysfs_path = GetUfsHostControllerSysfsPathOnce();
+    return ufshc_sysfs_path;
+}
diff --git a/VoldUtil.h b/VoldUtil.h
index ce6b411..9fb9e96 100644
--- a/VoldUtil.h
+++ b/VoldUtil.h
@@ -21,3 +21,5 @@
 extern android::fs_mgr::Fstab fstab_default;
 
 #define DATA_MNT_POINT "/data"
+
+std::string GetUfsHostControllerSysfsPath();
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d932ec8..657f051 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -1247,7 +1247,8 @@
     return android::vold::OpenAppFuseFile(uid, mountId, fileId, flags);
 }
 
-static android::status_t getDeviceSize(std::string& device, int64_t* storageSize) {
+static android::status_t getDeviceSize(std::string& device, int64_t* storageSize,
+                                       bool isF2fsPrimary) {
     // Follow any symbolic links
     std::string dataDevice;
     if (!android::base::Realpath(device, &dataDevice)) {
@@ -1256,14 +1257,35 @@
 
     // Handle mapped volumes.
     auto& dm = android::dm::DeviceMapper::Instance();
-    for (;;) {
-        auto parent = dm.GetParentBlockDeviceByPath(dataDevice);
-        if (!parent.has_value()) break;
-        dataDevice = *parent;
+    std::string dmPath = dataDevice;
+    if (dm.IsDmBlockDevice(dataDevice)) {
+        for (;;) {
+            auto parent = dm.GetParentBlockDeviceByPath(dataDevice);
+            if (!parent.has_value()) break;
+            dataDevice = *parent;
+        }
+    } else if (isF2fsPrimary) {
+        if (!dm.GetDmDevicePathByName(android::base::Basename(device), &dmPath)) {
+            LOG(WARNING) << "No proper dm device for " << device;
+            isF2fsPrimary = false;
+        }
+    }
+
+    // Find a device name for F2FS primary partition
+    std::string f2fsReservedBlocksSysfs;
+    std::size_t leaf;
+    if (isF2fsPrimary) {
+        leaf = dmPath.rfind('/');
+        if (leaf == std::string::npos) {
+            LOG(WARNING) << "dm device " << dmPath << " is not a path";
+            isF2fsPrimary = false;
+        }
+        f2fsReservedBlocksSysfs =
+                std::string() + "/sys/fs/f2fs/" + dmPath.substr(leaf + 1) + "/reserved_blocks";
     }
 
     // Get the potential /sys/block entry
-    std::size_t leaf = dataDevice.rfind('/');
+    leaf = dataDevice.rfind('/');
     if (leaf == std::string::npos) {
         LOG(ERROR) << "data device " << dataDevice << " is not a path";
         return EINVAL;
@@ -1293,14 +1315,32 @@
     }
 
     // Read the size file and be done
+    int64_t sizeNum;
     std::stringstream ssSize(size);
-    ssSize >> *storageSize;
+    ssSize >> sizeNum;
     if (ssSize.fail()) {
         LOG(ERROR) << sizeFile << " cannot be read as an integer";
         return EINVAL;
     }
 
-    *storageSize *= 512;
+    sizeNum *= 512;
+    if (isF2fsPrimary) {
+        int64_t reservedBlocksNum = 0;
+        if (!android::base::ReadFileToString(f2fsReservedBlocksSysfs, &size, true)) {
+            LOG(WARNING) << "Could not find valid entry from " << f2fsReservedBlocksSysfs;
+        } else {
+            std::stringstream reservedBlocks(size);
+            reservedBlocks >> reservedBlocksNum;
+            if (reservedBlocks.fail()) {
+                LOG(WARNING) << f2fsReservedBlocksSysfs << " cannot be read as an integer";
+                reservedBlocksNum = 0;
+            }
+        }
+        int64_t blockSize = android::base::GetIntProperty("ro.boot.hardware.cpu.pagesize", 0);
+        sizeNum -= reservedBlocksNum * blockSize;
+    }
+
+    *storageSize = sizeNum;
     return OK;
 }
 
@@ -1313,14 +1353,14 @@
         return EINVAL;
     }
 
-    status = getDeviceSize(entry->blk_device, storageSize);
+    status = getDeviceSize(entry->blk_device, storageSize, entry->fs_type == "f2fs");
     if (status != OK) {
         return status;
     }
 
     for (auto device : entry->user_devices) {
         int64_t deviceStorageSize;
-        status = getDeviceSize(device, &deviceStorageSize);
+        status = getDeviceSize(device, &deviceStorageSize, false);
         if (status != OK) {
             return status;
         }
diff --git a/WriteBooster.cpp b/WriteBooster.cpp
new file mode 100644
index 0000000..1d5c1bc
--- /dev/null
+++ b/WriteBooster.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2024 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 "WriteBooster.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/result.h>
+#include <android-base/strings.h>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+
+namespace android {
+namespace vold {
+
+template <typename T>
+static android::base::Result<T> readHexValue(const std::string_view path) {
+    std::string sysfs_path = GetUfsHostControllerSysfsPath();
+    if (sysfs_path.empty()) {
+        return android::base::Error();
+    }
+
+    std::string fullpath = sysfs_path + "/" + std::string(path);
+    std::string s;
+
+    if (!ReadFileToString(fullpath, &s)) {
+        PLOG(WARNING) << "Reading failed for " << fullpath;
+        return android::base::Error();
+    }
+
+    s = android::base::Trim(s);
+    T out;
+    if (!android::base::ParseUint(s, &out)) {
+        PLOG(WARNING) << "Parsing of " << fullpath << " failed. Content: " << s;
+        return android::base::Error();
+    }
+
+    return out;
+}
+
+int32_t GetWriteBoosterBufferSize() {
+    /* wb_cur_buf: in unit of allocation_unit_size (field width is 4 bytes)
+     * allocation_unit_size: in unit of segments (field width is 1 bytes)
+     * segment_size: in unit of 512 bytes (field width is 4 bytes)
+     * raw_device_capacity: in unit of 512 bytes (field width is 8 bytes)
+     */
+    auto allocation_unit_size = readHexValue<uint8_t>("geometry_descriptor/allocation_unit_size");
+    if (!allocation_unit_size.ok()) {
+        return -1;
+    }
+
+    auto segment_size = readHexValue<uint32_t>("geometry_descriptor/segment_size");
+    if (!segment_size.ok()) {
+        return -1;
+    }
+
+    auto wb_cur_buf = readHexValue<uint32_t>("attributes/wb_cur_buf");
+    if (!wb_cur_buf.ok()) {
+        return -1;
+    }
+
+    auto raw_device_capacity = readHexValue<uint64_t>("geometry_descriptor/raw_device_capacity");
+    if (!raw_device_capacity.ok()) {
+        return -1;
+    }
+
+    if (allocation_unit_size.value() == 0) {
+        LOG(DEBUG) << "Zero allocation_unit_size is invalid.";
+        return -1;
+    }
+
+    if (segment_size.value() == 0) {
+        LOG(DEBUG) << "Zero segment_size is invalid.";
+        return -1;
+    }
+
+    uint64_t wb_cur_buf_allocation_units =
+            static_cast<uint64_t>(wb_cur_buf.value()) * segment_size.value();
+
+    if (wb_cur_buf_allocation_units >
+        (raw_device_capacity.value() / allocation_unit_size.value())) {
+        LOG(DEBUG) << "invalid wb_cur_buff > raw_device_capacity ";
+        return -1;
+    }
+
+    /* The allocation_unit_size is represented in the number of sectors.
+     * Since the sector size is 512 bytes, the allocation_unit_size in MiB can be calculated as
+     * follows: allocation_unit_size in MiB = (allocation_unit_size * 512) / 1024 / 1024 =
+     * allocation_unit_size / 2048
+     */
+    uint64_t wb_cur_buf_mib = wb_cur_buf_allocation_units * allocation_unit_size.value() / 2048ULL;
+
+    if (wb_cur_buf_mib > INT32_MAX) {
+        LOG(DEBUG) << "wb_cur_buff overflow";
+        return -1;
+    }
+
+    /* return in unit of MiB */
+    return static_cast<int32_t>(wb_cur_buf_mib);
+}
+
+/*
+ * Returns the WriteBooster buffer's remaining capacity as a percentage (0-100).
+ */
+int32_t GetWriteBoosterBufferAvailablePercent() {
+    /*
+     * wb_avail_buf is in unit of 10% granularity.
+     * 00h: 0% buffer remains.
+     * 01h~09h: 10%~90% buffer remains
+     * 0Ah: 100% buffer remains
+     * Others : Reserved
+     */
+    auto out = readHexValue<uint8_t>("attributes/wb_avail_buf");
+    if (!out.ok()) {
+        return -1;
+    }
+
+    if (out.value() > 10) {
+        PLOG(WARNING) << "Invalid wb_avail_buf (" << out.value() << ")";
+        return -1;
+    }
+
+    return static_cast<int32_t>(out.value() * 10);
+}
+
+bool SetWriteBoosterBufferFlush(bool enable) {
+    std::string path = GetUfsHostControllerSysfsPath();
+    if (path.empty()) {
+        return false;
+    }
+
+    path += "/enable_wb_buf_flush";
+
+    std::string s = enable ? "1" : "0";
+
+    LOG(DEBUG) << "Toggle WriteBoosterBufferFlush to " << s;
+    if (!WriteStringToFile(s, std::string(path))) {
+        PLOG(WARNING) << "Failed to set WriteBoosterBufferFlush to " << s << " on " << path;
+        return false;
+    }
+    return true;
+}
+
+bool SetWriteBoosterBufferOn(bool enable) {
+    std::string path = GetUfsHostControllerSysfsPath();
+    if (path.empty()) {
+        return false;
+    }
+
+    path += "/wb_on";
+
+    std::string s = enable ? "1" : "0";
+
+    LOG(DEBUG) << "Toggle WriteBoosterBufferOn to " << s;
+    if (!WriteStringToFile(s, std::string(path))) {
+        PLOG(WARNING) << "Failed to set WriteBoosterBufferOn to " << s << " on " << path;
+        return false;
+    }
+    return true;
+}
+
+/**
+ * Returns WriteBooster buffer lifetime as a percentage (0-100).
+ *
+ */
+int32_t GetWriteBoosterLifeTimeEstimate() {
+    /*
+     * wb_life_time_est returns as follows:
+     * 00h: Information not available (WriteBooster Buffer is disabled)
+     * 01h: 0% - 10% WriteBooster Buffer life time used
+     * 02h-09h: 10% - 90% WriteBooster Buffer life time used
+     * 0Ah: 90% - 100% WriteBooster Buffer life time used
+     * 0Bh: Exceeded its maximum estimated WriteBooster Buffer life time
+     *      (write commands are processed as if WriteBooster feature was
+     *       disabled)
+     * Others: Reserved
+     */
+    auto out = readHexValue<uint8_t>("attributes/wb_life_time_est");
+    if (!out.ok()) {
+        return -1;
+    }
+
+    if (out.value() == 0) {
+        PLOG(WARNING) << "WriteBooster is disabled.";
+        return -1;
+    }
+
+    if (out.value() > 11) {
+        PLOG(WARNING) << "Invalid wb_life_time_est (" << out.value() << ")";
+        return -1;
+    }
+
+    return static_cast<int32_t>(10 * (out.value() - 1));
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/WriteBooster.h b/WriteBooster.h
new file mode 100644
index 0000000..6aa9799
--- /dev/null
+++ b/WriteBooster.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef ANDROID_VOLD_WRITE_BOOSTER_H
+#define ANDROID_VOLD_WRITE_BOOSTER_H
+
+#include <cstdint>
+
+namespace android {
+namespace vold {
+
+int32_t GetWriteBoosterBufferSize();
+int32_t GetWriteBoosterBufferAvailablePercent();
+bool SetWriteBoosterBufferFlush(bool enable);
+bool SetWriteBoosterBufferOn(bool enable);
+int32_t GetWriteBoosterLifeTimeEstimate();
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/android.system.vold-service.xml b/android.system.vold-service.xml
new file mode 100644
index 0000000..ea084b7
--- /dev/null
+++ b/android.system.vold-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.vold</name>
+        <version>1</version>
+        <interface>
+            <name>IVold</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
\ No newline at end of file
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 810fdad..15213ea 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -143,6 +143,12 @@
     // on failure.
     int getStorageRemainingLifetime();
 
+    int getWriteBoosterBufferSize();
+    int getWriteBoosterBufferAvailablePercent();
+    boolean setWriteBoosterBufferFlush(boolean enable);
+    boolean setWriteBoosterBufferOn(boolean enable);
+    int getWriteBoosterLifeTimeEstimate();
+
     const int FSTRIM_FLAG_DEEP_TRIM = 1;
 
     const int MOUNT_FLAG_PRIMARY = 1;
diff --git a/main.cpp b/main.cpp
index 078ee14..bdce76e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -19,6 +19,7 @@
 #include "FsCrypt.h"
 #include "MetadataCrypt.h"
 #include "NetlinkManager.h"
+#include "VendorVoldNativeService.h"
 #include "VoldNativeService.h"
 #include "VoldUtil.h"
 #include "VolumeManager.h"
@@ -126,9 +127,16 @@
         exit(1);
     }
     ATRACE_END();
-
     LOG(DEBUG) << "VoldNativeService::start() completed OK";
 
+    ATRACE_BEGIN("VendorVoldNativeService::try_start");
+    if (android::vold::VendorVoldNativeService::try_start() != android::OK) {
+        LOG(ERROR) << "Unable to start VendorVoldNativeService";
+        exit(1);
+    }
+    ATRACE_END();
+    LOG(DEBUG) << "VendorVoldNativeService::try_start() completed OK";
+
     ATRACE_BEGIN("NetlinkManager::start");
     if (nm->start()) {
         PLOG(ERROR) << "Unable to start NetlinkManager";
diff --git a/tests/VoldFuzzer.cpp b/tests/VoldFuzzer.cpp
index 630a785..173c765 100644
--- a/tests/VoldFuzzer.cpp
+++ b/tests/VoldFuzzer.cpp
@@ -17,6 +17,7 @@
 #include <android-base/logging.h>
 #include <fuzzbinder/libbinder_driver.h>
 
+#include "VendorVoldNativeService.h"
 #include "VoldNativeService.h"
 #include "sehandle.h"
 
@@ -36,7 +37,10 @@
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    // TODO(b/183141167): need to rewrite 'dump' to avoid SIGPIPE.
+    signal(SIGPIPE, SIG_IGN);
     auto voldService = sp<android::vold::VoldNativeService>::make();
-    fuzzService(voldService, FuzzedDataProvider(data, size));
+    auto voldVendorService = sp<android::vold::VendorVoldNativeService>::make();
+    fuzzService({voldService, voldVendorService}, FuzzedDataProvider(data, size));
     return 0;
 }
\ No newline at end of file