Replace vibrator/ from git_tm-qpr-dev

Lastest commit:
2724d61 vibrator/cs40l26: Fix unsupport primitive effects test

Bug: 267926374
Bug: 264625320
Test: HAL init properly.
Change-Id: I1c01ea8c6d584ae58d8d20f21a9ac43a8977f435
diff --git a/device-felix.mk b/device-felix.mk
index 0802815..c00832a 100644
--- a/device-felix.mk
+++ b/device-felix.mk
@@ -30,7 +30,7 @@
 include device/google/felix/audio/felix/audio-tables.mk
 include device/google/gs201/device-shipping-common.mk
 $(call soong_config_set,fp_hal_feature,pixel_product, product_a)
-include hardware/google/pixel/vibrator/cs40l26/device-stereo.mk
+include device/google/felix/vibrator/cs40l26/device.mk
 include device/google/gs-common/bcmbt/bluetooth.mk
 include device/google/gs-common/touch/gti/gti.mk
 include device/google/gs-common/touch/stm/stm6.mk
@@ -207,7 +207,9 @@
 
 # Vibrator HAL
 PRODUCT_PRODUCT_PROPERTIES +=\
-    ro.vendor.vibrator.hal.long.frequency.shift=0
+    ro.vendor.vibrator.hal.long.frequency.shift=0 \
+    ro.vendor.vibrator.hal.gpio.num=44 \
+    ro.vendor.vibrator.hal.gpio.shift=2
 ACTUATOR_MODEL := luxshare_ict_lt_xlra1906d
 
 # Fingerprint
diff --git a/vibrator/common/HardwareBase.cpp b/vibrator/common/HardwareBase.cpp
index 8e07e99..7d61b57 100644
--- a/vibrator/common/HardwareBase.cpp
+++ b/vibrator/common/HardwareBase.cpp
@@ -71,6 +71,7 @@
 
 HwCalBase::HwCalBase() {
     std::ifstream calfile;
+    std::ifstream calfile_dual;
     auto propertyPrefix = std::getenv("PROPERTY_PREFIX");
 
     if (propertyPrefix != NULL) {
@@ -91,6 +92,20 @@
             mCalData[utils::trim(key)] = utils::trim(value);
         }
     }
+
+    utils::fileFromEnv("CALIBRATION_FILEPATH_DUAL", &calfile_dual);
+
+    for (std::string line; std::getline(calfile_dual, line);) {
+        if (line.empty() || line[0] == '#') {
+            continue;
+        }
+        std::istringstream is_line(line);
+        std::string key, value;
+        if (std::getline(is_line, key, ':') && std::getline(is_line, value)) {
+            key = utils::trim(key) + "_dual";
+            mCalData[key] = utils::trim(value);
+        }
+    }
 }
 
 void HwCalBase::debug(int fd) {
diff --git a/vibrator/cs40l26/Android.bp b/vibrator/cs40l26/Android.bp
index 7857580..9ac51d1 100644
--- a/vibrator/cs40l26/Android.bp
+++ b/vibrator/cs40l26/Android.bp
@@ -34,7 +34,6 @@
         "android.hardware.vibrator-defaults.cs40l26-private",
     ],
     shared_libs: [
-        "android.hardware.vibrator.cs40l26-private-cpp",
         "libcutils",
         "libtinyalsa",
     ],
@@ -53,9 +52,6 @@
         "android.hardware.vibrator-impl.cs40l26-private",
         "libtinyalsa",
     ],
-    shared_libs: [
-        "android.hardware.vibrator.cs40l26-private-cpp",
-    ],
 }
 
 cc_library {
@@ -63,8 +59,6 @@
     defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
     srcs: [
         "Vibrator.cpp",
-        "VibratorSync.cpp",
-        "VibratorManager.cpp",
     ],
     export_include_dirs: ["."],
     vendor_available: true,
@@ -80,39 +74,5 @@
     shared_libs: [
         "android.hardware.vibrator-impl.cs40l26-private",
     ],
-    cflags: [
-        "-DLOG_TAG=\"android.hardware.vibrator-cs40l26-private\"",
-    ],
-    proprietary: true,
-}
-
-cc_binary {
-    name: "android.hardware.vibrator-service.cs40l26-dual-private",
-    defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
-    init_rc: ["android.hardware.vibrator-service.cs40l26-dual-private.rc"],
-    vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual-private.xml"],
-    srcs: ["service.cpp"],
-    shared_libs: [
-        "android.hardware.vibrator-impl.cs40l26-private",
-    ],
-    cflags: [
-        "-DVIBRATOR_NAME=\"dual\"",
-        "-DLOG_TAG=\"android.hardware.vibrator-cs40l26-dual-private\"",
-    ],
-    proprietary: true,
-}
-
-cc_binary {
-    name: "android.hardware.vibrator-service.cs40l26-stereo-private",
-    defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
-    init_rc: ["android.hardware.vibrator-service.cs40l26-stereo-private.rc"],
-    vintf_fragments: ["android.hardware.vibrator-service.cs40l26-stereo-private.xml"],
-    srcs: ["service-stereo.cpp"],
-    shared_libs: [
-        "android.hardware.vibrator-impl.cs40l26-private",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"android.hardware.vibrator-cs40l26-stereo-private\"",
-    ],
     proprietary: true,
 }
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index 9cf831c..00ae2d2 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include <algorithm>
+#include <cmath>
 
 #include "HardwareBase.h"
 #include "Vibrator.h"
@@ -66,6 +67,10 @@
 
 class HwApi : public Vibrator::HwApi, private HwApiBase {
   public:
+    static std::unique_ptr<HwApi> Create() {
+        auto hwapi = std::unique_ptr<HwApi>(new HwApi());
+        return hwapi;
+    }
     HwApi() {
         open("calibration/f0_stored", &mF0);
         open("default/f0_offset", &mF0Offset);
@@ -220,7 +225,10 @@
             ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
             return false;
         }
-
+        // Turn off the waiting time for SVC init phase to complete since chip
+        // should already under STOP state
+        setMinOnOffInterval(0);
+        // Do erase flow
         if (effectIndex < WAVEFORM_MAX_INDEX) {
             /* Normal situation. Only erase the effect which we just played. */
             if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
@@ -248,19 +256,10 @@
                 (*effect)[i].id = -1;
             }
         }
+        // Turn on the waiting time for SVC init phase to complete
+        setMinOnOffInterval(Vibrator::MIN_ON_OFF_INTERVAL_US);
         return true;
     }
-    void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) override {
-        if ((*effect).trigger.button != 0x00) {
-            (*effect).trigger.button = 0x00;
-            if (id < WAVEFORM_MAX_PHYSICAL_INDEX) {
-                /* Clear the trigger pin setting */
-                if ((ioctl(fd, EVIOCSFF, effect) < 0)) {
-                    ALOGE("OFF: Failed to edit effect %d (%d): %s", id, errno, strerror(errno));
-                }
-            }
-        }
-    }
 
     void debug(int fd) override { HwApiBase::debug(fd); }
 
@@ -281,6 +280,7 @@
   private:
     static constexpr char VERSION[] = "version";
     static constexpr char F0_CONFIG[] = "f0_measured";
+    static constexpr char F0_CONFIG_DUAL[] = "f0_measured_dual";
     static constexpr char REDC_CONFIG[] = "redc_measured";
     static constexpr char Q_CONFIG[] = "q_measured";
     static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
@@ -295,6 +295,10 @@
 
   public:
     HwCal() {}
+    static std::unique_ptr<HwCal> Create() {
+        auto hwcal = std::unique_ptr<HwCal>(new HwCal());
+        return hwcal;
+    }
 
     bool getVersion(uint32_t *value) override {
         if (getPersist(VERSION, value)) {
@@ -307,6 +311,30 @@
         return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT);
     }
     bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); }
+    bool getF0SyncOffset(uint32_t *value) override {
+        std::string cal_0{8, '0'};
+        std::string cal_1{8, '0'};
+
+        if (getPersist(F0_CONFIG, &cal_0) && getPersist(F0_CONFIG_DUAL, &cal_1)) {
+            float f0_0 = static_cast<float>(std::stoul(cal_0, nullptr, 16)) / (1 << 14);
+            float f0_1 = static_cast<float>(std::stoul(cal_1, nullptr, 16)) / (1 << 14);
+            float f0_offset = std::abs(f0_0 - f0_1)/2;
+
+            if (f0_0 < f0_1) {
+                *value = static_cast<uint32_t>(f0_offset * std::pow(2, 14));
+            } else if (f0_0 > f0_1) {
+                *value = static_cast<uint32_t>(std::pow(2, 24) - std::abs(f0_offset) * std::pow(2, 14));
+            } else {
+                *value = 0;
+            }
+
+            return true;
+        } else {
+            ALOGE("Vibrator: Unable to load F0_CONFIG or F0_CONFIG_DUAL config");
+            *value = 0;
+            return false;
+        }
+    }
     bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); }
     bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); }
     bool getTickVolLevels(std::array<uint32_t, 2> *value) override {
diff --git a/vibrator/cs40l26/VibMgrHwApi.h b/vibrator/cs40l26/VibMgrHwApi.h
index f263a18..a243f69 100644
--- a/vibrator/cs40l26/VibMgrHwApi.h
+++ b/vibrator/cs40l26/VibMgrHwApi.h
@@ -21,7 +21,7 @@
 
 #include <map>
 
-#include "VibratorManager.h"
+#include "Vibrator.h"
 #include "utils.h"
 
 namespace aidl {
@@ -29,7 +29,7 @@
 namespace hardware {
 namespace vibrator {
 
-class VibMgrHwApi : public VibratorManager::HwApi {
+class VibMgrHwApi : public Vibrator::HwGPIO {
   private:
     const uint32_t DEBUG_GPI_PIN = UINT16_MAX;
     const uint32_t DEBUG_GPI_PIN_SHIFT = UINT16_MAX;
@@ -54,13 +54,13 @@
         }
         mGPIOPin = utils::getProperty(mPropertyPrefix + "gpio.num", DEBUG_GPI_PIN);
         if (mGPIOPin == DEBUG_GPI_PIN) {
-            ALOGE("GetGPIO: Fail to get the GPIO num: %s", strerror(errno));
+            ALOGE("GetGPIO: Failed to get the GPIO num: %s", strerror(errno));
             return false;
         }
         mGPIOShift = utils::getProperty(mPropertyPrefix + "gpio.shift", DEBUG_GPI_PIN_SHIFT);
 
         if (mGPIOShift == DEBUG_GPI_PIN_SHIFT) {
-            ALOGE("GetGPIO: Fail to get the GPIO shift num: %s", strerror(errno));
+            ALOGE("GetGPIO: Failed to get the GPIO shift num: %s", strerror(errno));
             return false;
         }
 
@@ -68,9 +68,9 @@
     }
     bool initGPIO() override {
         const auto gpio_dev = std::string() + "/dev/gpiochip" + std::to_string(mGPIOPin);
-        int fd = open(gpio_dev.c_str(), O_RDONLY);
+        int fd = open(gpio_dev.c_str(), O_CREAT | O_WRONLY, 0777);
         if (fd < 0) {
-            ALOGE("InitGPIO: Unabled to open gpio dev: %s", strerror(errno));
+            ALOGE("InitGPIO: Unable to open gpio dev: %s", strerror(errno));
             return false;
         }
 
@@ -79,12 +79,15 @@
         mRq.flags = GPIOHANDLE_REQUEST_OUTPUT;
 
         int ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &mRq);
-        close(fd);
         if (ret == -1) {
             ALOGE("InitGPIO: Unable to line handle from ioctl : %s", strerror(errno));
             close(fd);
             return false;
         }
+        if (close(fd) == -1) {
+            ALOGE("Failed to close GPIO char dev");
+            return false;
+        }
         // Reset gpio status to LOW
         struct gpiohandle_data data;
         data.values[0] = 0;
@@ -97,7 +100,7 @@
         }
         return true;
     }
-    bool setTrigger(bool value) override {
+    bool setGPIOOutput(bool value) override {
         struct gpiohandle_data data;
         data.values[0] = value;
 
@@ -107,7 +110,6 @@
             close(mRq.fd);
             return false;
         }
-        close(mRq.fd);
 
         return true;
     }
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index cc4b97f..131a376 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -33,6 +33,11 @@
 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
 #endif
 
+#ifdef LOG_TAG
+#undef LOG_TAG
+#define LOG_TAG std::getenv("HAPTIC_NAME")
+#endif
+
 namespace aidl {
 namespace android {
 namespace hardware {
@@ -48,13 +53,16 @@
 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 uint32_t MIN_ON_OFF_INTERVAL_US = 8500;  // SVC initialization time
-static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1;    // ALERT Irq Handling
+static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1;  // ALERT Irq Handling
 static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
+static constexpr float SETTING_TIME_OVERHEAD = 26;  // This time was combined by
+                                                    // HAL set the effect to
+                                                    // driver and the kernel
+                                                    // executes the effect before
+                                                    // chip play the effect
 
 static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
 static constexpr auto POLLING_TIMEOUT = 20;
-static constexpr auto POLLING_TIMEOUT_IN_SYNC = 100;
 static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
 
 /* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
@@ -93,12 +101,12 @@
 /*
  * [15] Edge, 0:Falling, 1:Rising
  * [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI)
- * [8] BANK, 0:ROM, 1:RAM
+ * [8] BANK, 0:RAM, 1:R0M
  * [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen
  * [6:0] WAVEFORM_INDEX
- * 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + ROM + Not buzzgen
+ * 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + RAM + Not buzzgen
  */
-static constexpr uint32_t GPIO_TRIGGER_CONFIG = 0x9100;
+static constexpr uint16_t GPIO_TRIGGER_CONFIG = 0x9100;
 
 static uint16_t amplitudeToScale(float amplitude, float maximum) {
     float ratio = 100; /* Unit: % */
@@ -154,8 +162,6 @@
     VIBE_STATE_ASP,
 };
 
-std::mutex mActiveId_mutex;  // protects mActiveId
-
 static int min(int x, int y) {
     return x < y ? x : y;
 }
@@ -230,12 +236,24 @@
     return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
 }
 
-Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
-    : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) {
+Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
+                   std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
+                   std::unique_ptr<HwGPIO> hwgpio)
+    : mHwApiDef(std::move(hwApiDefault)),
+      mHwCalDef(std::move(hwCalDefault)),
+      mHwApiDual(std::move(hwApiDual)),
+      mHwCalDual(std::move(hwCalDual)),
+      mHwGPIO(std::move(hwgpio)),
+      mAsyncHandle(std::async([] {})) {
     int32_t longFrequencyShift;
     std::string caldata{8, '0'};
     uint32_t calVer;
 
+    // ==================Single actuators and dual actuators checking =============================
+    if ((mHwApiDual != nullptr) && (mHwCalDual != nullptr))
+        mIsDual = true;
+
+    // ==================INPUT Devices== Base =================
     const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
     const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
     if ((strstr(inputEventName, "cs40l26") != nullptr) ||
@@ -248,7 +266,7 @@
         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));
+                ALOGE("Failed 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));
@@ -278,16 +296,69 @@
         }
 
         if (!mInputFd.ok()) {
-            ALOGE("Fail to get an input event with name %s", inputEventName);
+            ALOGE("Failed to get an input event with name %s", inputEventName);
         }
     } else {
         ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName);
     }
 
+    // ==================INPUT Devices== Flip =================
+    if (mIsDual) {
+        const char *inputEventNameDual = std::getenv("INPUT_EVENT_NAME_DUAL");
+        if ((strstr(inputEventNameDual, "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("Failed to get flip's 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, inputEventNameDual) != nullptr) {
+                                mInputFdDual.reset(fd);
+                                ALOGI("Control %s through %s", inputEventNameDual,
+                                      inputEventPaths.gl_pathv[i]);
+                                break;
+                            }
+                            close(fd);
+                        }
+                    }
+                }
+
+                if (ret == 0) {
+                    globfree(&inputEventPaths);
+                }
+                if (mInputFdDual.ok()) {
+                    break;
+                }
+
+                sleep(1);
+                ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
+            }
+
+            if (!mInputFdDual.ok()) {
+                ALOGE("Failed to get an input event with name %s", inputEventNameDual);
+            }
+            ALOGE("HWAPI: %s", std::getenv("HWAPI_PATH_PREFIX"));
+        } else {
+            ALOGE("The input name %s is not cs40l26_dual_input", inputEventNameDual);
+        }
+    }
+    // ====================HAL internal effect table== Base ==================================
+
     mFfEffects.resize(WAVEFORM_MAX_INDEX);
     mEffectDurations.resize(WAVEFORM_MAX_INDEX);
     mEffectDurations = {
-            1000, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000,
+            1000, 100, 32, 1000, 300, 130, 150, 500, 100, 10, 12, 1000, 1000, 1000,
     }; /* 11+3 waveforms. The duration must < UINT16_MAX */
 
     uint8_t effectIndex;
@@ -305,7 +376,7 @@
             // Bypass the waveform update due to different input name
             if ((strstr(inputEventName, "cs40l26") != nullptr) ||
                 (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
-                if (!mHwApi->setFFEffect(
+                if (!mHwApiDef->setFFEffect(
                             mInputFd, &mFfEffects[effectIndex],
                             static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
                     ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
@@ -327,45 +398,124 @@
         }
     }
 
-    if (mHwCal->getF0(&caldata)) {
-        mHwApi->setF0(caldata);
+    // ====================HAL internal effect table== Flip ==================================
+    if (mIsDual) {
+        mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
+
+        for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
+            if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
+                /* Initialize physical waveforms. */
+                mFfEffectsDual[effectIndex] = {
+                        .type = FF_PERIODIC,
+                        .id = -1,
+                        .replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
+                        .u.periodic.waveform = FF_CUSTOM,
+                        .u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex},
+                        .u.periodic.custom_len = FF_CUSTOM_DATA_LEN,
+                };
+                // Bypass the waveform update due to different input name
+                if ((strstr(inputEventName, "cs40l26") != nullptr) ||
+                    (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
+                    if (!mHwApiDual->setFFEffect(
+                                mInputFdDual, &mFfEffectsDual[effectIndex],
+                                static_cast<uint16_t>(mFfEffectsDual[effectIndex].replay.length))) {
+                        ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno,
+                              strerror(errno));
+                    }
+                }
+                if (mFfEffectsDual[effectIndex].id != effectIndex) {
+                    ALOGW("Unexpected effect index: %d -> %d", effectIndex,
+                          mFfEffectsDual[effectIndex].id);
+                }
+            } else {
+                /* Initiate placeholders for OWT effects. */
+                mFfEffectsDual[effectIndex] = {
+                        .type = FF_PERIODIC,
+                        .id = -1,
+                        .replay.length = 0,
+                        .u.periodic.waveform = FF_CUSTOM,
+                        .u.periodic.custom_data = nullptr,
+                        .u.periodic.custom_len = 0,
+                };
+            }
+        }
     }
-    if (mHwCal->getRedc(&caldata)) {
-        mHwApi->setRedc(caldata);
+    // ==============Calibration data checking======================================
+
+    if (mHwCalDef->getF0(&caldata)) {
+        mHwApiDef->setF0(caldata);
     }
-    if (mHwCal->getQ(&caldata)) {
-        mHwApi->setQ(caldata);
+    if (mHwCalDef->getRedc(&caldata)) {
+        mHwApiDef->setRedc(caldata);
+    }
+    if (mHwCalDef->getQ(&caldata)) {
+        mHwApiDef->setQ(caldata);
     }
 
-    mHwCal->getLongFrequencyShift(&longFrequencyShift);
-    if (longFrequencyShift > 0) {
-        mF0Offset = longFrequencyShift * std::pow(2, 14);
-    } else if (longFrequencyShift < 0) {
-        mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
+    if (mHwCalDef->getF0SyncOffset(&mF0Offset)) {
+        ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: "
+              "%u",
+              mF0Offset);
     } else {
-        mF0Offset = 0;
+        mHwCalDef->getLongFrequencyShift(&longFrequencyShift);
+        if (longFrequencyShift > 0) {
+            mF0Offset = longFrequencyShift * std::pow(2, 14);
+        } else if (longFrequencyShift < 0) {
+            mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
+        } else {
+            mF0Offset = 0;
+        }
+        ALOGD("Vibrator::Vibrator: F0 offset calculated from long shift frequency: %u", mF0Offset);
     }
 
-    mHwCal->getVersion(&calVer);
+    if (mIsDual) {
+        if (mHwCalDual->getF0(&caldata)) {
+            mHwApiDual->setF0(caldata);
+        }
+        if (mHwCalDual->getRedc(&caldata)) {
+            mHwApiDual->setRedc(caldata);
+        }
+        if (mHwCalDual->getQ(&caldata)) {
+            mHwApiDual->setQ(caldata);
+        }
+
+        if (mHwCalDual->getF0SyncOffset(&mF0OffsetDual)) {
+            ALOGD("Vibrator::Vibrator: Dual: F0 offset calculated from both base and flip "
+                  "calibration data: "
+                  "%u",
+                  mF0OffsetDual);
+        }
+    }
+
+    mHwCalDef->getVersion(&calVer);
     if (calVer == 2) {
-        mHwCal->getTickVolLevels(&mTickEffectVol);
-        mHwCal->getClickVolLevels(&mClickEffectVol);
-        mHwCal->getLongVolLevels(&mLongEffectVol);
+        mHwCalDef->getTickVolLevels(&(mTickEffectVol));
+        mHwCalDef->getClickVolLevels(&(mClickEffectVol));
+        mHwCalDef->getLongVolLevels(&(mLongEffectVol));
     } else {
         ALOGW("Unsupported calibration version! Using the default calibration value");
-        mHwCal->getTickVolLevels(&mTickEffectVol);
-        mHwCal->getClickVolLevels(&mClickEffectVol);
-        mHwCal->getLongVolLevels(&mLongEffectVol);
+        mHwCalDef->getTickVolLevels(&(mTickEffectVol));
+        mHwCalDef->getClickVolLevels(&(mClickEffectVol));
+        mHwCalDef->getLongVolLevels(&(mLongEffectVol));
     }
 
-    mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
-    mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
+    // ================Project specific setting to driver===============================
 
+    mHwApiDef->setF0CompEnable(mHwCalDef->isF0CompEnabled());
+    mHwApiDef->setRedcCompEnable(mHwCalDef->isRedcCompEnabled());
+    mHwApiDef->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
+    if (mIsDual) {
+        mHwApiDual->setF0CompEnable(mHwCalDual->isF0CompEnabled());
+        mHwApiDual->setRedcCompEnable(mHwCalDual->isRedcCompEnabled());
+        mHwApiDual->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
+    }
+    // ===============Audio coupled haptics bool init ========
     mIsUnderExternalControl = false;
 
-    mIsChirpEnabled = mHwCal->isChirpEnabled();
+    // =============== Compose PWLE check =====================================
+    mIsChirpEnabled = mHwCalDef->isChirpEnabled();
 
-    mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits);
+    mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits);
     if (mSupportedPrimitivesBits > 0) {
         for (auto e : defaultSupportedPrimitives) {
             if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
@@ -378,7 +528,14 @@
         }
         mSupportedPrimitives = defaultSupportedPrimitives;
     }
-    mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
+
+    mPrimitiveMinScale = {0.0f, 0.01f, 0.11f, 0.23f, 0.0f, 0.25f, 0.02f, 0.03f, 0.16f};
+
+    // ====== Get GPIO status and init it ================
+    mGPIOStatus = mHwGPIO->getGPIO();
+    if (!mGPIOStatus || !mHwGPIO->initGPIO()) {
+        ALOGE("Vibrator: GPIO initialization process error");
+    }
 }
 
 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@@ -392,7 +549,7 @@
     } else {
         ALOGE("No haptics ALSA device");
     }
-    if (mHwApi->hasOwtFreeSpace()) {
+    if (mHwApiDef->hasOwtFreeSpace()) {
         ret |= IVibrator::CAP_COMPOSE_EFFECTS;
         if (mIsChirpEnabled) {
             ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
@@ -408,31 +565,48 @@
     const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
 
     if (mActiveId >= 0) {
-        ALOGV("Off: Stop the active effect: %d", mActiveId);
+        ALOGD("Off: Stop the active effect: %d", mActiveId);
         /* Stop the active effect. */
-        if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) {
-            ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
+        if (!mHwApiDef->setFFPlay(mInputFd, mActiveId, false)) {
+            ALOGE("Off: Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
             ret = false;
         }
-
+        if (mIsDual && (!mHwApiDual->setFFPlay(mInputFdDual, mActiveId, false))) {
+            ALOGE("Off: Failed to stop flip's effect %d (%d): %s", mActiveId, errno,
+                  strerror(errno));
+            ret = false;
+        }
+        /* Do erase process */
         if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
-            (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
-            ALOGE("Failed to clean up the composed effect %d", mActiveId);
+            (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
+            ALOGE("Off: Failed to clean up the composed effect %d", mActiveId);
             ret = false;
         }
 
-        mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
+        if (mIsDual && (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
+            (!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
+            ALOGE("Off: Failed to clean up flip's the composed effect %d", mActiveId);
+            ret = false;
+        }
+        if (!mHwGPIO->setGPIOOutput(false)) {
+            ALOGE("Off: Failed to reset GPIO(%d): %s", errno, strerror(errno));
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
     } else {
-        ALOGV("Off: Vibrator is already off");
+        ALOGD("Off: Vibrator is already off");
     }
 
-    mActiveId = -1;
     setGlobalAmplitude(false);
     if (mF0Offset) {
-        mHwApi->setF0Offset(0);
+        mHwApiDef->setF0Offset(0);
+        if (mIsDual && mF0OffsetDual) {
+            mHwApiDual->setF0Offset(0);
+        }
     }
 
     if (ret) {
+        ALOGD("Off: Done.");
+        mActiveId = -1;
         return ndk::ScopedAStatus::ok();
     } else {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@@ -441,13 +615,8 @@
 
 ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
                                 const std::shared_ptr<IVibratorCallback> &callback) {
-    std::scoped_lock lock(mApiMutex);
     ATRACE_NAME("Vibrator::on");
-
-    if (isBusy()) {
-        ALOGD("Vibrator::on, isBusy");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
+    ALOGD("Vibrator::on");
 
     if (timeoutMs > MAX_TIME_MS) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -460,7 +629,10 @@
     }
     setGlobalAmplitude(true);
     if (mF0Offset) {
-        mHwApi->setF0Offset(mF0Offset);
+        mHwApiDef->setF0Offset(mF0Offset);
+        if (mIsDual && mF0OffsetDual) {
+            mHwApiDual->setF0Offset(mF0OffsetDual);
+        }
     }
     return on(timeoutMs, index, nullptr /*ignored*/, callback);
 }
@@ -468,8 +640,8 @@
 ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
                                      const std::shared_ptr<IVibratorCallback> &callback,
                                      int32_t *_aidl_return) {
-    std::scoped_lock lock(mApiMutex);
     ATRACE_NAME("Vibrator::perform");
+    ALOGD("Vibrator::perform");
     return performEffect(effect, strength, callback, _aidl_return);
 }
 
@@ -480,7 +652,6 @@
 }
 
 ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
-    std::scoped_lock lock(mApiMutex);
     ATRACE_NAME("Vibrator::setAmplitude");
 
     if (amplitude <= 0.0f || amplitude > 1.0f) {
@@ -496,17 +667,12 @@
 }
 
 ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
-    std::scoped_lock lock(mApiMutex);
     ATRACE_NAME("Vibrator::setExternalControl");
 
-    if (isSynced()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-
     setGlobalAmplitude(enabled);
 
     if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
-        if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
+        if (!mHwApiDef->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
             ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
@@ -545,8 +711,8 @@
         if (!status.isOk()) {
             return status;
         }
-
-        *durationMs = mEffectDurations[effectIndex];
+        // Please check the overhead time detail in b/261841035
+        *durationMs = mEffectDurations[effectIndex] + SETTING_TIME_OVERHEAD;
     } else {
         *durationMs = 0;
     }
@@ -555,10 +721,11 @@
 
 ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
                                      const std::shared_ptr<IVibratorCallback> &callback) {
-    std::scoped_lock lock(mApiMutex);
     ATRACE_NAME("Vibrator::compose");
+    ALOGD("Vibrator::compose");
     uint16_t size;
     uint16_t nextEffectDelay;
+    uint16_t totalDuration = 0;
 
     auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
                                   FF_CUSTOM_DATA_LEN_MAX_COMP);
@@ -569,6 +736,7 @@
 
     /* Check if there is a wait before the first effect. */
     nextEffectDelay = composite.front().delayMs;
+    totalDuration += nextEffectDelay;
     if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     } else if (nextEffectDelay > 0) {
@@ -592,7 +760,8 @@
         auto &e_curr = composite[i_curr];
         uint32_t effectIndex = 0;
         uint32_t effectVolLevel = 0;
-        if (e_curr.scale < 0.0f || e_curr.scale > 1.0f) {
+        float effectScale = e_curr.scale;
+        if (effectScale < 0.0f || effectScale > 1.0f) {
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
 
@@ -602,7 +771,11 @@
             if (!status.isOk()) {
                 return status;
             }
-            effectVolLevel = intensityToVolLevel(e_curr.scale, effectIndex);
+            if (effectScale < mPrimitiveMinScale[static_cast<uint32_t>(e_curr.primitive)]) {
+                effectScale = mPrimitiveMinScale[static_cast<uint32_t>(e_curr.primitive)];
+            }
+            effectVolLevel = intensityToVolLevel(effectScale, effectIndex);
+            totalDuration += mEffectDurations[effectIndex];
         }
 
         /* Fetch the next composite effect delay and fill into the current section */
@@ -615,6 +788,7 @@
                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
             }
             nextEffectDelay = delay;
+            totalDuration += delay;
         }
 
         if (effectIndex == 0 && nextEffectDelay == 0) {
@@ -631,6 +805,10 @@
     if (header_count == dspmem_chunk_bytes(ch)) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     } else {
+        mFfEffects[WAVEFORM_COMPOSE].replay.length = totalDuration;
+        if (mIsDual) {
+            mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = totalDuration;
+        }
         return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
                              callback);
     }
@@ -660,68 +838,134 @@
         effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
 
         uint32_t freeBytes;
-        mHwApi->getOwtFreeSpace(&freeBytes);
+        mHwApiDef->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 (isSynced()) {
-            mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+        if (mIsDual) {
+            mHwApiDual->getOwtFreeSpace(&freeBytes);
+            if (dspmem_chunk_bytes(ch) > freeBytes) {
+                ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
+                      dspmem_chunk_bytes(ch), freeBytes);
+                delete ch;
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
         }
-        if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
-                                     &mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
+
+        int errorStatus;
+        if (mGPIOStatus && mIsDual) {
+            mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+            mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+        } else {
+            ALOGD("Not dual haptics HAL and GPIO status fail");
+        }
+
+        if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
+                                        &mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
             delete ch;
             ALOGE("Invalid uploadOwtEffect");
             return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
         }
+        if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch),
+                                                    &mFfEffectsDual[effectIndex], &effectIndex,
+                                                    &errorStatus)) {
+            delete ch;
+            ALOGE("Invalid uploadOwtEffect in flip");
+            return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
+        }
         delete ch;
 
     } 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 (isSynced()) {
+        if (mGPIOStatus && mIsDual) {
             mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+            mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+        } else {
+            ALOGD("Not dual haptics HAL and GPIO status fail");
         }
-        if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex],
-                                 static_cast<uint16_t>(timeoutMs))) {
+        if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
+                                    static_cast<uint16_t>(timeoutMs))) {
             ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
-    }
-    {
-        const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
-        mActiveId = effectIndex;
-        if (isSynced() &&
-            (effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
-            mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
-            if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], mFfEffects[effectIndex].replay.length)) {
-                ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
+        if (mIsDual) {
+            mFfEffectsDual[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
+            if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
+                                         static_cast<uint16_t>(timeoutMs))) {
+                ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
+                      strerror(errno));
                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
             }
-        } else if (!isSynced()) {
-            // /* Play the event now. */
-            if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
+        }
+    }
+
+    {
+        const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+        /* Play the event now. */
+        mActiveId = effectIndex;
+        if (!mGPIOStatus) {
+            ALOGE("GetVibrator: GPIO status error");
+            // Do playcode to play effect
+            if (!mHwApiDef->setFFPlay(mInputFd, effectIndex, true)) {
                 ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
+                mActiveId = -1;
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+            if (mIsDual && !mHwApiDual->setFFPlay(mInputFdDual, effectIndex, true)) {
+                ALOGE("Failed to play flip's effect %d (%d): %s", effectIndex, errno,
+                      strerror(errno));
+                mActiveId = -1;
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+        } else {
+            // Using GPIO to play effect
+            if ((effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
+                mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+                if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
+                                            mFfEffects[effectIndex].replay.length)) {
+                    ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+                }
+                if (mIsDual) {
+                    mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
+                    if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
+                                                 mFfEffectsDual[effectIndex].replay.length)) {
+                        ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
+                              strerror(errno));
+                        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+                    }
+                }
+            }
+            if (!mHwGPIO->setGPIOOutput(true)) {
+                ALOGE("Failed to trigger effect %d (%d) by GPIO: %s", effectIndex, errno,
+                      strerror(errno));
                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
             }
         }
     }
 
     mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
-
+    ALOGD("Vibrator::on, set done.");
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
     uint16_t scale = amplitudeToScale(amplitude, maximum);
-    if (!mHwApi->setFFGain(mInputFd, scale)) {
+    if (!mHwApiDef->setFFGain(mInputFd, scale)) {
         ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
+    if (mIsDual) {
+        if (!mHwApiDual->setFFGain(mInputFdDual, scale)) {
+            ALOGE("Failed to set flip's gain to %u (%d): %s", scale, errno, strerror(errno));
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    }
     return ndk::ScopedAStatus::ok();
 }
 
@@ -730,6 +974,7 @@
     if (!set) {
         mLongEffectScale = 1.0;  // Reset the scale for the later new effect.
     }
+
     return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
 }
 
@@ -747,7 +992,7 @@
 
 ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
     std::string caldata{8, '0'};
-    if (!mHwCal->getF0(&caldata)) {
+    if (!mHwCalDef->getF0(&caldata)) {
         ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
@@ -758,7 +1003,7 @@
 
 ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
     std::string caldata{8, '0'};
-    if (!mHwCal->getQ(&caldata)) {
+    if (!mHwCalDef->getQ(&caldata)) {
         ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
@@ -1049,37 +1294,6 @@
     return mIsUnderExternalControl;
 }
 
-// BnVibratorSync APIs
-
-Status Vibrator::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
-    std::scoped_lock lock(mApiMutex);
-    ATRACE_NAME("Vibrator::prepareSynced");
-
-    if (isBusy() || isSynced() || isUnderExternalControl()) {
-        ALOGE("Vibrator::prepareSynced, isBusy or isSynced");
-        return Status::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    mSyncedCallback = callback;
-
-    return Status::ok();
-}
-
-Status Vibrator::cancelSynced() {
-    std::scoped_lock lock(mApiMutex);
-    ATRACE_NAME("Vibrator::cancelSynced");
-
-    if (!isSynced()) {
-        ALOGE("Vibrator::cancelSynced, isSynced");
-        return Status::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    off();
-    mSyncedCallback = nullptr;
-
-    return Status::ok();
-}
-
 // BnCInterface APIs
 
 binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
@@ -1093,29 +1307,38 @@
 
     dprintf(fd, "AIDL:\n");
 
-    dprintf(fd, "  F0 Offset: %" PRIu32 "\n", mF0Offset);
+    dprintf(fd, "  F0 Offset: base: %" PRIu32 " flip: %" PRIu32 "\n", mF0Offset, mF0OffsetDual);
 
     dprintf(fd, "  Voltage Levels:\n");
-    dprintf(fd, "    Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
+    dprintf(fd, "     Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
             mTickEffectVol[1]);
-    dprintf(fd, "    Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
+    dprintf(fd, "     Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
             mClickEffectVol[1]);
-    dprintf(fd, "    Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
+    dprintf(fd, "     Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
             mLongEffectVol[1]);
 
     dprintf(fd, "  FF effect:\n");
     dprintf(fd, "    Physical waveform:\n");
-    dprintf(fd, "\tId\tIndex\tt   ->\tt'\ttrigger button\n");
-    for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
+    dprintf(fd, "==== Base ====\n\tId\tIndex\tt   ->\tt'\ttrigger button\n");
+    uint8_t effectId;
+    for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
         dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id,
                 mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
                 mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button);
     }
+    if (mIsDual) {
+        dprintf(fd, "==== Flip ====\n\tId\tIndex\tt   ->\tt'\ttrigger button\n");
+        for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
+            dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffectsDual[effectId].id,
+                    mFfEffectsDual[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
+                    mFfEffectsDual[effectId].replay.length,
+                    mFfEffectsDual[effectId].trigger.button);
+        }
+    }
 
-    dprintf(fd, "    OWT waveform:\n");
-    dprintf(fd, "\tId\tBytes\tData\ttrigger button\n");
-    for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX;
-         effectId++) {
+    dprintf(fd, "Base: OWT waveform:\n");
+    dprintf(fd, "\tId\tBytes\tData\tt\ttrigger button\n");
+    for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
         uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
         std::stringstream ss;
         ss << " ";
@@ -1126,18 +1349,42 @@
                           i))
                << " ";
         }
-        dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
-                mFfEffects[effectId].trigger.button);
+        dprintf(fd, "\t%d\t%d\t{%s}\t%u\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
+                mFfEffectsDual[effectId].replay.length, mFfEffects[effectId].trigger.button);
     }
-
+    if (mIsDual) {
+        dprintf(fd, "Flip: OWT waveform:\n");
+        dprintf(fd, "\tId\tBytes\tData\tt\ttrigger button\n");
+        for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
+            uint32_t numBytes = mFfEffectsDual[effectId].u.periodic.custom_len * 2;
+            std::stringstream ss;
+            ss << " ";
+            for (int i = 0; i < numBytes; i++) {
+                ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex
+                   << (uint16_t)(*(reinterpret_cast<uint8_t *>(
+                                           mFfEffectsDual[effectId].u.periodic.custom_data) +
+                                   i))
+                   << " ";
+            }
+            dprintf(fd, "\t%d\t%d\t{%s}\t%u\t%X\n", mFfEffectsDual[effectId].id, numBytes,
+                    ss.str().c_str(), mFfEffectsDual[effectId].replay.length,
+                    mFfEffectsDual[effectId].trigger.button);
+        }
+    }
     dprintf(fd, "\n");
     dprintf(fd, "\n");
 
-    mHwApi->debug(fd);
+    mHwApiDef->debug(fd);
 
     dprintf(fd, "\n");
 
-    mHwCal->debug(fd);
+    mHwCalDef->debug(fd);
+
+    if (mIsDual) {
+        mHwApiDual->debug(fd);
+        dprintf(fd, "\n");
+        mHwCalDual->debug(fd);
+    }
 
     fsync(fd);
     return STATUS_OK;
@@ -1148,7 +1395,7 @@
     // 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.
     if (!mConfigHapticAlsaDeviceDone) {
-        if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) {
+        if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) {
             mHasHapticAlsaDevice = true;
             mConfigHapticAlsaDeviceDone = true;
         } else {
@@ -1260,6 +1507,10 @@
     }
 
     *outTimeMs = timeMs;
+    mFfEffects[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
+    if (mIsDual) {
+        mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
+    }
 
     return ndk::ScopedAStatus::ok();
 }
@@ -1356,26 +1607,55 @@
 }
 
 void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
-    ALOGD("Callback status in waitForComplete(): mSync: %d, callBack: %d", isSynced(),
+    ALOGD("waitForComplete: Callback status in waitForComplete(): callBack: %d",
           (callback != nullptr));
 
-    if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC,
-                               (mSyncedCallback) ? POLLING_TIMEOUT_IN_SYNC : POLLING_TIMEOUT)) {
-        ALOGV("Fail to get state \"Haptic\"");
+    // Bypass checking flip part's haptic state
+    if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
+        ALOGD("Failed to get state \"Haptic\"");
     }
 
-    mHwApi->pollVibeState(VIBE_STATE_STOPPED);
+    mHwApiDef->pollVibeState(VIBE_STATE_STOPPED);
+    // Check flip's state after base was done
+    if (mIsDual) {
+        mHwApiDual->pollVibeState(VIBE_STATE_STOPPED);
+    }
+    ALOGD("waitForComplete: get STOP");
     {
         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);
-        }
-        if (mActiveId >= 0) {
-            mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
-            mActiveId = -1;
+        if (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) {
+            if (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects)) {
+                ALOGE("Failed to clean up the composed effect %d", mActiveId);
+            }
+            if (mIsDual &&
+                (!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
+                ALOGE("Failed to clean up flip's composed effect %d", mActiveId);
+            }
         } else {
-            ALOGV("waitForComplete: Vibrator is already off");
+            ALOGD("waitForComplete: Vibrator is already off");
+        }
+        mActiveId = -1;
+        if (mGPIOStatus && !mHwGPIO->setGPIOOutput(false)) {
+            ALOGE("waitForComplete: Failed to reset GPIO(%d): %s", errno, strerror(errno));
+        }
+        // Do waveform number checking
+        uint32_t effectCount = WAVEFORM_MAX_PHYSICAL_INDEX;
+        mHwApiDef->getEffectCount(&effectCount);
+        if (effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) {
+            // Forcibly clean all OWT waveforms
+            if (!mHwApiDef->eraseOwtEffect(mInputFd, WAVEFORM_MAX_INDEX, &mFfEffects)) {
+                ALOGE("Failed to clean up all base's composed effect");
+            }
+        }
+
+        if (mIsDual) {
+            // Forcibly clean all OWT waveforms
+            effectCount = WAVEFORM_MAX_PHYSICAL_INDEX;
+            mHwApiDual->getEffectCount(&effectCount);
+            if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) &&
+                (!mHwApiDual->eraseOwtEffect(mInputFdDual, WAVEFORM_MAX_INDEX, &mFfEffectsDual))) {
+                ALOGE("Failed to clean up all flip's composed effect");
+            }
         }
     }
 
@@ -1385,14 +1665,7 @@
             ALOGE("Failed completion callback: %d", ret.getExceptionCode());
         }
     }
-    if (mSyncedCallback) {
-        auto ret = mSyncedCallback->onComplete();
-        if (!ret.isOk()) {
-            ALOGE("Failed completion mSyncedCallback: %d", ret.exceptionCode());
-        }
-        mSyncedCallback = nullptr;
-    }
-    ALOGV("waitForComplete: Done");
+    ALOGD("waitForComplete: Done.");
 }
 
 uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
@@ -1425,16 +1698,6 @@
     return volLevel;
 }
 
-bool Vibrator::isBusy() {
-    auto timeout = std::chrono::seconds::zero();
-    auto status = mAsyncHandle.wait_for(timeout);
-    return status != std::future_status::ready;
-}
-
-bool Vibrator::isSynced() {
-    return (mSyncedCallback != nullptr);
-}
-
 }  // namespace vibrator
 }  // namespace hardware
 }  // namespace android
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
index 578a86a..6a29343 100644
--- a/vibrator/cs40l26/Vibrator.h
+++ b/vibrator/cs40l26/Vibrator.h
@@ -17,7 +17,6 @@
 
 #include <aidl/android/hardware/vibrator/BnVibrator.h>
 #include <android-base/unique_fd.h>
-#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
 #include <linux/input.h>
 #include <tinyalsa/asoundlib.h>
 
@@ -30,12 +29,22 @@
 namespace hardware {
 namespace vibrator {
 
-using ::android::sp;
-using ::android::binder::Status;
-using ::android::hardware::vibrator::IVibratorSyncCallback;
-
 class Vibrator : public BnVibrator {
   public:
+    // APIs for interfacing with the GPIO pin.
+    class HwGPIO {
+      public:
+        virtual ~HwGPIO() = default;
+        // Get the GPIO pin num and address shift information
+        virtual bool getGPIO() = 0;
+        // Init the GPIO function
+        virtual bool initGPIO() = 0;
+        // Trigger the GPIO pin to synchronize both vibrators's play
+        virtual bool setGPIOOutput(bool value) = 0;
+        // Emit diagnostic information to the given file.
+        virtual void debug(int fd) = 0;
+    };
+
     // APIs for interfacing with the kernel driver.
     class HwApi {
       public:
@@ -85,8 +94,6 @@
                                      int *status) = 0;
         // Erase OWT waveform
         virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
-        // Erase trigger button information
-        virtual void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) = 0;
         // Emit diagnostic information to the given file.
         virtual void debug(int fd) = 0;
     };
@@ -100,6 +107,9 @@
         // Obtains the LRA resonant frequency to be used for PWLE playback
         // and click compensation.
         virtual bool getF0(std::string *value) = 0;
+        // Obtains the offset for actuator that will adjust configured F0 to target
+        // frequency for dual actuators
+        virtual bool getF0SyncOffset(uint32_t *value) = 0;
         // Obtains the LRA series resistance to be used for click
         // compensation.
         virtual bool getRedc(std::string *value) = 0;
@@ -126,7 +136,9 @@
     };
 
   public:
-    Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
+    Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
+             std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
+             std::unique_ptr<HwGPIO> hwgpio);
 
     // BnVibrator APIs
     ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
@@ -160,13 +172,11 @@
     ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
                                    const std::shared_ptr<IVibratorCallback> &callback) override;
 
-    // BnVibratorSync APIs
-    Status prepareSynced(const sp<IVibratorSyncCallback> &callback);
-    Status cancelSynced();
-
     // BnCInterface APIs
     binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
 
+    static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;  // SVC initialization time
+
   private:
     ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
                           const std::shared_ptr<IVibratorCallback> &callback);
@@ -195,33 +205,37 @@
     bool hasHapticAlsaDevice();
     bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
 
-    bool isBusy();
-    bool isSynced();
-
-    std::unique_ptr<HwApi> mHwApi;
-    std::unique_ptr<HwCal> mHwCal;
+    std::unique_ptr<HwApi> mHwApiDef;
+    std::unique_ptr<HwCal> mHwCalDef;
+    std::unique_ptr<HwApi> mHwApiDual;
+    std::unique_ptr<HwCal> mHwCalDual;
+    std::unique_ptr<HwGPIO> mHwGPIO;
     uint32_t mF0Offset;
+    uint32_t mF0OffsetDual;
     std::array<uint32_t, 2> mTickEffectVol;
     std::array<uint32_t, 2> mClickEffectVol;
     std::array<uint32_t, 2> mLongEffectVol;
     std::vector<ff_effect> mFfEffects;
+    std::vector<ff_effect> mFfEffectsDual;
     std::vector<uint32_t> mEffectDurations;
     std::future<void> mAsyncHandle;
     ::android::base::unique_fd mInputFd;
+    ::android::base::unique_fd mInputFdDual;
     int8_t mActiveId{-1};
     struct pcm *mHapticPcm;
     int mCard;
     int mDevice;
     bool mHasHapticAlsaDevice{false};
     bool mIsUnderExternalControl;
-    float mLongEffectScale = 1.0;
+    float mLongEffectScale{1.0};
     bool mIsChirpEnabled;
     uint32_t mSupportedPrimitivesBits = 0x0;
     std::vector<CompositePrimitive> mSupportedPrimitives;
+    std::vector<float> mPrimitiveMinScale;
     bool mConfigHapticAlsaDeviceDone{false};
-    // prevent concurrent execution of IVibrator and IVibratorSync APIs
-    sp<IVibratorSyncCallback> mSyncedCallback;
-    std::recursive_mutex mApiMutex;
+    bool mGPIOStatus;
+    bool mIsDual{false};
+    std::mutex mActiveId_mutex;  // protects mActiveId
 };
 
 }  // namespace vibrator
diff --git a/vibrator/cs40l26/VibratorManager.cpp b/vibrator/cs40l26/VibratorManager.cpp
deleted file mode 100644
index b6c1b48..0000000
--- a/vibrator/cs40l26/VibratorManager.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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.
- */
-
-#include "VibratorManager.h"
-
-#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-#include <numeric>
-
-namespace aidl {
-namespace android {
-namespace hardware {
-namespace vibrator {
-
-using ::android::sp;
-using ::android::binder::Status;
-using ::android::hardware::vibrator::BnVibratorSyncCallback;
-
-class VibratorSyncCallback : public BnVibratorSyncCallback {
-  public:
-    Status onComplete() override {
-        mPromise.set_value();
-        return Status::ok();
-    }
-    auto getFuture() { return mPromise.get_future(); }
-
-  private:
-    std::promise<void> mPromise;
-};
-
-VibratorManager::VibratorManager(std::unique_ptr<HwApi> hwapi,
-                                 const std::vector<VibratorTuple> &&vibrators)
-    : mHwApi(std::move(hwapi)), mVibrators(std::move(vibrators)), mAsyncHandle(std::async([] {})) {
-    mGPIOStatus = mHwApi->getGPIO();
-}
-
-// BnVibratorManager APIs
-
-ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t *_aidl_return) {
-    ATRACE_NAME("VibratorManager::getCapabilities");
-    int32_t ret =
-            IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
-            IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
-            IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
-            IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
-
-    *_aidl_return = ret;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int> *_aidl_return) {
-    ATRACE_NAME("VibratorManager::getVibratorIds");
-    _aidl_return->resize(mVibrators.size());
-    std::iota(_aidl_return->begin(), _aidl_return->end(), 0);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus VibratorManager::getVibrator(int vibratorId,
-                                                std::shared_ptr<IVibrator> *_aidl_return) {
-    ATRACE_NAME("VibratorManager::getVibrator");
-    if (!mGPIOStatus) {
-        ALOGE("GetVibrator: GPIO status error");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    if (vibratorId >= mVibrators.size()) {
-        ALOGE("GetVibrator: wrong requested vibrator ID");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    std::tie(*_aidl_return, std::ignore) = mVibrators.at(vibratorId);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t> &ids) {
-    ATRACE_NAME("VibratorManager::prepareSynced");
-
-    if (!mGPIOStatus) {
-        ALOGE("prepareSynced: GPIO status error");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    if (ids.empty()) {
-        ALOGE("PrepareSynced: No vibrator could be synced");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    if (!mSyncContext.empty()) {
-        ALOGE("PrepareSynced: mSyncContext is not EMPTY!!!");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    if (isBusy()) {
-        ALOGE("PrepareSynced: IS BUSY!!!");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    for (auto &id : ids) {
-        auto &[vib, ext] = mVibrators.at(id);
-        auto callback = sp<VibratorSyncCallback>::make();
-
-        if (ext->prepareSynced(callback).isOk()) {
-            mSyncContext.emplace_back(id, callback->getFuture());
-        } else {
-            cancelSynced();
-            ALOGV("prepareSynced: Fail: %d", id);
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
-    }
-    ALOGV("prepareSynced: Done");
-    if (mHwApi->initGPIO()) {
-        return ndk::ScopedAStatus::ok();
-    } else {
-        ALOGE("PrepareSynced: GPIO status init fail");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-}
-
-ndk::ScopedAStatus VibratorManager::triggerSynced(
-        const std::shared_ptr<IVibratorCallback> &callback) {
-    ATRACE_NAME("VibratorManager::triggerSynced");
-    ALOGV("TriggerSynced");
-    if (isBusy()) {
-        ALOGE("TriggerSynced isBusy");
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    mHwApi->setTrigger(true);
-
-    doAsync([=]() {
-        {
-            std::shared_lock lock(mContextMutex);
-            for (auto &[id, future] : mSyncContext) {
-                future.wait();
-            }
-        }
-        {
-            std::unique_lock lock(mContextMutex);
-            mSyncContext.clear();
-        }
-        if (callback) {
-            auto ret = callback->onComplete();
-            if (!ret.isOk()) {
-                ALOGE("Failed completion callback: %d", ret.getExceptionCode());
-            }
-            ALOGD("Callback in MANAGER onComplete()");
-        }
-    });
-
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus VibratorManager::cancelSynced() {
-    ATRACE_NAME("VibratorManager::cancelSynced");
-
-    ALOGV("Do cancelSynced");
-    mHwApi->setTrigger(false);
-    {
-        std::shared_lock lock(mContextMutex);
-        for (auto &[id, future] : mSyncContext) {
-            auto &[vib, ext] = mVibrators.at(id);
-            ext->cancelSynced();
-        }
-    }
-    {
-        std::unique_lock lock(mContextMutex);
-        mSyncContext.clear();
-    }
-    mAsyncHandle.wait();
-
-    return ndk::ScopedAStatus::ok();
-}
-
-// BnCInterface APIs
-
-binder_status_t VibratorManager::dump(int fd, const char ** /*args*/, uint32_t /*numArgs*/) {
-    if (fd < 0) {
-        ALOGE("Called debug() with invalid fd.");
-        return STATUS_OK;
-    }
-
-    mHwApi->debug(fd);
-
-    fsync(fd);
-
-    return STATUS_OK;
-}
-
-// Private Methods
-
-template <typename Func, typename... Args>
-void VibratorManager::doAsync(Func &&func, Args &&...args) {
-    mAsyncHandle = std::async(func, std::forward<Args>(args)...);
-}
-
-bool VibratorManager::isBusy() {
-    auto timeout = std::chrono::seconds::zero();
-    auto status = mAsyncHandle.wait_for(timeout);
-    return status != std::future_status::ready;
-}
-
-}  // namespace vibrator
-}  // namespace hardware
-}  // namespace android
-}  // namespace aidl
diff --git a/vibrator/cs40l26/VibratorManager.h b/vibrator/cs40l26/VibratorManager.h
deleted file mode 100644
index e518a20..0000000
--- a/vibrator/cs40l26/VibratorManager.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <aidl/android/hardware/vibrator/BnVibrator.h>
-#include <aidl/android/hardware/vibrator/BnVibratorManager.h>
-#include <android/hardware/vibrator/IVibratorSync.h>
-
-#include <future>
-#include <shared_mutex>
-
-namespace aidl {
-namespace android {
-namespace hardware {
-namespace vibrator {
-
-using ::android::hardware::vibrator::IVibratorSync;
-
-class VibratorManager : public BnVibratorManager {
-  public:
-    // APIs for interfacing with the kernel driver.
-    class HwApi {
-      public:
-        virtual ~HwApi() = default;
-        // Get the GPIO pin num and address shift information
-        virtual bool getGPIO() = 0;
-        // Init the GPIO function
-        virtual bool initGPIO() = 0;
-        // Trigger activation of the synchronized vibrators.
-        virtual bool setTrigger(bool value) = 0;
-        // Emit diagnostic information to the given file.
-        virtual void debug(int fd) = 0;
-    };
-
-    using VibratorTuple = std::tuple<std::shared_ptr<IVibrator>, ::android::sp<IVibratorSync>>;
-
-  public:
-    VibratorManager(std::unique_ptr<HwApi> hwapi, const std::vector<VibratorTuple> &&vibrators);
-
-    // BnVibratorManager APIs
-    ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return);
-    ndk::ScopedAStatus getVibratorIds(std::vector<int> *_aidl_return);
-    ndk::ScopedAStatus getVibrator(int vibratorId, std::shared_ptr<IVibrator> *_aidl_return);
-    ndk::ScopedAStatus prepareSynced(const std::vector<int32_t> &ids) override;
-    ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback> &callback) override;
-    ndk::ScopedAStatus cancelSynced() override;
-
-    // BnCInterface APIs
-    binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
-
-  private:
-    template <typename Func, typename... Args>
-    void doAsync(Func &&func, Args &&...args);
-    bool isBusy();
-
-  private:
-    using SyncContext = std::tuple<int32_t, std::future<void>>;
-
-    const std::unique_ptr<HwApi> mHwApi;
-    const std::vector<VibratorTuple> mVibrators;
-    std::vector<SyncContext> mSyncContext;
-    std::shared_mutex mContextMutex;
-    std::future<void> mAsyncHandle;
-    bool mGPIOStatus;
-};
-
-}  // namespace vibrator
-}  // namespace hardware
-}  // namespace android
-}  // namespace aidl
diff --git a/vibrator/cs40l26/VibratorSync.cpp b/vibrator/cs40l26/VibratorSync.cpp
deleted file mode 100644
index 437ee01..0000000
--- a/vibrator/cs40l26/VibratorSync.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-#include "VibratorSync.h"
-
-namespace android {
-namespace hardware {
-namespace vibrator {
-
-VibratorSync::VibratorSync(std::shared_ptr<Vibrator> vibrator) : mVibrator(vibrator) {
-    ALOGE("VibratorSync constructor");
-}
-
-// BnVibratorSync APIs
-
-binder::Status VibratorSync::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
-    return mVibrator->prepareSynced(callback);
-}
-
-binder::Status VibratorSync::cancelSynced() {
-    return mVibrator->cancelSynced();
-}
-
-}  // namespace vibrator
-}  // namespace hardware
-}  // namespace android
diff --git a/vibrator/cs40l26/VibratorSync.h b/vibrator/cs40l26/VibratorSync.h
deleted file mode 100644
index fd9195a..0000000
--- a/vibrator/cs40l26/VibratorSync.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <android/hardware/vibrator/BnVibratorSync.h>
-
-#include <array>
-#include <fstream>
-#include <future>
-
-#include "Vibrator.h"
-
-namespace android {
-namespace hardware {
-namespace vibrator {
-
-using ::aidl::android::hardware::vibrator::Vibrator;
-
-class VibratorSync : public BnVibratorSync {
-  public:
-    VibratorSync(std::shared_ptr<Vibrator> vibrator);
-
-    // BnVibratorSync APIs
-    binder::Status prepareSynced(const android::sp<IVibratorSyncCallback> &callback) override;
-    binder::Status cancelSynced() override;
-
-  private:
-    std::shared_ptr<Vibrator> mVibrator;
-};
-
-}  // namespace vibrator
-}  // namespace hardware
-}  // namespace android
diff --git a/vibrator/cs40l26/aidl/Android.bp b/vibrator/cs40l26/aidl/Android.bp
deleted file mode 100644
index 8876278..0000000
--- a/vibrator/cs40l26/aidl/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "device_google_felix_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["device_google_felix_license"],
-}
-
-aidl_interface {
-    name: "android.hardware.vibrator.cs40l26-private",
-    srcs: [
-        "android/hardware/vibrator/*.aidl"
-    ],
-    backend: {
-        java: {
-            enabled: false,
-        },
-    },
-    unstable: true,
-    vendor_available: true,
-}
diff --git a/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSync.aidl b/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSync.aidl
deleted file mode 100644
index 5371b13..0000000
--- a/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSync.aidl
+++ /dev/null
@@ -1,24 +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.
- */
-
-package android.hardware.vibrator;
-
-import android.hardware.vibrator.IVibratorSyncCallback;
-
-interface IVibratorSync {
-    void prepareSynced(in IVibratorSyncCallback callback);
-    void cancelSynced();
-}
diff --git a/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSyncCallback.aidl b/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSyncCallback.aidl
deleted file mode 100644
index 5281229..0000000
--- a/vibrator/cs40l26/aidl/android/hardware/vibrator/IVibratorSyncCallback.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-package android.hardware.vibrator;
-
-interface IVibratorSyncCallback {
-    oneway void onComplete();
-}
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.rc
deleted file mode 100644
index 07a43ff..0000000
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.rc
+++ /dev/null
@@ -1,47 +0,0 @@
-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
-    chmod 770 /mnt/vendor/persist/haptics
-    chmod 440 /mnt/vendor/persist/haptics/cs40l26_dual.cal
-    chown system system /mnt/vendor/persist/haptics
-    chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
-
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
-    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
-
-    enable vendor.vibrator.cs40l26-dual
-
-service vendor.vibrator.cs40l26-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-dual-private
-    class hal
-    user system
-    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
-
-    setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a-dual/
-    setenv HWAPI_DEBUG_PATHS "
-        calibration/f0_stored
-        calibration/redc_stored
-        calibration/q_stored
-        default/vibe_state
-        default/num_waves
-        default/f0_offset
-        default/owt_free_space
-        default/f0_comp_enable
-        default/redc_comp_enable
-        default/delay_before_stop_playback_us
-        "
-
-    disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.xml b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.xml
deleted file mode 100644
index 1bd3e7e..0000000
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual-private.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.vibrator</name>
-        <version>2</version>
-        <fqname>IVibrator/dual</fqname>
-    </hal>
-</manifest>
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc
index bbc0135..2799cdc 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc
@@ -4,19 +4,32 @@
     mkdir /mnt/vendor/persist/haptics 0770 system system
     chmod 770 /mnt/vendor/persist/haptics
     chmod 440 /mnt/vendor/persist/haptics/cs40l26.cal
+    chmod 440 /mnt/vendor/persist/haptics/cs40l26_dual.cal
     chown system system /mnt/vendor/persist/haptics
     chown system system /mnt/vendor/persist/haptics/cs40l26.cal
+    chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
 
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
     chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
+    chown system system /dev/gpiochip44
 
     enable vendor.vibrator.cs40l26
 
@@ -25,12 +38,16 @@
     user system
     group system input
 
+    setenv HAPTIC_NAME Haptics
     setenv INPUT_EVENT_NAME cs40l26_input
+    setenv INPUT_EVENT_NAME_DUAL 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.cal
+    setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal
 
     setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
+    setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/
     setenv HWAPI_DEBUG_PATHS "
         calibration/f0_stored
         calibration/redc_stored
@@ -45,3 +62,4 @@
         "
 
     disabled
+
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.rc
deleted file mode 100644
index 01642dd..0000000
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.rc
+++ /dev/null
@@ -1,14 +0,0 @@
-on property:vendor.all.modules.ready=1
-    wait_for_prop init.svc.vendor.vibrator.cs40l26 running
-    wait_for_prop init.svc.vendor.vibrator.cs40l26-dual running
-
-    enable vendor.vibrator.cs40l26-stereo
-
-service vendor.vibrator.cs40l26-stereo /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-stereo-private
-    class hal
-    user root
-    group root
-
-    setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
-
-    disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.xml b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.xml
deleted file mode 100644
index 9056bd0..0000000
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-stereo-private.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.vibrator</name>
-        <version>2</version>
-        <fqname>IVibratorManager/default</fqname>
-    </hal>
-</manifest>
diff --git a/vibrator/cs40l26/device-stereo.mk b/vibrator/cs40l26/device-stereo.mk
deleted file mode 100644
index 171d204..0000000
--- a/vibrator/cs40l26/device-stereo.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-PRODUCT_PACKAGES += \
-    android.hardware.vibrator-service.cs40l26-private \
-    android.hardware.vibrator-service.cs40l26-dual-private \
-    android.hardware.vibrator-service.cs40l26-stereo-private \
-
-BOARD_SEPOLICY_DIRS += \
-    hardware/google/pixel-sepolicy/vibrator/cs40l26 \
diff --git a/vibrator/cs40l26/device.mk b/vibrator/cs40l26/device.mk
index fe4bbc8..2c4b2e2 100644
--- a/vibrator/cs40l26/device.mk
+++ b/vibrator/cs40l26/device.mk
@@ -2,5 +2,5 @@
     android.hardware.vibrator-service.cs40l26-private
 
 BOARD_SEPOLICY_DIRS += \
-    device/google/felix-sepolicy/vibrator/common \
-    device/google/felix-sepolicy/vibrator/cs40l26
+    hardware/google/pixel-sepolicy/vibrator/common \
+    hardware/google/pixel-sepolicy/vibrator/cs40l26
diff --git a/vibrator/cs40l26/service-stereo.cpp b/vibrator/cs40l26/service-stereo.cpp
deleted file mode 100644
index cd05035..0000000
--- a/vibrator/cs40l26/service-stereo.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.
- */
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <log/log.h>
-
-#include "VibMgrHwApi.h"
-#include "VibratorManager.h"
-
-using ::aidl::android::hardware::vibrator::IVibrator;
-using ::aidl::android::hardware::vibrator::VibMgrHwApi;
-using ::aidl::android::hardware::vibrator::VibratorManager;
-using ::android::ProcessState;
-using ::android::String16;
-using ::android::waitForService;
-using ::android::hardware::vibrator::IVibratorSync;
-
-#if !defined(VIBRATOR_NAME)
-#define VIBRATOR_NAME "default"
-#endif
-
-using ndk::SharedRefBase;
-using ndk::SpAIBinder;
-
-int main() {
-    auto hwapi = VibMgrHwApi::Create();
-    if (!hwapi) {
-        return EXIT_FAILURE;
-    }
-
-    const std::string vibratorInstances[] = {
-            "default",
-            "dual",
-    };
-    std::vector<VibratorManager::VibratorTuple> vibrators;
-
-    ProcessState::initWithDriver("/dev/vndbinder");
-
-    for (auto &instance : vibratorInstances) {
-        const auto svcName = std::string() + IVibrator::descriptor + "/" + instance;
-        const auto extName = std::stringstream() << IVibratorSync::descriptor << "/" << instance;
-
-        SpAIBinder svcBinder;
-        svcBinder = SpAIBinder(AServiceManager_getService(svcName.c_str()));
-        auto svc = IVibrator::fromBinder(svcBinder);
-
-        auto ext = waitForService<IVibratorSync>(String16(extName.str().c_str()));
-
-        vibrators.emplace_back(svc, ext);
-    }
-
-    auto mgr = ndk::SharedRefBase::make<VibratorManager>(std::move(hwapi), std::move(vibrators));
-    binder_status_t status;
-
-    const std::string mgrInst = std::string() + VibratorManager::descriptor + "/" VIBRATOR_NAME;
-    status = AServiceManager_addService(mgr->asBinder().get(), mgrInst.c_str());
-    LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
-
-    // Only used for callbacks
-    ProcessState::self()->setThreadPoolMaxThreadCount(1);
-    ProcessState::self()->startThreadPool();
-
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-    ABinderProcess_joinThreadPool();
-
-    return EXIT_FAILURE;  // should not reach
-}
diff --git a/vibrator/cs40l26/service.cpp b/vibrator/cs40l26/service.cpp
index fd76b19..605de43 100644
--- a/vibrator/cs40l26/service.cpp
+++ b/vibrator/cs40l26/service.cpp
@@ -20,34 +20,60 @@
 #include <log/log.h>
 
 #include "Hardware.h"
+#include "VibMgrHwApi.h"
 #include "Vibrator.h"
-#include "VibratorSync.h"
 
 using ::aidl::android::hardware::vibrator::HwApi;
 using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::VibMgrHwApi;
 using ::aidl::android::hardware::vibrator::Vibrator;
 using ::android::defaultServiceManager;
 using ::android::ProcessState;
 using ::android::sp;
 using ::android::String16;
-using ::android::hardware::vibrator::VibratorSync;
 
 #if !defined(VIBRATOR_NAME)
 #define VIBRATOR_NAME "default"
 #endif
 
 int main() {
-    auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(),
-                                                  std::make_unique<HwCal>());
+    const char *hwApiPathPrefixDual = std::getenv("HWAPI_PATH_PREFIX_DUAL");
+    const char *calFilePath = std::getenv("CALIBRATION_FILEPATH");
+    const char *calFilePathDual = std::getenv("CALIBRATION_FILEPATH_DUAL");
+
+    auto hwgpio = VibMgrHwApi::Create();
+    if (!hwgpio) {
+        return EXIT_FAILURE;
+    }
+    auto hwApiDef = HwApi::Create();
+    if (!hwApiDef) {
+        return EXIT_FAILURE;
+    }
+    auto hwCalDef = HwCal::Create();
+    if (!hwCalDef) {
+        return EXIT_FAILURE;
+    }
+
+    std::shared_ptr<Vibrator> svc;
+    // Synchronize base and flip actuator F0.
+    // Replace dual cal file path to base and copy the base to dual's path.
+    if ((hwApiPathPrefixDual != nullptr) && !setenv("HWAPI_PATH_PREFIX", hwApiPathPrefixDual, 1) &&
+        (calFilePathDual != nullptr) && !setenv("CALIBRATION_FILEPATH", calFilePathDual, 1) &&
+        !setenv("CALIBRATION_FILEPATH_DUAL", calFilePath, 1)) {
+        ALOGD("Init dual HAL: %s", std::getenv("HWAPI_PATH_PREFIX"));
+        svc = ndk::SharedRefBase::make<Vibrator>(std::move(hwApiDef), std::move(hwCalDef),
+                                                 std::make_unique<HwApi>(),
+                                                 std::make_unique<HwCal>(), std::move(hwgpio));
+    } else {
+        ALOGD("Failed to init dual HAL");
+        svc = ndk::SharedRefBase::make<Vibrator>(std::move(hwApiDef), std::move(hwCalDef), nullptr,
+                                                 nullptr, std::move(hwgpio));
+    }
+
     const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
 
-    auto ext = sp<VibratorSync>::make(svc);
-    const auto extName = std::stringstream() << ext->descriptor << "/" << VIBRATOR_NAME;
-
     ProcessState::initWithDriver("/dev/vndbinder");
 
-    defaultServiceManager()->addService(String16(extName.str().c_str()), ext);
-
     auto svcBinder = svc->asBinder();
     binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
     LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
diff --git a/vibrator/cs40l26/tests/Android.bp b/vibrator/cs40l26/tests/Android.bp
index 6061ce8..48baa90 100644
--- a/vibrator/cs40l26/tests/Android.bp
+++ b/vibrator/cs40l26/tests/Android.bp
@@ -22,8 +22,8 @@
     defaults: ["VibratorHalCs40l26TestDefaultsPrivate"],
     srcs: [
         "test-hwcal.cpp",
-	"test-hwapi.cpp",
-	"test-vibrator.cpp",
+        "test-hwapi.cpp",
+        "test-vibrator.cpp",
     ],
     static_libs: [
         "libc++fs",
diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h
index e497f2a..c85b0b5 100644
--- a/vibrator/cs40l26/tests/mocks.h
+++ b/vibrator/cs40l26/tests/mocks.h
@@ -20,6 +20,17 @@
 
 #include "Vibrator.h"
 
+class MockGPIO : public ::aidl::android::hardware::vibrator::Vibrator::HwGPIO {
+  public:
+    MOCK_METHOD0(destructor, void());
+    MOCK_METHOD0(getGPIO, bool());
+    MOCK_METHOD0(initGPIO, bool());
+    MOCK_METHOD1(setGPIOOutput, bool(bool value));
+    MOCK_METHOD1(debug, void(int fd));
+
+    ~MockGPIO() override { destructor(); };
+};
+
 class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
   public:
     MOCK_METHOD0(destructor, void());
@@ -43,7 +54,6 @@
                  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_METHOD3(clearTrigBtn, void(int fd, struct ff_effect *effect, int8_t index));
     MOCK_METHOD1(debug, void(int fd));
 
     ~MockApi() override { destructor(); };
@@ -54,6 +64,7 @@
     MOCK_METHOD0(destructor, void());
     MOCK_METHOD1(getVersion, bool(uint32_t *value));
     MOCK_METHOD1(getF0, bool(std::string &value));
+    MOCK_METHOD1(getF0SyncOffset, bool(uint32_t *value));
     MOCK_METHOD1(getRedc, bool(std::string &value));
     MOCK_METHOD1(getQ, bool(std::string &value));
     MOCK_METHOD1(getLongFrequencyShift, bool(int32_t *value));
diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp
index a8bedd5..3ea6bda 100644
--- a/vibrator/cs40l26/tests/test-vibrator.cpp
+++ b/vibrator/cs40l26/tests/test-vibrator.cpp
@@ -203,20 +203,24 @@
         setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
         std::unique_ptr<MockApi> mockapi;
         std::unique_ptr<MockCal> mockcal;
+        std::unique_ptr<MockGPIO> mockgpio;
 
-        createMock(&mockapi, &mockcal);
-        createVibrator(std::move(mockapi), std::move(mockcal));
+        createMock(&mockapi, &mockcal, &mockgpio);
+        createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio));
     }
 
     void TearDown() override { deleteVibrator(); }
 
   protected:
-    void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal) {
+    void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal,
+                    std::unique_ptr<MockGPIO> *mockgpio) {
         *mockapi = std::make_unique<MockApi>();
         *mockcal = std::make_unique<MockCal>();
+        *mockgpio = std::make_unique<MockGPIO>();
 
         mMockApi = mockapi->get();
         mMockCal = mockcal->get();
+        mMockGpio = mockgpio->get();
 
         ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
 
@@ -242,15 +246,20 @@
         ON_CALL(*mMockCal, getLongVolLevels(_))
                 .WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
 
+        ON_CALL(*mMockGpio, destructor()).WillByDefault(Assign(&mMockGpio, nullptr));
+
         relaxMock(false);
     }
 
     void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
-                        bool relaxed = true) {
+                        std::unique_ptr<MockGPIO> mockgpio, bool relaxed = true) {
         if (relaxed) {
             relaxMock(true);
         }
-        mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal));
+        // TODO(b/261415845): Need to add dual parameters to test the vibrator HAL's code in haptics
+        // mock test
+        mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal),
+                                                       nullptr, nullptr, std::move(mockgpio));
         if (relaxed) {
             relaxMock(false);
         }
@@ -306,6 +315,7 @@
   protected:
     MockApi *mMockApi;
     MockCal *mMockCal;
+    MockGPIO *mMockGpio;
     std::shared_ptr<IVibrator> mVibrator;
     uint32_t mEffectIndex;
 };
@@ -313,6 +323,7 @@
 TEST_F(VibratorTest, Constructor) {
     std::unique_ptr<MockApi> mockapi;
     std::unique_ptr<MockCal> mockcal;
+    std::unique_ptr<MockGPIO> mockgpio;
     std::string f0Val = std::to_string(std::rand());
     std::string redcVal = std::to_string(std::rand());
     std::string qVal = std::to_string(std::rand());
@@ -323,10 +334,11 @@
 
     EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault());
     EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockGpio, destructor()).WillOnce(DoDefault());
 
     deleteVibrator(false);
 
-    createMock(&mockapi, &mockcal);
+    createMock(&mockapi, &mockcal, &mockgpio);
 
     EXPECT_CALL(*mMockCal, getF0(_))
             .InSequence(f0Seq)
@@ -363,7 +375,7 @@
             .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);
+    createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio), false);
 }
 
 TEST_F(VibratorTest, on) {