Merge "Add new common USB Gadget Aidl file"
diff --git a/battery_mitigation/MitigationThermalManager.cpp b/battery_mitigation/MitigationThermalManager.cpp
index b1320e7..833e294 100644
--- a/battery_mitigation/MitigationThermalManager.cpp
+++ b/battery_mitigation/MitigationThermalManager.cpp
@@ -124,6 +124,7 @@
     std::stringstream oss;
     oss << temperature.name << " triggered at " << temperature.value << std::endl << std::flush;
     android::base::WriteStringToFd(oss.str(), fd);
+    fsync(fd);
 
     for (int i = 0; i < NUM_OF_SAMPLES; i++) {
         auto now = std::chrono::system_clock::now();
@@ -135,6 +136,7 @@
         oss << std::put_time(&now_tm, "%m-%d %H:%M:%S.") << std::setw(3) << std::setfill('0')
                 << ms.count() << std::endl << std::flush;
         android::base::WriteStringToFd(oss.str(), fd);
+        fsync(fd);
         oss.str("");
         /* log System info */
         for (int j = 0; j < kSystemName.size(); j++) {
diff --git a/common/pixel-common-device.mk b/common/pixel-common-device.mk
index 4bb4398..93e764d 100644
--- a/common/pixel-common-device.mk
+++ b/common/pixel-common-device.mk
@@ -27,6 +27,11 @@
 PRODUCT_PACKAGES_DEBUG += wifi_perf_diag
 BOARD_VENDOR_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/wifi_perf_diag
 
+# Pixel storage tool
+PRODUCT_PACKAGES_DEBUG += \
+	sg_write_buffer \
+	sg_read_buffer
+
 # Enable whole-program R8 Java optimizations for SystemUI and system_server,
 # but also allow explicit overriding for testing and development.
 SYSTEM_OPTIMIZE_JAVA ?= true
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index dd2f80d..ee1cda3 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -42,3 +42,31 @@
         "libfstab",
     ],
 }
+
+
+cc_binary {
+    name: "android.hardware.fastboot-service.pixel_recovery",
+    init_rc: ["android.hardware.fastboot-service.pixel_recovery.rc"],
+    vintf_fragments: ["android.hardware.fastboot-service.pixel.xml"],
+    recovery: true,
+    srcs: [
+        "Fastboot_aidl.cpp",
+        "main.cpp",
+    ],
+    relative_install_path: "hw",
+    shared_libs: [
+        "android.hardware.fastboot-V1-ndk",
+        "libbinder_ndk",
+        "libbase",
+        "libcutils",
+        "libext4_utils",
+        "libfs_mgr",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "libnos_for_fastboot",
+        "libnos_citadel_for_fastboot",
+        "libfstab",
+    ],
+}
diff --git a/fastboot/Fastboot_aidl.cpp b/fastboot/Fastboot_aidl.cpp
new file mode 100644
index 0000000..32df5f3
--- /dev/null
+++ b/fastboot/Fastboot_aidl.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2022 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 "include/fastboot/Fastboot_aidl.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <dlfcn.h>
+#include <endian.h>
+
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+// FS headers
+#include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
+
+// Nugget headers
+#include <app_nugget.h>
+#include <nos/NuggetClient.h>
+#include <nos/debug.h>
+
+using ndk::ScopedAStatus;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+
+constexpr const char *BRIGHTNESS_FILE = "/sys/class/backlight/panel0-backlight/brightness";
+constexpr int DISPLAY_BRIGHTNESS_DIM_THRESHOLD = 20;
+
+using OEMCommandHandler =
+        std::function<ScopedAStatus(const std::vector<std::string> &, std::string *)>;
+
+ScopedAStatus Fastboot::getPartitionType(const std::string &in_partitionName,
+                                         FileSystemType *_aidl_return) {
+    if (in_partitionName.empty()) {
+        return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                           "Invalid partition name");
+    }
+    // For bluecross devices, all partitions need to return raw.
+    *_aidl_return = FileSystemType::RAW;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::getVariant(std::string *_aidl_return) {
+    *_aidl_return = "MSM USF";
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::getOffModeChargeState(bool *_aidl_return) {
+    constexpr const char *kDevinfoPath = "/dev/block/by-name/devinfo";
+    constexpr int kDevInfoOffModeChargeOffset = 15;
+
+    uint8_t off_mode_charge_status = 0;
+    ::android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kDevinfoPath, O_RDONLY | O_BINARY)));
+    if (fd < 0) {
+        std::string message = "Unable to open devinfo " + std::to_string(errno);
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  message.c_str());
+    }
+    auto ret = ::android::base::ReadFullyAtOffset(fd, &off_mode_charge_status, 1 /* byte count */,
+                                                  kDevInfoOffModeChargeOffset);
+    if (!ret) {
+        std::string message = "Reading devifo failed errno:" + std::to_string(errno) +
+                              " Unable to read off-mode-charge state";
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  message.c_str());
+    } else {
+        *_aidl_return = (off_mode_charge_status != 0);
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::getBatteryVoltageFlashingThreshold(int32_t *_aidl_return) {
+    constexpr int kMinVoltageForFlashing = 3500;
+    *_aidl_return = kMinVoltageForFlashing;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus SetBrightnessLevel(const std::vector<std::string> &args, std::string *_aidl_return) {
+    if (!args.size()) {
+        return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                           "Brightness level unspecified");
+    }
+
+    auto level = std::stoi(args[0]);
+
+    if (level < 0 || level > 100) {
+        return ScopedAStatus::fromExceptionCodeWithMessage(
+                EX_ILLEGAL_ARGUMENT, "Brighness level must be between 0 and 100");
+    }
+
+    // Avoid screen being dimmed too much.
+    if (level < DISPLAY_BRIGHTNESS_DIM_THRESHOLD) {
+        level = DISPLAY_BRIGHTNESS_DIM_THRESHOLD;
+    }
+
+    if (::android::base::WriteStringToFile(std::to_string(level), BRIGHTNESS_FILE)) {
+        *_aidl_return = "";
+        return ScopedAStatus::ok();
+    }
+    std::string message = "Writing to brightness file failed errno: " + std::to_string(errno) +
+                          " Unable to set display brightness";
+
+    return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                              message.c_str());
+}
+
+ScopedAStatus Fastboot::doOemCommand(const std::string &in_oemCmd, std::string *_aidl_return) {
+    const std::unordered_map<std::string, OEMCommandHandler> kOEMCmdMap = {
+            {FB_OEM_SET_BRIGHTNESS, SetBrightnessLevel},
+    };
+
+    auto args = ::android::base::Split(in_oemCmd, " ");
+    if (args.size() < 2) {
+        return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                           "Invalid OEM command");
+    }
+
+    // args[0] will be "oem", args[1] will be the command name
+    auto cmd_handler = kOEMCmdMap.find(args[1]);
+    if (cmd_handler != kOEMCmdMap.end()) {
+        return cmd_handler->second(std::vector<std::string>(args.begin() + 2, args.end()),
+                                   _aidl_return);
+    } else {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  "Unknown OEM Command");
+    }
+
+    return ScopedAStatus::ok();
+}
+
+static ::android::fs_mgr::Fstab fstab;
+enum WipeVolumeStatus {
+    WIPE_OK = 0,
+    VOL_FSTAB,
+    VOL_UNKNOWN,
+    VOL_MOUNTED,
+    VOL_BLK_DEV_OPEN,
+    WIPE_ERROR_MAX = 0xffffffff,
+};
+std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{
+        {WIPE_OK, ""},
+        {VOL_FSTAB, "Unknown FS table"},
+        {VOL_UNKNOWN, "Unknown volume"},
+        {VOL_MOUNTED, "Fail to unmount volume"},
+        {VOL_BLK_DEV_OPEN, "Fail to open block device"},
+        {WIPE_ERROR_MAX, "Unknown wipe error"}};
+
+enum WipeVolumeStatus wipe_volume(const std::string &volume) {
+    if (!::android::fs_mgr::ReadDefaultFstab(&fstab)) {
+        return VOL_FSTAB;
+    }
+    const ::android::fs_mgr::FstabEntry *v = ::android::fs_mgr::GetEntryForPath(&fstab, volume);
+    if (v == nullptr) {
+        return VOL_UNKNOWN;
+    }
+    if (::android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) {
+        return VOL_MOUNTED;
+    }
+
+    int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644);
+    if (fd == -1) {
+        return VOL_BLK_DEV_OPEN;
+    }
+    wipe_block_device(fd, get_block_device_size(fd));
+    close(fd);
+
+    return WIPE_OK;
+}
+
+// Attempt to reuse a WipeKeys function that might be found in the recovery
+// library in order to clear any digital car keys on the secure element.
+bool WipeDigitalCarKeys(void) {
+    static constexpr const char *kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
+    void *librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
+    if (librecovery_ui_ext == nullptr) {
+        // Dynamic library not found. Returning true since this likely
+        // means target does not support DCK.
+        return true;
+    }
+
+    bool *(*WipeKeysFunc)(void *const);
+    reinterpret_cast<void *&>(WipeKeysFunc) = dlsym(librecovery_ui_ext, "WipeKeys");
+    if (WipeKeysFunc == nullptr) {
+        // No WipeKeys implementation found. Returning true since this likely
+        // means target does not support DCK.
+        return true;
+    }
+
+    return (*WipeKeysFunc)(nullptr);
+}
+
+ScopedAStatus Fastboot::doOemSpecificErase() {
+    // Erase metadata partition along with userdata partition.
+    // Keep erasing Titan M even if failing on this case.
+    auto wipe_status = wipe_volume("/metadata");
+
+    bool dck_wipe_success = WipeDigitalCarKeys();
+
+    // Connect to Titan M
+    ::nos::NuggetClient client;
+    client.Open();
+    if (!client.IsOpen()) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  "open Titan M fail");
+    }
+
+    // Tell Titan M to wipe user data
+    const uint32_t magicValue = htole32(ERASE_CONFIRMATION);
+    std::vector<uint8_t> magic(sizeof(magicValue));
+    memcpy(magic.data(), &magicValue, sizeof(magicValue));
+    const uint8_t retry_count = 5;
+    uint32_t nugget_status;
+    for (uint8_t i = 0; i < retry_count; i++) {
+        nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
+        if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) {
+            return ScopedAStatus::ok();
+        }
+    }
+
+    // Return exactly what happened
+    if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK && !dck_wipe_success) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata, Titan M user data, and DCK");
+    } else if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
+    } else if (nugget_status != APP_SUCCESS && !dck_wipe_success) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                BnFastboot::FAILURE_UNKNOWN, "Titan M user data and DCK wipe failed");
+    } else if (nugget_status != APP_SUCCESS) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  "Titan M user data wipe failed");
+    } else if (wipe_status != WIPE_OK && !dck_wipe_success) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and DCK");
+    } else if (!dck_wipe_success) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  "DCK wipe failed");
+    } else {
+        if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
+        else  // Should not reach here, but handle it anyway
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                      "Unknown failure");
+    }
+
+    // Return exactly what happened
+    if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
+
+    } else if (nugget_status != APP_SUCCESS) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                  "Titan M user data wipe failed");
+    } else {
+        if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
+        else  // Should not reach here, but handle it anyway
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
+                                                                      "Unknown failure");
+    }
+
+    return ScopedAStatus::ok();
+}
+
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/fastboot/android.hardware.fastboot-service.pixel.xml b/fastboot/android.hardware.fastboot-service.pixel.xml
new file mode 100644
index 0000000..9490f98
--- /dev/null
+++ b/fastboot/android.hardware.fastboot-service.pixel.xml
@@ -0,0 +1,8 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.fastboot</name>
+        <version>1</version>
+        <fqname>IFastboot/default</fqname>
+    </hal>
+</manifest>
+
diff --git a/fastboot/android.hardware.fastboot-service.pixel_recovery.rc b/fastboot/android.hardware.fastboot-service.pixel_recovery.rc
new file mode 100644
index 0000000..799b716
--- /dev/null
+++ b/fastboot/android.hardware.fastboot-service.pixel_recovery.rc
@@ -0,0 +1,6 @@
+service vendor.pixel-fastboot /system/bin/hw/android.hardware.fastboot-service.pixel_recovery
+    class hal
+    seclabel u:r:hal_fastboot_default:s0
+    user root
+    group root
+    interface aidl android.hardware.fastboot.IFastboot/default
diff --git a/fastboot/include/fastboot/Fastboot_aidl.h b/fastboot/include/fastboot/Fastboot_aidl.h
new file mode 100644
index 0000000..51ae738
--- /dev/null
+++ b/fastboot/include/fastboot/Fastboot_aidl.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 "aidl/android/hardware/fastboot/BnFastboot.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+class Fastboot : public BnFastboot {
+#define FB_OEM_SET_BRIGHTNESS "setbrightness"
+    ::ndk::ScopedAStatus doOemCommand(const std::string &in_oemCmd,
+                                      std::string *_aidl_return) override;
+    ::ndk::ScopedAStatus doOemSpecificErase() override;
+    ::ndk::ScopedAStatus getBatteryVoltageFlashingThreshold(int32_t *_aidl_return) override;
+    ::ndk::ScopedAStatus getOffModeChargeState(bool *_aidl_return) override;
+    ::ndk::ScopedAStatus getPartitionType(
+            const std::string &in_partitionName,
+            ::aidl::android::hardware::fastboot::FileSystemType *_aidl_return) override;
+    ::ndk::ScopedAStatus getVariant(std::string *_aidl_return) override;
+};
+
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
new file mode 100644
index 0000000..d4c1117
--- /dev/null
+++ b/fastboot/main.cpp
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (C) 2022 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 <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "include/fastboot/Fastboot_aidl.h"
+
+using aidl::android::hardware::fastboot::Fastboot;
+using aidl::android::hardware::fastboot::IFastboot;
+
+int main(int, char *argv[]) {
+    android::base::InitLogging(argv, android::base::KernelLogger);
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<IFastboot> service = ndk::SharedRefBase::make<Fastboot>();
+
+    const std::string instance = std::string(IFastboot::descriptor) + "/default";
+    auto status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK) << "Failed to add service " << instance << " " << status;
+    LOG(INFO) << "IFastboot AIDL service running...";
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/health/Android.bp b/health/Android.bp
index 136e7db..421fe8c 100644
--- a/health/Android.bp
+++ b/health/Android.bp
@@ -36,7 +36,7 @@
     export_shared_lib_headers: [
         "android.frameworks.stats-V1-ndk",
         "android.hardware.health-V1-ndk",
-        "pixelatoms-cpp",
+        "libpixelatoms_defs",
     ],
 
     shared_libs: [
@@ -46,9 +46,8 @@
         "libbinder_ndk",
         "libcutils",
         "libhidlbase",
-        "libprotobuf-cpp-lite",
+        "libpixelatoms_defs",
         "libutils",
-        "pixelatoms-cpp",
     ],
 }
 
diff --git a/health/BatteryDefender.cpp b/health/BatteryDefender.cpp
index 8b693f0..66876a5 100644
--- a/health/BatteryDefender.cpp
+++ b/health/BatteryDefender.cpp
@@ -239,6 +239,10 @@
     return isOverrideDisabled || (isDefaultVendorChargeLevel == false) || (isCtrlEnabled == false);
 }
 
+bool BatteryDefender::isDockDefendTrigger(void) {
+    return readFileToInt(kPathDockState, true) == 1;
+}
+
 void BatteryDefender::addTimeToChargeTimers(void) {
     if (mIsPowerAvailable) {
         if (mHasReachedHighCapacityLevel) {
@@ -410,6 +414,11 @@
         health_info->batteryHealth = BatteryHealth::OVERHEAT;
     }
 
+    /* Do the same as above when dock-defend triggers */
+    if (mIsDockDefendTrigger) {
+        health_info->batteryHealth = BatteryHealth::OVERHEAT;
+    }
+
     /**
      * If the kernel is forcing the input current limit to 0, then the online status may
      * need to be overwritten. Also, setting a charge limit below the current charge level
@@ -461,6 +470,7 @@
     mIsDefenderDisabled = isBatteryDefenderDisabled(chargeLevelVendorStart, chargeLevelVendorStop);
     mIsPowerAvailable = isChargePowerAvailable();
     mTimeBetweenUpdateCalls = getDeltaTimeSeconds(&mTimePreviousSecs);
+    mIsDockDefendTrigger = isDockDefendTrigger();
 
     // Run state machine
     stateMachine_runAction(mCurrentState, *health_info);
diff --git a/health/BatteryMetricsLogger.cpp b/health/BatteryMetricsLogger.cpp
index 5e4c7fb..7f6adb3 100644
--- a/health/BatteryMetricsLogger.cpp
+++ b/health/BatteryMetricsLogger.cpp
@@ -19,17 +19,13 @@
 #include <pixelhealth/HealthHelper.h>
 #include <pixelhealth/StatsHelper.h>
 
-using aidl::android::hardware::health::BatteryStatus;
-using aidl::android::hardware::health::HealthInfo;
-
 namespace hardware {
 namespace google {
 namespace pixel {
 namespace health {
 
-VendorBatteryHealthSnapshot::BatterySnapshotType toBatterySnapshotType(int type) {
-    return static_cast<VendorBatteryHealthSnapshot::BatterySnapshotType>(type);
-}
+using aidl::android::hardware::health::BatteryStatus;
+using aidl::android::hardware::health::HealthInfo;
 
 BatteryMetricsLogger::BatteryMetricsLogger(const char *const batt_res, const char *const batt_ocv,
                                            const char *const batt_avg_res, int sample_period,
@@ -57,26 +53,12 @@
     if (kStatsSnapshotType[type] < 0)
         return false;
 
-    VendorBatteryHealthSnapshot min_stats_ss;
-    min_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type]));
-    min_stats_ss.set_temperature_deci_celsius(min_[type][TEMP]);
-    min_stats_ss.set_voltage_micro_volt(min_[type][VOLT]);
-    min_stats_ss.set_current_micro_amps(min_[type][CURR]);
-    min_stats_ss.set_open_circuit_micro_volt(min_[type][OCV]);
-    min_stats_ss.set_resistance_micro_ohm(min_[type][RES]);
-    min_stats_ss.set_level_percent(min_[type][SOC]);
-
-    VendorBatteryHealthSnapshot max_stats_ss;
-    max_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type] + 1));
-    max_stats_ss.set_temperature_deci_celsius(max_[type][TEMP]);
-    max_stats_ss.set_voltage_micro_volt(max_[type][VOLT]);
-    max_stats_ss.set_current_micro_amps(max_[type][CURR]);
-    max_stats_ss.set_open_circuit_micro_volt(max_[type][OCV]);
-    max_stats_ss.set_resistance_micro_ohm(max_[type][RES]);
-    max_stats_ss.set_level_percent(max_[type][SOC]);
-
-    reportBatteryHealthSnapshot(stats_client, min_stats_ss);
-    reportBatteryHealthSnapshot(stats_client, max_stats_ss);
+    reportBatteryHealthSnapshot(stats_client, kStatsSnapshotType[type], min_[type][TEMP],
+                                min_[type][VOLT], min_[type][CURR], min_[type][OCV],
+                                min_[type][RES], min_[type][SOC]);
+    reportBatteryHealthSnapshot(stats_client, kStatsSnapshotType[type] + 1, max_[type][TEMP],
+                                max_[type][VOLT], max_[type][CURR], max_[type][OCV],
+                                max_[type][RES], max_[type][SOC]);
 
     return true;
 }
@@ -101,16 +83,9 @@
         return false;
     }
     // Upload average metric
-    VendorBatteryHealthSnapshot avg_res_ss_stats;
-    avg_res_ss_stats.set_type(VendorBatteryHealthSnapshot::BATTERY_SNAPSHOT_TYPE_AVG_RESISTANCE);
-    avg_res_ss_stats.set_temperature_deci_celsius(0);
-    avg_res_ss_stats.set_voltage_micro_volt(0);
-    avg_res_ss_stats.set_current_micro_amps(0);
-    avg_res_ss_stats.set_open_circuit_micro_volt(0);
-    avg_res_ss_stats.set_resistance_micro_ohm(batt_avg_res);
-    avg_res_ss_stats.set_level_percent(0);
-
-    reportBatteryHealthSnapshot(stats_client, avg_res_ss_stats);
+    reportBatteryHealthSnapshot(stats_client,
+                                VendorBatteryHealthSnapshot::BATTERY_SNAPSHOT_TYPE_AVG_RESISTANCE,
+                                0, 0, 0, 0, batt_avg_res, 0);
     return true;
 }
 
diff --git a/health/LowBatteryShutdownMetrics.cpp b/health/LowBatteryShutdownMetrics.cpp
index 3ff606c..b3d43f8 100644
--- a/health/LowBatteryShutdownMetrics.cpp
+++ b/health/LowBatteryShutdownMetrics.cpp
@@ -52,7 +52,6 @@
     }
 
     // Process and upload comma-delimited last voltage values
-    VendorBatteryCausedShutdown shutdown;
     int32_t voltage_avg;
     for (const auto &item : android::base::Split(prop_contents, ",")) {
         if (!(voltage_avg = stoi(item))) {
@@ -60,8 +59,7 @@
             continue;
         }
         LOG(INFO) << "Uploading voltage_avg: " << std::to_string(voltage_avg);
-        shutdown.set_last_recorded_micro_volt(voltage_avg);
-        reportBatteryCausedShutdown(stats_client, shutdown);
+        reportBatteryCausedShutdown(stats_client, voltage_avg);
     }
 
     // Clear property now that we've uploaded its contents
diff --git a/health/StatsHelper.cpp b/health/StatsHelper.cpp
index 22fb9f3..4900e4f 100644
--- a/health/StatsHelper.cpp
+++ b/health/StatsHelper.cpp
@@ -18,6 +18,8 @@
 #include <android/binder_manager.h>
 #include <pixelhealth/StatsHelper.h>
 
+#include "pixelatoms_defs.h"
+
 #define LOG_TAG "pixelhealth-vendor"
 
 #include <utils/Log.h>
@@ -29,7 +31,8 @@
 
 using aidl::android::frameworks::stats::VendorAtom;
 using aidl::android::frameworks::stats::VendorAtomValue;
-namespace PixelAtoms = android::hardware::google::pixel::PixelAtoms;
+
+namespace PixelAtoms = hardware::google::pixel::PixelAtoms;
 
 std::shared_ptr<IStats> getStatsService() {
     const std::string instance = std::string() + IStats::descriptor + "/default";
@@ -48,29 +51,30 @@
     return IStats::fromBinder(ndk::SpAIBinder(AServiceManager_getService(instance.c_str())));
 }
 
-void reportBatteryHealthSnapshot(const std::shared_ptr<IStats> &stats_client,
-                                 const VendorBatteryHealthSnapshot &batteryHealthSnapshot) {
+void reportBatteryHealthSnapshot(const std::shared_ptr<IStats> &stats_client, int32_t type,
+                                 int32_t temperature_deci_celsius, int32_t voltage_micro_volt,
+                                 int32_t current_micro_amps, int32_t open_circuit_micro_volt,
+                                 int32_t resistance_micro_ohm, int32_t level_percent) {
     // Load values array
     std::vector<VendorAtomValue> values(7);
     VendorAtomValue tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.type());
+    tmp.set<VendorAtomValue::intValue>(type);
     values[0] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.temperature_deci_celsius());
+    tmp.set<VendorAtomValue::intValue>(temperature_deci_celsius);
     values[1] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.voltage_micro_volt());
+    tmp.set<VendorAtomValue::intValue>(voltage_micro_volt);
     values[2] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.current_micro_amps());
+    tmp.set<VendorAtomValue::intValue>(current_micro_amps);
     values[3] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.open_circuit_micro_volt());
+    tmp.set<VendorAtomValue::intValue>(open_circuit_micro_volt);
     values[4] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.resistance_micro_ohm());
+    tmp.set<VendorAtomValue::intValue>(resistance_micro_ohm);
     values[5] = tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryHealthSnapshot.level_percent());
+    tmp.set<VendorAtomValue::intValue>(level_percent);
     values[6] = tmp;
 
     // Send vendor atom to IStats HAL
-    VendorAtom event = {.reverseDomainName = "",
-                        .atomId = PixelAtoms::Atom::kVendorBatteryHealthSnapshot,
+    VendorAtom event = {.atomId = PixelAtoms::VENDOR_BATTERY_HEALTH_SNAPSHOT,
                         .values = std::move(values)};
     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
     if (!ret.isOk())
@@ -78,16 +82,15 @@
 }
 
 void reportBatteryCausedShutdown(const std::shared_ptr<IStats> &stats_client,
-                                 const VendorBatteryCausedShutdown &batteryCausedShutdown) {
+                                 int32_t last_recorded_micro_volt) {
     // Load values array
     std::vector<VendorAtomValue> values(1);
     VendorAtomValue tmp;
-    tmp.set<VendorAtomValue::intValue>(batteryCausedShutdown.last_recorded_micro_volt());
+    tmp.set<VendorAtomValue::intValue>(last_recorded_micro_volt);
     values[0] = tmp;
 
     // Send vendor atom to IStats HAL
-    VendorAtom event = {.reverseDomainName = "",
-                        .atomId = PixelAtoms::Atom::kVendorBatteryCausedShutdown,
+    VendorAtom event = {.atomId = PixelAtoms::VENDOR_BATTERY_CAUSED_SHUTDOWN,
                         .values = std::move(values)};
     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
     if (!ret.isOk())
diff --git a/health/include/pixelhealth/BatteryDefender.h b/health/include/pixelhealth/BatteryDefender.h
index 40d190b..58dd080 100644
--- a/health/include/pixelhealth/BatteryDefender.h
+++ b/health/include/pixelhealth/BatteryDefender.h
@@ -101,6 +101,7 @@
             "/mnt/vendor/persist/battery/defender_charger_time";
     const std::string kPathPersistDefenderActiveTime =
             "/mnt/vendor/persist/battery/defender_active_time";
+    const std::string kPathDockState = "/sys/devices/platform/google,charger/dd_state";
 
     // Properties
     const char *const kPropChargeLevelVendorStart = "persist.vendor.charge.start.level";
@@ -140,6 +141,7 @@
     bool mIsDockPresent = false;
     bool mIsPowerAvailable = false;
     bool mIsDefenderDisabled = false;
+    bool mIsDockDefendTrigger = false;
     int32_t mTimeToActivateSecsModified;
 
     // State
@@ -183,6 +185,7 @@
     bool isDefaultChargeLevel(const int start, const int stop);
     bool isBatteryDefenderDisabled(const int vendorStart, const int vendorStop);
     void addTimeToChargeTimers(void);
+    bool isDockDefendTrigger(void);
 };
 
 }  // namespace health
diff --git a/health/include/pixelhealth/BatteryMetricsLogger.h b/health/include/pixelhealth/BatteryMetricsLogger.h
index 9bf2a69..e4d4d75 100644
--- a/health/include/pixelhealth/BatteryMetricsLogger.h
+++ b/health/include/pixelhealth/BatteryMetricsLogger.h
@@ -23,20 +23,22 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <batteryservice/BatteryService.h>
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
 #include <math.h>
 #include <time.h>
 #include <utils/Timers.h>
 
 #include <string>
 
+#include "pixelatoms_defs.h"
+
 namespace hardware {
 namespace google {
 namespace pixel {
 namespace health {
 
 using aidl::android::frameworks::stats::IStats;
-using android::hardware::google::pixel::PixelAtoms::VendorBatteryHealthSnapshot;
+
+using hardware::google::pixel::PixelAtoms::VendorBatteryHealthSnapshot;
 
 class BatteryMetricsLogger {
   public:
diff --git a/health/include/pixelhealth/StatsHelper.h b/health/include/pixelhealth/StatsHelper.h
index 90bf985..ea49bee 100644
--- a/health/include/pixelhealth/StatsHelper.h
+++ b/health/include/pixelhealth/StatsHelper.h
@@ -18,7 +18,6 @@
 #define HARDWARE_GOOGLE_PIXEL_HEALTH_STATSHELPER_H
 
 #include <aidl/android/frameworks/stats/IStats.h>
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
 
 namespace hardware {
 namespace google {
@@ -26,16 +25,16 @@
 namespace health {
 
 using aidl::android::frameworks::stats::IStats;
-using android::hardware::google::pixel::PixelAtoms::VendorBatteryCausedShutdown;
-using android::hardware::google::pixel::PixelAtoms::VendorBatteryHealthSnapshot;
 
 std::shared_ptr<IStats> getStatsService();
 
-void reportBatteryHealthSnapshot(const std::shared_ptr<IStats> &stats_client,
-                                 const VendorBatteryHealthSnapshot &batteryHealthSnapshot);
+void reportBatteryHealthSnapshot(const std::shared_ptr<IStats> &stats_client, int32_t type,
+                                 int32_t temperature_deci_celsius, int32_t voltage_micro_volt,
+                                 int32_t current_micro_amps, int32_t open_circuit_micro_volt,
+                                 int32_t resistance_micro_ohm, int32_t level_percent);
 
 void reportBatteryCausedShutdown(const std::shared_ptr<IStats> &stats_client,
-                                 const VendorBatteryCausedShutdown &batteryCausedShutdown);
+                                 int32_t last_recorded_micro_volt);
 
 }  // namespace health
 }  // namespace pixel
diff --git a/mm/device_gki.mk b/mm/device_gki.mk
index cd4fdf8..adcf227 100644
--- a/mm/device_gki.mk
+++ b/mm/device_gki.mk
@@ -1,7 +1,13 @@
 PRODUCT_COPY_FILES += \
       hardware/google/pixel/mm/pixel-mm-gki.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/pixel-mm-gki.rc \
       hardware/google/pixel/mm/fstab.zram.2g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.2g \
-      hardware/google/pixel/mm/fstab.zram.3g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.3g
+      hardware/google/pixel/mm/fstab.zram.3g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.3g \
+      hardware/google/pixel/mm/fstab.zram.4g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.4g \
+      hardware/google/pixel/mm/fstab.zram.5g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.5g \
+      hardware/google/pixel/mm/fstab.zram.6g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.6g \
+      hardware/google/pixel/mm/fstab.zram.40p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.40p \
+      hardware/google/pixel/mm/fstab.zram.50p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.50p \
+      hardware/google/pixel/mm/fstab.zram.60p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.60p
 
 ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
 PRODUCT_PACKAGES += \
diff --git a/mm/fstab.zram.40p b/mm/fstab.zram.40p
new file mode 100644
index 0000000..c85a59b
--- /dev/null
+++ b/mm/fstab.zram.40p
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=40%,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.4g b/mm/fstab.zram.4g
new file mode 100644
index 0000000..0cfad5a
--- /dev/null
+++ b/mm/fstab.zram.4g
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=4294967296,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.50p b/mm/fstab.zram.50p
new file mode 100644
index 0000000..b0e1ed6
--- /dev/null
+++ b/mm/fstab.zram.50p
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=50%,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.5g b/mm/fstab.zram.5g
new file mode 100644
index 0000000..8cd4d56
--- /dev/null
+++ b/mm/fstab.zram.5g
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=5368709120,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.60p b/mm/fstab.zram.60p
new file mode 100644
index 0000000..0e05048
--- /dev/null
+++ b/mm/fstab.zram.60p
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=60%,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.6g b/mm/fstab.zram.6g
new file mode 100644
index 0000000..04fcd49
--- /dev/null
+++ b/mm/fstab.zram.6g
@@ -0,0 +1 @@
+/dev/block/zram0	none	swap	defaults	zramsize=6442450944,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/pixel-mm-gki.rc b/mm/pixel-mm-gki.rc
index e5ab7f7..fa2cd35 100644
--- a/mm/pixel-mm-gki.rc
+++ b/mm/pixel-mm-gki.rc
@@ -19,8 +19,13 @@
     # khugepaged tuning
     write /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs 60000
 
-on property:persist.vendor.zram.size=*
-    setprop vendor.zram.size ${persist.vendor.zram.size}
+# Property from experiments - server config
+on property:persist.device_config.vendor_system_native_boot.zram_size=*
+    setprop vendor.zram.size ${persist.device_config.vendor_system_native_boot.zram_size}
+
+# Property for local test. It can overwrite the server side config
+on property:sys.boot_completed=1 && property:persist.vendor.boot.zram.size=*
+    setprop vendor.zram.size ${persist.vendor.boot.zram.size}
 
 on property:sys.boot_completed=1
     swapon_all /vendor/etc/fstab.zram.${vendor.zram.size}
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp
index f99e8e8..fa27425 100644
--- a/pixelstats/Android.bp
+++ b/pixelstats/Android.bp
@@ -51,6 +51,22 @@
     ],
 }
 
+genrule {
+    name: "pixelatoms_defs.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/pixelatoms_defs.h --namespace hardware,google,pixel,PixelAtoms --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
+    out: [
+        "pixelatoms_defs.h",
+    ],
+}
+
+cc_library {
+    name: "libpixelatoms_defs",
+    vendor: true,
+    generated_headers: ["pixelatoms_defs.h"],
+    export_generated_headers: ["pixelatoms_defs.h"],
+}
+
 cc_library {
   name: "libpixelstats",
   vendor: true,
@@ -58,7 +74,6 @@
 
   tidy_disabled_srcs: [
     "MmMetricsReporter.cpp", // b/215238264
-    "WlcReporter.cpp", // b/215238264
   ],
   srcs: [
     "BatteryCapacityReporter.cpp",
@@ -68,13 +83,12 @@
     "DropDetect.cpp",
     "MmMetricsReporter.cpp",
     "MitigationStatsReporter.cpp",
-    "OrientationCollector.cpp",
     "PcaChargeStats.cpp",
     "StatsHelper.cpp",
     "SysfsCollector.cpp",
+    "ThermalStatsReporter.cpp",
     "UeventListener.cpp",
     "WirelessChargeStats.cpp",
-    "WlcReporter.cpp",
   ],
   cflags: [
     "-Wall",
diff --git a/pixelstats/BatteryHealthReporter.cpp b/pixelstats/BatteryHealthReporter.cpp
index 41df779..102b5fe 100644
--- a/pixelstats/BatteryHealthReporter.cpp
+++ b/pixelstats/BatteryHealthReporter.cpp
@@ -79,24 +79,28 @@
             BatteryHealthStatus::kCurrentImpedanceFieldNumber,
             BatteryHealthStatus::kBatteryAgeFieldNumber,
             BatteryHealthStatus::kCycleCountFieldNumber,
+            BatteryHealthStatus::kBatteryDisconnectStatusFieldNumber,
     };
 
     const int32_t vtier_fields_size = std::size(health_status_stats_fields);
-    static_assert(vtier_fields_size == 10, "Unexpected battery health status fields size");
+    static_assert(vtier_fields_size == 11, "Unexpected battery health status fields size");
     std::vector<VendorAtomValue> values(vtier_fields_size);
     VendorAtomValue val;
-    int32_t i = 0, tmp[vtier_fields_size] = {0};
+    int32_t i = 0, fields_size = 0, tmp[vtier_fields_size] = {0};
 
     // health_algo: health_status, health_index,healh_capacity_index,health_imp_index,
-    // swelling_cumulative,health_full_capacity,current_impedance, battery_age,cycle_count
-    if (sscanf(line, "%d: %d, %d,%d,%d %d,%d,%d %d,%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
-               &tmp[5], &tmp[6], &tmp[7], &tmp[8], &tmp[9]) != vtier_fields_size) {
-        /* If format isn't as expected, then ignore line on purpose */
+    // swelling_cumulative,health_full_capacity,current_impedance, battery_age,cycle_count,
+    // bpst_status
+    fields_size = sscanf(line, "%d: %d, %d,%d,%d %d,%d,%d %d,%d, %d", &tmp[0], &tmp[1], &tmp[2],
+                         &tmp[3], &tmp[4], &tmp[5], &tmp[6], &tmp[7], &tmp[8], &tmp[9], &tmp[10]);
+    if (fields_size < (vtier_fields_size - 1) || fields_size > vtier_fields_size) {
+        // Whether bpst_status exists or not, it needs to be compatible
+        // If format isn't as expected, then ignore line on purpose
         return;
     }
 
     ALOGD("BatteryHealthStatus: processed %s", line);
-    for (i = 0; i < vtier_fields_size; i++) {
+    for (i = 0; i < fields_size; i++) {
         val.set<VendorAtomValue::intValue>(tmp[i]);
         values[health_status_stats_fields[i] - kVendorAtomOffset] = val;
     }
diff --git a/pixelstats/ChargeStatsReporter.cpp b/pixelstats/ChargeStatsReporter.cpp
index 8bc2720..6bc2b64 100644
--- a/pixelstats/ChargeStatsReporter.cpp
+++ b/pixelstats/ChargeStatsReporter.cpp
@@ -45,21 +45,23 @@
                                             const std::string line, const std::string wline_at,
                                             const std::string wline_ac,
                                             const std::string pca_line) {
-    int charge_stats_fields[] = {ChargeStats::kAdapterTypeFieldNumber,
-                                 ChargeStats::kAdapterVoltageFieldNumber,
-                                 ChargeStats::kAdapterAmperageFieldNumber,
-                                 ChargeStats::kSsocInFieldNumber,
-                                 ChargeStats::kVoltageInFieldNumber,
-                                 ChargeStats::kSsocOutFieldNumber,
-                                 ChargeStats::kVoltageOutFieldNumber,
-                                 ChargeStats::kAdapterCapabilities0FieldNumber,
-                                 ChargeStats::kAdapterCapabilities1FieldNumber,
-                                 ChargeStats::kAdapterCapabilities2FieldNumber,
-                                 ChargeStats::kAdapterCapabilities3FieldNumber,
-                                 ChargeStats::kAdapterCapabilities4FieldNumber,
-                                 ChargeStats::kReceiverState0FieldNumber,
-                                 ChargeStats::kReceiverState1FieldNumber,
-                                 ChargeStats::kChargeCapacityFieldNumber};
+    int charge_stats_fields[] = {
+            ChargeStats::kAdapterTypeFieldNumber,
+            ChargeStats::kAdapterVoltageFieldNumber,
+            ChargeStats::kAdapterAmperageFieldNumber,
+            ChargeStats::kSsocInFieldNumber,
+            ChargeStats::kVoltageInFieldNumber,
+            ChargeStats::kSsocOutFieldNumber,
+            ChargeStats::kVoltageOutFieldNumber,
+            ChargeStats::kChargeCapacityFieldNumber,
+            ChargeStats::kAdapterCapabilities0FieldNumber,
+            ChargeStats::kAdapterCapabilities1FieldNumber,
+            ChargeStats::kAdapterCapabilities2FieldNumber,
+            ChargeStats::kAdapterCapabilities3FieldNumber,
+            ChargeStats::kAdapterCapabilities4FieldNumber,
+            ChargeStats::kReceiverState0FieldNumber,
+            ChargeStats::kReceiverState1FieldNumber,
+    };
     const int32_t chg_fields_size = std::size(charge_stats_fields);
     static_assert(chg_fields_size == 15, "Unexpected charge stats fields size");
     const int32_t wlc_fields_size = 7;
@@ -70,7 +72,7 @@
 
     ALOGD("processing %s", line.c_str());
     if (sscanf(line.c_str(), "%d,%d,%d, %d,%d,%d,%d %d", &tmp[0], &tmp[1], &tmp[2], &tmp[3],
-               &tmp[4], &tmp[5], &tmp[6], &tmp[14]) == 8) {
+               &tmp[4], &tmp[5], &tmp[6], &tmp[7]) == 8) {
         /* Age Adjusted Charge Rate (AACR) logs an additional battery capacity in order to determine
          * the charge curve needed to minimize battery cycle life degradation, while also minimizing
          * impact to the user.
@@ -89,8 +91,8 @@
         } else {
             tmp[0] = wireless_charge_stats_.TranslateSysModeToAtomValue(ssoc_tmp);
             ALOGD("wlc: processing %s", wline_ac.c_str());
-            if (sscanf(wline_ac.c_str(), "D:%x,%x,%x,%x,%x, %x,%x", &tmp[7], &tmp[8], &tmp[9],
-                       &tmp[10], &tmp[11], &tmp[12], &tmp[13]) != 7)
+            if (sscanf(wline_ac.c_str(), "D:%x,%x,%x,%x,%x, %x,%x", &tmp[8], &tmp[9], &tmp[10],
+                       &tmp[11], &tmp[12], &tmp[13], &tmp[14]) != 7)
                 ALOGE("Couldn't process %s", wline_ac.c_str());
             else
                 fields_size = chg_fields_size; /* include wlc stats */
@@ -104,14 +106,14 @@
             ALOGE("Couldn't process %s", pca_line.c_str());
         } else {
             fields_size = chg_fields_size; /* include pca stats */
-            tmp[9] = pca_rs[2];
-            tmp[10] = pca_rs[3];
-            tmp[11] = pca_rs[4];
-            tmp[13] = pca_rs[1];
+            tmp[10] = pca_rs[2];
+            tmp[11] = pca_rs[3];
+            tmp[12] = pca_rs[4];
+            tmp[14] = pca_rs[1];
             if (wline_at.empty()) {
-                tmp[7] = pca_ac[0];
-                tmp[8] = pca_ac[1];
-                tmp[12] = pca_rs[0];
+                tmp[8] = pca_ac[0];
+                tmp[9] = pca_ac[1];
+                tmp[13] = pca_rs[0];
             }
         }
     }
@@ -201,11 +203,12 @@
 void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
                                          const std::string &path) {
     std::string file_contents, line, wfile_contents, wline_at, wline_ac, pca_file_contents,
-            pca_line, thermal_file_contents;
+            pca_line, thermal_file_contents, gcharger_file_contents;
     std::istringstream ss;
     bool has_wireless = wireless_charge_stats_.CheckWirelessContentsAndAck(&wfile_contents);
     bool has_pca = pca_charge_stats_.CheckPcaContentsAndAck(&pca_file_contents);
-    bool has_thermal = checkThermalContentsAndAck(&thermal_file_contents);
+    bool has_thermal = checkContentsAndAck(&thermal_file_contents, kThermalChargeMetricsPath);
+    bool has_gcharger = checkContentsAndAck(&gcharger_file_contents, kGChargerMetricsPath);
 
     if (!ReadFileToString(path.c_str(), &file_contents)) {
         ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
@@ -255,15 +258,23 @@
             ReportVoltageTierStats(stats_client, line.c_str());
         }
     }
+
+    if (has_gcharger) {
+        std::istringstream wss;
+        wss.str(gcharger_file_contents);
+        while (std::getline(wss, line)) {
+            ReportVoltageTierStats(stats_client, line.c_str());
+        }
+    }
 }
 
-bool ChargeStatsReporter::checkThermalContentsAndAck(std::string *file_contents) {
-    if (!ReadFileToString(kThermalChargeMetricsPath.c_str(), file_contents)) {
+bool ChargeStatsReporter::checkContentsAndAck(std::string *file_contents, const std::string &path) {
+    if (!ReadFileToString(path.c_str(), file_contents)) {
         return false;
     }
 
-    if (!WriteStringToFile("0", kThermalChargeMetricsPath.c_str())) {
-        ALOGE("Couldn't clear %s - %s", kThermalChargeMetricsPath.c_str(), strerror(errno));
+    if (!WriteStringToFile("0", path.c_str())) {
+        ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
         return false;
     }
     return true;
diff --git a/pixelstats/MmMetricsReporter.cpp b/pixelstats/MmMetricsReporter.cpp
index 558fd2d..874cc5b 100644
--- a/pixelstats/MmMetricsReporter.cpp
+++ b/pixelstats/MmMetricsReporter.cpp
@@ -18,6 +18,7 @@
 
 #include <aidl/android/frameworks/stats/IStats.h>
 #include <android-base/file.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -53,13 +54,13 @@
         {"nr_anon_pages", PixelMmMetricsPerHour::kAnonPagesFieldNumber, false},
         {"nr_file_pages", PixelMmMetricsPerHour::kFilePagesFieldNumber, false},
         {"nr_slab_reclaimable", PixelMmMetricsPerHour::kSlabReclaimableFieldNumber, false},
+        {"nr_slab_unreclaimable", PixelMmMetricsPerHour::kSlabUnreclaimableFieldNumber, false},
         {"nr_zspages", PixelMmMetricsPerHour::kZspagesFieldNumber, false},
         {"nr_unevictable", PixelMmMetricsPerHour::kUnevictableFieldNumber, false},
 };
 
 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerDayInfo = {
         {"workingset_refault", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
-        {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
         {"pswpin", PixelMmMetricsPerDay::kPswpinFieldNumber, true},
         {"pswpout", PixelMmMetricsPerDay::kPswpoutFieldNumber, true},
         {"allocstall_dma", PixelMmMetricsPerDay::kAllocstallDmaFieldNumber, true},
@@ -78,6 +79,18 @@
         {"pgalloc_costly_order", PixelMmMetricsPerDay::kPgallocHighFieldNumber, true},
         {"pgcache_hit", PixelMmMetricsPerDay::kPgcacheHitFieldNumber, true},
         {"pgcache_miss", PixelMmMetricsPerDay::kPgcacheMissFieldNumber, true},
+        {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFileFieldNumber, true},
+        {"workingset_refault_anon", PixelMmMetricsPerDay::kWorkingsetRefaultAnonFieldNumber, true},
+        {"compact_success", PixelMmMetricsPerDay::kCompactSuccessFieldNumber, true},
+        {"compact_fail", PixelMmMetricsPerDay::kCompactFailFieldNumber, true},
+        {"kswapd_low_wmark_hit_quickly", PixelMmMetricsPerDay::kKswapdLowWmarkHqFieldNumber, true},
+        {"kswapd_high_wmark_hit_quickly", PixelMmMetricsPerDay::kKswapdHighWmarkHqFieldNumber,
+         true},
+        {"thp_file_alloc", PixelMmMetricsPerDay::kThpFileAllocFieldNumber, true},
+        {"thp_zero_page_alloc", PixelMmMetricsPerDay::kThpZeroPageAllocFieldNumber, true},
+        {"thp_split_page", PixelMmMetricsPerDay::kThpSplitPageFieldNumber, true},
+        {"thp_migration_split", PixelMmMetricsPerDay::kThpMigrationSplitFieldNumber, true},
+        {"thp_deferred_split_page", PixelMmMetricsPerDay::kThpDeferredSplitPageFieldNumber, true},
 };
 
 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusInfo = {
@@ -146,7 +159,11 @@
       kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"),
       kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb"),
       kGpuTotalPages("/sys/kernel/pixel_stat/gpu/mem/total_page_count"),
-      kPixelStatMm("/sys/kernel/pixel_stat/mm") {
+      kCompactDuration("/sys/kernel/pixel_stat/mm/compaction/mm_compaction_duration"),
+      kDirectReclaimBasePath("/sys/kernel/pixel_stat/mm/vmscan/direct_reclaim"),
+      kPixelStatMm("/sys/kernel/pixel_stat/mm"),
+      prev_compaction_duration_(kNumCompactionDurationPrevMetrics, 0),
+      prev_direct_reclaim_(kNumDirectReclaimPrevMetrics, 0) {
     is_user_build_ = checkUserBuild();
     ker_mm_metrics_support_ = checkKernelMMMetricSupport();
 }
@@ -170,6 +187,90 @@
     return true;
 }
 
+/*
+ * This function reads whole file and parses tokens separated by <delim> into
+ * long integers.  Useful for direct reclaim & compaction duration sysfs nodes.
+ * Data write is using all or none policy: It will not write partial data unless
+ * all data values are good.
+ *
+ * path: file to open/read
+ * data: where to store the results
+ * start_idx: index into data[] where to start saving the results
+ * delim: delimiters separating different longs
+ * skip: how many resulting longs to skip before saving
+ * nonnegtive: set to true to validate positive numbers
+ *
+ * Return value: number of longs actually stored on success.  negative
+ *               error codes on errors.
+ */
+static int ReadFileToLongs(const std::string &path, std::vector<long> *data, int start_idx,
+                           const char *delim, int skip, bool nonnegative = false) {
+    std::vector<long> out;
+    enum { err_read_file = -1, err_parse = -2 };
+    std::string file_contents;
+
+    if (!ReadFileToString(path, &file_contents)) {
+        // Don't print this log if the file doesn't exist, since logs will be printed repeatedly.
+        if (errno != ENOENT) {
+            ALOGI("Unable to read %s - %s", path.c_str(), strerror(errno));
+        }
+        return err_read_file;
+    }
+
+    file_contents = android::base::Trim(file_contents);
+    std::vector<std::string> words = android::base::Tokenize(file_contents, delim);
+    if (words.size() == 0)
+        return 0;
+
+    for (auto &w : words) {
+        if (skip) {
+            skip--;
+            continue;
+        }
+        long tmp;
+        if (!android::base::ParseInt(w, &tmp) || (nonnegative && tmp < 0))
+            return err_parse;
+        out.push_back(tmp);
+    }
+
+    int min_size = std::max(static_cast<int>(out.size()) + start_idx, 0);
+    if (min_size > data->size())
+        data->resize(min_size);
+    std::copy(out.begin(), out.end(), data->begin() + start_idx);
+
+    return out.size();
+}
+
+/*
+ * This function calls ReadFileToLongs, and checks the expected number
+ * of long integers read.  Useful for direct reclaim & compaction duration
+ * sysfs nodes.
+ *
+ *  path: file to open/read
+ *  data: where to store the results
+ *  start_idx: index into data[] where to start saving the results
+ *  delim: delimiters separating different longs
+ *  skip: how many resulting longs to skip before saving
+ *  expected_num: number of expected longs to be read.
+ *  nonnegtive: set to true to validate positive numbers
+ *
+ *  Return value: true if successfully get expected number of long values.
+ *                otherwise false.
+ */
+static inline bool ReadFileToLongsCheck(const std::string &path, std::vector<long> *store,
+                                        int start_idx, const char *delim, int skip,
+                                        int expected_num, bool nonnegative = false) {
+    int num = ReadFileToLongs(path, store, start_idx, delim, skip, nonnegative);
+
+    if (num == expected_num)
+        return true;
+
+    int last_idx = std::min(start_idx + expected_num, static_cast<int>(store->size()));
+    std::fill(store->begin() + start_idx, store->begin() + last_idx, -1);
+
+    return false;
+}
+
 bool MmMetricsReporter::reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
                                          const std::vector<VendorAtomValue> &values,
                                          const std::string &atom_name) {
@@ -272,7 +373,7 @@
         if (max_idx < entry.atom_key)
             max_idx = entry.atom_key;
     }
-    int size = max_idx - kVendorAtomOffset + 1;
+    unsigned int size = max_idx - kVendorAtomOffset + 1;
     if (atom_values->size() < size)
         atom_values->resize(size, tmp);
 
@@ -301,6 +402,10 @@
     (*prev_mm_metrics) = mm_metrics;
 }
 
+void MmMetricsReporter::aggregatePixelMmMetricsPer5Min() {
+    aggregatePressureStall();
+}
+
 void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client) {
     if (!MmMetricsSupported())
         return;
@@ -312,28 +417,23 @@
     uint64_t ion_total_pools = getIonTotalPools();
     uint64_t gpu_memory = getGpuMemory();
 
-    std::vector<VendorAtomValue> values;
-    bool is_first_atom = (prev_hour_vmstat_.size() == 0) ? true : false;
-    fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values);
-
-    // resize values to add the following fields
+    // allocate enough values[] entries for the metrics.
     VendorAtomValue tmp;
     tmp.set<VendorAtomValue::longValue>(0);
-    int size = PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset + 1;
-    if (values.size() < size) {
-        values.resize(size, tmp);
-    }
+    int last_value_index =
+            PixelMmMetricsPerHour::kPsiMemSomeAvg300AvgFieldNumber - kVendorAtomOffset;
+    std::vector<VendorAtomValue> values(last_value_index + 1, tmp);
+
+    fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values);
     tmp.set<VendorAtomValue::longValue>(ion_total_pools);
     values[PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset] = tmp;
     tmp.set<VendorAtomValue::longValue>(gpu_memory);
     values[PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset] = tmp;
+    fillPressureStallAtom(&values);
 
-    // Don't report the first atom to avoid big spike in accumulated values.
-    if (!is_first_atom) {
-        // Send vendor atom to IStats HAL
-        reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerHour, values,
-                         "PixelMmMetricsPerHour");
-    }
+    // Send vendor atom to IStats HAL
+    reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerHour, values,
+                     "PixelMmMetricsPerHour");
 }
 
 void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client) {
@@ -344,8 +444,21 @@
     if (vmstat.size() == 0)
         return;
 
-    std::vector<VendorAtomValue> values;
+    std::vector<long> direct_reclaim;
+    readDirectReclaimStat(&direct_reclaim);
+
+    std::vector<long> compaction_duration;
+    readCompactionDurationStat(&compaction_duration);
+
     bool is_first_atom = (prev_day_vmstat_.size() == 0) ? true : false;
+
+    // allocate enough values[] entries for the metrics.
+    VendorAtomValue tmp;
+    tmp.set<VendorAtomValue::longValue>(0);
+    int last_value_index =
+            PixelMmMetricsPerDay::kThpDeferredSplitPageFieldNumber - kVendorAtomOffset;
+    std::vector<VendorAtomValue> values(last_value_index + 1, tmp);
+
     fillAtomValues(kMmMetricsPerDayInfo, vmstat, &prev_day_vmstat_, &values);
 
     std::map<std::string, uint64_t> pixel_vmstat =
@@ -355,6 +468,8 @@
                      &prev_kswapd_stime_, &values);
     fillProcessStime(PixelMmMetricsPerDay::kKcompactdStimeClksFieldNumber, "kcompactd0",
                      &kcompactd_pid_, &prev_kcompactd_stime_, &values);
+    fillDirectReclaimStatAtom(direct_reclaim, &values);
+    fillCompactionDurationStatAtom(direct_reclaim, &values);
 
     // Don't report the first atom to avoid big spike in accumulated values.
     if (!is_first_atom) {
@@ -498,6 +613,544 @@
 }
 
 /**
+ * This function reads compaction duration sysfs node
+ * (/sys/kernel/pixel_stat/mm/compaction/mm_compaction_duration)
+ *
+ * store: vector to save compaction duration info
+ */
+void MmMetricsReporter::readCompactionDurationStat(std::vector<long> *store) {
+    static const std::string path(kCompactDuration);
+    constexpr int num_metrics = 6;
+
+    store->resize(num_metrics);
+
+    int start_idx = 0;
+    int expected_num = num_metrics;
+
+    if (!ReadFileToLongsCheck(path, store, start_idx, " ", 1, expected_num, true)) {
+        ALOGI("Unable to read %s for the direct reclaim info.", path.c_str());
+    }
+}
+
+/**
+ * This function fills atom values (values) from acquired compaction duration
+ * information from vector store
+ *
+ * store: the already collected (by readCompactionDurationStat()) compaction
+ *        duration information
+ * values: the atom value vector to be filled.
+ */
+void MmMetricsReporter::fillCompactionDurationStatAtom(const std::vector<long> &store,
+                                                       std::vector<VendorAtomValue> *values) {
+    // first metric index
+    constexpr int start_idx = PixelMmMetricsPerDay::kCompactionTotalTimeFieldNumber;
+    constexpr int num_metrics = 6;
+
+    if (!MmMetricsSupported())
+        return;
+
+    int size = start_idx + num_metrics - kVendorAtomOffset;
+    if (values->size() < size)
+        values->resize(size);
+
+    for (int i = 0; i < num_metrics; i++) {
+        VendorAtomValue tmp;
+        if (store[i] == -1) {
+            tmp.set<VendorAtomValue::longValue>(0);
+        } else {
+            tmp.set<VendorAtomValue::longValue>(store[i] - prev_compaction_duration_[i]);
+            prev_compaction_duration_[i] = store[i];
+        }
+        (*values)[start_idx + i] = tmp;
+    }
+    prev_compaction_duration_ = store;
+}
+
+/**
+ * This function reads direct reclaim sysfs node (4 files:
+ * /sys/kernel/pixel_stat/mm/vmscan/direct_reclaim/<level>/latency_stat,
+ * where <level> = native, top, visible, other.), and save total time and
+ * 4 latency information per file. Total (1+4) x 4 = 20 metrics will be
+ * saved.
+ *
+ * store: vector to save direct reclaim info
+ */
+void MmMetricsReporter::readDirectReclaimStat(std::vector<long> *store) {
+    static const std::string base_path(kDirectReclaimBasePath);
+    static const std::vector<std::string> dr_levels{"native", "top", "visible", "other"};
+    static const std::string sysfs_name = "latency_stat";
+    constexpr int num_metrics_per_file = 5;
+    int num_file = dr_levels.size();
+    int num_metrics = num_metrics_per_file * num_file;
+
+    store->resize(num_metrics);
+    int pass = -1;
+    for (auto level : dr_levels) {
+        ++pass;
+        std::string path = base_path + '/' + level + '/' + sysfs_name;
+        int start_idx = pass * num_metrics_per_file;
+        int expected_num = num_metrics_per_file;
+        if (!ReadFileToLongsCheck(path, store, start_idx, " ", 1, expected_num, true)) {
+            ALOGI("Unable to read %s for the direct reclaim info.", path.c_str());
+        }
+    }
+}
+
+/**
+ * This function fills atom values (values) from acquired direct reclaim
+ * information from vector store
+ *
+ * store: the already collected (by readDirectReclaimStat()) direct reclaim
+ *        information
+ * values: the atom value vector to be filled.
+ */
+void MmMetricsReporter::fillDirectReclaimStatAtom(const std::vector<long> &store,
+                                                  std::vector<VendorAtomValue> *values) {
+    // first metric index
+    constexpr int start_idx = PixelMmMetricsPerDay::kDirectReclaimNativeLatencyTotalTimeFieldNumber;
+    constexpr int num_metrics = 20; /* num_metrics_per_file * num_file */
+
+    if (!MmMetricsSupported())
+        return;
+
+    int size = start_idx + num_metrics - kVendorAtomOffset;
+    if (values->size() < size)
+        values->resize(size);
+
+    for (int i = 0; i < num_metrics; i++) {
+        VendorAtomValue tmp;
+        tmp.set<VendorAtomValue::longValue>(store[i] - prev_direct_reclaim_[i]);
+        (*values)[start_idx + i] = tmp;
+    }
+    prev_direct_reclaim_ = store;
+}
+
+/**
+ * This function reads pressure (PSI) files (loop thru all 3 files: cpu, io, and
+ * memory) and calls the parser to parse and store the metric values.
+ * Note that each file have two lines (except cpu has one line only): one with
+ * a leading "full", and the other with a leading "some", showing the category
+ * for that line.
+ * A category has 4 metrics, avg10, avg60, avg300, and total.
+ * i.e. the moving average % of PSI in 10s, 60s, 300s time window plus lastly
+ * the total stalled time, except that 'cpu' has no 'full' category.
+ * In total, we have 3 x 2 x 4 - 4 = 24 - 4  = 20 metrics, arranged in
+ * the order of
+ *
+ *    cpu_some_avg<xyz>
+ *    cpu_some_total
+ *    io_full_avg<xyz>
+ *    io_full_total
+ *    io_some_avg<xyz>
+ *    io_some_total
+ *    mem_full_avg<xyz>
+ *    mem_full_total
+ *    mem_some_avg<xyz>
+ *    mem_some_total
+ *
+ *    where <xyz>=10, 60, 300 in the order as they appear.
+ *
+ *    Note that for those avg values (i.e.  <abc>_<def>_avg<xyz>), they
+ *    are in percentage with 2-decimal digit accuracy.  We will use an
+ *    integer in 2-decimal fixed point format to represent the values.
+ *    i.e. value x 100, or to cope with floating point errors,
+ *         floor(value x 100 + 0.5)
+ *
+ *    In fact, in newer kernels, "cpu" PSI has no "full" category.  Some
+ *    old kernel has them all zeros, to keep backward compatibility.  The
+ *    parse function called by this function is able to detect and ignore
+ *    the "cpu, full" category.
+ *
+ *    sample pressure stall files:
+ *    /proc/pressure # cat cpu
+ *    some avg10=2.93 avg60=3.17 avg300=3.15 total=94628150260
+ *    /proc/pressure # cat io
+ *    some avg10=1.06 avg60=1.15 avg300=1.18 total=37709873805
+ *    full avg10=1.06 avg60=1.10 avg300=1.11 total=36592322936
+ *    /proc/pressure # cat memory
+ *    some avg10=0.00 avg60=0.00 avg300=0.00 total=29705314
+ *    full avg10=0.00 avg60=0.00 avg300=0.00 total=17234456
+ *
+ *    PSI information definitions could be found at
+ *    https://www.kernel.org/doc/html/latest/accounting/psi.html
+ *
+ * basePath: the base path to the pressure stall information
+ * store: pointer to the vector to store the 20 metrics in the mentioned
+ *        order
+ */
+void MmMetricsReporter::readPressureStall(const char *basePath, std::vector<long> *store) {
+    constexpr int kTypeIdxCpu = 0;
+
+    // Callers should have already prepared this, but we resize it here for safety
+    store->resize(kPsiNumAllMetrics);
+    std::fill(store->begin(), store->end(), -1);
+
+    // To make the process unified, we prepend an imaginary "cpu + full"
+    // type-category combination.  Now, each file (cpu, io, memnry) contains
+    // two categories, i.e. "full" and "some".
+    // Each category has <kPsiNumNames> merics and thus need that many entries
+    // to store them, except that the first category (the imaginary one) do not
+    // need any storage. So we set the save index for the 1st file ("cpu") to
+    // -kPsiNumNames.
+    int file_save_idx = -kPsiNumNames;
+
+    // loop thru all pressure stall files: cpu, io, memory
+    for (int type_idx = 0; type_idx < kPsiNumFiles;
+         ++type_idx, file_save_idx += kPsiMetricsPerFile) {
+        std::string file_contents;
+        std::string path = std::string("") + basePath + '/' + kPsiTypes[type_idx];
+
+        if (!ReadFileToString(path, &file_contents)) {
+            // Don't print this log if the file doesn't exist, since logs will be printed
+            // repeatedly.
+            if (errno != ENOENT)
+                ALOGI("Unable to read %s - %s", path.c_str(), strerror(errno));
+            goto err_out;
+        }
+        if (!MmMetricsReporter::parsePressureStallFileContent(type_idx == kTypeIdxCpu,
+                                                              file_contents, store, file_save_idx))
+            goto err_out;
+    }
+    return;
+
+err_out:
+    std::fill(store->begin(), store->end(), -1);
+}
+
+/*
+ * This function parses a pressure stall file, which contains two
+ * lines, i.e. the "full", and "some" lines, except that the 'cpu' file
+ * contains only one line ("some"). Refer to the function comments of
+ * readPressureStall() for pressure stall file format.
+ *
+ * For old kernel, 'cpu' file might contain an extra line for "full", which
+ * will be ignored.
+ *
+ * is_cpu: Is the data from the file 'cpu'
+ * lines: the file content
+ * store: the output vector to hold the parsed data.
+ * file_save_idx: base index to start saving 'store' vector for this file.
+ *
+ * Return value: true on success, false otherwise.
+ */
+bool MmMetricsReporter::parsePressureStallFileContent(bool is_cpu, std::string lines,
+                                                      std::vector<long> *store, int file_save_idx) {
+    constexpr int kNumOfWords = 5;  // expected number of words separated by spaces.
+    constexpr int kCategoryFull = 0;
+
+    std::istringstream data(lines);
+    std::string line;
+
+    while (std::getline(data, line)) {
+        int category_idx = 0;
+
+        line = android::base::Trim(line);
+        std::vector<std::string> words = android::base::Tokenize(line, " ");
+        if (words.size() != kNumOfWords) {
+            ALOGE("PSI parse fail: num of words = %d != expected %d",
+                  static_cast<int>(words.size()), kNumOfWords);
+            return false;
+        }
+
+        // words[0] should be either "full" or "some", the category name.
+        for (auto &cat : kPsiCategories) {
+            if (words[0].compare(cat) == 0)
+                break;
+            ++category_idx;
+        }
+        if (category_idx == kPsiNumCategories) {
+            ALOGE("PSI parse fail: unknown category %s", words[0].c_str());
+            return false;
+        }
+
+        // skip (cpu, full) combination.
+        if (is_cpu && category_idx == kCategoryFull) {
+            ALOGI("kernel: old PSI sysfs node.");
+            continue;
+        }
+
+        // Now we have separated words in a vector, e.g.
+        // ["some", "avg10=2.93", "avg60=3.17", "avg300=3.15",  total=94628150260"]
+        // call parsePressureStallWords to parse them.
+        int line_save_idx = file_save_idx + category_idx * kPsiNumNames;
+        if (!parsePressureStallWords(words, store, line_save_idx))
+            return false;
+    }
+    return true;
+}
+
+// This function parses the already split words, e.g.
+// ["some", "avg10=0.00", "avg60=0.00", "avg300=0.00", "total=29705314"],
+// from a line (category) in a pressure stall file.
+//
+// words: the split words in the form of "name=value"
+// store: the output vector
+// line_save_idx: the base start index to save in vector for this line (category)
+//
+// Return value: true on success, false otherwise.
+bool MmMetricsReporter::parsePressureStallWords(std::vector<std::string> words,
+                                                std::vector<long> *store, int line_save_idx) {
+    // Skip the first word, which is already parsed by the caller.
+    // All others are value pairs in "name=value" form.
+    // e.g. ["some", "avg10=0.00", "avg60=0.00", "avg300=0.00", "total=29705314"]
+    // "some" is skipped.
+    for (int i = 1; i < words.size(); ++i) {
+        std::vector<std::string> metric = android::base::Tokenize(words[i], "=");
+        if (metric.size() != 2) {
+            ALOGE("%s: parse error (name=value) @ idx %d", __FUNCTION__, i);
+            return false;
+        }
+        if (!MmMetricsReporter::savePressureMetrics(metric[0], metric[1], store, line_save_idx))
+            return false;
+    }
+    return true;
+}
+
+// This function parses one value pair in "name=value" format, and depending on
+// the name, save to its proper location in the store vector.
+// name = "avg10" -> save to index base_save_idx.
+// name = "avg60" -> save to index base_save_idx + 1.
+// name = "avg300" -> save to index base_save_idx + 2.
+// name = "total" -> save to index base_save_idx + 3.
+//
+// name: the metrics name
+// value: the metrics value
+// store: the output vector
+// base_save_idx: the base save index
+//
+// Return value: true on success, false otherwise.
+//
+bool MmMetricsReporter::savePressureMetrics(std::string name, std::string value,
+                                            std::vector<long> *store, int base_save_idx) {
+    int name_idx = 0;
+    constexpr int kNameIdxTotal = 3;
+
+    for (auto &mn : kPsiMetricNames) {
+        if (name.compare(mn) == 0)
+            break;
+        ++name_idx;
+    }
+    if (name_idx == kPsiNumNames) {
+        ALOGE("%s: parse error: unknown metric name.", __FUNCTION__);
+        return false;
+    }
+
+    long out;
+    if (name_idx == kNameIdxTotal) {
+        // 'total' metrics
+        unsigned long tmp;
+        if (!android::base::ParseUint(value, &tmp))
+            out = -1;
+        else
+            out = tmp;
+    } else {
+        // 'avg' metrics
+        double d = -1.0;
+        if (android::base::ParseDouble(value, &d))
+            out = static_cast<long>(d * 100 + 0.5);
+        else
+            out = -1;
+    }
+
+    if (base_save_idx + name_idx >= store->size()) {
+        // should never reach here
+        ALOGE("out of bound access to store[] (src line %d) @ index %d", __LINE__,
+              base_save_idx + name_idx);
+        return false;
+    } else {
+        (*store)[base_save_idx + name_idx] = out;
+    }
+    return true;
+}
+
+/**
+ * This function reads in the current pressure (PSI) information, and aggregates
+ * it (except for the "total" information, which will overwrite
+ * the previous value without aggregation.
+ *
+ * data are arranged in the following order, and must comply the order defined
+ * in the proto:
+ *
+ *    // note: these 5 'total' metrics are not aggregated.
+ *    cpu_some_total
+ *    io_full_total
+ *    io_some_total
+ *    mem_full_total
+ *    mem_some_total
+ *
+ *    //  9 aggregated metrics as above avg<xyz>_<aggregate>
+ *    //  where <xyz> = 10, 60, 300; <aggregate> = min, max, sum
+ *    cpu_some_avg10_min
+ *    cpu_some_avg10_max
+ *    cpu_some_avg10_sum
+ *    cpu_some_avg60_min
+ *    cpu_some_avg60_max
+ *    cpu_some_avg60_sum
+ *    cpu_some_avg300_min
+ *    cpu_some_avg300_max
+ *    cpu_some_avg300_sum
+ *
+ *    // similar 9 metrics as above avg<xyz>_<aggregate>
+ *    io_full_avg<xyz>_<aggregate>
+ *
+ *    // similar 9 metrics as above avg<xyz>_<aggregate>
+ *    io_some_avg<xyz>_<aggregate>
+ *
+ *    // similar 9 metrics as above avg<xyz>_<aggregate>
+ *    mem_full_avg<xyz>_<aggregate>
+ *
+ *    // similar 9 metrics as above avg<xyz>_<aggregate>
+ *    mem_some_avg<xyz>_<aggregate>
+ *
+ * In addition, it increases psi_data_set_count_ by 1 (in order to calculate
+ * the average from the "_sum" aggregate.)
+ */
+void MmMetricsReporter::aggregatePressureStall() {
+    constexpr int kFirstTotalOffset = kPsiNumAvgs;
+
+    if (!MmMetricsSupported())
+        return;
+
+    std::vector<long> psi(kPsiNumAllMetrics, -1);
+    readPressureStall(kPsiBasePath, &psi);
+
+    // Pre-check for possible later out of bound error, if readPressureStall()
+    // decreases the vector size.
+    // It's for safety only.  The condition should never be true.
+    if (psi.size() != kPsiNumAllMetrics) {
+        ALOGE("Wrong psi[] size %d != expected %d after read.", static_cast<int>(psi.size()),
+              kPsiNumAllMetrics);
+        return;
+    }
+
+    // check raw metrics and preventively handle errors: Although we don't expect read sysfs
+    // node could fail.  Discard all current readings on any error.
+    for (int i = 0; i < kPsiNumAllMetrics; ++i) {
+        if (psi[i] == -1) {
+            ALOGE("Bad data @ psi[%ld] = -1", psi[i]);
+            goto err_out;
+        }
+    }
+
+    // "total" metrics are accumulative: just replace the previous accumulation.
+    for (int i = 0; i < kPsiNumAllTotals; ++i) {
+        int psi_idx;
+
+        psi_idx = i * kPsiNumNames + kFirstTotalOffset;
+        if (psi_idx >= psi.size()) {
+            // should never reach here
+            ALOGE("out of bound access to psi[] (src line %d) @ index %d", __LINE__, psi_idx);
+            goto err_out;
+        } else {
+            psi_total_[i] = psi[psi_idx];
+        }
+    }
+
+    // "avg" metrics will be aggregated to min, max and sum
+    // later on, the sum will be divided by psi_data_set_count_ to get the average.
+    int aggr_idx;
+    aggr_idx = 0;
+    for (int psi_idx = 0; psi_idx < kPsiNumAllMetrics; ++psi_idx) {
+        if (psi_idx % kPsiNumNames == kFirstTotalOffset)
+            continue;  // skip 'total' metrics, already processed.
+
+        if (aggr_idx + 3 > kPsiNumAllUploadAvgMetrics) {
+            // should never reach here
+            ALOGE("out of bound access to psi_aggregated_[] (src line %d) @ index %d ~ %d",
+                  __LINE__, aggr_idx, aggr_idx + 2);
+            return;  // give up avgs, but keep totals (so don't go err_out
+        }
+
+        long value = psi[psi_idx];
+        if (psi_data_set_count_ == 0) {
+            psi_aggregated_[aggr_idx++] = value;
+            psi_aggregated_[aggr_idx++] = value;
+            psi_aggregated_[aggr_idx++] = value;
+        } else {
+            psi_aggregated_[aggr_idx++] = std::min(value, psi_aggregated_[aggr_idx]);
+            psi_aggregated_[aggr_idx++] = std::max(value, psi_aggregated_[aggr_idx]);
+            psi_aggregated_[aggr_idx++] += value;
+        }
+    }
+    ++psi_data_set_count_;
+    return;
+
+err_out:
+    for (int i = 0; i < kPsiNumAllTotals; ++i) psi_total_[i] = -1;
+}
+
+/**
+ * This function fills atom values (values) from psi_aggregated_[]
+ *
+ * values: the atom value vector to be filled.
+ */
+void MmMetricsReporter::fillPressureStallAtom(std::vector<VendorAtomValue> *values) {
+    constexpr int avg_of_avg_offset = 2;
+    constexpr int total_start_idx =
+            PixelMmMetricsPerHour::kPsiCpuSomeTotalFieldNumber - kVendorAtomOffset;
+    constexpr int avg_start_idx = total_start_idx + kPsiNumAllTotals;
+
+    if (!MmMetricsSupported())
+        return;
+
+    VendorAtomValue tmp;
+
+    // The caller should have setup the correct total size,
+    // but we check and extend the size when it's too small for safety.
+    unsigned int min_value_size = total_start_idx + kPsiNumAllUploadMetrics;
+    if (values->size() < min_value_size)
+        values->resize(min_value_size);
+
+    // "total" metric
+    int metric_idx = total_start_idx;
+    for (int save = 0; save < kPsiNumAllTotals; ++save, ++metric_idx) {
+        if (psi_data_set_count_ == 0)
+            psi_total_[save] = -1;  // no data: invalidate the current total
+
+        // A good difference needs a good previous value and a good current value.
+        if (psi_total_[save] != -1 && prev_psi_total_[save] != -1)
+            tmp.set<VendorAtomValue::longValue>(psi_total_[save] - prev_psi_total_[save]);
+        else
+            tmp.set<VendorAtomValue::longValue>(-1);
+
+        prev_psi_total_[save] = psi_total_[save];
+        if (metric_idx >= values->size()) {
+            // should never reach here
+            ALOGE("out of bound access to value[] for psi-total @ index %d", metric_idx);
+            goto cleanup;
+        } else {
+            (*values)[metric_idx] = tmp;
+        }
+    }
+
+    // "avg" metrics -> aggregate to min,  max, and avg of the original avg
+    metric_idx = avg_start_idx;
+    for (int save = 0; save < kPsiNumAllUploadAvgMetrics; ++save, ++metric_idx) {
+        if (psi_data_set_count_) {
+            if (save % kPsiNumOfAggregatedType == avg_of_avg_offset) {
+                // avg of avg
+                tmp.set<VendorAtomValue::intValue>(psi_aggregated_[save] / psi_data_set_count_);
+            } else {
+                // min or max of avg
+                tmp.set<VendorAtomValue::intValue>(psi_aggregated_[save]);
+            }
+        } else {
+            tmp.set<VendorAtomValue::intValue>(-1);
+        }
+        if (metric_idx >= values->size()) {
+            // should never reach here
+            ALOGE("out of bound access to value[] for psi-avg @ index %d", metric_idx);
+            goto cleanup;
+        } else {
+            (*values)[metric_idx] = tmp;
+        }
+    }
+
+cleanup:
+    psi_data_set_count_ = 0;
+}
+
+/**
  * This function is to collect CMA metrics and upload them.
  * The CMA metrics are collected by readCmaStat(), copied into atom values
  * by fillAtomValues(), and then uploaded by reportVendorAtom(). The collected
diff --git a/pixelstats/OWNERS b/pixelstats/OWNERS
index 3c862ab..124a755 100644
--- a/pixelstats/OWNERS
+++ b/pixelstats/OWNERS
@@ -1,3 +1,5 @@
-stayfan@google.com
-joaodias@google.com
 achant@google.com
+apelosi@google.com
+joaodias@google.com
+stayfan@google.com
+vincentwang@google.com
diff --git a/pixelstats/OrientationCollector.cpp b/pixelstats/OrientationCollector.cpp
deleted file mode 100644
index b33d3c6..0000000
--- a/pixelstats/OrientationCollector.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#define LOG_TAG "pixelstats-orientation"
-
-#include <android/sensor.h>
-#include <log/log.h>
-#include <pixelstats/OrientationCollector.h>
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-#define GET_EVENT_TIMEOUT_MILLIS 200
-#define SENSOR_TYPE_DEVICE_ORIENTATION 27
-#define ORIENTATION_UNKNOWN -1
-
-sp<OrientationCollector> OrientationCollector::createOrientationCollector() {
-    sp<OrientationCollector> orientationCollector;
-    orientationCollector = new OrientationCollector();
-    if (orientationCollector != nullptr && orientationCollector->init() < 0) {
-        orientationCollector->disableOrientationSensor();
-        return nullptr;
-    }
-    return orientationCollector;
-}
-
-/* pollOrientation
- * orientation: [out] orientation value from sensor Hal.
- * Return: OK, or error code from sensor Hal
- */
-int32_t OrientationCollector::pollOrientation(int *orientation) {
-    int err = OK;
-    int eventCount = 0;
-    ASensorEvent sensorEvent;
-
-    *orientation = ORIENTATION_UNKNOWN;
-    /* Get sensor events. */
-    /* Get sensor sample events. */
-    err = getEvents(&sensorEvent, 1, &eventCount);
-    if (eventCount > 0) {
-        ALOGV("%s: ##event data: %f,%f,%f", __func__, sensorEvent.data[0], sensorEvent.data[1],
-              sensorEvent.data[2]);
-        *orientation = sensorEvent.data[0];
-    }
-    return err;
-}
-
-/*
- * Collects sensor samples and returns them in the sensor sample event list
- * specified by event_list. Limits the number of returned sample events to the
- * value specified by event_list_size. Returns the number of returned sample
- * events in p_event_count.
- *
- * event_list               List of collected sample events.
- * event_list_size          Size of sample event list.
- * event_count              Returned count of the number of collected sample
- *                          events.
- */
-int OrientationCollector::getEvents(ASensorEvent *event_list, size_t event_list_size,
-                                    int *event_count) {
-    int rv;
-    int err = OK;
-
-    /* Wait for a sensor event to be available */
-    /* The timeout is used to prevent blocking for long time, when sensor pool is
-     * empty, for example the device is put on a horizontal wireless charger.
-     */
-    rv = ALooper_pollOnce(GET_EVENT_TIMEOUT_MILLIS, nullptr, nullptr, nullptr);
-    if (rv == ALOOPER_POLL_ERROR) {
-        ALOGI("Sensor event looper returned a poll error.\n");
-        err = UNKNOWN_ERROR;
-        goto out;
-    }
-
-    /* Get sensor events. */
-    *event_count = ASensorEventQueue_getEvents(mQueue, event_list, event_list_size);
-
-out:
-    return err;
-}
-
-int32_t OrientationCollector::init() {
-    int err = OK;
-    ALooper *looper = nullptr;
-
-    // Get orientation sensor events from the NDK
-    mSensorManager = ASensorManager_getInstanceForPackage(nullptr);
-    if (mSensorManager == nullptr) {
-        ALOGE("%s: Unable to get sensorManager.\n", __func__);
-        return UNKNOWN_ERROR;
-    }
-    looper = ALooper_forThread();
-    if (looper == nullptr) {
-        looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
-    }
-    if (looper == nullptr) {
-        ALOGE("%s: Failed to prepare an event looper.\n", __FUNCTION__);
-        return UNKNOWN_ERROR;
-    }
-    mQueue = ASensorManager_createEventQueue(mSensorManager, looper, 0, nullptr, nullptr);
-    mOrientationSensor =
-            ASensorManager_getDefaultSensor(mSensorManager, SENSOR_TYPE_DEVICE_ORIENTATION);
-    if (mOrientationSensor == nullptr) {
-        ALOGE("%s: Unable to get orientation sensor.\n", __func__);
-        return UNKNOWN_ERROR;
-    }
-    err = ASensorEventQueue_registerSensor(mQueue, mOrientationSensor,
-                                           ASensor_getMinDelay(mOrientationSensor), 0);
-    if (err < 0) {
-        ALOGE("%s: Unable to register for orientation sensor events\n", __func__);
-    }
-    return err;
-}
-
-void OrientationCollector::disableOrientationSensor() {
-    if (mSensorManager != nullptr && mQueue != nullptr) {
-        if (mOrientationSensor != nullptr) {
-            ASensorEventQueue_disableSensor(mQueue, mOrientationSensor);
-        }
-        ASensorManager_destroyEventQueue(mSensorManager, mQueue);
-    }
-}
-
-}  // namespace pixel
-}  // namespace google
-}  // namespace hardware
-}  // namespace android
diff --git a/pixelstats/PcaChargeStats.cpp b/pixelstats/PcaChargeStats.cpp
index b81e423..125e6c6 100644
--- a/pixelstats/PcaChargeStats.cpp
+++ b/pixelstats/PcaChargeStats.cpp
@@ -33,28 +33,34 @@
 using android::base::WriteStringToFile;
 
 bool PcaChargeStats::CheckPcaContentsAndAck(std::string *file_contents) {
+    std::string path = kPcaChargeMetricsPath;
     std::string line;
     std::istringstream ss;
 
-    if (!ReadFileToString(kPcaChargeMetricsPath.c_str(), file_contents)) {
-        return false;
+    if (!ReadFileToString(path.c_str(), file_contents)) {
+        path = kPca94xxChargeMetricsPath;
+        if (!ReadFileToString(path.c_str(), file_contents)) {
+            return false;
+        }
     }
 
     ss.str(*file_contents);
 
     if (!std::getline(ss, line)) {
-        ALOGE("Unable to read first line %s - %s", kPcaChargeMetricsPath.c_str(), strerror(errno));
+        ALOGE("Unable to read first line %s - %s", path.c_str(), strerror(errno));
         return false;
     }
-    if (!WriteStringToFile(std::to_string(0), kPcaChargeMetricsPath.c_str())) {
-        ALOGE("Couldn't clear %s - %s", kPcaChargeMetricsPath.c_str(), strerror(errno));
+    if (!WriteStringToFile(std::to_string(0), path.c_str())) {
+        ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
         return false;
     }
     return true;
 }
 
-PcaChargeStats::PcaChargeStats(const std::string pca_charge_metrics_path)
-    : kPcaChargeMetricsPath(pca_charge_metrics_path) {}
+PcaChargeStats::PcaChargeStats(const std::string pca_charge_metrics_path,
+                               const std::string pca94xx_charge_metrics_path)
+    : kPcaChargeMetricsPath(pca_charge_metrics_path),
+      kPca94xxChargeMetricsPath(pca94xx_charge_metrics_path) {}
 
 }  // namespace pixel
 }  // namespace google
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index 14cba98..391c8b1 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -43,12 +43,16 @@
 using android::base::StartsWith;
 using android::base::WriteStringToFile;
 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
+using android::hardware::google::pixel::PixelAtoms::BlockStatsReported;
 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
 using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
 using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
+using android::hardware::google::pixel::PixelAtoms::F2fsSmartIdleMaintEnabledStateChanged;
 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
 using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount;
+using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats;
+using android::hardware::google::pixel::PixelAtoms::VendorAudioHardwareStatsReported;
 using android::hardware::google::pixel::PixelAtoms::VendorChargeCycles;
 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
 using android::hardware::google::pixel::PixelAtoms::VendorSlowIo;
@@ -81,7 +85,10 @@
       kSpeakerTemperaturePath(sysfs_paths.SpeakerTemperaturePath),
       kSpeakerExcursionPath(sysfs_paths.SpeakerExcursionPath),
       kSpeakerHeartbeatPath(sysfs_paths.SpeakerHeartBeatPath),
-      kUFSErrStatsPath(sysfs_paths.UFSErrStatsPath) {}
+      kUFSErrStatsPath(sysfs_paths.UFSErrStatsPath),
+      kBlockStatsLength(sysfs_paths.BlockStatsLength),
+      kAmsRatePath(sysfs_paths.AmsRatePath),
+      kThermalStatsPaths(sysfs_paths.ThermalStatsPaths) {}
 
 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
     return ReadFileToInt(path.c_str(), val);
@@ -366,6 +373,10 @@
     reportSpeakerHealthStat(stats_client, right_obj);
 }
 
+void SysfsCollector::logThermalStats(const std::shared_ptr<IStats> &stats_client) {
+    thermal_stats_reporter_.logThermalStats(stats_client, kThermalStatsPaths);
+}
+
 /**
  * Report the Speech DSP state.
  */
@@ -684,10 +695,12 @@
 }
 
 void SysfsCollector::logF2fsGcSegmentInfo(const std::shared_ptr<IStats> &stats_client) {
-    int reclaimed_segments_normal, reclaimed_segments_urgent_high, reclaimed_segments_urgent_low;
+    int reclaimed_segments_normal, reclaimed_segments_urgent_high;
+    int reclaimed_segments_urgent_mid, reclaimed_segments_urgent_low;
     std::string gc_normal_mode = std::to_string(0);         // GC normal mode
     std::string gc_urgent_high_mode = std::to_string(4);    // GC urgent high mode
     std::string gc_urgent_low_mode = std::to_string(5);     // GC urgent low mode
+    std::string gc_urgent_mid_mode = std::to_string(6);     // GC urgent mid mode
 
     if (kF2fsStatsPath == nullptr) {
         ALOGV("F2fs stats path not specified");
@@ -700,9 +713,11 @@
     if (reclaimed_segments_urgent_high == -1) return;
     reclaimed_segments_urgent_low = getReclaimedSegments(gc_urgent_low_mode);
     if (reclaimed_segments_urgent_low == -1) return;
+    reclaimed_segments_urgent_mid = getReclaimedSegments(gc_urgent_mid_mode);
+    if (reclaimed_segments_urgent_mid == -1) return;
 
     // Load values array
-    std::vector<VendorAtomValue> values(3);
+    std::vector<VendorAtomValue> values(4);
     VendorAtomValue tmp;
     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_normal);
     values[F2fsGcSegmentInfo::kReclaimedSegmentsNormalFieldNumber - kVendorAtomOffset] = tmp;
@@ -710,6 +725,8 @@
     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentHighFieldNumber - kVendorAtomOffset] = tmp;
     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_low);
     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp;
+    tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_mid);
+    values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentMidFieldNumber - kVendorAtomOffset] = tmp;
 
     // Send vendor atom to IStats HAL
     VendorAtom event = {.reverseDomainName = "",
@@ -721,6 +738,88 @@
     }
 }
 
+void SysfsCollector::logF2fsSmartIdleMaintEnabled(const std::shared_ptr<IStats> &stats_client) {
+    bool smart_idle_enabled = android::base::GetBoolProperty(
+        "persist.device_config.storage_native_boot.smart_idle_maint_enabled", false);
+
+    // Load values array
+    VendorAtomValue tmp;
+    std::vector<VendorAtomValue> values(1);
+    tmp.set<VendorAtomValue::intValue>(smart_idle_enabled);
+    values[F2fsSmartIdleMaintEnabledStateChanged::kEnabledFieldNumber - kVendorAtomOffset] = tmp;
+
+    // Send vendor atom to IStats HAL
+    VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+                        .atomId = PixelAtoms::Atom::kF2FsSmartIdleMaintEnabledStateChanged,
+                        .values = std::move(values)};
+    const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+    if (!ret.isOk()) {
+        ALOGE("Unable to report Boot stats to Stats service");
+    }
+}
+
+void SysfsCollector::logBlockStatsReported(const std::shared_ptr<IStats> &stats_client) {
+    std::string sdaPath = "/sys/block/sda/stat";
+    std::string file_contents;
+    std::string stat;
+    std::vector<std::string> stats;
+    std::stringstream ss;
+
+    // These index comes from kernel Document
+    // Documentation/ABI/stable/sysfs-block
+    const int READ_IO_IDX = 0, READ_SEC_IDX = 2, READ_TICK_IDX = 3;
+    const int WRITE_IO_IDX = 4, WRITE_SEC_IDX = 6, WRITE_TICK_IDX = 7;
+    uint64_t read_io, read_sectors, read_ticks;
+    uint64_t write_io, write_sectors, write_ticks;
+
+    if (!ReadFileToString(sdaPath.c_str(), &file_contents)) {
+        ALOGE("Failed to read block layer stat %s", sdaPath.c_str());
+        return;
+    }
+
+    ss.str(file_contents);
+    while (ss >> stat) {
+        stats.push_back(stat);
+    }
+
+    if (stats.size() < kBlockStatsLength) {
+        ALOGE("block layer stat format is incorrect %s, length %lu/%d",
+            file_contents.c_str(), stats.size(), kBlockStatsLength);
+        return;
+    }
+
+    read_io = std::stoul(stats[READ_IO_IDX]);
+    read_sectors = std::stoul(stats[READ_SEC_IDX]);
+    read_ticks = std::stoul(stats[READ_TICK_IDX]);
+    write_io = std::stoul(stats[WRITE_IO_IDX]);
+    write_sectors = std::stoul(stats[WRITE_SEC_IDX]);
+    write_ticks = std::stoul(stats[WRITE_TICK_IDX]);
+
+    // Load values array
+    std::vector<VendorAtomValue> values(6);
+    values[BlockStatsReported::kReadIoFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(read_io);
+    values[BlockStatsReported::kReadSectorsFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(read_sectors);
+    values[BlockStatsReported::kReadTicksFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(read_ticks);
+    values[BlockStatsReported::kWriteIoFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(write_io);
+    values[BlockStatsReported::kWriteSectorsFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(write_sectors);
+    values[BlockStatsReported::kWriteTicksFieldNumber - kVendorAtomOffset] =
+                        VendorAtomValue::make<VendorAtomValue::longValue>(write_ticks);
+
+    // Send vendor atom to IStats HAL
+    VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+                        .atomId = PixelAtoms::Atom::kBlockStatsReported,
+                        .values = std::move(values)};
+    const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+    if (!ret.isOk()) {
+        ALOGE("Unable to report block layer stats to Stats service");
+    }
+}
+
 void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_client) {
     std::string file_contents;
     if (!kZramMmStatPath) {
@@ -878,6 +977,44 @@
     }
 }
 
+/**
+ * Report the AMS rate.
+ */
+void SysfsCollector::logVendorAudioHardwareStats(const std::shared_ptr<IStats> &stats_client) {
+    std::string file_contents;
+    if (kAmsRatePath == nullptr) {
+        ALOGD("Audio AMS_Rate path not specified");
+        return;
+    }
+
+    uint32_t milli_ams_rate;
+    if (!ReadFileToString(kAmsRatePath, &file_contents)) {
+        ALOGE("Unable to read ams_rate path %s", kAmsRatePath);
+        return;
+    }
+
+    if (sscanf(file_contents.c_str(), "%u", &milli_ams_rate) != 1) {
+        ALOGE("Unable to parse ams_rate %s", file_contents.c_str());
+        return;
+    }
+
+    ALOGD("milli_ams_rate = %s", file_contents.c_str());
+
+    std::vector<VendorAtomValue> values(1);
+    VendorAtomValue tmp;
+    tmp.set<VendorAtomValue::intValue>(milli_ams_rate);
+    values[0] = tmp;
+
+    // Send vendor atom to IStats HAL
+    VendorAtom event = {.reverseDomainName = "",
+                        .atomId = PixelAtoms::Atom::kVendorAudioHardwareStatsReported,
+                        .values = std::move(values)};
+
+    const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+    if (!ret.isOk())
+        ALOGE("Unable to report VendorAudioHardwareStatsReported to Stats service");
+}
+
 void SysfsCollector::logPerDay() {
     const std::shared_ptr<IStats> stats_client = getStatsService();
     if (!stats_client) {
@@ -892,11 +1029,13 @@
     logBatteryChargeCycles(stats_client);
     logBatteryEEPROM(stats_client);
     logBatteryHealth(stats_client);
+    logBlockStatsReported(stats_client);
     logCodec1Failed(stats_client);
     logCodecFailed(stats_client);
     logF2fsStats(stats_client);
     logF2fsCompressionInfo(stats_client);
     logF2fsGcSegmentInfo(stats_client);
+    logF2fsSmartIdleMaintEnabled(stats_client);
     logSlowIO(stats_client);
     logSpeakerImpedance(stats_client);
     logSpeechDspStat(stats_client);
@@ -905,6 +1044,12 @@
     logSpeakerHealthStats(stats_client);
     mm_metrics_reporter_.logCmaStatus(stats_client);
     mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
+    logVendorAudioHardwareStats(stats_client);
+    logThermalStats(stats_client);
+}
+
+void SysfsCollector::aggregatePer5Min() {
+    mm_metrics_reporter_.aggregatePixelMmMetricsPer5Min();
 }
 
 void SysfsCollector::logPerHour() {
@@ -934,17 +1079,29 @@
     // Sleep for 30 seconds on launch to allow codec driver to load.
     sleep(30);
 
+    // sample & aggregate for the first time.
+    aggregatePer5Min();
+
     // Collect first set of stats on boot.
     logPerHour();
     logPerDay();
 
-    // Set an one-hour timer.
     struct itimerspec period;
-    const int kSecondsPerHour = 60 * 60;
-    int hours = 0;
-    period.it_interval.tv_sec = kSecondsPerHour;
+
+    // gcd (greatest common divisor) of all the following timings
+    constexpr int kSecondsPerWake = 5 * 60;
+
+    constexpr int kWakesPer5Min = 5 * 60 / kSecondsPerWake;
+    constexpr int kWakesPerHour = 60 * 60 / kSecondsPerWake;
+    constexpr int kWakesPerDay = 24 * 60 * 60 / kSecondsPerWake;
+
+    int wake_5min = 0;
+    int wake_hours = 0;
+    int wake_days = 0;
+
+    period.it_interval.tv_sec = kSecondsPerWake;
     period.it_interval.tv_nsec = 0;
-    period.it_value.tv_sec = kSecondsPerHour;
+    period.it_value.tv_sec = kSecondsPerWake;
     period.it_value.tv_nsec = 0;
 
     if (timerfd_settime(timerfd, 0, &period, NULL)) {
@@ -954,22 +1111,41 @@
 
     while (1) {
         int readval;
-        do {
+        union {
             char buf[8];
+            uint64_t count;
+        } expire;
+
+        do {
             errno = 0;
-            readval = read(timerfd, buf, sizeof(buf));
+            readval = read(timerfd, expire.buf, sizeof(expire.buf));
         } while (readval < 0 && errno == EINTR);
         if (readval < 0) {
             ALOGE("Timerfd error - %s\n", strerror(errno));
             return;
         }
 
-        hours++;
-        logPerHour();
-        if (hours == 24) {
-            // Collect stats every 24hrs after.
+        wake_5min += expire.count;
+        wake_hours += expire.count;
+        wake_days += expire.count;
+
+        if (wake_5min >= kWakesPer5Min) {
+            wake_5min %= kWakesPer5Min;
+            aggregatePer5Min();
+        }
+
+        if (wake_hours >= kWakesPerHour) {
+            if (wake_hours >= 2 * kWakesPerHour)
+                ALOGW("Hourly wake: sleep too much: expire.count=%" PRId64, expire.count);
+            wake_hours %= kWakesPerHour;
+            logPerHour();
+        }
+
+        if (wake_days >= kWakesPerDay) {
+            if (wake_hours >= 2 * kWakesPerDay)
+                ALOGW("Daily wake: sleep too much: expire.count=%" PRId64, expire.count);
+            wake_days %= kWakesPerDay;
             logPerDay();
-            hours = 0;
         }
     }
 }
diff --git a/pixelstats/ThermalStatsReporter.cpp b/pixelstats/ThermalStatsReporter.cpp
new file mode 100644
index 0000000..30fb30d
--- /dev/null
+++ b/pixelstats/ThermalStatsReporter.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "pixelstats: ThermalStats"
+
+#include <aidl/android/frameworks/stats/IStats.h>
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+#include <pixelstats/ThermalStatsReporter.h>
+#include <utils/Log.h>
+
+#include <cinttypes>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using aidl::android::frameworks::stats::IStats;
+using aidl::android::frameworks::stats::VendorAtom;
+using aidl::android::frameworks::stats::VendorAtomValue;
+using android::base::ReadFileToString;
+using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats;
+
+ThermalStatsReporter::ThermalStatsReporter() {}
+
+bool ThermalStatsReporter::readDfsCount(const std::string &path, int64_t *val) {
+    std::string file_contents;
+
+    if (path.empty()) {
+        ALOGE("Empty path");
+        return false;
+    }
+
+    if (!ReadFileToString(path.c_str(), &file_contents)) {
+        ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
+        return false;
+    } else {
+        int64_t trips[8];
+
+        if (sscanf(file_contents.c_str(),
+                   "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+                   " %" SCNd64 " %" SCNd64,
+                   &trips[0], &trips[1], &trips[2], &trips[3], &trips[4], &trips[5], &trips[6],
+                   &trips[7]) < 8) {
+            ALOGE("Unable to parse trip_counters %s from file %s", file_contents.c_str(),
+                  path.c_str());
+            return false;
+        }
+
+        /* Trip#6 corresponds to DFS count */
+        *val = trips[6];
+    }
+
+    return true;
+}
+
+bool ThermalStatsReporter::captureThermalDfsStats(
+        const std::vector<std::string> &thermal_stats_paths, struct ThermalDfsCounts *pcur_data) {
+    bool report_stats = false;
+    std::string path;
+
+    if (thermal_stats_paths.size() < kNumOfThermalDfsStats) {
+        ALOGE("Number of thermal stats paths (%lu) is less than expected (%d)",
+              thermal_stats_paths.size(), kNumOfThermalDfsStats);
+        return false;
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->big_count))) {
+        pcur_data->big_count = prev_data.big_count;
+    } else {
+        report_stats |= (pcur_data->big_count > prev_data.big_count);
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->mid_count))) {
+        pcur_data->mid_count = prev_data.mid_count;
+    } else {
+        report_stats |= (pcur_data->mid_count > prev_data.mid_count);
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->little_count))) {
+        pcur_data->little_count = prev_data.little_count;
+    } else {
+        report_stats |= (pcur_data->little_count > prev_data.little_count);
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->gpu_count))) {
+        pcur_data->gpu_count = prev_data.gpu_count;
+    } else {
+        report_stats |= (pcur_data->gpu_count > prev_data.gpu_count);
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->tpu_count))) {
+        pcur_data->tpu_count = prev_data.tpu_count;
+    } else {
+        report_stats |= (pcur_data->tpu_count > prev_data.tpu_count);
+    }
+
+    path = thermal_stats_paths[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset];
+    if (!readDfsCount(path, &(pcur_data->aur_count))) {
+        pcur_data->aur_count = prev_data.aur_count;
+    } else {
+        report_stats |= (pcur_data->aur_count > prev_data.aur_count);
+    }
+
+    return report_stats;
+}
+
+void ThermalStatsReporter::logThermalDfsStats(const std::shared_ptr<IStats> &stats_client,
+                                              const std::vector<std::string> &thermal_stats_paths) {
+    struct ThermalDfsCounts cur_data = prev_data;
+
+    if (!captureThermalDfsStats(thermal_stats_paths, &cur_data)) {
+        prev_data = cur_data;
+        ALOGI("No update found for thermal stats");
+        return;
+    }
+
+    VendorAtomValue tmp;
+    int64_t max_dfs_count = static_cast<int64_t>(INT32_MAX);
+    int dfs_count;
+    std::vector<VendorAtomValue> values(kNumOfThermalDfsStats);
+
+    dfs_count = std::min<int64_t>(cur_data.big_count - prev_data.big_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    dfs_count = std::min<int64_t>(cur_data.mid_count - prev_data.mid_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    dfs_count = std::min<int64_t>(cur_data.little_count - prev_data.little_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    dfs_count = std::min<int64_t>(cur_data.gpu_count - prev_data.gpu_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    dfs_count = std::min<int64_t>(cur_data.tpu_count - prev_data.tpu_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    dfs_count = std::min<int64_t>(cur_data.aur_count - prev_data.aur_count, max_dfs_count);
+    tmp.set<VendorAtomValue::intValue>(dfs_count);
+    values[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset] = tmp;
+
+    prev_data = cur_data;
+
+    ALOGD("Report updated thermal metrics to stats service");
+    // Send vendor atom to IStats HAL
+    VendorAtom event = {.reverseDomainName = "",
+                        .atomId = PixelAtoms::Atom::kThermalDfsStats,
+                        .values = std::move(values)};
+    const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+    if (!ret.isOk())
+        ALOGE("Unable to report thermal DFS stats to Stats service");
+}
+
+void ThermalStatsReporter::logThermalStats(const std::shared_ptr<IStats> &stats_client,
+                                           const std::vector<std::string> &thermal_stats_paths) {
+    logThermalDfsStats(stats_client, thermal_stats_paths);
+}
+
+}  // namespace pixel
+}  // namespace google
+}  // namespace hardware
+}  // namespace android
diff --git a/pixelstats/UeventListener.cpp b/pixelstats/UeventListener.cpp
index b5f31cb..07c8c95 100644
--- a/pixelstats/UeventListener.cpp
+++ b/pixelstats/UeventListener.cpp
@@ -48,7 +48,6 @@
 #include <log/log.h>
 #include <pixelstats/StatsHelper.h>
 #include <pixelstats/UeventListener.h>
-#include <pixelstats/WlcReporter.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -67,7 +66,6 @@
 using android::sp;
 using android::base::ReadFileToString;
 using android::base::WriteStringToFile;
-using android::hardware::google::pixel::WlcReporter;
 using android::hardware::google::pixel::PixelAtoms::PdVidPid;
 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
 using android::hardware::google::pixel::PixelAtoms::VendorUsbPortOverheat;
@@ -185,17 +183,6 @@
     charge_stats_reporter_.checkAndReport(stats_client, kChargeMetricsPath);
 }
 
-/* ReportWlc
- * Report wireless relate  metrics when wireless charging start
- */
-void UeventListener::ReportWlc(const std::shared_ptr<IStats> &stats_client, const bool pow_wireless,
-                               const bool online, const char *ptmc) {
-    if (!pow_wireless) {
-        return;
-    }
-
-    wlc_reporter_.checkAndReport(stats_client, online, ptmc);
-}
 /**
  * Report raw battery capacity, system battery capacity and associated
  * battery capacity curves. This data is collected to verify the filter
@@ -291,9 +278,6 @@
     const char *mic_break_status, *mic_degrade_status;
     const char *devpath;
     bool collect_partner_id = false;
-    bool pow_online;
-    bool pow_wireless;
-    const char *pow_ptmc;
     int n;
 
     if (uevent_fd_ < 0) {
@@ -322,8 +306,7 @@
     msg[n + 1] = '\0';
 
     driver = product = subsystem = NULL;
-    mic_break_status = mic_degrade_status = devpath = pow_ptmc = NULL;
-    pow_online = pow_wireless = false;
+    mic_break_status = mic_degrade_status = devpath = NULL;
 
     /**
      * msg is a sequence of null-terminated strings.
@@ -350,15 +333,6 @@
             subsystem = cp;
         } else if (!strncmp(cp, kTypeCPartnerUevent.c_str(), kTypeCPartnerUevent.size())) {
             collect_partner_id = true;
-        } else if (!strncmp(cp, "POWER_SUPPLY_NAME=wireless",
-                            strlen("POWER_SUPPLY_NAME=wireless"))) {
-            pow_wireless = true;
-        } else if (!strncmp(cp, "POWER_SUPPLY_ONLINE=1", strlen("POWER_SUPPLY_ONLINE=1"))) {
-            pow_online = true;
-        } else if (!kWirelessChargerPtmcUevent.empty() &&
-                   !strncmp(cp, kWirelessChargerPtmcUevent.c_str(),
-                            strlen(kWirelessChargerPtmcUevent.c_str()))) {
-            pow_ptmc = cp + strlen(kWirelessChargerPtmcUevent.c_str());
         }
         /* advance to after the next \0 */
         while (*cp++) {
@@ -374,7 +348,6 @@
         ReportMicStatusUevents(stats_client, devpath, mic_degrade_status);
         ReportUsbPortOverheatEvent(stats_client, driver);
         ReportChargeMetricsEvent(stats_client, driver);
-        ReportWlc(stats_client, pow_wireless, pow_online, pow_ptmc);
         ReportBatteryCapacityFGEvent(stats_client, subsystem);
         if (collect_partner_id) {
             ReportTypeCPartnerId(stats_client);
@@ -399,8 +372,6 @@
       kTypeCPartnerUevent(typec_partner_uevent_default),
       kTypeCPartnerVidPath(typec_partner_vid_path),
       kTypeCPartnerPidPath(typec_partner_pid_path),
-      kWirelessChargerPtmcUevent(""),
-      kWirelessChargerPtmcPath(""),
       uevent_fd_(-1),
       log_fd_(-1) {}
 
@@ -422,12 +393,6 @@
       kTypeCPartnerPidPath((uevents_paths.TypeCPartnerPidPath == nullptr)
                                    ? typec_partner_pid_path_default
                                    : uevents_paths.TypeCPartnerPidPath),
-      kWirelessChargerPtmcUevent((uevents_paths.WirelessChargerPtmcUevent == nullptr)
-                                         ? ""
-                                         : uevents_paths.WirelessChargerPtmcUevent),
-      kWirelessChargerPtmcPath((uevents_paths.WirelessChargerPtmcPath == nullptr)
-                                       ? ""
-                                       : uevents_paths.WirelessChargerPtmcPath),
       uevent_fd_(-1),
       log_fd_(-1) {}
 
diff --git a/pixelstats/WlcReporter.cpp b/pixelstats/WlcReporter.cpp
deleted file mode 100644
index 5a404d3..0000000
--- a/pixelstats/WlcReporter.cpp
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#define LOG_TAG "pixelstats-wlc"
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
-#include <log/log.h>
-#include <pixelstats/OrientationCollector.h>
-#include <pixelstats/WlcReporter.h>
-#include <sys/timerfd.h>
-#include <time.h>
-#include <utils/Timers.h>
-
-#include <thread>
-
-/* I set a higher rare limit ti orientation, beacuae user might try to adjust
- * orientation when start charge
- **/
-#define GOOGLE_PTMC_ID (0x72)
-#define ID_UNKNOWN (0)
-#define WLC_VENDOR_REPORT_RATE_LIMIT_MIN_SEC (60 * 60)
-#define WLC_VENDOR_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY (10)
-#define ORIENTATION_REPORT_RATE_LIMIT_MIN_SEC (0)
-#define ORIENTATION_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY (10)
-#define DAY_SECOND (86400)
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-using aidl::android::frameworks::stats::IStats;
-using aidl::android::frameworks::stats::VendorAtom;
-using aidl::android::frameworks::stats::VendorAtomValue;
-using android::base::ReadFileToString;
-
-WlcReporter::WlcStatus::WlcStatus()
-    : is_charging(false),
-      check_charger_vendor_id(false),
-      check_charger_vendor_id_scheduled(false),
-      check_vendor_id_attempts(0) {}
-
-WlcReporter::ReportRecord::ReportRecord(char const *name_)
-    : name(name_),
-      last_reported_time_in_sec_today(0),
-      last_reported_time_in_sec(0),
-      count_today(0) {}
-
-WlcReporter::WlcReporter(const char *ptmc_path) : kWirelessChargerPtmcPath(ptmc_path) {}
-void WlcReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client, bool online,
-                                 const char *ptmc_uevent) {
-    bool wireless_charging = online;
-    bool started_wireless_charging = wireless_charging && !wlc_status_.is_charging;
-    wlc_status_.is_charging = wireless_charging;
-    ALOGV("reportVendorId is_charging: %s, started_wireless_charging: %s",
-          (wireless_charging) ? "true" : "false", (started_wireless_charging) ? "true" : "false");
-
-    if (started_wireless_charging) {
-        reportOrientation(stats_client);
-        wlc_status_.check_vendor_id_attempts = 0;
-        if (checkRateLimit(WLC_VENDOR_REPORT_RATE_LIMIT_MIN_SEC,
-                           WLC_VENDOR_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY, &rec_wlc_vendor_)) {
-            wlc_status_.check_charger_vendor_id = true;
-            if (kWirelessChargerPtmcPath != nullptr && strlen(kWirelessChargerPtmcPath) != 0) {
-                scheduleReportVendorId(stats_client);
-            } else {
-                ALOGV("ptmc_path not set");
-            }
-        }
-    }
-    if (!wireless_charging) {
-        wlc_status_.check_charger_vendor_id = false;
-    }
-    if (wireless_charging) {
-        checkVendorId(stats_client, ptmc_uevent);
-    }
-}
-
-void WlcReporter::checkVendorId(const std::shared_ptr<IStats> &stats_client,
-                                const char *ptmc_uevent) {
-    if (!ptmc_uevent || !wlc_status_.check_charger_vendor_id) {
-        return;
-    }
-    if (reportVendorMayRetry(stats_client, ptmc_uevent)) {
-        wlc_status_.check_charger_vendor_id = false;
-    }
-}
-
-bool WlcReporter::reportVendorMayRetry(const std::shared_ptr<IStats> &stats_client,
-                                       const char *ptmc_uevent) {
-    int ptmcId = readPtmcId(ptmc_uevent);
-    if (ptmcId == ID_UNKNOWN) {
-        if (++(wlc_status_.check_vendor_id_attempts) < kMaxVendorIdAttempts) {
-            return false;
-        } /* else if ptmc not ready after retry assume ptmc not supported by charger */
-    }
-    ALOGV("reportVendorId from Uevent");
-    reportVendor(stats_client, ptmcId);
-    return true;
-}
-void WlcReporter::reportVendor(const std::shared_ptr<IStats> &stats_client, const int ptmcId) {
-    int vendorCharger = (ptmcId == GOOGLE_PTMC_ID)
-                                ? PixelAtoms::WirelessChargingStats::VENDOR_GOOGLE
-                                : PixelAtoms::WirelessChargingStats::VENDOR_UNKNOWN;
-    ALOGV("ptmcId: 0x%x; vendorCharger: %d", ptmcId, vendorCharger);
-    VendorAtomValue tmp;
-    tmp.set<VendorAtomValue::intValue>(vendorCharger);
-
-    std::vector<VendorAtomValue> values(1);
-    values[PixelAtoms::WirelessChargingStats::kChargerVendorFieldNumber - kVendorAtomOffset] = tmp;
-
-    // Send vendor atom to IStats HAL
-    VendorAtom event = {.reverseDomainName = "",
-                        .atomId = PixelAtoms::Atom::kWirelessChargingStats,
-                        .values = std::move(values)};
-    const ndk::ScopedAStatus retStat = stats_client->reportVendorAtom(event);
-    if (!retStat.isOk()) {
-        ALOGE("Unable to report WLC_STATS to Stats service");
-    }
-    return;
-}
-
-int WlcReporter::readPtmcId(const char *ptmc_str) {
-    int id;
-    if (sscanf(ptmc_str, "%x", &id) != 1) {
-        return ID_UNKNOWN;
-    }
-    return id;
-}
-
-void WlcReporter::scheduleReportVendorId(const std::shared_ptr<IStats> &stats_client) {
-    if (wlc_status_.check_charger_vendor_id_scheduled) {
-        return;
-    }
-
-    wlc_status_.check_charger_vendor_id_scheduled = true;
-    std::thread taskThread(&WlcReporter::reportInBackground, this, stats_client,
-                           kWirelessChargerPtmcPath);
-    taskThread.detach();
-}
-
-bool WlcReporter::ptmcWaitTimer(int timerfd) {
-    const int kDelaytimeBeforeReadPtmcId = 60;
-    struct itimerspec period;
-    period.it_interval.tv_sec = 0;
-    period.it_interval.tv_nsec = 0;
-    period.it_value.tv_sec = kDelaytimeBeforeReadPtmcId;
-    period.it_value.tv_nsec = 0;
-
-    if (timerfd_settime(timerfd, 0, &period, nullptr)) {
-        ALOGE("Unable to set timer - %s", strerror(errno));
-        return false;
-    }
-    int readval;
-    do {
-        char buf[8];
-        errno = 0;
-        readval = read(timerfd, buf, sizeof(buf));
-    } while (readval < 0 && errno == EINTR);
-    if (readval < 0) {
-        ALOGE("Timerfd error - %s", strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-/*
- * PTMC path use to sore wireless charger vendor id,
- * and it take some time to get ready after wireless chagre start,
- * to prevnt busy wait, I use timer and background thread to check PTMC ID
- **/
-void WlcReporter::reportInBackground(const std::shared_ptr<IStats> &stats_client,
-                                     const char *ptmc_path) {
-    int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
-    if (timerfd < 0) {
-        ALOGE("Unable to create timerfd - %s", strerror(errno));
-        return;
-    }
-    if (ptmcWaitTimer(timerfd)) {
-        if (!wlc_status_.is_charging) {
-            ALOGV("Not charging, skip report vender id");
-        } else if (!wlc_status_.check_charger_vendor_id) {
-            ALOGV("id reported by uevnt, skip report vender id");
-        } else {
-            std::string file_contents;
-            if (!ReadFileToString(ptmc_path, &file_contents)) {
-                ALOGE("ReadFileToString %s fail", ptmc_path);
-            } else {
-                int ptmcId = readPtmcId(file_contents.c_str());
-                ALOGV("reportVendorId from file");
-                reportVendor(stats_client, ptmcId);
-            }
-        }
-    }
-    wlc_status_.check_charger_vendor_id_scheduled = false;
-    close(timerfd);
-}
-
-/*  Reference to frameworks/native/libs/ui/include/ui/DisplayInfo.h
- *  translate orientation value from sensor to enum define in
- *  pixelatoms.proto
- */
-int WlcReporter::translateDeviceOrientationToAtomValue(int orientation) {
-    switch (orientation) {
-        case 0:
-            return PixelAtoms::DeviceOrientation::ORIENTATION_0;
-        case 1:
-            return PixelAtoms::DeviceOrientation::ORIENTATION_90;
-        case 2:
-            return PixelAtoms::DeviceOrientation::ORIENTATION_180;
-        case 3:
-            return PixelAtoms::DeviceOrientation::ORIENTATION_270;
-        default:
-            return PixelAtoms::DeviceOrientation::ORIENTATION_UNKNOWN;
-    }
-}
-
-void WlcReporter::reportOrientation(const std::shared_ptr<IStats> &stats_client) {
-    ALOGV("reportOrientation");
-    if (!checkRateLimit(ORIENTATION_REPORT_RATE_LIMIT_MIN_SEC,
-                        ORIENTATION_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY, &rec_orientation_)) {
-        return;
-    }
-    sp<OrientationCollector> orientationCollector =
-            OrientationCollector::createOrientationCollector();
-    if (orientationCollector != nullptr) {
-        int orientationFromSensor = -1;
-        orientationCollector->pollOrientation(&orientationFromSensor);
-        VendorAtomValue tmp;
-        tmp.set<VendorAtomValue::intValue>(
-                translateDeviceOrientationToAtomValue(orientationFromSensor));
-
-        std::vector<VendorAtomValue> values(1);
-        values[PixelAtoms::DeviceOrientation::kOrientationFieldNumber - kVendorAtomOffset] = tmp;
-
-        VendorAtom event = {.reverseDomainName = "",
-                            .atomId = PixelAtoms::Atom::kDeviceOrientation,
-                            .values = std::move(values)};
-        const ndk::ScopedAStatus retStat = stats_client->reportVendorAtom(event);
-        if (!retStat.isOk()) {
-            ALOGE("Unable to report Orientation to Stats service");
-        }
-        orientationCollector->disableOrientationSensor();
-    }
-}
-
-bool WlcReporter::checkRateLimit(int64_t minSecond, int maxCount, ReportRecord *rec) {
-    if (rec == nullptr) {
-        ALOGE("ReportRecord should not be NULL");
-        return false;
-    }
-    int64_t now = nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
-    if (rec->last_reported_time_in_sec > 0 && now - rec->last_reported_time_in_sec < minSecond) {
-        ALOGV("%s: Rate limit, min period: %ld", rec->name, minSecond);
-        return false;
-    }
-    if (rec->last_reported_time_in_sec_today == 0) {
-        rec->last_reported_time_in_sec_today = now;
-        ALOGV("%s: reset day time (init)", rec->name);
-    } else if (now - rec->last_reported_time_in_sec_today > DAY_SECOND) {
-        rec->last_reported_time_in_sec_today = now;
-        rec->count_today = 0;
-        ALOGV("%s: reset day time", rec->name);
-    } else if (rec->count_today >= maxCount) {
-        ALOGV("%s: Rate limit, max count: %d", rec->name, maxCount);
-        return false;
-    }
-    (rec->count_today)++;
-    ALOGV("%s: checkRateLimit count: %d", rec->name, rec->count_today);
-    rec->last_reported_time_in_sec = now;
-    return true;
-}
-}  // namespace pixel
-}  // namespace google
-}  // namespace hardware
-}  // namespace android
diff --git a/pixelstats/include/pixelstats/ChargeStatsReporter.h b/pixelstats/include/pixelstats/ChargeStatsReporter.h
index 098154a..760c7ba 100644
--- a/pixelstats/include/pixelstats/ChargeStatsReporter.h
+++ b/pixelstats/include/pixelstats/ChargeStatsReporter.h
@@ -37,7 +37,7 @@
     void checkAndReport(const std::shared_ptr<IStats> &stats_client, const std::string &path);
 
   private:
-    bool checkThermalContentsAndAck(std::string *file_contents);
+    bool checkContentsAndAck(std::string *file_contents, const std::string &path);
     void ReportVoltageTierStats(const std::shared_ptr<IStats> &stats_client, const char *line,
                                 const bool has_wireless, const std::string &wfile_contents);
     void ReportChargeStats(const std::shared_ptr<IStats> &stats_client, const std::string line,
@@ -54,6 +54,8 @@
 
     const std::string kThermalChargeMetricsPath =
             "/sys/devices/platform/google,charger/thermal_stats";
+
+    const std::string kGChargerMetricsPath = "/sys/devices/platform/google,charger/charge_stats";
 };
 
 }  // namespace pixel
diff --git a/pixelstats/include/pixelstats/MmMetricsReporter.h b/pixelstats/include/pixelstats/MmMetricsReporter.h
index 89f12f5..cfcfac3 100644
--- a/pixelstats/include/pixelstats/MmMetricsReporter.h
+++ b/pixelstats/include/pixelstats/MmMetricsReporter.h
@@ -37,6 +37,7 @@
 class MmMetricsReporter {
   public:
     MmMetricsReporter();
+    void aggregatePixelMmMetricsPer5Min();
     void logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client);
     void logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client);
     void logCmaStatus(const std::shared_ptr<IStats> &stats_client);
@@ -62,6 +63,43 @@
     static const std::vector<MmMetricsInfo> kCmaStatusInfo;
     static const std::vector<MmMetricsInfo> kCmaStatusExtInfo;
 
+    // raw PSI
+    static constexpr const char *kPsiBasePath = "/proc/pressure";
+    static constexpr const char *kPsiTypes[3] = {"cpu", "io", "memory"};
+    static constexpr const char *kPsiCategories[2] = {"full", "some"};
+    static constexpr const char *kPsiMetricNames[4] = {"avg10", "avg60", "avg300", "total"};
+    static constexpr int kPsiNumFiles = sizeof(kPsiTypes) / sizeof(kPsiTypes[0]);
+    static constexpr int kPsiNumCategories = sizeof(kPsiCategories) / sizeof(kPsiCategories[0]);
+    // number of statistics metric names (one total and several timed averages, per category)
+    static constexpr int kPsiNumNames = sizeof(kPsiMetricNames) / sizeof(kPsiMetricNames[0]);
+
+    // Though cpu has no 'full' category, here we assume it has
+    // So, all file will contain 2 lines x 4 metrics per line = 8 metrics total.
+    static constexpr int kPsiMetricsPerFile = kPsiNumCategories * kPsiNumNames;
+
+    // we have 1 'total' and all others 'averages' per category
+    // "total" metrics are already accumulative and thus no aggregation is needed.
+    //  raw values are used.
+    static constexpr int kPsiNumTotals = 1;
+    static constexpr int kPsiNumAvgs = kPsiNumNames - kPsiNumTotals;
+
+    // -1 since "cpu" type has no "full" category
+    static constexpr int kPsiNumAllCategories = kPsiNumFiles * kPsiNumCategories - 1;
+
+    // number of raw metrics: total and avgs, and the combined all: added together.
+    static constexpr int kPsiNumAllTotals = kPsiNumAllCategories * kPsiNumTotals;
+    static constexpr int kPsiNumAllAvgs = kPsiNumAllCategories * kPsiNumAvgs;
+    static constexpr int kPsiNumAllMetrics = kPsiNumAllTotals + kPsiNumAllAvgs;
+
+    // aggregated into (1) min, (2) max, (3) average (internally the sum is kept than the average)
+    static constexpr int kPsiNumOfAggregatedType = 3;
+
+    // # of upload metrics will have a aggregation factor on all 'average' type raw metrics.
+    static constexpr int kPsiNumAllUploadAvgMetrics = kPsiNumAllAvgs * kPsiNumOfAggregatedType;
+    static constexpr int kPsiNumAllUploadTotalMetrics = kPsiNumAllTotals;
+    static constexpr int kPsiNumAllUploadMetrics =
+            kPsiNumAllUploadTotalMetrics + kPsiNumAllUploadAvgMetrics;
+
     bool checkKernelMMMetricSupport();
 
     bool MmMetricsSupported() {
@@ -80,6 +118,21 @@
     bool ReadFileToUint(const char *const path, uint64_t *val);
     bool reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
                           const std::vector<VendorAtomValue> &values, const std::string &atom_name);
+    void readCompactionDurationStat(std::vector<long> *store);
+    void fillCompactionDurationStatAtom(const std::vector<long> &store,
+                                        std::vector<VendorAtomValue> *values);
+    void readDirectReclaimStat(std::vector<long> *store);
+    void fillDirectReclaimStatAtom(const std::vector<long> &store,
+                                   std::vector<VendorAtomValue> *values);
+    void readPressureStall(const char *basePath, std::vector<long> *store);
+    bool parsePressureStallFileContent(bool is_cpu, std::string lines, std::vector<long> *store,
+                                       int file_save_idx);
+    bool parsePressureStallWords(std::vector<std::string> words, std::vector<long> *store,
+                                 int line_save_idx);
+    bool savePressureMetrics(std::string name, std::string value, std::vector<long> *store,
+                             int base_save_idx);
+    void fillPressureStallAtom(std::vector<VendorAtomValue> *values);
+    void aggregatePressureStall();
     std::map<std::string, uint64_t> readVmStat(const char *path);
     uint64_t getIonTotalPools();
     uint64_t getGpuMemory();
@@ -103,12 +156,22 @@
     const char *const kIonTotalPoolsPath;
     const char *const kIonTotalPoolsPathForLegacy;
     const char *const kGpuTotalPages;
+    const char *const kCompactDuration;
+    const char *const kDirectReclaimBasePath;
     const char *const kPixelStatMm;
     // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
     // store everything in the values array at the index of the field number
     // -2.
-    const int kVendorAtomOffset = 2;
+    static constexpr int kVendorAtomOffset = 2;
+    static constexpr int kNumCompactionDurationPrevMetrics = 6;
+    static constexpr int kNumDirectReclaimPrevMetrics = 20;
 
+    std::vector<long> prev_compaction_duration_;
+    std::vector<long> prev_direct_reclaim_;
+    long prev_psi_total_[kPsiNumAllTotals];
+    long psi_total_[kPsiNumAllTotals];
+    long psi_aggregated_[kPsiNumAllUploadAvgMetrics];  // min, max and avg of original avgXXX
+    int psi_data_set_count_ = 0;
     std::map<std::string, uint64_t> prev_hour_vmstat_;
     std::map<std::string, uint64_t> prev_day_vmstat_;
     std::map<std::string, uint64_t> prev_day_pixel_vmstat_;
diff --git a/pixelstats/include/pixelstats/OrientationCollector.h b/pixelstats/include/pixelstats/OrientationCollector.h
deleted file mode 100644
index e3ca641..0000000
--- a/pixelstats/include/pixelstats/OrientationCollector.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2020 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 <android/hidl/base/1.0/IBase.h>
-#include <android/sensor.h>
-
-#ifndef HARDWARE_GOOGLE_PIXEL_ORIENTATION_COLLECTOR_H
-#define HARDWARE_GOOGLE_PIXEL_ORIENTATION_COLLECTOR_H
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-/**
- * A class to help use Senser
- */
-class OrientationCollector : public RefBase {
-  public:
-    static sp<OrientationCollector> createOrientationCollector();
-
-    int32_t pollOrientation(int *orientation);
-    int32_t init();
-    void disableOrientationSensor();
-
-  private:
-    ASensorEventQueue *mQueue;
-    ASensorManager *mSensorManager = nullptr;
-    ASensorRef mOrientationSensor;
-
-    int getEvents(ASensorEvent *event_list, size_t event_list_size, int *event_count);
-};
-
-}  // namespace pixel
-}  // namespace google
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HARDWARE_GOOGLE_PIXEL_ORIENTATION_COLLECTOR_H
diff --git a/pixelstats/include/pixelstats/PcaChargeStats.h b/pixelstats/include/pixelstats/PcaChargeStats.h
index 90a6c3e..0e81be0 100644
--- a/pixelstats/include/pixelstats/PcaChargeStats.h
+++ b/pixelstats/include/pixelstats/PcaChargeStats.h
@@ -25,12 +25,15 @@
 class PcaChargeStats {
   public:
     PcaChargeStats(const std::string pca_charge_metrics_path =
-                           "/sys/class/power_supply/pca9468-mains/device/chg_stats");
+                           "/sys/class/power_supply/pca9468-mains/device/chg_stats",
+                   const std::string pca94xx_charge_metrics_path =
+                           "/sys/class/power_supply/pca94xx-mains/device/chg_stats");
 
     bool CheckPcaContentsAndAck(std::string *file_contents);
 
   private:
     const std::string kPcaChargeMetricsPath;
+    const std::string kPca94xxChargeMetricsPath;
 };
 
 }  // namespace pixel
diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h
index 2baa53c..75b1a09 100644
--- a/pixelstats/include/pixelstats/SysfsCollector.h
+++ b/pixelstats/include/pixelstats/SysfsCollector.h
@@ -24,6 +24,7 @@
 #include "BatteryHealthReporter.h"
 #include "MitigationStatsReporter.h"
 #include "MmMetricsReporter.h"
+#include "ThermalStatsReporter.h"
 
 namespace android {
 namespace hardware {
@@ -60,6 +61,9 @@
         const char *const SpeakerExcursionPath;
         const char *const SpeakerHeartBeatPath;
         const std::vector<std::string> UFSErrStatsPath;
+        const int BlockStatsLength;
+        const char *const AmsRatePath;
+        const std::vector<std::string> ThermalStatsPaths;
     };
 
     SysfsCollector(const struct SysfsPaths &paths);
@@ -68,11 +72,13 @@
   private:
     bool ReadFileToInt(const std::string &path, int *val);
     bool ReadFileToInt(const char *path, int *val);
+    void aggregatePer5Min();
     void logPerDay();
     void logPerHour();
 
     void logBatteryChargeCycles(const std::shared_ptr<IStats> &stats_client);
     void logBatteryHealth(const std::shared_ptr<IStats> &stats_client);
+    void logBlockStatsReported(const std::shared_ptr<IStats> &stats_client);
     void logCodecFailed(const std::shared_ptr<IStats> &stats_client);
     void logCodec1Failed(const std::shared_ptr<IStats> &stats_client);
     void logSlowIO(const std::shared_ptr<IStats> &stats_client);
@@ -88,12 +94,15 @@
     void logBootStats(const std::shared_ptr<IStats> &stats_client);
     void logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client);
     void logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_client);
+    void logF2fsSmartIdleMaintEnabled(const std::shared_ptr<IStats> &stats_client);
+    void logThermalStats(const std::shared_ptr<IStats> &stats_client);
 
     void reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client, const char *path,
                               const VendorSlowIo::IoOperation &operation_s);
     void reportZramMmStat(const std::shared_ptr<IStats> &stats_client);
     void reportZramBdStat(const std::shared_ptr<IStats> &stats_client);
     int getReclaimedSegments(const std::string &mode);
+    void logVendorAudioHardwareStats(const std::shared_ptr<IStats> &stats_client);
 
     const char *const kSlowioReadCntPath;
     const char *const kSlowioWriteCntPath;
@@ -118,10 +127,14 @@
     const char *const kSpeakerExcursionPath;
     const char *const kSpeakerHeartbeatPath;
     const std::vector<std::string> kUFSErrStatsPath;
+    const int kBlockStatsLength;
+    const char *const kAmsRatePath;
+    const std::vector<std::string> kThermalStatsPaths;
 
     BatteryEEPROMReporter battery_EEPROM_reporter_;
     MmMetricsReporter mm_metrics_reporter_;
     MitigationStatsReporter mitigation_stats_reporter_;
+    ThermalStatsReporter thermal_stats_reporter_;
     BatteryHealthReporter battery_health_reporter_;
 
     // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
diff --git a/pixelstats/include/pixelstats/ThermalStatsReporter.h b/pixelstats/include/pixelstats/ThermalStatsReporter.h
new file mode 100644
index 0000000..57f246e
--- /dev/null
+++ b/pixelstats/include/pixelstats/ThermalStatsReporter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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 HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H
+#define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H
+
+#include <aidl/android/frameworks/stats/IStats.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using aidl::android::frameworks::stats::IStats;
+using aidl::android::frameworks::stats::VendorAtomValue;
+
+/**
+ * A class to upload Pixel Thermal Stats metrics
+ */
+class ThermalStatsReporter {
+  public:
+    ThermalStatsReporter();
+    void logThermalStats(const std::shared_ptr<IStats> &stats_client,
+                         const std::vector<std::string> &thermal_stats_paths);
+
+  private:
+    struct ThermalDfsCounts {
+        int64_t big_count;
+        int64_t mid_count;
+        int64_t little_count;
+        int64_t gpu_count;
+        int64_t tpu_count;
+        int64_t aur_count;
+    };
+
+    // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
+    // store everything in the values array at the index of the field number
+    // -2.
+    const int kVendorAtomOffset = 2;
+    const int kNumOfThermalDfsStats = 6;
+    struct ThermalDfsCounts prev_data;
+
+    void logThermalDfsStats(const std::shared_ptr<IStats> &stats_client,
+                            const std::vector<std::string> &thermal_stats_paths);
+    bool captureThermalDfsStats(const std::vector<std::string> &thermal_stats_paths,
+                                struct ThermalDfsCounts *cur_data);
+    bool readDfsCount(const std::string &path, int64_t *val);
+};
+
+}  // namespace pixel
+}  // namespace google
+}  // namespace hardware
+}  // namespace android
+
+#endif  // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H
diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h
index 876098d..5316afa 100644
--- a/pixelstats/include/pixelstats/UeventListener.h
+++ b/pixelstats/include/pixelstats/UeventListener.h
@@ -21,8 +21,6 @@
 #include <android-base/chrono_utils.h>
 #include <pixelstats/BatteryCapacityReporter.h>
 #include <pixelstats/ChargeStatsReporter.h>
-#include <pixelstats/PcaChargeStats.h>
-#include <pixelstats/WlcReporter.h>
 
 namespace android {
 namespace hardware {
@@ -39,10 +37,6 @@
  */
 class UeventListener {
   public:
-    /* Both WirelessChargerPtmcUevent and WirelessChargerPtmcPath is use to get
-     * wireless charger ptmx id, for most case we don't need asisign both of
-     * them.
-     **/
     struct UeventPaths {
         const char *const AudioUevent;
         const char *const SsocDetailsPath;
@@ -51,8 +45,8 @@
         const char *const TypeCPartnerUevent;
         const char *const TypeCPartnerVidPath;
         const char *const TypeCPartnerPidPath;
-        const char *const WirelessChargerPtmcUevent;
-        const char *const WirelessChargerPtmcPath;
+        const char *const WirelessChargerPtmcUevent;  // Deprecated.
+        const char *const WirelessChargerPtmcPath;    // Deprecated.
     };
     constexpr static const char *const ssoc_details_path =
             "/sys/class/power_supply/battery/ssoc_details";
@@ -91,8 +85,6 @@
     void ReportVoltageTierStats(const std::shared_ptr<IStats> &stats_client, const char *line,
                                 const bool has_wireless, const std::string wfile_contents);
     void ReportChargeMetricsEvent(const std::shared_ptr<IStats> &stats_client, const char *driver);
-    void ReportWlc(const std::shared_ptr<IStats> &stats_client, const bool pow_wireless,
-                   const bool online, const char *ptmc);
     void ReportBatteryCapacityFGEvent(const std::shared_ptr<IStats> &stats_client,
                                       const char *subsystem);
     void ReportTypeCPartnerId(const std::shared_ptr<IStats> &stats_client);
@@ -104,8 +96,6 @@
     const std::string kTypeCPartnerUevent;
     const std::string kTypeCPartnerVidPath;
     const std::string kTypeCPartnerPidPath;
-    const std::string kWirelessChargerPtmcUevent;
-    const std::string kWirelessChargerPtmcPath;
 
     BatteryCapacityReporter battery_capacity_reporter_;
     ChargeStatsReporter charge_stats_reporter_;
@@ -117,8 +107,6 @@
 
     int uevent_fd_;
     int log_fd_;
-
-    WlcReporter wlc_reporter_ = WlcReporter(kWirelessChargerPtmcPath.c_str());
 };
 
 }  // namespace pixel
diff --git a/pixelstats/include/pixelstats/WlcReporter.h b/pixelstats/include/pixelstats/WlcReporter.h
deleted file mode 100644
index e979369..0000000
--- a/pixelstats/include/pixelstats/WlcReporter.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 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 HARDWARE_GOOGLE_PIXEL_PIXELSTATS_WLCREPORTER_H
-#define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_WLCREPORTER_H
-
-#include <aidl/android/frameworks/stats/IStats.h>
-#include <utils/RefBase.h>
-
-#include <cstdint>
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-using aidl::android::frameworks::stats::IStats;
-
-/**
- * A class to upload wireless metrics
- */
-class WlcReporter : public RefBase {
-  public:
-    void checkAndReport(const std::shared_ptr<IStats> &stats_client, const bool online,
-                        const char *ptmc_uevent);
-
-    WlcReporter(const char *ptmc_path);
-
-  private:
-    struct WlcStatus {
-        bool is_charging;
-        bool check_charger_vendor_id;
-        bool check_charger_vendor_id_scheduled;
-        int check_vendor_id_attempts;
-        WlcStatus();
-    };
-    WlcStatus wlc_status_;
-    struct ReportRecord {
-        char const *name;
-        int64_t last_reported_time_in_sec_today;
-        int64_t last_reported_time_in_sec;
-        int count_today;
-        ReportRecord(char const *name_);
-    };
-    ReportRecord rec_wlc_vendor_{"wlc_vendor"};
-    ReportRecord rec_orientation_{"orientation"};
-
-    void checkVendorId(const std::shared_ptr<IStats> &stats_client, const char *ptmc_uevent);
-    void scheduleReportVendorId(const std::shared_ptr<IStats> &stats_client);
-    void reportOrientation(const std::shared_ptr<IStats> &stats_client);
-    void reportVendor(const std::shared_ptr<IStats> &stats_client, const int ptmcId);
-    bool reportVendorMayRetry(const std::shared_ptr<IStats> &stats_client, const char *ptmc_uevent);
-    // Translate device orientation value from sensor Hal to atom enum value
-    int translateDeviceOrientationToAtomValue(int orientation);
-    void reportInBackground(const std::shared_ptr<IStats> &stats_client, const char *ptmc_path);
-    /*
-     * Wait timer for make delay before read ptmc path, return false on error
-     * timerfd: fd create by timerfd_create, need create/close by caller
-     **/
-    bool ptmcWaitTimer(int timerfd);
-    /* For some case (ex if wireless charger was connect to a low power PTMC AC
-     * adapter), the wireless charger keep restaring (it might casuse will
-     * check and update data in a not reasonable rare).
-     * return: true, it has not hit upload rare limit
-     *         false, it has hit rate litmit, we should drop current
-     *                upload atom
-     **/
-    bool checkRateLimit(int64_t minSecond, int maxCount, ReportRecord *rec);
-
-    // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
-    // store everything in the values array at the index of the field number
-    // -2.
-    const int kVendorAtomOffset = 2;
-    const int kMaxVendorIdAttempts = 5;
-    const char *kWirelessChargerPtmcPath;
-
-    int readPtmcId(const char *ptmc_str);
-};
-
-}  // namespace pixel
-}  // namespace google
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_WLCREPORTER_H
diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto
index 9ffb708..ece1c18 100644
--- a/pixelstats/pixelatoms.proto
+++ b/pixelstats/pixelatoms.proto
@@ -43,8 +43,7 @@
       ZramMmStat zram_mm_stat = 105005;
       ZramBdStat zram_bd_stat = 105006;
       BootStatsInfo boot_stats = 105007;
-      WirelessChargingStats wireless_charging_stats = 105008;
-      DeviceOrientation device_orientation = 105009;
+      // 105008 - 105009 is available.
       BatteryCapacityFG fg_capacity = 105010;
       PdVidPid pd_vid_pid = 105011;
       BatteryEEPROM battery_eeprom = 105012;
@@ -79,6 +78,11 @@
 
       BatteryHealthStatus battery_health_status = 105037;
       BatteryHealthUsage battery_health_usage = 105038;
+      F2fsSmartIdleMaintEnabledStateChanged f2fs_smart_idle_maint_enabled_state_changed = 105039;
+      BlockStatsReported block_stats_reported = 105040;
+      VendorAudioHardwareStatsReported vendor_audio_hardware_stats_reported = 105041;
+
+      ThermalDfsStats thermal_dfs_stats = 105042;
     }
     // AOSP atom ID range ends at 109999
 }
@@ -118,6 +122,10 @@
         ADAPTER_TYPE_WPC_10W = 23;
         ADAPTER_TYPE_WPC_BPP = 24;
         ADAPTER_TYPE_WPC_L7 = 25;
+        ADAPTER_TYPE_EXT = 26;
+        ADAPTER_TYPE_EXT1 = 27;
+        ADAPTER_TYPE_EXT2 = 28;
+        ADAPTER_TYPE_EXT_UNKNOWN = 29;
     }
     optional string reverse_domain_name = 1;
     /* Type of charge adapter, enumerated above. */
@@ -419,6 +427,7 @@
     REBOOTED = 2;
     UPGRADED = 3;
     ALERT_V2 = 4;
+    SEC_CH_STATE = 5;
   };
   optional string reverse_domain_name = 1;
   optional Event event = 2;
@@ -462,6 +471,10 @@
   optional int32 temp_min = 20;
   optional int32 temp_max = 21;
   optional int32 bus_err = 22;
+
+  // SEC_CH_STATE-specific filed. This field corresponds to the state
+  // of GSA-GSC secure channel.
+  optional int32 sec_ch_state = 23;
 }
 
 /* A message containing the citadel firmware version. */
@@ -514,12 +527,63 @@
     optional int64 unevictable = 7;
     optional int64 ion_total_pools = 8;
     optional int64 gpu_memory = 9;
+    optional int64 slab_unreclaimable = 10;
+    optional int64 psi_cpu_some_total = 11;
+    optional int64 psi_io_full_total = 12;
+    optional int64 psi_io_some_total = 13;
+    optional int64 psi_mem_full_total = 14;
+    optional int64 psi_mem_some_total = 15;
+    optional int32 psi_cpu_some_avg10_min = 16;
+    optional int32 psi_cpu_some_avg10_max = 17;
+    optional int32 psi_cpu_some_avg10_avg = 18;
+    optional int32 psi_cpu_some_avg60_min = 19;
+    optional int32 psi_cpu_some_avg60_max = 20;
+    optional int32 psi_cpu_some_avg60_avg = 21;
+    optional int32 psi_cpu_some_avg300_min = 22;
+    optional int32 psi_cpu_some_avg300_max = 23;
+    optional int32 psi_cpu_some_avg300_avg = 24;
+    optional int32 psi_io_full_avg10_min = 25;
+    optional int32 psi_io_full_avg10_max = 26;
+    optional int32 psi_io_full_avg10_avg = 27;
+    optional int32 psi_io_full_avg60_min = 28;
+    optional int32 psi_io_full_avg60_max = 29;
+    optional int32 psi_io_full_avg60_avg = 30;
+    optional int32 psi_io_full_avg300_min = 31;
+    optional int32 psi_io_full_avg300_max = 32;
+    optional int32 psi_io_full_avg300_avg = 33;
+    optional int32 psi_io_some_avg10_min = 34;
+    optional int32 psi_io_some_avg10_max = 35;
+    optional int32 psi_io_some_avg10_avg = 36;
+    optional int32 psi_io_some_avg60_min = 37;
+    optional int32 psi_io_some_avg60_max = 38;
+    optional int32 psi_io_some_avg60_avg = 39;
+    optional int32 psi_io_some_avg300_min = 40;
+    optional int32 psi_io_some_avg300_max = 41;
+    optional int32 psi_io_some_avg300_avg = 42;
+    optional int32 psi_mem_full_avg10_min = 43;
+    optional int32 psi_mem_full_avg10_max = 44;
+    optional int32 psi_mem_full_avg10_avg = 45;
+    optional int32 psi_mem_full_avg60_min = 46;
+    optional int32 psi_mem_full_avg60_max = 47;
+    optional int32 psi_mem_full_avg60_avg = 48;
+    optional int32 psi_mem_full_avg300_min = 49;
+    optional int32 psi_mem_full_avg300_max = 50;
+    optional int32 psi_mem_full_avg300_avg = 51;
+    optional int32 psi_mem_some_avg10_min = 52;
+    optional int32 psi_mem_some_avg10_max = 53;
+    optional int32 psi_mem_some_avg10_avg = 54;
+    optional int32 psi_mem_some_avg60_min = 55;
+    optional int32 psi_mem_some_avg60_max = 56;
+    optional int32 psi_mem_some_avg60_avg = 57;
+    optional int32 psi_mem_some_avg300_min = 58;
+    optional int32 psi_mem_some_avg300_max = 59;
+    optional int32 psi_mem_some_avg300_avg = 60;
 }
 
 /* A message containing Pixel memory metrics collected daily. */
 message PixelMmMetricsPerDay {
     optional string reverse_domain_name = 1;
-    optional int64 workingset_refault = 2;
+    optional int64 workingset_refault = 2;  /* refault_file */
     optional int64 pswpin = 3;
     optional int64 pswpout = 4;
     optional int64 allocstall_dma = 5;
@@ -540,6 +604,43 @@
     optional int64 pgcache_miss = 20;
     optional int64 kswapd_stime_clks = 21;
     optional int64 kcompactd_stime_clks = 22;
+    optional int64 direct_reclaim_native_latency_total_time = 23;
+    optional int64 direct_reclaim_native_latency0 = 24;
+    optional int64 direct_reclaim_native_latency1 = 25;
+    optional int64 direct_reclaim_native_latency2 = 26;
+    optional int64 direct_reclaim_native_latency3 = 27;
+    optional int64 direct_reclaim_visible_latency_total_time = 28;
+    optional int64 direct_reclaim_visible_latency0 = 29;
+    optional int64 direct_reclaim_visible_latency1 = 30;
+    optional int64 direct_reclaim_visible_latency2 = 31;
+    optional int64 direct_reclaim_visible_latency3 = 32;
+    optional int64 direct_reclaim_top_latency_total_time = 33;
+    optional int64 direct_reclaim_top_latency0 = 34;
+    optional int64 direct_reclaim_top_latency1 = 35;
+    optional int64 direct_reclaim_top_latency2 = 36;
+    optional int64 direct_reclaim_top_latency3 = 37;
+    optional int64 direct_reclaim_other_latency_total_time = 38;
+    optional int64 direct_reclaim_other_latency0 = 39;
+    optional int64 direct_reclaim_other_latency1 = 40;
+    optional int64 direct_reclaim_other_latency2 = 41;
+    optional int64 direct_reclaim_other_latency3 = 42;
+    optional int64 compaction_total_time = 43;
+    optional int64 compaction_ev_count0 = 44;
+    optional int64 compaction_ev_count1 = 45;
+    optional int64 compaction_ev_count2 = 46;
+    optional int64 compaction_ev_count3 = 47;
+    optional int64 compaction_ev_count4 = 48;
+    optional int64 workingset_refault_anon = 49;
+    optional int64 workingset_refault_file = 50;
+    optional int64 compact_success = 51;
+    optional int64 compact_fail = 52;
+    optional int64 kswapd_low_wmark_hq = 53;
+    optional int64 kswapd_high_wmark_hq = 54;
+    optional int64 thp_file_alloc = 55;
+    optional int64 thp_zero_page_alloc = 56;
+    optional int64 thp_split_page = 57;
+    optional int64 thp_migration_split = 58;
+    optional int64 thp_deferred_split_page = 59;
 }
 
 /* A message containing CMA metrics collected from dogfooding only. */
@@ -803,6 +904,25 @@
 }
 
 /**
+ * Log thermal statistics.
+ */
+message ThermalDfsStats {
+ optional string reverse_domain_name = 1;
+ // The last count of BIG cluster dfs triggers
+ optional int32 big_dfs_count = 2;
+ // The last count of MID cluster dfs triggers
+ optional int32 mid_dfs_count = 3;
+ // The last count of LITTLE cluster dfs triggers
+ optional int32 little_dfs_count = 4;
+ // The last count of GPU dfs triggers
+ optional int32 gpu_dfs_count = 5;
+ // The last count of TPU dfs triggers
+ optional int32 tpu_dfs_count = 6;
+ // The last count of DSP dfs triggers
+ optional int32 aur_dfs_count = 7;
+}
+
+/**
  * Log how many segments have been reclaimed in a specific GC mode.
  */
 message F2fsGcSegmentInfo {
@@ -813,6 +933,8 @@
     optional int32 reclaimed_segments_urgent_high = 3;
     /* Reclaimed segments in GC urgent low mode */
     optional int32 reclaimed_segments_urgent_low = 4;
+    /* Reclaimed segments in GC urgent mid mode */
+    optional int32 reclaimed_segments_urgent_mid = 5;
 }
 
 /**
@@ -1068,6 +1190,16 @@
 
   // The cycle count at the time of the log event.
   optional int32 cycle_count = 11;
+
+  enum BattDisconnectStatus {
+    BPST_BATT_UNKNOWN = 0;
+    BPST_BATT_CONNECT = 1;
+    BPST_BATT_DISCONNECT = 2; // Result of single battery disconnect detection
+    BPST_BATT_CELL_FAULT = 3; // Result of cell fault detection which means actual disconnection
+  }
+
+  // Battery disconnect detection stats.
+  optional BattDisconnectStatus battery_disconnect_status = 12;
 }
 
 /**
@@ -1096,3 +1228,43 @@
   // Time (s) accumulated only during discharge at the given thresholds.
   optional int32 discharge_time_secs = 5;
 }
+
+/*
+ * A Value of F2FS smart idle maintenance feature enabled
+ */
+message F2fsSmartIdleMaintEnabledStateChanged {
+    optional string reverse_domain_name = 1;
+    /* Smart idle maint is enabled */
+    optional bool enabled = 2;
+}
+
+/*
+ * Snapshot of the block layer read write stats
+ */
+message BlockStatsReported {
+    optional string reverse_domain_name = 1;
+        /* number of read I/Os processed */
+        optional int64 read_io = 2;
+        /* number of sectors read */
+        optional int64 read_sectors = 3;
+        /* total wait time for read requests */
+        optional int64 read_ticks = 4;
+        /* number of write I/Os processed */
+        optional int64 write_io = 5;
+        /* number of sectors written */
+        optional int64 write_sectors = 6;
+        /* total wait time for write requests */
+        optional int64 write_ticks = 7;
+}
+
+/**
+ * Logs the reported vendor audio hardware stats.
+ */
+message VendorAudioHardwareStatsReported {
+  optional string reverse_domain_name = 1;
+  /* The percentage of calls in a day where auto-mic-switch triggered.
+   * It represented as a fixed-point integer with three decimal place.
+   * E.g.:12.345% is repsented by 12345.
+   */
+  optional int32 milli_rate_of_ams_per_day = 2;
+}
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index 7998c53..14cbf01 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -265,14 +265,10 @@
     }
     // Remove the session from PowerSessionManager first to avoid racing.
     PowerSessionManager::getInstance()->removePowerSession(this);
-    setSessionUclampMin(0);
-    {
-        std::lock_guard<std::mutex> guard(mSessionLock);
-        mSessionClosed.store(true);
-    }
-    mDescriptor->is_active.store(false);
     mEarlyBoostHandler->setSessionDead();
     mStaleTimerHandler->setSessionDead();
+    setSessionUclampMin(0);
+    mDescriptor->is_active.store(false);
     updateUniveralBoostMode();
     return ndk::ScopedAStatus::ok();
 }
@@ -430,14 +426,13 @@
         ATRACE_NAME(tag.c_str());
     }
     std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
-    int min = std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit));
-    mDescriptor->current_min = min;
-    PowerSessionManager::getInstance()->setUclampMinLocked(this, min);
+    mDescriptor->current_min =
+            std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit));
 
     if (ATRACE_ENABLED()) {
         const std::string idstr = getIdString();
         std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
-        ATRACE_INT(sz.c_str(), min);
+        ATRACE_INT(sz.c_str(), mDescriptor->current_min);
     }
 }
 
@@ -506,6 +501,7 @@
 }
 
 void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) {
+    std::lock_guard<std::mutex> guard(mClosedLock);
     if (mIsSessionDead) {
         return;
     }
@@ -535,7 +531,7 @@
 }
 
 void PowerHintSession::StaleTimerHandler::setSessionDead() {
-    std::lock_guard<std::mutex> guard(mStaleLock);
+    std::lock_guard<std::mutex> guard(mClosedLock);
     mIsSessionDead = true;
     PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
 }
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
index 96b445e..9bd9a2c 100644
--- a/power-libperfmgr/aidl/PowerHintSession.h
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -105,7 +105,7 @@
 
       private:
         PowerHintSession *mSession;
-        std::mutex mStaleLock;
+        std::mutex mClosedLock;
         std::mutex mMessageLock;
         std::atomic<time_point<steady_clock>> mStaleTime;
         std::atomic<bool> mIsMonitoring;
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
index 516942a..947208b 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.cpp
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -33,6 +33,7 @@
 namespace impl {
 namespace pixel {
 
+using ::android::perfmgr::AdpfConfig;
 using ::android::perfmgr::HintManager;
 
 namespace {
@@ -103,7 +104,28 @@
 
 void PowerSessionManager::wakeSessions() {
     std::lock_guard<std::mutex> guard(mLock);
-    for (PowerHintSession *s : mSessions) {
+    std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+    std::unordered_set<PowerHintSession *> wakeupList;
+    const int wakeupBoostValue = static_cast<int>(adpfConfig->mUclampMinInit);
+    for (auto &it : mTidSessionListMap) {
+        int tid = it.first;
+        int maxboost = -1;
+        // Find the max boost value among all the sessions that include the same TID.
+        for (PowerHintSession *s : it.second) {
+            if (!s->isActive())
+                continue;
+            // all active sessions need to be awakened.
+            wakeupList.insert(s);
+            if (s->isTimeout()) {
+                maxboost = std::max(maxboost, s->getUclampMin());
+            }
+        }
+        // Found the max boost and actally set to the task.
+        if (maxboost != -1) {
+            set_uclamp_min(tid, std::max(maxboost, wakeupBoostValue));
+        }
+    }
+    for (PowerHintSession *s : wakeupList) {
         s->wakeup();
     }
 }
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index 2f41445..d3dc032 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -282,7 +282,7 @@
 }
 
 void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
-    *dump_buf << "VirtualSensorInfo:" << std::endl;
+    *dump_buf << "getVirtualSensorInfo:" << std::endl;
     const auto &map = thermal_helper_.GetSensorInfoMap();
     for (const auto &sensor_info_pair : map) {
         if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
@@ -301,11 +301,17 @@
             *dump_buf << "]" << std::endl;
             *dump_buf << "  Offset: " << sensor_info_pair.second.virtual_sensor_info->offset
                       << std::endl;
-            *dump_buf << "  Trigger Sensor: "
-                      << (sensor_info_pair.second.virtual_sensor_info->trigger_sensor.empty()
-                                  ? "N/A"
-                                  : sensor_info_pair.second.virtual_sensor_info->trigger_sensor)
-                      << std::endl;
+            *dump_buf << "  Trigger Sensor: ";
+            if (sensor_info_pair.second.virtual_sensor_info->trigger_sensors.empty()) {
+                *dump_buf << "N/A" << std::endl;
+            } else {
+                for (size_t i = 0;
+                     i < sensor_info_pair.second.virtual_sensor_info->trigger_sensors.size(); i++) {
+                    *dump_buf << sensor_info_pair.second.virtual_sensor_info->trigger_sensors[i]
+                              << " ";
+                }
+                *dump_buf << std::endl;
+            }
             *dump_buf << "  Formula: ";
             switch (sensor_info_pair.second.virtual_sensor_info->formula) {
                 case FormulaOption::COUNT_THRESHOLD:
@@ -331,7 +337,7 @@
 }
 
 void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) {
-    *dump_buf << "Throttling Info:" << std::endl;
+    *dump_buf << "getThrottlingInfo:" << std::endl;
     const auto &map = thermal_helper_.GetSensorInfoMap();
     const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
     for (const auto &name_info_pair : map) {
@@ -458,7 +464,7 @@
     if (!thermal_throttling_status_map.size()) {
         return;
     }
-    *dump_buf << "Throttling Request Status " << std::endl;
+    *dump_buf << "getThrottlingRequestStatus:" << std::endl;
     for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) {
         *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl;
         if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) {
@@ -507,7 +513,7 @@
     const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap();
     const auto &power_status_map = thermal_helper_.GetPowerStatusMap();
 
-    *dump_buf << "Power Rail Info " << std::endl;
+    *dump_buf << "getPowerRailInfo:" << std::endl;
     for (const auto &power_rail_pair : power_rail_info_map) {
         *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl;
         *dump_buf << "  Power Sample Count: " << power_rail_pair.second.power_sample_count
@@ -577,7 +583,7 @@
                 hidl_vec<CpuUsage> cpu_usages;
                 dump_buf << "getCpuUsages:" << std::endl;
                 if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
-                    dump_buf << "Failed to get CPU usages." << std::endl;
+                    dump_buf << " Failed to get CPU usages." << std::endl;
                 }
 
                 for (const auto &usage : cpu_usages) {
@@ -673,7 +679,7 @@
                 hidl_vec<CoolingDevice_2_0> cooling_devices;
                 if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU,
                                                                &cooling_devices)) {
-                    dump_buf << "Failed to getCurrentCoolingDevices." << std::endl;
+                    dump_buf << " Failed to getCurrentCoolingDevices." << std::endl;
                 }
 
                 for (const auto &c : cooling_devices) {
@@ -682,7 +688,8 @@
                 }
             }
             {
-                dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl;
+                dump_buf << "getCallbacks:" << std::endl;
+                dump_buf << " Total: " << callbacks_.size() << std::endl;
                 for (const auto &c : callbacks_) {
                     dump_buf << " IsFilter: " << c.is_filter_type
                              << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
@@ -690,7 +697,7 @@
                 }
             }
             {
-                dump_buf << "SendCallback" << std::endl;
+                dump_buf << "sendCallback:" << std::endl;
                 dump_buf << "  Enabled List: ";
                 const auto &map = thermal_helper_.GetSensorInfoMap();
                 for (const auto &name_info_pair : map) {
@@ -701,7 +708,7 @@
                 dump_buf << std::endl;
             }
             {
-                dump_buf << "SendPowerHint" << std::endl;
+                dump_buf << "sendPowerHint:" << std::endl;
                 dump_buf << "  Enabled List: ";
                 const auto &map = thermal_helper_.GetSensorInfoMap();
                 for (const auto &name_info_pair : map) {
@@ -716,11 +723,12 @@
             dumpThrottlingRequestStatus(&dump_buf);
             dumpPowerRailInfo(&dump_buf);
             {
-                dump_buf << "AIDL Power Hal exist: " << std::boolalpha
-                         << thermal_helper_.isAidlPowerHalExist() << std::endl;
-                dump_buf << "AIDL Power Hal connected: " << std::boolalpha
+                dump_buf << "getAIDLPowerHalInfo:" << std::endl;
+                dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist()
+                         << std::endl;
+                dump_buf << " Connected: " << std::boolalpha
                          << thermal_helper_.isPowerHalConnected() << std::endl;
-                dump_buf << "AIDL Power Hal Ext connected: " << std::boolalpha
+                dump_buf << " Ext connected: " << std::boolalpha
                          << thermal_helper_.isPowerHalExtConnected() << std::endl;
             }
         }
diff --git a/thermal/android.hardware.thermal@2.0-service.pixel.rc b/thermal/android.hardware.thermal@2.0-service.pixel.rc
index 0e23bef..3ed7865 100644
--- a/thermal/android.hardware.thermal@2.0-service.pixel.rc
+++ b/thermal/android.hardware.thermal@2.0-service.pixel.rc
@@ -4,11 +4,9 @@
     trigger enable-thermal-hal
 
 on enable-thermal-hal
-    start vendor.thermal-hal-2-0
+    restart vendor.thermal-hal-2-0
 
 service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.pixel
-    interface android.hardware.thermal@1.0::IThermal default
-    interface android.hardware.thermal@2.0::IThermal default
     class hal
     user system
     group system
diff --git a/thermal/pid_1_0/Thermal.cpp b/thermal/pid_1_0/Thermal.cpp
index 2f41445..7e6f7c0 100644
--- a/thermal/pid_1_0/Thermal.cpp
+++ b/thermal/pid_1_0/Thermal.cpp
@@ -282,7 +282,7 @@
 }
 
 void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
-    *dump_buf << "VirtualSensorInfo:" << std::endl;
+    *dump_buf << "getVirtualSensorInfo:" << std::endl;
     const auto &map = thermal_helper_.GetSensorInfoMap();
     for (const auto &sensor_info_pair : map) {
         if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
@@ -331,7 +331,7 @@
 }
 
 void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) {
-    *dump_buf << "Throttling Info:" << std::endl;
+    *dump_buf << "getThrottlingInfo:" << std::endl;
     const auto &map = thermal_helper_.GetSensorInfoMap();
     const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
     for (const auto &name_info_pair : map) {
@@ -458,7 +458,7 @@
     if (!thermal_throttling_status_map.size()) {
         return;
     }
-    *dump_buf << "Throttling Request Status " << std::endl;
+    *dump_buf << "getThrottlingRequestStatus:" << std::endl;
     for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) {
         *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl;
         if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) {
@@ -507,7 +507,7 @@
     const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap();
     const auto &power_status_map = thermal_helper_.GetPowerStatusMap();
 
-    *dump_buf << "Power Rail Info " << std::endl;
+    *dump_buf << "getPowerRailInfo:" << std::endl;
     for (const auto &power_rail_pair : power_rail_info_map) {
         *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl;
         *dump_buf << "  Power Sample Count: " << power_rail_pair.second.power_sample_count
@@ -577,7 +577,7 @@
                 hidl_vec<CpuUsage> cpu_usages;
                 dump_buf << "getCpuUsages:" << std::endl;
                 if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
-                    dump_buf << "Failed to get CPU usages." << std::endl;
+                    dump_buf << " Failed to get CPU usages." << std::endl;
                 }
 
                 for (const auto &usage : cpu_usages) {
@@ -673,7 +673,7 @@
                 hidl_vec<CoolingDevice_2_0> cooling_devices;
                 if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU,
                                                                &cooling_devices)) {
-                    dump_buf << "Failed to getCurrentCoolingDevices." << std::endl;
+                    dump_buf << " Failed to getCurrentCoolingDevices." << std::endl;
                 }
 
                 for (const auto &c : cooling_devices) {
@@ -682,7 +682,8 @@
                 }
             }
             {
-                dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl;
+                dump_buf << "getCallbacks:" << std::endl;
+                dump_buf << " Total: " << callbacks_.size() << std::endl;
                 for (const auto &c : callbacks_) {
                     dump_buf << " IsFilter: " << c.is_filter_type
                              << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
@@ -690,7 +691,7 @@
                 }
             }
             {
-                dump_buf << "SendCallback" << std::endl;
+                dump_buf << "sendCallback:" << std::endl;
                 dump_buf << "  Enabled List: ";
                 const auto &map = thermal_helper_.GetSensorInfoMap();
                 for (const auto &name_info_pair : map) {
@@ -701,7 +702,7 @@
                 dump_buf << std::endl;
             }
             {
-                dump_buf << "SendPowerHint" << std::endl;
+                dump_buf << "sendPowerHint:" << std::endl;
                 dump_buf << "  Enabled List: ";
                 const auto &map = thermal_helper_.GetSensorInfoMap();
                 for (const auto &name_info_pair : map) {
@@ -716,11 +717,12 @@
             dumpThrottlingRequestStatus(&dump_buf);
             dumpPowerRailInfo(&dump_buf);
             {
-                dump_buf << "AIDL Power Hal exist: " << std::boolalpha
-                         << thermal_helper_.isAidlPowerHalExist() << std::endl;
-                dump_buf << "AIDL Power Hal connected: " << std::boolalpha
+                dump_buf << "getAIDLPowerHalInfo:" << std::endl;
+                dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist()
+                         << std::endl;
+                dump_buf << " Connected: " << std::boolalpha
                          << thermal_helper_.isPowerHalConnected() << std::endl;
-                dump_buf << "AIDL Power Hal Ext connected: " << std::boolalpha
+                dump_buf << " Ext connected: " << std::boolalpha
                          << thermal_helper_.isPowerHalExtConnected() << std::endl;
             }
         }
diff --git a/thermal/pixel-thermal-symlinks.rc b/thermal/pixel-thermal-symlinks.rc
index a1f6509..132ec5f 100644
--- a/thermal/pixel-thermal-symlinks.rc
+++ b/thermal/pixel-thermal-symlinks.rc
@@ -1,11 +1,11 @@
-on early-boot
+on boot
     mkdir /dev/thermal 0750 system system
     mkdir /dev/thermal/tz-by-name 0750 system system
     mkdir /dev/thermal/cdev-by-name 0750 system system
     start vendor.thermal.symlinks
 
 service vendor.thermal.symlinks /vendor/bin/thermal_symlinks
-    class hal
     user system
     group system
     oneshot
+    disabled
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index 8bc6741..85ccc5d 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -250,16 +250,21 @@
         }
 
         if (name_status_pair.second.virtual_sensor_info != nullptr &&
-            !name_status_pair.second.virtual_sensor_info->trigger_sensor.empty() &&
+            !name_status_pair.second.virtual_sensor_info->trigger_sensors.empty() &&
             name_status_pair.second.is_watch) {
-            if (sensor_info_map_.count(
-                        name_status_pair.second.virtual_sensor_info->trigger_sensor)) {
-                sensor_info_map_[name_status_pair.second.virtual_sensor_info->trigger_sensor]
-                        .is_watch = true;
-            } else {
-                LOG(FATAL) << name_status_pair.first << "'s trigger sensor: "
-                           << name_status_pair.second.virtual_sensor_info->trigger_sensor
-                           << " is invalid";
+            for (size_t i = 0;
+                 i < name_status_pair.second.virtual_sensor_info->trigger_sensors.size(); i++) {
+                if (sensor_info_map_.find(
+                            name_status_pair.second.virtual_sensor_info->trigger_sensors[i]) !=
+                    sensor_info_map_.end()) {
+                    sensor_info_map_[name_status_pair.second.virtual_sensor_info
+                                             ->trigger_sensors[i]]
+                            .is_watch = true;
+                } else {
+                    LOG(FATAL) << name_status_pair.first << "'s trigger sensor: "
+                               << name_status_pair.second.virtual_sensor_info->trigger_sensors[i]
+                               << " is invalid";
+                }
             }
         }
     }
@@ -329,7 +334,8 @@
 bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out) {
     // Return fail if the thermal sensor cannot be read.
     float temp;
-    if (!readThermalSensor(sensor_name, &temp, false)) {
+    std::string sensor_log;
+    if (!readThermalSensor(sensor_name, &temp, false, &sensor_log)) {
         LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
         return false;
     }
@@ -348,17 +354,22 @@
             sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)];
     out->vrThrottlingThreshold = sensor_info.vr_threshold;
 
+    if (sensor_info.is_watch) {
+        LOG(INFO) << sensor_name.data() << ":" << out->currentValue << " raw data:[" << sensor_log
+                  << "]";
+    }
     return true;
 }
 
 bool ThermalHelper::readTemperature(
         std::string_view sensor_name, Temperature_2_0 *out,
         std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status,
-        const bool force_sysfs) {
+        const bool force_no_cache) {
     // Return fail if the thermal sensor cannot be read.
     float temp;
+    std::string sensor_log;
 
-    if (!readThermalSensor(sensor_name, &temp, force_sysfs)) {
+    if (!readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log)) {
         LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
         return false;
     }
@@ -390,7 +401,9 @@
     out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
                                     ? status.first
                                     : status.second;
-
+    if (sensor_info.is_watch) {
+        LOG(INFO) << sensor_name.data() << ":" << out->value << " raw data:[" << sensor_log << "]";
+    }
     return true;
 }
 
@@ -779,10 +792,10 @@
 }
 
 bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp,
-                                      const bool force_sysfs) {
+                                      const bool force_no_cache, std::string *sensor_log) {
     float temp_val = 0.0;
     std::string file_reading;
-    std::string log_buf;
+    std::string sub_sensor_log;
     boot_clock::time_point now = boot_clock::now();
 
     ATRACE_NAME(StringPrintf("ThermalHelper::readThermalSensor - %s", sensor_name.data()).c_str());
@@ -795,11 +808,13 @@
     auto &sensor_status = sensor_status_map_.at(sensor_name.data());
 
     // Check if thermal data need to be read from buffer
-    if (!force_sysfs && (sensor_status.thermal_cached.timestamp != boot_clock::time_point::min()) &&
+    if (!force_no_cache &&
+        (sensor_status.thermal_cached.timestamp != boot_clock::time_point::min()) &&
         (std::chrono::duration_cast<std::chrono::milliseconds>(
                  now - sensor_status.thermal_cached.timestamp) < sensor_info.time_resolution) &&
         !isnan(sensor_status.thermal_cached.temp)) {
         *temp = sensor_status.thermal_cached.temp;
+        sensor_log->append(StringPrintf("%s:%0.f ", sensor_name.data(), *temp));
         LOG(VERBOSE) << "read " << sensor_name.data() << " from buffer, value:" << *temp;
         return true;
     }
@@ -815,16 +830,14 @@
             return false;
         }
         *temp = std::stof(::android::base::Trim(file_reading));
+        sensor_log->append(StringPrintf("%s:%0.f ", sensor_name.data(), *temp));
     } else {
         for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) {
             float sensor_reading = 0.0;
             if (!readThermalSensor(sensor_info.virtual_sensor_info->linked_sensors[i],
-                                   &sensor_reading, force_sysfs)) {
+                                   &sensor_reading, force_no_cache, &sub_sensor_log)) {
                 return false;
             }
-            log_buf.append(StringPrintf("(%s: %0.2f)",
-                                        sensor_info.virtual_sensor_info->linked_sensors[i].c_str(),
-                                        sensor_reading));
             if (std::isnan(sensor_info.virtual_sensor_info->coefficients[i])) {
                 return false;
             }
@@ -855,8 +868,9 @@
                     break;
             }
         }
-        LOG(VERBOSE) << sensor_name.data() << "'s sub sensors:" << log_buf;
         *temp = (temp_val + sensor_info.virtual_sensor_info->offset);
+        sensor_log->append(
+                StringPrintf("%s:%0.f(%s) ", sensor_name.data(), *temp, sub_sensor_log.data()));
     }
 
     {
@@ -881,7 +895,7 @@
     ATRACE_CALL();
     for (auto &name_status_pair : sensor_status_map_) {
         bool force_update = false;
-        bool force_sysfs = false;
+        bool force_no_cache = false;
         Temperature_2_0 temp;
         TemperatureThreshold threshold;
         SensorStatus &sensor_status = name_status_pair.second;
@@ -902,39 +916,45 @@
                                 : sensor_info.polling_delay;
 
         if (sensor_info.virtual_sensor_info != nullptr &&
-            !sensor_info.virtual_sensor_info->trigger_sensor.empty()) {
-            const auto trigger_sensor_status =
-                    sensor_status_map_.at(sensor_info.virtual_sensor_info->trigger_sensor);
-            if (trigger_sensor_status.severity != ThrottlingSeverity::NONE) {
-                sleep_ms = sensor_info.passive_delay;
+            !sensor_info.virtual_sensor_info->trigger_sensors.empty()) {
+            for (size_t i = 0; i < sensor_info.virtual_sensor_info->trigger_sensors.size(); i++) {
+                const auto &trigger_sensor_status =
+                        sensor_status_map_.at(sensor_info.virtual_sensor_info->trigger_sensors[i]);
+                if (trigger_sensor_status.severity != ThrottlingSeverity::NONE) {
+                    sleep_ms = sensor_info.passive_delay;
+                }
             }
         }
         // Check if the sensor need to be updated
         if (sensor_status.last_update_time == boot_clock::time_point::min()) {
             force_update = true;
-            LOG(VERBOSE) << "Force update " << name_status_pair.first
-                         << "'s temperature after booting";
+            force_no_cache = true;
         } else {
             time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
                     now - sensor_status.last_update_time);
-            if (time_elapsed_ms > sleep_ms) {
-                // Update the sensor because sleep timeout
+            if (uevent_sensors.size()) {
+                if (sensor_info.virtual_sensor_info != nullptr) {
+                    for (size_t i = 0; i < sensor_info.virtual_sensor_info->trigger_sensors.size();
+                         i++) {
+                        if (uevent_sensors.find(
+                                    sensor_info.virtual_sensor_info->trigger_sensors[i]) !=
+                            uevent_sensors.end()) {
+                            force_update = true;
+                            force_no_cache = true;
+                        }
+                    }
+                } else if (uevent_sensors.find(name_status_pair.first) != uevent_sensors.end()) {
+                    force_update = true;
+                    force_no_cache = true;
+                }
+            } else if (time_elapsed_ms > sleep_ms) {
                 force_update = true;
-            } else if (uevent_sensors.size() &&
-                       uevent_sensors.find((sensor_info.virtual_sensor_info != nullptr)
-                                                   ? sensor_info.virtual_sensor_info->trigger_sensor
-                                                   : name_status_pair.first) !=
-                               uevent_sensors.end()) {
-                // Force update the sensor from sysfs
-                force_update = true;
-                force_sysfs = true;
             }
         }
-
         LOG(VERBOSE) << "sensor " << name_status_pair.first
                      << ": time_elpased=" << time_elapsed_ms.count()
                      << ", sleep_ms=" << sleep_ms.count() << ", force_update = " << force_update
-                     << ", force_sysfs = " << force_sysfs;
+                     << ", force_no_cache = " << force_no_cache;
 
         if (!force_update) {
             auto timeout_remaining = sleep_ms - time_elapsed_ms;
@@ -947,7 +967,7 @@
         }
 
         std::pair<ThrottlingSeverity, ThrottlingSeverity> throtting_status;
-        if (!readTemperature(name_status_pair.first, &temp, &throtting_status, force_sysfs)) {
+        if (!readTemperature(name_status_pair.first, &temp, &throtting_status, force_no_cache)) {
             LOG(ERROR) << __func__
                        << ": error reading temperature for sensor: " << name_status_pair.first;
             continue;
@@ -982,10 +1002,8 @@
         }
 
         if (sensor_status.severity == ThrottlingSeverity::NONE) {
-            LOG(VERBOSE) << temp.name << ": " << temp.value;
             thermal_throttling_.clearThrottlingData(name_status_pair.first, sensor_info);
         } else {
-            LOG(INFO) << temp.name << ": " << temp.value;
             // update thermal throttling request
             thermal_throttling_.thermalThrottlingUpdate(
                     temp, sensor_info, sensor_status.severity, time_elapsed_ms,
diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h
index 6b7cd9a..eca9805 100644
--- a/thermal/thermal-helper.h
+++ b/thermal/thermal-helper.h
@@ -150,7 +150,8 @@
             ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
             float value) const;
     // Read temperature data according to thermal sensor's info
-    bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs);
+    bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs,
+                           std::string *sensor_log);
     bool connectToPowerHal();
     void updateSupportedPowerHints();
     void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
diff --git a/thermal/utils/thermal_info.cpp b/thermal/utils/thermal_info.cpp
index f01d11b..a24347f 100644
--- a/thermal/utils/thermal_info.cpp
+++ b/thermal/utils/thermal_info.cpp
@@ -136,6 +136,151 @@
 }
 }  // namespace
 
+bool ParseBindedCdevInfo(const Json::Value &values,
+                         std::unordered_map<std::string, BindedCdevInfo> *binded_cdev_info_map,
+                         const bool support_pid, bool *support_hard_limit) {
+    for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+        Json::Value sub_values;
+        const std::string &cdev_name = values[j]["CdevRequest"].asString();
+        ThrottlingArray cdev_weight_for_pid;
+        cdev_weight_for_pid.fill(NAN);
+        CdevArray cdev_ceiling;
+        cdev_ceiling.fill(std::numeric_limits<int>::max());
+        int max_release_step = std::numeric_limits<int>::max();
+        int max_throttle_step = std::numeric_limits<int>::max();
+        if (support_pid) {
+            if (!values[j]["CdevWeightForPID"].empty()) {
+                LOG(INFO) << "Star to parse " << cdev_name << "'s CdevWeightForPID";
+                if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
+                                            false, false)) {
+                    LOG(ERROR) << "Failed to parse CdevWeightForPID";
+                    binded_cdev_info_map->clear();
+                    return false;
+                }
+            }
+            if (!values[j]["CdevCeiling"].empty()) {
+                LOG(INFO) << "Start to parse CdevCeiling: " << cdev_name;
+                if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false, false)) {
+                    LOG(ERROR) << "Failed to parse CdevCeiling";
+                    binded_cdev_info_map->clear();
+                    return false;
+                }
+            }
+
+            if (!values[j]["MaxReleaseStep"].empty()) {
+                max_release_step = getIntFromValue(values[j]["MaxReleaseStep"]);
+                if (max_release_step < 0) {
+                    LOG(ERROR) << cdev_name << " MaxReleaseStep: " << max_release_step;
+                    binded_cdev_info_map->clear();
+                    return false;
+                } else {
+                    LOG(INFO) << cdev_name << " MaxReleaseStep: " << max_release_step;
+                }
+            }
+            if (!values[j]["MaxThrottleStep"].empty()) {
+                max_throttle_step = getIntFromValue(values[j]["MaxThrottleStep"]);
+                if (max_throttle_step < 0) {
+                    LOG(ERROR) << cdev_name << " MaxThrottleStep: " << max_throttle_step;
+                    binded_cdev_info_map->clear();
+                    return false;
+                } else {
+                    LOG(INFO) << cdev_name << " MaxThrottleStep: " << max_throttle_step;
+                }
+            }
+        }
+        CdevArray limit_info;
+        limit_info.fill(0);
+        ThrottlingArray power_thresholds;
+        power_thresholds.fill(NAN);
+        ReleaseLogic release_logic = ReleaseLogic::NONE;
+
+        sub_values = values[j]["LimitInfo"];
+        if (sub_values.size()) {
+            LOG(INFO) << "Start to parse LimitInfo: " << cdev_name;
+            if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) {
+                LOG(ERROR) << "Failed to parse LimitInfo";
+                binded_cdev_info_map->clear();
+                return false;
+            }
+            *support_hard_limit = true;
+        }
+        // Parse linked power info
+        std::string power_rail;
+        bool high_power_check = false;
+        bool throttling_with_power_link = false;
+        CdevArray cdev_floor_with_power_link;
+        cdev_floor_with_power_link.fill(0);
+
+        const bool power_link_disabled =
+                android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
+        if (!power_link_disabled) {
+            power_rail = values[j]["BindedPowerRail"].asString();
+
+            if (values[j]["HighPowerCheck"].asBool()) {
+                high_power_check = true;
+            }
+            LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
+
+            if (values[j]["ThrottlingWithPowerLink"].asBool()) {
+                throttling_with_power_link = true;
+            }
+            LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
+                      << throttling_with_power_link;
+
+            sub_values = values[j]["CdevFloorWithPowerLink"];
+            if (sub_values.size()) {
+                LOG(INFO) << "Start to parse " << cdev_name << "'s CdevFloorWithPowerLink";
+                if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false, false)) {
+                    LOG(ERROR) << "Failed to parse CdevFloor";
+                    binded_cdev_info_map->clear();
+                    return false;
+                }
+            }
+            sub_values = values[j]["PowerThreshold"];
+            if (sub_values.size()) {
+                LOG(INFO) << "Start to parse " << cdev_name << "'s PowerThreshold";
+                if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
+                    LOG(ERROR) << "Failed to parse power thresholds";
+                    binded_cdev_info_map->clear();
+                    return false;
+                }
+                if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
+                    release_logic = ReleaseLogic::INCREASE;
+                    LOG(INFO) << "Release logic: INCREASE";
+                } else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
+                    release_logic = ReleaseLogic::DECREASE;
+                    LOG(INFO) << "Release logic: DECREASE";
+                } else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
+                    release_logic = ReleaseLogic::STEPWISE;
+                    LOG(INFO) << "Release logic: STEPWISE";
+                } else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
+                    release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
+                    LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
+                } else {
+                    LOG(ERROR) << "Release logic is invalid";
+                    binded_cdev_info_map->clear();
+                    return false;
+                }
+            }
+        }
+
+        (*binded_cdev_info_map)[cdev_name] = {
+                .limit_info = limit_info,
+                .power_thresholds = power_thresholds,
+                .release_logic = release_logic,
+                .high_power_check = high_power_check,
+                .throttling_with_power_link = throttling_with_power_link,
+                .cdev_weight_for_pid = cdev_weight_for_pid,
+                .cdev_ceiling = cdev_ceiling,
+                .max_release_step = max_release_step,
+                .max_throttle_step = max_throttle_step,
+                .cdev_floor_with_power_link = cdev_floor_with_power_link,
+                .power_rail = power_rail,
+        };
+    }
+    return true;
+}
+
 bool ParseSensorInfo(std::string_view config_path,
                      std::unordered_map<std::string, SensorInfo> *sensors_parsed) {
     std::string json_doc;
@@ -186,10 +331,10 @@
         }
 
         bool send_cb = false;
-        if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
-            LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
-        } else if (sensors[i]["Monitor"].asBool()) {
-            send_cb = true;
+        if (!sensors[i]["Monitor"].empty() && sensors[i]["Monitor"].isBool()) {
+            send_cb = sensors[i]["Monitor"].asBool();
+        } else if (!sensors[i]["SendCallback"].empty() && sensors[i]["SendCallback"].isBool()) {
+            send_cb = sensors[i]["SendCallback"].asBool();
         }
         LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb
                   << std::noboolalpha;
@@ -223,10 +368,10 @@
         std::vector<std::string> linked_sensors;
         std::vector<float> coefficients;
         float offset = 0;
-        std::string trigger_sensor;
-
+        std::vector<std::string> trigger_sensors;
         FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
         bool is_virtual_sensor = false;
+
         if (sensors[i]["VirtualSensor"].empty() || !sensors[i]["VirtualSensor"].isBool()) {
             LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor, set to 'false'";
         } else {
@@ -394,19 +539,40 @@
                 sensors_parsed->clear();
                 return false;
             }
-
             if (linked_sensors.size() != coefficients.size()) {
                 LOG(ERROR) << "Sensor[" << name
                            << "]'s combination size is not matched with coefficient size";
                 sensors_parsed->clear();
                 return false;
             }
-
             if (!sensors[i]["Offset"].empty()) {
                 offset = sensors[i]["Offset"].asFloat();
             }
 
-            trigger_sensor = sensors[i]["TriggerSensor"].asString();
+            values = sensors[i]["TriggerSensor"];
+            if (!values.empty()) {
+                if (values.isString()) {
+                    trigger_sensors.emplace_back(values.asString());
+                    LOG(INFO) << "Sensor[" << name << "]'s TriggerSensor: " << values.asString();
+                } else if (values.size()) {
+                    trigger_sensors.reserve(values.size());
+                    for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+                        if (!values[j].isString()) {
+                            LOG(ERROR) << name << " TriggerSensor should be an array of string";
+                            sensors_parsed->clear();
+                            return false;
+                        }
+                        trigger_sensors.emplace_back(values[j].asString());
+                        LOG(INFO) << "Sensor[" << name << "]'s TriggerSensor[" << j
+                                  << "]: " << trigger_sensors[j];
+                    }
+                } else {
+                    LOG(ERROR) << "Sensor[" << name << "]'s TriggerSensor should be a string";
+                    sensors_parsed->clear();
+                    return false;
+                }
+            }
+
             if (sensors[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
                 formula = FormulaOption::COUNT_THRESHOLD;
             } else if (sensors[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
@@ -543,16 +709,14 @@
                 sensors_parsed->clear();
                 return false;
             }
-            LOG(INFO) << "Start to parse"
-                      << " Sensor[" << name << "]'s S_Power";
+            LOG(INFO) << "Start to parse Sensor[" << name << "]'s S_Power";
             if (sensors[i]["PIDInfo"]["S_Power"].empty() ||
                 !getFloatFromJsonValues(sensors[i]["PIDInfo"]["S_Power"], &s_power, false, true)) {
                 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power";
                 sensors_parsed->clear();
                 return false;
             }
-            LOG(INFO) << "Start to parse"
-                      << " Sensor[" << name << "]'s I_Cutoff";
+            LOG(INFO) << "Start to parse Sensor[" << name << "]'s I_Cutoff";
             if (sensors[i]["PIDInfo"]["I_Cutoff"].empty() ||
                 !getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Cutoff"], &i_cutoff, false,
                                         false)) {
@@ -560,13 +724,9 @@
                 sensors_parsed->clear();
                 return false;
             }
-            LOG(INFO) << "Start to parse"
-                      << " Sensor[" << name << "]'s I_Default";
             i_default = getFloatFromValue(sensors[i]["PIDInfo"]["I_Default"]);
             LOG(INFO) << "Sensor[" << name << "]'s I_Default: " << i_default;
 
-            LOG(INFO) << "Start to parse"
-                      << " Sensor[" << name << "]'s TranCycle";
             tran_cycle = getFloatFromValue(sensors[i]["PIDInfo"]["TranCycle"]);
             LOG(INFO) << "Sensor[" << name << "]'s TranCycle: " << tran_cycle;
 
@@ -598,155 +758,11 @@
         bool support_hard_limit = false;
         std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
         values = sensors[i]["BindedCdevInfo"];
-        for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
-            Json::Value sub_values;
-            const std::string &cdev_name = values[j]["CdevRequest"].asString();
-            ThrottlingArray cdev_weight_for_pid;
-            cdev_weight_for_pid.fill(NAN);
-            CdevArray cdev_ceiling;
-            cdev_ceiling.fill(std::numeric_limits<int>::max());
-            int max_release_step = std::numeric_limits<int>::max();
-            int max_throttle_step = std::numeric_limits<int>::max();
-            if (support_pid) {
-                if (!values[j]["CdevWeightForPID"].empty()) {
-                    LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name
-                              << "'s CdevWeightForPID";
-                    if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
-                                                false, false)) {
-                        LOG(ERROR) << "Failed to parse CdevWeightForPID";
-                        sensors_parsed->clear();
-                        return false;
-                    }
-                }
-                if (!values[j]["CdevCeiling"].empty()) {
-                    LOG(INFO) << "Sensor[" << name
-                              << "]: Start to parse CdevCeiling: " << cdev_name;
-                    if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false,
-                                              false)) {
-                        LOG(ERROR) << "Failed to parse CdevCeiling";
-                        sensors_parsed->clear();
-                        return false;
-                    }
-                }
-                if (!values[j]["MaxReleaseStep"].empty()) {
-                    max_release_step = getIntFromValue(values[j]["MaxReleaseStep"]);
-                    if (max_release_step < 0) {
-                        LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name
-                                   << " MaxReleaseStep: " << max_release_step;
-                        sensors_parsed->clear();
-                        return false;
-                    } else {
-                        LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name
-                                  << " MaxReleaseStep: " << max_release_step;
-                    }
-                }
-                if (!values[j]["MaxThrottleStep"].empty()) {
-                    max_throttle_step = getIntFromValue(values[j]["MaxThrottleStep"]);
-                    if (max_throttle_step < 0) {
-                        LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name
-                                   << " MaxThrottleStep: " << max_throttle_step;
-                        sensors_parsed->clear();
-                        return false;
-                    } else {
-                        LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name
-                                  << " MaxThrottleStep: " << max_throttle_step;
-                    }
-                }
-            }
-            CdevArray limit_info;
-            limit_info.fill(0);
-            ThrottlingArray power_thresholds;
-            power_thresholds.fill(NAN);
-
-            ReleaseLogic release_logic = ReleaseLogic::NONE;
-
-            sub_values = values[j]["LimitInfo"];
-            if (sub_values.size()) {
-                LOG(INFO) << "Sensor[" << name << "]: Start to parse LimitInfo: " << cdev_name;
-                if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) {
-                    LOG(ERROR) << "Failed to parse LimitInfo";
-                    sensors_parsed->clear();
-                    return false;
-                }
-                support_hard_limit = true;
-            }
-
-            // Parse linked power info
-            std::string power_rail;
-            bool high_power_check = false;
-            bool throttling_with_power_link = false;
-            CdevArray cdev_floor_with_power_link;
-            cdev_floor_with_power_link.fill(0);
-
-            const bool power_link_disabled =
-                    android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
-            if (!power_link_disabled) {
-                power_rail = values[j]["BindedPowerRail"].asString();
-
-                if (values[j]["HighPowerCheck"].asBool()) {
-                    high_power_check = true;
-                }
-                LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
-
-                if (values[j]["ThrottlingWithPowerLink"].asBool()) {
-                    throttling_with_power_link = true;
-                }
-                LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
-                          << throttling_with_power_link;
-
-                sub_values = values[j]["CdevFloorWithPowerLink"];
-                if (sub_values.size()) {
-                    LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
-                              << "'s CdevFloorWithPowerLink";
-                    if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false,
-                                              false)) {
-                        LOG(ERROR) << "Failed to parse CdevFloor";
-                        sensors_parsed->clear();
-                        return false;
-                    }
-                }
-                sub_values = values[j]["PowerThreshold"];
-                if (sub_values.size()) {
-                    LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
-                              << "'s PowerThreshold";
-                    if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
-                        LOG(ERROR) << "Failed to parse power thresholds";
-                        sensors_parsed->clear();
-                        return false;
-                    }
-                    if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
-                        release_logic = ReleaseLogic::INCREASE;
-                        LOG(INFO) << "Release logic: INCREASE";
-                    } else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
-                        release_logic = ReleaseLogic::DECREASE;
-                        LOG(INFO) << "Release logic: DECREASE";
-                    } else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
-                        release_logic = ReleaseLogic::STEPWISE;
-                        LOG(INFO) << "Release logic: STEPWISE";
-                    } else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
-                        release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
-                        LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
-                    } else {
-                        LOG(ERROR) << "Release logic is invalid";
-                        sensors_parsed->clear();
-                        return false;
-                    }
-                }
-            }
-
-            binded_cdev_info_map[cdev_name] = {
-                    .limit_info = limit_info,
-                    .power_thresholds = power_thresholds,
-                    .release_logic = release_logic,
-                    .high_power_check = high_power_check,
-                    .throttling_with_power_link = throttling_with_power_link,
-                    .cdev_weight_for_pid = cdev_weight_for_pid,
-                    .cdev_ceiling = cdev_ceiling,
-                    .max_release_step = max_release_step,
-                    .max_throttle_step = max_throttle_step,
-                    .cdev_floor_with_power_link = cdev_floor_with_power_link,
-                    .power_rail = power_rail,
-            };
+        if (!ParseBindedCdevInfo(sensors[i]["BindedCdevInfo"], &binded_cdev_info_map, support_pid,
+                                 &support_hard_limit)) {
+            LOG(ERROR) << "Sensor[" << name << "]: failed to parse BindedCdevInfo";
+            sensors_parsed->clear();
+            return false;
         }
 
         std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map;
@@ -786,7 +802,7 @@
         std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
         if (is_virtual_sensor) {
             virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset,
-                                                            trigger_sensor, formula});
+                                                            trigger_sensors, formula});
         }
 
         std::shared_ptr<ThrottlingInfo> throttling_info(new ThrottlingInfo{
diff --git a/thermal/utils/thermal_info.h b/thermal/utils/thermal_info.h
index e56b358..6c7bc25 100644
--- a/thermal/utils/thermal_info.h
+++ b/thermal/utils/thermal_info.h
@@ -49,7 +49,7 @@
     std::vector<std::string> linked_sensors;
     std::vector<float> coefficients;
     float offset;
-    std::string trigger_sensor;
+    std::vector<std::string> trigger_sensors;
     FormulaOption formula;
 };
 
diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp
index 6f2d120..fcaee8f 100644
--- a/thermal/utils/thermal_throttling.cpp
+++ b/thermal/utils/thermal_throttling.cpp
@@ -346,18 +346,21 @@
                     allocated_power += last_updated_avg_power;
                     allocated_weight += cdev_weight;
                     allocated_cdev.insert(binded_cdev_info_pair.first);
-                    log_buf.append(StringPrintf("(%s: %0.2f mW)",
-                                                binded_cdev_info_pair.second.power_rail.c_str(),
-                                                last_updated_avg_power));
-
+                    if (!binded_cdev_info_pair.second.power_rail.empty()) {
+                        log_buf.append(StringPrintf("(%s: %0.2f mW)",
+                                                    binded_cdev_info_pair.second.power_rail.c_str(),
+                                                    last_updated_avg_power));
+                    }
                     LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first
                                  << " has been already at min state 0";
                 }
             } else {
                 const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first);
-                log_buf.append(StringPrintf("(%s: %0.2f mW)",
-                                            binded_cdev_info_pair.second.power_rail.c_str(),
-                                            last_updated_avg_power));
+                if (!binded_cdev_info_pair.second.power_rail.empty()) {
+                    log_buf.append(StringPrintf("(%s: %0.2f mW)",
+                                                binded_cdev_info_pair.second.power_rail.c_str(),
+                                                last_updated_avg_power));
+                }
                 // Ignore the power distribution if the CDEV has no space to reduce power
                 if ((cdev_power_adjustment < 0 &&
                      thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
diff --git a/vibrator/cs40l25/Vibrator.cpp b/vibrator/cs40l25/Vibrator.cpp
index 669258f..ba4efad 100644
--- a/vibrator/cs40l25/Vibrator.cpp
+++ b/vibrator/cs40l25/Vibrator.cpp
@@ -112,7 +112,7 @@
 static constexpr float PWLE_BW_MAP_SIZE =
     1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
 static constexpr float RAMP_DOWN_CONSTANT = 1048.576f;
-static constexpr float RAMP_DOWN_TIME_MS = 50.0f;
+static constexpr float RAMP_DOWN_TIME_MS = 0.0f;
 
 static struct pcm_config haptic_nohost_config = {
     .channels = 1,
@@ -1276,12 +1276,18 @@
     // where Trd is the desired ramp down time in seconds
     // pwle_ramp_down accepts only 24 bit integers values
 
-    const float seconds = RAMP_DOWN_TIME_MS / 1000;
-    const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds);
-
-    if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) {
-        ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno,
-              strerror(errno));
+    if (RAMP_DOWN_TIME_MS != 0.0) {
+        const float seconds = RAMP_DOWN_TIME_MS / 1000;
+        const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds);
+        if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) {
+            ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno,
+                  strerror(errno));
+        }
+    } else {
+        // Turn off the low level PWLE Ramp Down feature
+        if (!mHwApi->setPwleRampDown(0)) {
+            ALOGE("Failed to write 0 to pwle_ramp_down (%d): %s", errno, strerror(errno));
+        }
     }
 }
 
diff --git a/vibrator/cs40l26/Android.bp b/vibrator/cs40l26/Android.bp
index 2399a94..53764de 100644
--- a/vibrator/cs40l26/Android.bp
+++ b/vibrator/cs40l26/Android.bp
@@ -34,6 +34,13 @@
         "PixelVibratorBinaryDefaults",
         "android.hardware.vibrator-defaults.cs40l26",
     ],
+    include_dirs: [
+        "external/tinyalsa/include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libtinyalsa",
+    ],
 }
 
 cc_defaults {
@@ -42,23 +49,16 @@
         "PixelVibratorTestDefaults",
         "android.hardware.vibrator-defaults.cs40l26",
     ],
-    shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
-    include_dirs: [
-        "external/tinyalsa/include",
+    static_libs: [
+        "android.hardware.vibrator-impl.cs40l26",
+        "libtinyalsa",
     ],
 }
 
-cc_library_shared {
+cc_library {
     name: "android.hardware.vibrator-impl.cs40l26",
     defaults: ["VibratorHalCs40l26BinaryDefaults"],
     srcs: ["Vibrator.cpp"],
-    include_dirs: [
-        "external/tinyalsa/include",
-    ],
-    shared_libs: [
-        "libcutils",
-        "libtinyalsa",
-    ],
     export_include_dirs: ["."],
     vendor_available: true,
     visibility: [":__subpackages__"],
@@ -70,14 +70,7 @@
     init_rc: ["android.hardware.vibrator-service.cs40l26.rc"],
     vintf_fragments: ["android.hardware.vibrator-service.cs40l26.xml"],
     srcs: ["service.cpp"],
-    include_dirs: [
-        "external/tinyalsa/include"
-    ],
-    shared_libs: [
-        "android.hardware.vibrator-impl.cs40l26",
-        "libcutils",
-        "libtinyalsa",
-    ],
+    shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
     proprietary: true,
 }
 
@@ -87,14 +80,7 @@
     init_rc: ["android.hardware.vibrator-service.cs40l26-dual.rc"],
     vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual.xml"],
     srcs: ["service.cpp"],
-    include_dirs: [
-        "external/tinyalsa/include"
-    ],
-    shared_libs: [
-        "android.hardware.vibrator-impl.cs40l26",
-        "libcutils",
-        "libtinyalsa",
-    ],
+    shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
     cflags: ["-DVIBRATOR_NAME=\"dual\""],
     proprietary: true,
 }
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index f0d7038..ae052ba 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -15,9 +15,50 @@
  */
 #pragma once
 
+#include <algorithm>
+
 #include "HardwareBase.h"
 #include "Vibrator.h"
 
+#define PROC_SND_PCM "/proc/asound/pcm"
+#define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback"
+
+static struct pcm_config haptic_nohost_config = {
+        .channels = 1,
+        .rate = 48000,
+        .period_size = 80,
+        .period_count = 2,
+        .format = PCM_FORMAT_S16_LE,
+};
+
+enum WaveformIndex : uint16_t {
+    /* Physical waveform */
+    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+    WAVEFORM_RESERVED_INDEX_1 = 1,
+    WAVEFORM_CLICK_INDEX = 2,
+    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+    WAVEFORM_THUD_INDEX = 4,
+    WAVEFORM_SPIN_INDEX = 5,
+    WAVEFORM_QUICK_RISE_INDEX = 6,
+    WAVEFORM_SLOW_RISE_INDEX = 7,
+    WAVEFORM_QUICK_FALL_INDEX = 8,
+    WAVEFORM_LIGHT_TICK_INDEX = 9,
+    WAVEFORM_LOW_TICK_INDEX = 10,
+    WAVEFORM_RESERVED_MFG_1,
+    WAVEFORM_RESERVED_MFG_2,
+    WAVEFORM_RESERVED_MFG_3,
+    WAVEFORM_MAX_PHYSICAL_INDEX,
+    /* OWT waveform */
+    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+    WAVEFORM_PWLE,
+    /*
+     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+     * #define FF_GAIN          0x60  // 96 in decimal
+     * #define FF_MAX_EFFECTS   FF_GAIN
+     */
+    WAVEFORM_MAX_INDEX,
+};
+
 namespace aidl {
 namespace android {
 namespace hardware {
@@ -51,6 +92,165 @@
     bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
     bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
     bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); }
+    // TODO(b/234338136): Need to add the force feedback HW API test cases
+    bool setFFGain(int fd, uint16_t value) override {
+        struct input_event gain = {
+                .type = EV_FF,
+                .code = FF_GAIN,
+                .value = value,
+        };
+        if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
+            return false;
+        }
+        return true;
+    }
+    bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override {
+        if (((*effect).replay.length != timeoutMs) || (ioctl(fd, EVIOCSFF, effect) < 0)) {
+            ALOGE("setFFEffect fail");
+            return false;
+        } else {
+            return true;
+        }
+    }
+    bool setFFPlay(int fd, int8_t index, bool value) override {
+        struct input_event play = {
+                .type = EV_FF,
+                .code = static_cast<uint16_t>(index),
+                .value = value,
+        };
+        if (write(fd, (const void *)&play, sizeof(play)) != sizeof(play)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+    bool getHapticAlsaDevice(int *card, int *device) override {
+        std::string line;
+        std::ifstream myfile(PROC_SND_PCM);
+        if (myfile.is_open()) {
+            while (getline(myfile, line)) {
+                if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) {
+                    std::stringstream ss(line);
+                    std::string currentToken;
+                    std::getline(ss, currentToken, ':');
+                    sscanf(currentToken.c_str(), "%d-%d", card, device);
+                    return true;
+                }
+            }
+            myfile.close();
+        } else {
+            ALOGE("Failed to read file: %s", PROC_SND_PCM);
+        }
+        return false;
+    }
+    bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) override {
+        int ret = 0;
+
+        if (enable) {
+            *haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config);
+            if (!pcm_is_ready(*haptic_pcm)) {
+                ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            ret = pcm_prepare(*haptic_pcm);
+            if (ret < 0) {
+                ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            ret = pcm_start(*haptic_pcm);
+            if (ret < 0) {
+                ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            return true;
+        } else {
+            if (*haptic_pcm) {
+                pcm_close(*haptic_pcm);
+                *haptic_pcm = NULL;
+            }
+            return true;
+        }
+
+    fail:
+        pcm_close(*haptic_pcm);
+        *haptic_pcm = NULL;
+        return false;
+    }
+    bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
+                         uint32_t *outEffectIndex, int *status) override {
+        (*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
+        delete[] ((*effect).u.periodic.custom_data);
+        (*effect).u.periodic.custom_data = new int16_t[(*effect).u.periodic.custom_len]{0x0000};
+        if ((*effect).u.periodic.custom_data == nullptr) {
+            ALOGE("Failed to allocate memory for custom data\n");
+            *status = EX_NULL_POINTER;
+            return false;
+        }
+        memcpy((*effect).u.periodic.custom_data, owtData, numBytes);
+
+        if ((*effect).id != -1) {
+            ALOGE("(*effect).id != -1");
+        }
+
+        /* Create a new OWT waveform to update the PWLE or composite effect. */
+        (*effect).id = -1;
+        if (ioctl(fd, EVIOCSFF, effect) < 0) {
+            ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno));
+            delete[] ((*effect).u.periodic.custom_data);
+            *status = EX_ILLEGAL_STATE;
+            return false;
+        }
+
+        if ((*effect).id >= FF_MAX_EFFECTS || (*effect).id < 0) {
+            ALOGE("Invalid waveform index after upload OWT effect: %d", (*effect).id);
+            *status = EX_ILLEGAL_ARGUMENT;
+            return false;
+        }
+        *outEffectIndex = (*effect).id;
+        *status = 0;
+        return true;
+    }
+    bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) override {
+        uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0;
+
+        if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
+            ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
+            return false;
+        }
+
+        if (effectIndex < WAVEFORM_MAX_INDEX) {
+            /* Normal situation. Only erase the effect which we just played. */
+            if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
+                ALOGE("Failed to erase effect %d (%d): %s", effectIndex, errno, strerror(errno));
+            }
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
+                if ((*effect)[i].id == effectIndex) {
+                    (*effect)[i].id = -1;
+                    break;
+                }
+            }
+        } else {
+            /* Flush all non-prestored effects of ff-core and driver. */
+            getEffectCount(&effectCountBefore);
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < FF_MAX_EFFECTS; i++) {
+                if (ioctl(fd, EVIOCRMFF, i) >= 0) {
+                    successFlush++;
+                }
+            }
+            getEffectCount(&effectCountAfter);
+            ALOGW("Flushed effects: ff: %d; driver: %d -> %d; success: %d", effectIndex,
+                  effectCountBefore, effectCountAfter, successFlush);
+            /* Reset all OWT effect index of HAL. */
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
+                (*effect)[i].id = -1;
+            }
+        }
+        return true;
+    }
+
     void debug(int fd) override { HwApiBase::debug(fd); }
 
   private:
@@ -79,7 +279,7 @@
     static constexpr uint32_t VERSION_DEFAULT = 2;
     static constexpr int32_t DEFAULT_FREQUENCY_SHIFT = 0;
     static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
-    static constexpr std::array<uint32_t, 2> V_CTICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
     static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
 
   public:
@@ -109,7 +309,7 @@
         if (getPersist(CLICK_VOLTAGES_CONFIG, value)) {
             return true;
         }
-        *value = V_CTICK_DEFAULT;
+        *value = V_CLICK_DEFAULT;
         return true;
     }
     bool getLongVolLevels(std::array<uint32_t, 2> *value) override {
@@ -127,6 +327,16 @@
     bool getSupportedPrimitives(uint32_t *value) override {
         return getProperty("supported_primitives", value, (uint32_t)0);
     }
+    bool isF0CompEnabled() override {
+        bool value;
+        getProperty("f0.comp.enabled", &value, true);
+        return value;
+    }
+    bool isRedcCompEnabled() override {
+        bool value;
+        getProperty("redc.comp.enabled", &value, true);
+        return value;
+    }
     void debug(int fd) override { HwCalBase::debug(fd); }
 };
 
diff --git a/vibrator/cs40l26/TEST_MAPPING b/vibrator/cs40l26/TEST_MAPPING
new file mode 100644
index 0000000..1d8ff7a
--- /dev/null
+++ b/vibrator/cs40l26/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "VibratorHalCs40l26TestSuite",
+      "keywords": [
+        "nextgen"
+      ]
+    }
+  ]
+}
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index c6324c7..3af057c 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -33,9 +33,6 @@
 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
 #endif
 
-#define PROC_SND_PCM "/proc/asound/pcm"
-#define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback"
-
 namespace aidl {
 namespace android {
 namespace hardware {
@@ -92,14 +89,6 @@
 static constexpr float PWLE_BW_MAP_SIZE =
         1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
 
-static struct pcm_config haptic_nohost_config = {
-        .channels = 1,
-        .rate = 48000,
-        .period_size = 80,
-        .period_count = 2,
-        .format = PCM_FORMAT_S16_LE,
-};
-
 static uint16_t amplitudeToScale(float amplitude, float maximum) {
     float ratio = 100; /* Unit: % */
     if (maximum != 0)
@@ -111,11 +100,6 @@
     return std::round(ratio);
 }
 
-enum class AlwaysOnId : uint32_t {
-    GPIO_RISE,
-    GPIO_FALL,
-};
-
 enum WaveformBankID : uint8_t {
     RAM_WVFRM_BANK,
     ROM_WVFRM_BANK,
@@ -159,6 +143,8 @@
     VIBE_STATE_ASP,
 };
 
+std::mutex mActiveId_mutex;  // protects mActiveId
+
 static int min(int x, int y) {
     return x < y ? x : y;
 }
@@ -239,46 +225,52 @@
     std::string caldata{8, '0'};
     uint32_t calVer;
 
-    glob_t inputEventPaths;
-    int fd = -1;
-    int ret;
-    uint32_t val = 0;
-    char str[20] = {0x00};
     const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
-    for (uint8_t retry = 0; retry < 10; retry++) {
-        ret = glob("/dev/input/event*", 0, nullptr, &inputEventPaths);
-        if (ret) {
-            ALOGE("Fail to get input event paths (%d): %s", errno, strerror(errno));
-        } else {
-            for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
-                fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
-                if (fd > 0) {
-                    if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 && (val & (1 << EV_FF)) &&
-                        ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
-                        strcmp(str, inputEventName) == 0) {
-                        mInputFd.reset(fd);
-                        ALOGI("Control %s through %s", inputEventName, inputEventPaths.gl_pathv[i]);
-                        break;
+    const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
+    if ((strstr(inputEventName, "cs40l26") != nullptr) ||
+        (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
+        glob_t inputEventPaths;
+        int fd = -1;
+        int ret;
+        uint32_t val = 0;
+        char str[20] = {0x00};
+        for (uint8_t retry = 0; retry < 10; retry++) {
+            ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
+            if (ret) {
+                ALOGE("Fail to get input event paths (%d): %s", errno, strerror(errno));
+            } else {
+                for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
+                    fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
+                    if (fd > 0) {
+                        if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 &&
+                            (val & (1 << EV_FF)) && ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
+                            strstr(str, inputEventName) != nullptr) {
+                            mInputFd.reset(fd);
+                            ALOGI("Control %s through %s", inputEventName,
+                                  inputEventPaths.gl_pathv[i]);
+                            break;
+                        }
+                        close(fd);
                     }
-                    close(fd);
                 }
             }
+
+            if (ret == 0) {
+                globfree(&inputEventPaths);
+            }
+            if (mInputFd.ok()) {
+                break;
+            }
+
+            sleep(1);
+            ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
         }
 
-        if (ret == 0) {
-            globfree(&inputEventPaths);
+        if (!mInputFd.ok()) {
+            ALOGE("Fail to get an input event with name %s", inputEventName);
         }
-        if (mInputFd.ok()) {
-            break;
-        }
-
-        sleep(1);
-        ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
-    }
-
-    if (!mInputFd.ok()) {
-        ALOGE("Fail to get an input event with name %s", inputEventName);
-        return;
+    } else {
+        ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName);
     }
 
     mFfEffects.resize(WAVEFORM_MAX_INDEX);
@@ -299,9 +291,14 @@
                     .u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex},
                     .u.periodic.custom_len = FF_CUSTOM_DATA_LEN,
             };
-
-            if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[effectIndex]) < 0) {
-                ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
+            // Bypass the waveform update due to different input name
+            if ((strstr(inputEventName, "cs40l26") != nullptr) ||
+                (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
+                if (!mHwApi->setFFEffect(
+                            mInputFd, &mFfEffects[effectIndex],
+                            static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
+                    ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
+                }
             }
             if (mFfEffects[effectIndex].id != effectIndex) {
                 ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffects[effectIndex].id);
@@ -344,11 +341,11 @@
         mHwCal->getClickVolLevels(&mClickEffectVol);
         mHwCal->getLongVolLevels(&mLongEffectVol);
     } else {
-        ALOGW("Unsupported calibration version!");
+        ALOGD("Unsupported calibration version: %u!", calVer);
     }
 
-    mHwApi->setF0CompEnable(true);
-    mHwApi->setRedcCompEnable(true);
+    mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
+    mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
 
     mIsUnderExternalControl = false;
 
@@ -375,14 +372,15 @@
     ATRACE_NAME("Vibrator::getCapabilities");
 
     int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
-                  IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_ALWAYS_ON_CONTROL |
-                  IVibrator::CAP_GET_RESONANT_FREQUENCY | IVibrator::CAP_GET_Q_FACTOR;
+                  IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
+                  IVibrator::CAP_GET_Q_FACTOR;
     if (hasHapticAlsaDevice()) {
         ret |= IVibrator::CAP_EXTERNAL_CONTROL;
+    } else {
+        ALOGE("No haptics ALSA device");
     }
     if (mHwApi->hasOwtFreeSpace()) {
         ret |= IVibrator::CAP_COMPOSE_EFFECTS;
-
         if (mIsChirpEnabled) {
             ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
         }
@@ -393,20 +391,23 @@
 
 ndk::ScopedAStatus Vibrator::off() {
     ATRACE_NAME("Vibrator::off");
-    if (mActiveId < 0) {
-        return ndk::ScopedAStatus::ok();
-    }
+    bool ret{true};
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
 
-    struct input_event play = {
-            .type = EV_FF,
-            .code = static_cast<uint16_t>(mActiveId),
-            .value = 0,
-    };
+    if (mActiveId >= 0) {
+        /* Stop the active effect. */
+        if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) {
+            ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
+            ret = false;
+        }
 
-    /* Stop the active effect. */
-    if (write(mInputFd, (const void *)&play, sizeof(play)) != sizeof(play)) {
-        ALOGE("Failed to stop effect %d (%d): %s", play.code, errno, strerror(errno));
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
+            (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
+            ALOGE("Failed to clean up the composed effect %d", mActiveId);
+            ret = false;
+        }
+    } else {
+        ALOGV("Vibrator is already off");
     }
 
     mActiveId = -1;
@@ -415,11 +416,11 @@
         mHwApi->setF0Offset(0);
     }
 
-    if (play.code >= WAVEFORM_MAX_PHYSICAL_INDEX) {
-        eraseOwtEffect(play.code);
+    if (ret) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
-
-    return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
@@ -472,11 +473,14 @@
     ATRACE_NAME("Vibrator::setExternalControl");
     setGlobalAmplitude(enabled);
 
-    if (mHasHapticAlsaDevice) {
-        if (!enableHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
+    if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
+        if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
             ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
+    } else {
+        ALOGE("No haptics ALSA device");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
     mIsUnderExternalControl = enabled;
@@ -602,65 +606,69 @@
 ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem_chunk *ch,
                                 const std::shared_ptr<IVibratorCallback> &callback) {
     ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
-    struct input_event play = {
-            .type = EV_FF,
-            .value = 1,
-    };
 
     if (effectIndex >= FF_MAX_EFFECTS) {
         ALOGE("Invalid waveform index %d", effectIndex);
-        status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-        goto end;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) {
         ALOGE("Previous vibration pending: prev: %d, curr: %d", mActiveId, effectIndex);
-        status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        goto end;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
     if (ch) {
         /* Upload OWT effect. */
-        effectIndex = 0;
-        status = uploadOwtEffect(ch->head, dspmem_chunk_bytes(ch), &effectIndex);
+        if (ch->head == nullptr) {
+            ALOGE("Invalid OWT bank");
+            delete ch;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        bool isPwle = (*reinterpret_cast<uint16_t *>(ch->head) != 0x0000);
+        effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
+
+        uint32_t freeBytes;
+        mHwApi->getOwtFreeSpace(&freeBytes);
+        if (dspmem_chunk_bytes(ch) > freeBytes) {
+            ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
+                  freeBytes);
+            delete ch;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        int errorStatus;
+        if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
+                                     &mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
+            delete ch;
+            ALOGE("Invalid uploadOwtEffect");
+            return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
+        }
         delete ch;
 
-        if (!status.isOk()) {
-            goto end;
-        }
     } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
                effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
         /* Update duration for long/short vibration. */
         mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
-        if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[effectIndex]) < 0) {
+        if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex],
+                                 static_cast<uint16_t>(timeoutMs))) {
             ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
-            status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-            goto end;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
     }
 
-    play.code = effectIndex;
-    mActiveId = play.code;
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+    mActiveId = effectIndex;
     /* Play the event now. */
-    if (write(mInputFd, (const void *)&play, sizeof(play)) != sizeof(play)) {
-        ALOGE("Failed to play effect %d (%d): %s", play.code, errno, strerror(errno));
-        status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        goto end;
+    if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
+        ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
     mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
-
-end:
-    return status;
+    return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
     uint16_t scale = amplitudeToScale(amplitude, maximum);
-    struct input_event gain = {
-            .type = EV_FF,
-            .code = FF_GAIN,
-            .value = scale,
-    };
-    if (write(mInputFd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
+    if (!mHwApi->setFFGain(mInputFd, scale)) {
         ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
@@ -675,47 +683,15 @@
     return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
 }
 
-ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) {
-    *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK};
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
-    ndk::ScopedAStatus status;
-    uint32_t effectIndex;
-    uint32_t timeMs;
-    uint32_t volLevel;
-    uint16_t scale;
-    status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
-    if (!status.isOk()) {
-        return status;
-    }
-
-    scale = amplitudeToScale(volLevel, VOLTAGE_SCALE_MAX);
-
-    switch (static_cast<AlwaysOnId>(id)) {
-        case AlwaysOnId::GPIO_RISE:
-            // mHwApi->setGpioRiseIndex(effectIndex);
-            // mHwApi->setGpioRiseScale(scale);
-            return ndk::ScopedAStatus::ok();
-        case AlwaysOnId::GPIO_FALL:
-            // mHwApi->setGpioFallIndex(effectIndex);
-            // mHwApi->setGpioFallScale(scale);
-            return ndk::ScopedAStatus::ok();
-    }
-
+ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
-ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
-    switch (static_cast<AlwaysOnId>(id)) {
-        case AlwaysOnId::GPIO_RISE:
-            // mHwApi->setGpioRiseIndex(0);
-            return ndk::ScopedAStatus::ok();
-        case AlwaysOnId::GPIO_FALL:
-            // mHwApi->setGpioFallIndex(0);
-            return ndk::ScopedAStatus::ok();
-    }
 
+ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/,
+                                            EffectStrength /*strength*/) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
@@ -1082,79 +1058,23 @@
     return STATUS_OK;
 }
 
-bool Vibrator::findHapticAlsaDevice(int *card, int *device) {
-    std::string line;
-    std::ifstream myfile(PROC_SND_PCM);
-    if (myfile.is_open()) {
-        while (getline(myfile, line)) {
-            if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) {
-                std::stringstream ss(line);
-                std::string currentToken;
-                std::getline(ss, currentToken, ':');
-                sscanf(currentToken.c_str(), "%d-%d", card, device);
-                return true;
-            }
-        }
-        myfile.close();
-    } else {
-        ALOGE("Failed to read file: %s", PROC_SND_PCM);
-    }
-    return false;
-}
-
 bool Vibrator::hasHapticAlsaDevice() {
     // We need to call findHapticAlsaDevice once only. Calling in the
     // constructor is too early in the boot process and the pcm file contents
     // are empty. Hence we make the call here once only right before we need to.
-    static bool configHapticAlsaDeviceDone = false;
-    if (!configHapticAlsaDeviceDone) {
-        if (findHapticAlsaDevice(&mCard, &mDevice)) {
+    if (!mConfigHapticAlsaDeviceDone) {
+        if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) {
             mHasHapticAlsaDevice = true;
-            configHapticAlsaDeviceDone = true;
+            mConfigHapticAlsaDeviceDone = true;
         } else {
             ALOGE("Haptic ALSA device not supported");
         }
+    } else {
+        ALOGD("Haptic ALSA device configuration done.");
     }
     return mHasHapticAlsaDevice;
 }
 
-bool Vibrator::enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) {
-    int ret = 0;
-
-    if (enable) {
-        *haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config);
-        if (!pcm_is_ready(*haptic_pcm)) {
-            ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm));
-            goto fail;
-        }
-
-        ret = pcm_prepare(*haptic_pcm);
-        if (ret < 0) {
-            ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm));
-            goto fail;
-        }
-
-        ret = pcm_start(*haptic_pcm);
-        if (ret < 0) {
-            ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm));
-            goto fail;
-        }
-
-        return true;
-    } else {
-        if (*haptic_pcm) {
-            pcm_close(*haptic_pcm);
-            *haptic_pcm = NULL;
-        }
-        return true;
-    }
-
-fail:
-    pcm_close(*haptic_pcm);
-    *haptic_pcm = NULL;
-    return false;
-}
-
 ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength,
                                               uint32_t *outEffectIndex, uint32_t *outTimeMs,
                                               uint32_t *outVolLevel) {
@@ -1303,94 +1223,6 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Vibrator::uploadOwtEffect(uint8_t *owtData, uint32_t numBytes,
-                                             uint32_t *outEffectIndex) {
-    if (owtData == nullptr) {
-        ALOGE("Invalid OWT data");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    bool isPwle = (*reinterpret_cast<uint16_t *>(owtData) != 0x0000);
-    WaveformIndex targetId = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
-
-    uint32_t freeBytes;
-    mHwApi->getOwtFreeSpace(&freeBytes);
-    if (numBytes > freeBytes) {
-        ALOGE("Invalid OWT length: Effect %d: %d > %d!", targetId, numBytes, freeBytes);
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    mFfEffects[targetId].u.periodic.custom_len = numBytes / sizeof(uint16_t);
-    delete[](mFfEffects[targetId].u.periodic.custom_data);
-    mFfEffects[targetId].u.periodic.custom_data =
-            new int16_t[mFfEffects[targetId].u.periodic.custom_len]{0x0000};
-    if (mFfEffects[targetId].u.periodic.custom_data == nullptr) {
-        ALOGE("Failed to allocate memory for custom data\n");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
-    }
-    memcpy(mFfEffects[targetId].u.periodic.custom_data, owtData, numBytes);
-
-    if (mFfEffects[targetId].id != -1) {
-        ALOGE("mFfEffects[targetId].id != -1");
-    }
-
-    /* Create a new OWT waveform to update the PWLE or composite effect. */
-    mFfEffects[targetId].id = -1;
-    if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[targetId]) < 0) {
-        ALOGE("Failed to upload effect %d (%d): %s", targetId, errno, strerror(errno));
-        delete[](mFfEffects[targetId].u.periodic.custom_data);
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    if (mFfEffects[targetId].id >= FF_MAX_EFFECTS || mFfEffects[targetId].id < 0) {
-        ALOGE("Invalid waveform index after upload OWT effect: %d", mFfEffects[targetId].id);
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    *outEffectIndex = mFfEffects[targetId].id;
-
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus Vibrator::eraseOwtEffect(int8_t effectIndex) {
-    uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0;
-
-    if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
-        ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    if (effectIndex < WAVEFORM_MAX_INDEX) {
-        /* Normal situation. Only erase the effect which we just played. */
-        if (ioctl(mInputFd, EVIOCRMFF, effectIndex) < 0) {
-            ALOGE("Failed to erase effect %d (%d): %s", effectIndex, errno, strerror(errno));
-        }
-        for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
-            if (mFfEffects[i].id == effectIndex) {
-                mFfEffects[i].id = -1;
-                break;
-            }
-        }
-    } else {
-        /* Flush all non-prestored effects of ff-core and driver. */
-        mHwApi->getEffectCount(&effectCountBefore);
-        for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < FF_MAX_EFFECTS; i++) {
-            if (ioctl(mInputFd, EVIOCRMFF, i) >= 0) {
-                successFlush++;
-            }
-        }
-        mHwApi->getEffectCount(&effectCountAfter);
-        ALOGW("Flushed effects: ff: %d; driver: %d -> %d; success: %d", effectIndex,
-              effectCountBefore, effectCountAfter, successFlush);
-        /* Reset all OWT effect index of HAL. */
-        for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
-            mFfEffects[i].id = -1;
-        }
-    }
-
-    return ndk::ScopedAStatus::ok();
-}
-
 ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
                                            const std::shared_ptr<IVibratorCallback> &callback,
                                            int32_t *outTimeMs) {
@@ -1399,7 +1231,6 @@
     uint32_t timeMs = 0;
     uint32_t volLevel;
     dspmem_chunk *ch = nullptr;
-
     switch (effect) {
         case Effect::TEXTURE_TICK:
             // fall-through
@@ -1445,8 +1276,10 @@
     }
     mHwApi->pollVibeState(VIBE_STATE_STOPPED);
 
-    if (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) {
-        eraseOwtEffect(mActiveId);
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+    if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
+        (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
+        ALOGE("Failed to clean up the composed effect %d", mActiveId);
     }
     mActiveId = -1;
 
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
index 4647725..220c974 100644
--- a/vibrator/cs40l26/Vibrator.h
+++ b/vibrator/cs40l26/Vibrator.h
@@ -61,6 +61,25 @@
         virtual bool setRedcCompEnable(bool value) = 0;
         // Stores the minumun delay time between playback and stop effects.
         virtual bool setMinOnOffInterval(uint32_t value) = 0;
+        // Indicates the number of 0.125-dB steps of attenuation to apply to
+        // waveforms triggered in response to vibration calls from the
+        // Android vibrator HAL.
+        virtual bool setFFGain(int fd, uint16_t value) = 0;
+        // Create/modify custom effects for all physical waveforms.
+        virtual bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) = 0;
+        // Activates/deactivates the effect index after setFFGain() and setFFEffect().
+        virtual bool setFFPlay(int fd, int8_t index, bool value) = 0;
+        // Get the Alsa device for the audio coupled haptics effect
+        virtual bool getHapticAlsaDevice(int *card, int *device) = 0;
+        // Set haptics PCM amplifier before triggering audio haptics feature
+        virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
+                                     int device) = 0;
+        // Set OWT waveform for compose or compose PWLE request
+        virtual bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes,
+                                     struct ff_effect *effect, uint32_t *outEffectIndex,
+                                     int *status) = 0;
+        // Erase OWT waveform
+        virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
         // Emit diagnostic information to the given file.
         virtual void debug(int fd) = 0;
     };
@@ -91,6 +110,10 @@
         virtual bool isChirpEnabled() = 0;
         // Obtains the supported primitive effects.
         virtual bool getSupportedPrimitives(uint32_t *value) = 0;
+        // Checks if the f0 compensation feature needs to be enabled.
+        virtual bool isF0CompEnabled() = 0;
+        // Checks if the redc compensation feature needs to be enabled.
+        virtual bool isRedcCompEnabled() = 0;
         // Emit diagnostic information to the given file.
         virtual void debug(int fd) = 0;
     };
@@ -145,9 +168,6 @@
     ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength,
                                           uint32_t *outTimeMs, struct dspmem_chunk *outCh);
     ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
-    ndk::ScopedAStatus uploadOwtEffect(uint8_t *owtData, uint32_t num_bytes,
-                                       uint32_t *outEffectIndex);
-    ndk::ScopedAStatus eraseOwtEffect(int8_t effectIndex);
     ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
                                      const std::shared_ptr<IVibratorCallback> &callback,
                                      int32_t *outTimeMs);
@@ -176,12 +196,13 @@
     struct pcm *mHapticPcm;
     int mCard;
     int mDevice;
-    bool mHasHapticAlsaDevice;
+    bool mHasHapticAlsaDevice{false};
     bool mIsUnderExternalControl;
     float mLongEffectScale = 1.0;
     bool mIsChirpEnabled;
     uint32_t mSupportedPrimitivesBits = 0x0;
     std::vector<CompositePrimitive> mSupportedPrimitives;
+    bool mConfigHapticAlsaDeviceDone{false};
 };
 
 }  // namespace vibrator
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
index cd47c3a..84fd0e0 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
@@ -1,4 +1,4 @@
-on enable-thermal-hal
+on property:vendor.all.modules.ready=1
     wait /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_cal_time_ms
 
     mkdir /mnt/vendor/persist/haptics 0770 system system
@@ -26,6 +26,7 @@
     group system input
 
     setenv INPUT_EVENT_NAME cs40l26_dual_input
+    setenv INPUT_EVENT_PATH /dev/input/event*
     setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
     setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26_dual.cal
 
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
index c2b9e63..0fcca56 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
@@ -1,4 +1,4 @@
-on enable-thermal-hal
+on property:vendor.all.modules.ready=1
     wait /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_cal_time_ms
 
     mkdir /mnt/vendor/persist/haptics 0770 system system
@@ -26,6 +26,7 @@
     group system input
 
     setenv INPUT_EVENT_NAME cs40l26_input
+    setenv INPUT_EVENT_PATH /dev/input/event*
     setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
     setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
 
diff --git a/vibrator/cs40l26/tests/Android.bp b/vibrator/cs40l26/tests/Android.bp
new file mode 100644
index 0000000..93c9a9f
--- /dev/null
+++ b/vibrator/cs40l26/tests/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "VibratorHalCs40l26TestSuite",
+    defaults: ["VibratorHalCs40l26TestDefaults"],
+    srcs: [
+        "test-hwcal.cpp",
+	"test-hwapi.cpp",
+	"test-vibrator.cpp",
+    ],
+    static_libs: [
+        "libc++fs",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h
new file mode 100644
index 0000000..21466a0
--- /dev/null
+++ b/vibrator/cs40l26/tests/mocks.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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_HARDWARE_VIBRATOR_TEST_MOCKS_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+
+#include "Vibrator.h"
+
+class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
+  public:
+    MOCK_METHOD0(destructor, void());
+    MOCK_METHOD1(setF0, bool(std::string value));
+    MOCK_METHOD1(setF0Offset, bool(uint32_t value));
+    MOCK_METHOD1(setRedc, bool(std::string value));
+    MOCK_METHOD1(setQ, bool(std::string value));
+    MOCK_METHOD1(getEffectCount, bool(uint32_t *value));
+    MOCK_METHOD2(pollVibeState, bool(uint32_t value, int32_t timeoutMs));
+    MOCK_METHOD0(hasOwtFreeSpace, bool());
+    MOCK_METHOD1(getOwtFreeSpace, bool(uint32_t *value));
+    MOCK_METHOD1(setF0CompEnable, bool(bool value));
+    MOCK_METHOD1(setRedcCompEnable, bool(bool value));
+    MOCK_METHOD1(setMinOnOffInterval, bool(uint32_t value));
+    MOCK_METHOD2(setFFGain, bool(int fd, uint16_t value));
+    MOCK_METHOD3(setFFEffect, bool(int fd, struct ff_effect *effect, uint16_t timeoutMs));
+    MOCK_METHOD3(setFFPlay, bool(int fd, int8_t index, bool value));
+    MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
+    MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
+    MOCK_METHOD6(uploadOwtEffect,
+                 bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
+                      uint32_t *outEffectIndex, int *status));
+    MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
+    MOCK_METHOD1(debug, void(int fd));
+
+    ~MockApi() override { destructor(); };
+};
+
+class MockCal : public ::aidl::android::hardware::vibrator::Vibrator::HwCal {
+  public:
+    MOCK_METHOD0(destructor, void());
+    MOCK_METHOD1(getVersion, bool(uint32_t *value));
+    MOCK_METHOD1(getF0, bool(std::string &value));
+    MOCK_METHOD1(getRedc, bool(std::string &value));
+    MOCK_METHOD1(getQ, bool(std::string &value));
+    MOCK_METHOD1(getLongFrequencyShift, bool(int32_t *value));
+    MOCK_METHOD1(getTickVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD1(getClickVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD1(getLongVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD0(isChirpEnabled, bool());
+    MOCK_METHOD1(getSupportedPrimitives, bool(uint32_t *value));
+    MOCK_METHOD0(isF0CompEnabled, bool());
+    MOCK_METHOD0(isRedcCompEnabled, bool());
+    MOCK_METHOD1(debug, void(int fd));
+
+    ~MockCal() override { destructor(); };
+    // b/132668253: Workaround gMock Compilation Issue
+    bool getF0(std::string *value) { return getF0(*value); }
+    bool getRedc(std::string *value) { return getRedc(*value); }
+    bool getQ(std::string *value) { return getQ(*value); }
+};
+
+class MockVibratorCallback : public aidl::android::hardware::vibrator::BnVibratorCallback {
+  public:
+    MOCK_METHOD(ndk::ScopedAStatus, onComplete, ());
+};
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
diff --git a/vibrator/cs40l26/tests/test-hwapi.cpp b/vibrator/cs40l26/tests/test-hwapi.cpp
new file mode 100644
index 0000000..cc4d465
--- /dev/null
+++ b/vibrator/cs40l26/tests/test-hwapi.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 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 <android-base/file.h>
+#include <cutils/fs.h>
+#include <gtest/gtest.h>
+
+#include <cstdlib>
+#include <fstream>
+
+#include "Hardware.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::Test;
+using ::testing::TestParamInfo;
+using ::testing::ValuesIn;
+using ::testing::WithParamInterface;
+
+class HwApiTest : public Test {
+  private:
+    static constexpr const char *FILE_NAMES[]{
+            "calibration/f0_stored",
+            "default/f0_offset",
+            "calibration/redc_stored",
+            "calibration/q_stored",
+            "default/f0_comp_enable",
+            "default/redc_comp_enable",
+            "default/owt_free_space",
+            "default/num_waves",
+            "default/delay_before_stop_playback_us",
+    };
+
+  public:
+    void SetUp() override {
+        std::string prefix;
+        for (auto n : FILE_NAMES) {
+            auto name = std::filesystem::path(n);
+            auto path = std::filesystem::path(mFilesDir.path) / name;
+            fs_mkdirs(path.c_str(), S_IRWXU);
+            std::ofstream touch{path};
+            mFileMap[name] = path;
+        }
+        prefix = std::filesystem::path(mFilesDir.path) / "";
+        setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
+        mHwApi = std::make_unique<HwApi>();
+
+        for (auto n : FILE_NAMES) {
+            auto name = std::filesystem::path(n);
+            auto path = std::filesystem::path(mEmptyDir.path) / name;
+        }
+        prefix = std::filesystem::path(mEmptyDir.path) / "";
+        setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
+        mNoApi = std::make_unique<HwApi>();
+    }
+
+    void TearDown() override { verifyContents(); }
+
+    static auto ParamNameFixup(std::string str) {
+        std::replace(str.begin(), str.end(), '/', '_');
+        return str;
+    }
+
+  protected:
+    // Set expected file content for a test.
+    template <typename T>
+    void expectContent(const std::string &name, const T &value) {
+        mExpectedContent[name] << value << std::endl;
+    }
+
+    // Set actual file content for an input test.
+    template <typename T>
+    void updateContent(const std::string &name, const T &value) {
+        std::ofstream(mFileMap[name]) << value << std::endl;
+    }
+
+    template <typename T>
+    void expectAndUpdateContent(const std::string &name, const T &value) {
+        expectContent(name, value);
+        updateContent(name, value);
+    }
+
+    // Compare all file contents against expected contents.
+    void verifyContents() {
+        for (auto &a : mFileMap) {
+            std::ifstream file{a.second};
+            std::string expect = mExpectedContent[a.first].str();
+            std::string actual = std::string(std::istreambuf_iterator<char>(file),
+                                             std::istreambuf_iterator<char>());
+            EXPECT_EQ(expect, actual) << a.first;
+        }
+    }
+
+  protected:
+    std::unique_ptr<Vibrator::HwApi> mHwApi;
+    std::unique_ptr<Vibrator::HwApi> mNoApi;
+    std::map<std::string, std::string> mFileMap;
+    TemporaryDir mFilesDir;
+    TemporaryDir mEmptyDir;
+    std::map<std::string, std::stringstream> mExpectedContent;
+};
+
+template <typename T>
+class HwApiTypedTest : public HwApiTest,
+                       public WithParamInterface<std::tuple<std::string, std::function<T>>> {
+  public:
+    static auto PrintParam(const TestParamInfo<typename HwApiTypedTest::ParamType> &info) {
+        return ParamNameFixup(std::get<0>(info.param));
+    }
+    static auto MakeParam(std::string name, std::function<T> func) {
+        return std::make_tuple(name, func);
+    }
+};
+
+using HasTest = HwApiTypedTest<bool(Vibrator::HwApi &)>;
+
+TEST_P(HasTest, success_returnsTrue) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_TRUE(func(*mHwApi));
+}
+
+TEST_P(HasTest, success_returnsFalse) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_FALSE(func(*mNoApi));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, HasTest,
+                        ValuesIn({
+                                HasTest::MakeParam("default/owt_free_space",
+                                                   &Vibrator::HwApi::hasOwtFreeSpace),
+                        }),
+                        HasTest::PrintParam);
+
+using GetUint32Test = HwApiTypedTest<bool(Vibrator::HwApi &, uint32_t *)>;
+
+TEST_P(GetUint32Test, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    uint32_t expect = std::rand();
+    uint32_t actual = ~expect;
+
+    expectAndUpdateContent(name, expect);
+
+    EXPECT_TRUE(func(*mHwApi, &actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_P(GetUint32Test, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    uint32_t value;
+
+    EXPECT_FALSE(func(*mNoApi, &value));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, GetUint32Test,
+                        ValuesIn({
+                                GetUint32Test::MakeParam("default/num_waves",
+                                                         &Vibrator::HwApi::getEffectCount),
+                                GetUint32Test::MakeParam("default/owt_free_space",
+                                                         &Vibrator::HwApi::getOwtFreeSpace),
+                        }),
+                        GetUint32Test::PrintParam);
+
+using SetBoolTest = HwApiTypedTest<bool(Vibrator::HwApi &, bool)>;
+
+TEST_P(SetBoolTest, success_returnsTrue) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+
+    expectContent(name, "1");
+
+    EXPECT_TRUE(func(*mHwApi, true));
+}
+
+TEST_P(SetBoolTest, success_returnsFalse) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+
+    expectContent(name, "0");
+
+    EXPECT_TRUE(func(*mHwApi, false));
+}
+
+TEST_P(SetBoolTest, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_FALSE(func(*mNoApi, true));
+    EXPECT_FALSE(func(*mNoApi, false));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, SetBoolTest,
+                        ValuesIn({
+                                SetBoolTest::MakeParam("default/f0_comp_enable",
+                                                       &Vibrator::HwApi::setF0CompEnable),
+                                SetBoolTest::MakeParam("default/redc_comp_enable",
+                                                       &Vibrator::HwApi::setRedcCompEnable),
+                        }),
+                        SetBoolTest::PrintParam);
+
+using SetUint32Test = HwApiTypedTest<bool(Vibrator::HwApi &, uint32_t)>;
+
+TEST_P(SetUint32Test, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    uint32_t value = std::rand();
+
+    expectContent(name, value);
+
+    EXPECT_TRUE(func(*mHwApi, value));
+}
+
+TEST_P(SetUint32Test, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    uint32_t value = std::rand();
+
+    EXPECT_FALSE(func(*mNoApi, value));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, SetUint32Test,
+                        ValuesIn({
+                                SetUint32Test::MakeParam("default/f0_offset",
+                                                         &Vibrator::HwApi::setF0Offset),
+                                SetUint32Test::MakeParam("default/delay_before_stop_playback_us",
+                                                         &Vibrator::HwApi::setMinOnOffInterval),
+                        }),
+                        SetUint32Test::PrintParam);
+
+using SetStringTest = HwApiTypedTest<bool(Vibrator::HwApi &, std::string)>;
+
+TEST_P(SetStringTest, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    std::string value = TemporaryFile().path;
+
+    expectContent(name, value);
+
+    EXPECT_TRUE(func(*mHwApi, value));
+}
+
+TEST_P(SetStringTest, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    std::string value = TemporaryFile().path;
+
+    EXPECT_FALSE(func(*mNoApi, value));
+}
+
+INSTANTIATE_TEST_CASE_P(
+        HwApiTests, SetStringTest,
+        ValuesIn({
+                SetStringTest::MakeParam("calibration/f0_stored", &Vibrator::HwApi::setF0),
+                SetStringTest::MakeParam("calibration/redc_stored", &Vibrator::HwApi::setRedc),
+                SetStringTest::MakeParam("calibration/q_stored", &Vibrator::HwApi::setQ),
+        }),
+        SetStringTest::PrintParam);
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/cs40l26/tests/test-hwcal.cpp b/vibrator/cs40l26/tests/test-hwcal.cpp
new file mode 100644
index 0000000..e482b6c
--- /dev/null
+++ b/vibrator/cs40l26/tests/test-hwcal.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2022 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 <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <fstream>
+
+#include "Hardware.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::Test;
+
+class HwCalTest : public Test {
+  protected:
+    static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
+
+  public:
+    void SetUp() override { setenv("CALIBRATION_FILEPATH", mCalFile.path, true); }
+
+  private:
+    template <typename T>
+    static void pack(std::ostream &stream, const T &value, std::string lpad, std::string rpad) {
+        stream << lpad << value << rpad;
+    }
+
+    template <typename T, typename std::array<T, 0>::size_type N>
+    static void pack(std::ostream &stream, const std::array<T, N> &value, std::string lpad,
+                     std::string rpad) {
+        for (auto &entry : value) {
+            pack(stream, entry, lpad, rpad);
+        }
+    }
+
+  protected:
+    void createHwCal() { mHwCal = std::make_unique<HwCal>(); }
+
+    template <typename T>
+    void write(const std::string key, const T &value, std::string lpad = " ",
+               std::string rpad = "") {
+        std::ofstream calfile{mCalFile.path, std::ios_base::app};
+        calfile << key << ":";
+        pack(calfile, value, lpad, rpad);
+        calfile << std::endl;
+    }
+
+    void unlink() { ::unlink(mCalFile.path); }
+
+  protected:
+    std::unique_ptr<Vibrator::HwCal> mHwCal;
+    TemporaryFile mCalFile;
+};
+
+TEST_F(HwCalTest, f0_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("f0_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, f0_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getF0(&actual));
+}
+
+TEST_F(HwCalTest, redc_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("redc_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getRedc(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, redc_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getRedc(&actual));
+}
+
+TEST_F(HwCalTest, q_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("q_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getQ(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, q_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getQ(&actual));
+}
+
+TEST_F(HwCalTest, v_levels) {
+    std::array<uint32_t, 2> expect;
+    std::array<uint32_t, 2> actual;
+
+    // voltage for tick effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_tick", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    // voltage for click effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_click", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    // voltage for long effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_long", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_missing) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_short) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", std::array<uint32_t, expect.size() - 1>());
+    write("v_click", std::array<uint32_t, expect.size() - 1>());
+    write("v_long", std::array<uint32_t, expect.size() - 1>());
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_long) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", std::array<uint32_t, expect.size() + 1>());
+    write("v_click", std::array<uint32_t, expect.size() + 1>());
+    write("v_long", std::array<uint32_t, expect.size() + 1>());
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_nofile) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", actual);
+    write("v_click", actual);
+    write("v_long", actual);
+    unlink();
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, multiple) {
+    uint32_t randInput = std::rand();
+    std::string f0Expect = std::to_string(randInput);
+    std::string f0Actual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string redcExpect = std::to_string(randInput);
+    std::string redcActual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string qExpect = std::to_string(randInput);
+    std::string qActual = std::to_string(~randInput);
+    std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
+    std::array<uint32_t, 2> volActual;
+
+    std::transform(volTickExpect.begin(), volTickExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("f0_measured", f0Expect);
+    write("redc_measured", redcExpect);
+    write("q_measured", qExpect);
+    write("v_tick", volTickExpect);
+    std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
+                   [](uint32_t &e) {
+                       e = std::rand();
+                       return ~e;
+                   });
+    write("v_click", volClickExpect);
+    std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+    write("v_long", volLongExpect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&f0Actual));
+    EXPECT_EQ(f0Expect, f0Actual);
+    EXPECT_TRUE(mHwCal->getRedc(&redcActual));
+    EXPECT_EQ(redcExpect, redcActual);
+    EXPECT_TRUE(mHwCal->getQ(&qActual));
+    EXPECT_EQ(qExpect, qActual);
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&volActual));
+    EXPECT_EQ(volTickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
+    EXPECT_EQ(volClickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
+    EXPECT_EQ(volLongExpect, volActual);
+}
+
+TEST_F(HwCalTest, trimming) {
+    uint32_t randInput = std::rand();
+    std::string f0Expect = std::to_string(randInput);
+    std::string f0Actual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string redcExpect = std::to_string(randInput);
+    std::string redcActual = std::to_string(randInput);
+    randInput = std::rand();
+    std::string qExpect = std::to_string(randInput);
+    std::string qActual = std::to_string(randInput);
+    std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
+    std::array<uint32_t, 2> volActual;
+
+    std::transform(volTickExpect.begin(), volTickExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("f0_measured", f0Expect, " \t", "\t ");
+    write("redc_measured", redcExpect, " \t", "\t ");
+    write("q_measured", qExpect, " \t", "\t ");
+    write("v_tick", volTickExpect, " \t", "\t ");
+    std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
+                   [](uint32_t &e) {
+                       e = std::rand();
+                       return ~e;
+                   });
+    write("v_click", volClickExpect, " \t", "\t ");
+    std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+    write("v_long", volLongExpect, " \t", "\t ");
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&f0Actual));
+    EXPECT_EQ(f0Expect, f0Actual);
+    EXPECT_TRUE(mHwCal->getRedc(&redcActual));
+    EXPECT_EQ(redcExpect, redcActual);
+    EXPECT_TRUE(mHwCal->getQ(&qActual));
+    EXPECT_EQ(qExpect, qActual);
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&volActual));
+    EXPECT_EQ(volTickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
+    EXPECT_EQ(volClickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
+    EXPECT_EQ(volLongExpect, volActual);
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp
new file mode 100644
index 0000000..a8bedd5
--- /dev/null
+++ b/vibrator/cs40l26/tests/test-vibrator.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2022 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 <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+
+#include <future>
+
+#include "Vibrator.h"
+#include "mocks.h"
+#include "types.h"
+#include "utils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Assign;
+using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::Combine;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Exactly;
+using ::testing::Expectation;
+using ::testing::ExpectationSet;
+using ::testing::Ge;
+using ::testing::Mock;
+using ::testing::MockFunction;
+using ::testing::Range;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::SetArgPointee;
+using ::testing::SetArgReferee;
+using ::testing::Test;
+using ::testing::TestParamInfo;
+using ::testing::ValuesIn;
+using ::testing::WithParamInterface;
+
+// Forward Declarations
+
+static EffectQueue Queue(const QueueEffect &effect);
+static EffectQueue Queue(const QueueDelay &delay);
+template <typename T, typename U, typename... Args>
+static EffectQueue Queue(const T &first, const U &second, Args... rest);
+
+static EffectLevel Level(float intensity, float levelLow, float levelHigh);
+static EffectScale Scale(float intensity, float levelLow, float levelHigh);
+
+// Constants With Arbitrary Values
+
+static constexpr uint32_t CAL_VERSION = 2;
+static constexpr std::array<EffectLevel, 2> V_TICK_DEFAULT = {1, 100};
+static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
+static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
+static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
+        0, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000};
+
+// Constants With Prescribed Values
+
+static const std::map<Effect, EffectIndex> EFFECT_INDEX{
+        {Effect::CLICK, 2},
+        {Effect::TICK, 2},
+        {Effect::HEAVY_CLICK, 2},
+        {Effect::TEXTURE_TICK, 9},
+};
+static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;
+static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
+static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6;  // I2C Transaction + DSP Return-From-Standby
+static constexpr auto POLLING_TIMEOUT = 20;
+enum WaveformIndex : uint16_t {
+    /* Physical waveform */
+    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+    WAVEFORM_RESERVED_INDEX_1 = 1,
+    WAVEFORM_CLICK_INDEX = 2,
+    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+    WAVEFORM_THUD_INDEX = 4,
+    WAVEFORM_SPIN_INDEX = 5,
+    WAVEFORM_QUICK_RISE_INDEX = 6,
+    WAVEFORM_SLOW_RISE_INDEX = 7,
+    WAVEFORM_QUICK_FALL_INDEX = 8,
+    WAVEFORM_LIGHT_TICK_INDEX = 9,
+    WAVEFORM_LOW_TICK_INDEX = 10,
+    WAVEFORM_RESERVED_MFG_1,
+    WAVEFORM_RESERVED_MFG_2,
+    WAVEFORM_RESERVED_MFG_3,
+    WAVEFORM_MAX_PHYSICAL_INDEX,
+    /* OWT waveform */
+    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+    WAVEFORM_PWLE,
+    /*
+     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+     * #define FF_GAIN          0x60  // 96 in decimal
+     * #define FF_MAX_EFFECTS   FF_GAIN
+     */
+    WAVEFORM_MAX_INDEX,
+};
+
+static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LONG_DEFAULT[1])};
+static const EffectIndex ON_EFFECT_INDEX{0};
+
+static const std::map<EffectTuple, EffectScale> EFFECT_SCALE{
+        {{Effect::TICK, EffectStrength::LIGHT},
+         Scale(0.5f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TICK, EffectStrength::MEDIUM},
+         Scale(0.5f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TICK, EffectStrength::STRONG},
+         Scale(0.5f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::LIGHT},
+         Scale(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::MEDIUM},
+         Scale(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::STRONG},
+         Scale(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::LIGHT},
+         Scale(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::MEDIUM},
+         Scale(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::STRONG},
+         Scale(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::LIGHT},
+         Scale(0.5f * 0.5f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::MEDIUM},
+         Scale(0.5f * 0.7f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::STRONG},
+         Scale(0.5f * 1.0f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+};
+
+static const std::map<EffectTuple, EffectQueue> EFFECT_QUEUE{
+        {{Effect::DOUBLE_CLICK, EffectStrength::LIGHT},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+        {{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+        {{Effect::DOUBLE_CLICK, EffectStrength::STRONG},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+};
+
+EffectQueue Queue(const QueueEffect &effect) {
+    auto index = std::get<0>(effect);
+    auto level = std::get<1>(effect);
+    auto string = std::to_string(index) + "." + std::to_string(level);
+    auto duration = EFFECT_DURATIONS[index];
+    return {string, duration};
+}
+
+EffectQueue Queue(const QueueDelay &delay) {
+    auto string = std::to_string(delay);
+    return {string, delay};
+}
+
+template <typename T, typename U, typename... Args>
+EffectQueue Queue(const T &first, const U &second, Args... rest) {
+    auto head = Queue(first);
+    auto tail = Queue(second, rest...);
+    auto string = std::get<0>(head) + "," + std::get<0>(tail);
+    auto duration = std::get<1>(head) + std::get<1>(tail);
+    return {string, duration};
+}
+
+static EffectLevel Level(float intensity, float levelLow, float levelHigh) {
+    return std::lround(intensity * (levelHigh - levelLow)) + levelLow;
+}
+
+static EffectScale Scale(float intensity, float levelLow, float levelHigh) {
+    return levelToScale(Level(intensity, levelLow, levelHigh));
+}
+
+class VibratorTest : public Test {
+  public:
+    void SetUp() override {
+        setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
+        std::unique_ptr<MockApi> mockapi;
+        std::unique_ptr<MockCal> mockcal;
+
+        createMock(&mockapi, &mockcal);
+        createVibrator(std::move(mockapi), std::move(mockcal));
+    }
+
+    void TearDown() override { deleteVibrator(); }
+
+  protected:
+    void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal) {
+        *mockapi = std::make_unique<MockApi>();
+        *mockcal = std::make_unique<MockCal>();
+
+        mMockApi = mockapi->get();
+        mMockCal = mockcal->get();
+
+        ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
+
+        ON_CALL(*mMockApi, setFFGain(_, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, setFFEffect(_, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, setFFPlay(_, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, pollVibeState(_, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, eraseOwtEffect(_, _, _)).WillByDefault(Return(true));
+
+        ON_CALL(*mMockApi, getOwtFreeSpace(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(11504), Return(true)));
+
+        ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr));
+
+        ON_CALL(*mMockCal, getVersion(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(CAL_VERSION), Return(true)));
+
+        ON_CALL(*mMockCal, getTickVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_TICK_DEFAULT), Return(true)));
+        ON_CALL(*mMockCal, getClickVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_CLICK_DEFAULT), Return(true)));
+        ON_CALL(*mMockCal, getLongVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
+
+        relaxMock(false);
+    }
+
+    void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
+                        bool relaxed = true) {
+        if (relaxed) {
+            relaxMock(true);
+        }
+        mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal));
+        if (relaxed) {
+            relaxMock(false);
+        }
+    }
+
+    void deleteVibrator(bool relaxed = true) {
+        if (relaxed) {
+            relaxMock(true);
+        }
+        mVibrator.reset();
+    }
+
+  private:
+    void relaxMock(bool relax) {
+        auto times = relax ? AnyNumber() : Exactly(0);
+
+        Mock::VerifyAndClearExpectations(mMockApi);
+        Mock::VerifyAndClearExpectations(mMockCal);
+
+        EXPECT_CALL(*mMockApi, destructor()).Times(times);
+        EXPECT_CALL(*mMockApi, setF0(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setF0Offset(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setRedc(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setQ(_)).Times(times);
+        EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).Times(times);
+        EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setF0CompEnable(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setRedcCompEnable(_)).Times(times);
+        EXPECT_CALL(*mMockApi, pollVibeState(_, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFGain(_, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
+        EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).Times(times);
+
+        EXPECT_CALL(*mMockApi, debug(_)).Times(times);
+
+        EXPECT_CALL(*mMockCal, destructor()).Times(times);
+        EXPECT_CALL(*mMockCal, getF0(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getRedc(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getQ(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getTickVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getClickVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getLongVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, isChirpEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times);
+        EXPECT_CALL(*mMockCal, isF0CompEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, isRedcCompEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, debug(_)).Times(times);
+    }
+
+  protected:
+    MockApi *mMockApi;
+    MockCal *mMockCal;
+    std::shared_ptr<IVibrator> mVibrator;
+    uint32_t mEffectIndex;
+};
+
+TEST_F(VibratorTest, Constructor) {
+    std::unique_ptr<MockApi> mockapi;
+    std::unique_ptr<MockCal> mockcal;
+    std::string f0Val = std::to_string(std::rand());
+    std::string redcVal = std::to_string(std::rand());
+    std::string qVal = std::to_string(std::rand());
+    uint32_t calVer;
+    uint32_t supportedPrimitivesBits = 0x0;
+    Expectation volGet;
+    Sequence f0Seq, redcSeq, qSeq, supportedPrimitivesSeq;
+
+    EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
+
+    deleteVibrator(false);
+
+    createMock(&mockapi, &mockcal);
+
+    EXPECT_CALL(*mMockCal, getF0(_))
+            .InSequence(f0Seq)
+            .WillOnce(DoAll(SetArgReferee<0>(f0Val), Return(true)));
+    EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getRedc(_))
+            .InSequence(redcSeq)
+            .WillOnce(DoAll(SetArgReferee<0>(redcVal), Return(true)));
+    EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getQ(_))
+            .InSequence(qSeq)
+            .WillOnce(DoAll(SetArgReferee<0>(qVal), Return(true)));
+    EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).WillOnce(Return(true));
+
+    mMockCal->getVersion(&calVer);
+    if (calVer == 2) {
+        volGet = EXPECT_CALL(*mMockCal, getTickVolLevels(_)).WillOnce(DoDefault());
+        volGet = EXPECT_CALL(*mMockCal, getClickVolLevels(_)).WillOnce(DoDefault());
+        volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault());
+    }
+
+    EXPECT_CALL(*mMockCal, isF0CompEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setF0CompEnable(true)).WillOnce(Return(true));
+    EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockCal, getSupportedPrimitives(_))
+            .InSequence(supportedPrimitivesSeq)
+            .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
+
+    EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
+    createVibrator(std::move(mockapi), std::move(mockcal), false);
+}
+
+TEST_F(VibratorTest, on) {
+    Sequence s1, s2;
+    uint16_t duration = std::rand() + 1;
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setFFEffect(_, _, duration + MAX_COLD_START_LATENCY_MS))
+            .InSequence(s2)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setFFPlay(_, ON_EFFECT_INDEX, true))
+            .InSequence(s1, s2)
+            .WillOnce(DoDefault());
+    EXPECT_TRUE(mVibrator->on(duration, nullptr).isOk());
+}
+
+TEST_F(VibratorTest, off) {
+    Sequence s1;
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_TRUE(mVibrator->off().isOk());
+}
+
+TEST_F(VibratorTest, supportsAmplitudeControl_supported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_GT(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, supportsExternalAmplitudeControl_unsupported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, setAmplitude_supported) {
+    EffectAmplitude amplitude = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f;
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, amplitudeToScale(amplitude))).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setAmplitude(amplitude).isOk());
+}
+
+TEST_F(VibratorTest, supportsExternalControl_supported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_GT(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, supportsExternalControl_unsupported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(false));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, setExternalControl_enable) {
+    Sequence s1, s2;
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _))
+            .InSequence(s1, s2)
+            .WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
+}
+
+TEST_F(VibratorTest, setExternalControl_disable) {
+    Sequence s1, s2, s3, s4;
+
+    // The default mIsUnderExternalControl is false, so it needs to turn on the External Control
+    // to make mIsUnderExternalControl become true.
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+            .InSequence(s1)
+            .InSequence(s1)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _)).InSequence(s3).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(VOLTAGE_SCALE_MAX)))
+            .InSequence(s4)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, false, _, _))
+            .InSequence(s1, s2, s3, s4)
+            .WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(false).isOk());
+}
+
+class EffectsTest : public VibratorTest, public WithParamInterface<EffectTuple> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) {
+        auto param = info.param;
+        auto effect = std::get<0>(param);
+        auto strength = std::get<1>(param);
+        return toString(effect) + "_" + toString(strength);
+    }
+};
+
+TEST_P(EffectsTest, perform) {
+    auto param = GetParam();
+    auto effect = std::get<0>(param);
+    auto strength = std::get<1>(param);
+    auto scale = EFFECT_SCALE.find(param);
+    auto queue = EFFECT_QUEUE.find(param);
+    EffectDuration duration;
+    auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
+    std::promise<void> promise;
+    std::future<void> future{promise.get_future()};
+    auto complete = [&promise] {
+        promise.set_value();
+        return ndk::ScopedAStatus::ok();
+    };
+    bool composeEffect;
+
+    ExpectationSet eSetup;
+    Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
+
+    if (scale != EFFECT_SCALE.end()) {
+        EffectIndex index = EFFECT_INDEX.at(effect);
+        duration = EFFECT_DURATIONS[index];
+
+        eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(scale->second)))
+                          .WillOnce(DoDefault());
+        eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, index, true))
+                            .After(eSetup)
+                            .WillOnce(DoDefault());
+    } else if (queue != EFFECT_QUEUE.end()) {
+        duration = std::get<1>(queue->second);
+        eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+                          .After(eSetup)
+                          .WillOnce(DoDefault());
+        eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
+        eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
+                          .After(eSetup)
+                          .WillOnce(DoDefault());
+        eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
+                            .After(eSetup)
+                            .WillOnce(DoDefault());
+        composeEffect = true;
+    } else {
+        duration = 0;
+    }
+
+    if (duration) {
+        ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
+                               .After(eActivate)
+                               .WillOnce(DoDefault());
+        ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(0, -1))
+                            .After(ePollHaptics)
+                            .WillOnce(DoDefault());
+        if (composeEffect) {
+            eEraseDone = EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _))
+                                 .After(ePollStop)
+                                 .WillOnce(DoDefault());
+            EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
+        } else {
+            EXPECT_CALL(*callback, onComplete()).After(ePollStop).WillOnce(complete);
+        }
+    }
+
+    int32_t lengthMs;
+    ndk::ScopedAStatus status = mVibrator->perform(effect, strength, callback, &lengthMs);
+    if (status.isOk()) {
+        EXPECT_LE(duration, lengthMs);
+    } else {
+        EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+        EXPECT_EQ(0, lengthMs);
+    }
+
+    if (duration) {
+        EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
+    }
+}
+
+const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
+                                   ndk::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
+                                                   ndk::enum_range<EffectStrength>().end()};
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, EffectsTest,
+                        Combine(ValuesIn(kEffects.begin(), kEffects.end()),
+                                ValuesIn(kEffectStrengths.begin(), kEffectStrengths.end())),
+                        EffectsTest::PrintParam);
+
+struct PrimitiveParam {
+    CompositePrimitive primitive;
+    EffectIndex index;
+};
+
+class PrimitiveTest : public VibratorTest, public WithParamInterface<PrimitiveParam> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) {
+        return toString(info.param.primitive);
+    }
+};
+
+const std::vector<PrimitiveParam> kPrimitiveParams = {
+        {CompositePrimitive::CLICK, 2},      {CompositePrimitive::THUD, 4},
+        {CompositePrimitive::SPIN, 5},       {CompositePrimitive::QUICK_RISE, 6},
+        {CompositePrimitive::SLOW_RISE, 7},  {CompositePrimitive::QUICK_FALL, 8},
+        {CompositePrimitive::LIGHT_TICK, 9}, {CompositePrimitive::LOW_TICK, 10},
+};
+
+TEST_P(PrimitiveTest, getPrimitiveDuration) {
+    auto param = GetParam();
+    auto primitive = param.primitive;
+    auto index = param.index;
+    int32_t duration;
+
+    EXPECT_EQ(EX_NONE, mVibrator->getPrimitiveDuration(primitive, &duration).getExceptionCode());
+    EXPECT_EQ(EFFECT_DURATIONS[index], duration);
+}
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, PrimitiveTest,
+                        ValuesIn(kPrimitiveParams.begin(), kPrimitiveParams.end()),
+                        PrimitiveTest::PrintParam);
+
+struct ComposeParam {
+    std::string name;
+    std::vector<CompositeEffect> composite;
+    EffectQueue queue;
+};
+
+class ComposeTest : public VibratorTest, public WithParamInterface<ComposeParam> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) { return info.param.name; }
+};
+
+TEST_P(ComposeTest, compose) {
+    auto param = GetParam();
+    auto composite = param.composite;
+    auto queue = std::get<0>(param.queue);
+    ExpectationSet eSetup;
+    Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
+    auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
+    std::promise<void> promise;
+    std::future<void> future{promise.get_future()};
+    auto complete = [&promise] {
+        promise.set_value();
+        return ndk::ScopedAStatus::ok();
+    };
+
+    eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+                      .After(eSetup)
+                      .WillOnce(DoDefault());
+    eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
+    eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
+                      .After(eSetup)
+                      .WillOnce(DoDefault());
+    eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
+                        .After(eSetup)
+                        .WillOnce(DoDefault());
+
+    ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
+                           .After(eActivate)
+                           .WillOnce(DoDefault());
+    ePollStop =
+            EXPECT_CALL(*mMockApi, pollVibeState(0, -1)).After(ePollHaptics).WillOnce(DoDefault());
+    eEraseDone =
+            EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _)).After(ePollStop).WillOnce(DoDefault());
+    EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
+
+    EXPECT_EQ(EX_NONE, mVibrator->compose(composite, callback).getExceptionCode());
+
+    EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
+}
+
+const std::vector<ComposeParam> kComposeParams = {
+        {"click",
+         {{0, CompositePrimitive::CLICK, 1.0f}},
+         Queue(QueueEffect(2, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"thud",
+         {{1, CompositePrimitive::THUD, 0.8f}},
+         Queue(1, QueueEffect(4, Level(0.8f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"spin",
+         {{2, CompositePrimitive::SPIN, 0.6f}},
+         Queue(2, QueueEffect(5, Level(0.6f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"quick_rise",
+         {{3, CompositePrimitive::QUICK_RISE, 0.4f}},
+         Queue(3, QueueEffect(6, Level(0.4f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+        {"slow_rise",
+         {{4, CompositePrimitive::SLOW_RISE, 0.0f}},
+         Queue(4, QueueEffect(7, Level(0.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"quick_fall",
+         {{5, CompositePrimitive::QUICK_FALL, 1.0f}},
+         Queue(5, QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+        {"pop",
+         {{6, CompositePrimitive::SLOW_RISE, 1.0f}, {50, CompositePrimitive::THUD, 1.0f}},
+         Queue(6, QueueEffect(7, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 50,
+               QueueEffect(4, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"snap",
+         {{7, CompositePrimitive::QUICK_RISE, 1.0f}, {0, CompositePrimitive::QUICK_FALL, 1.0f}},
+         Queue(7, QueueEffect(6, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])),
+               QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+};
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest,
+                        ValuesIn(kComposeParams.begin(), kComposeParams.end()),
+                        ComposeTest::PrintParam);
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/cs40l26/tests/types.h b/vibrator/cs40l26/tests/types.h
new file mode 100644
index 0000000..e05c648
--- /dev/null
+++ b/vibrator/cs40l26/tests/types.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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_HARDWARE_VIBRATOR_TEST_TYPES_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+using EffectIndex = uint16_t;
+using EffectLevel = uint32_t;
+using EffectAmplitude = float;
+using EffectScale = uint16_t;
+using EffectDuration = uint32_t;
+using EffectQueue = std::tuple<std::string, EffectDuration>;
+using EffectTuple = std::tuple<::aidl::android::hardware::vibrator::Effect,
+                               ::aidl::android::hardware::vibrator::EffectStrength>;
+
+using QueueEffect = std::tuple<EffectIndex, EffectLevel>;
+using QueueDelay = uint32_t;
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
diff --git a/vibrator/cs40l26/tests/utils.h b/vibrator/cs40l26/tests/utils.h
new file mode 100644
index 0000000..e7f6055
--- /dev/null
+++ b/vibrator/cs40l26/tests/utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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_HARDWARE_VIBRATOR_TEST_UTILS_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H
+
+#include <cmath>
+
+#include "types.h"
+
+static inline EffectScale toScale(float amplitude, float maximum) {
+    float ratio = 100; /* Unit: % */
+    if (maximum != 0)
+        ratio = amplitude / maximum * 100;
+
+    if (maximum == 0 || ratio > 100)
+        ratio = 100;
+
+    return std::round(ratio);
+}
+
+static inline EffectScale levelToScale(EffectLevel level) {
+    return toScale(level, 100);
+}
+
+static inline EffectScale amplitudeToScale(EffectAmplitude amplitude) {
+    return toScale(amplitude, 1.0f);
+}
+
+static inline uint32_t msToCycles(EffectDuration ms) {
+    return ms * 48;
+}
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H