/*
 * 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 "Vibrator.h"

#include <glob.h>
#include <hardware/hardware.h>
#include <hardware/vibrator.h>
#include <linux/version.h>
#include <log/log.h>
#include <stdio.h>
#include <utils/Trace.h>

#include <cinttypes>
#include <cmath>
#include <fstream>
#include <iostream>
#include <memory>
#include <optional>
#include <sstream>

#ifndef ARRAY_SIZE
#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 {
namespace vibrator {

#define RECORD(fmt, ...) { \
  this->mHwApiDef->recordEvent(__func__, StringPrintf(fmt, ##__VA_ARGS__)); \
  if (this->mIsDual) { \
      this->mHwApiDual->recordEvent(__func__, StringPrintf(fmt, ##__VA_ARGS__)); \
    } \
}

#define RECORD_COMPOSE(...) \
    std::string effectString = ""; \
    for (auto &effect : composite) { \
        effectString += effect.toString() + ", "; \
    } \
    this->mHwApiDef->recordEvent(__func__, effectString.c_str()); \
    if (this->mIsDual) { \
      this->mHwApiDual->recordEvent(__func__, effectString.c_str()); \
    } \

static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044;  // (COMPOSE_SIZE_MAX + 1) * 8 + 4
static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_PWLE = 2302;

static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100;

static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50;

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 int8_t MAX_PAUSE_TIMING_ERROR_MS = 1;  // ALERT Irq Handling
static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;

static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 50;
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. */
static constexpr int32_t COMPOSE_SIZE_MAX = 254;
static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127;

// Measured resonant frequency, f0_measured, is represented by Q10.14 fixed
// point format on cs40l26 devices. The expression to calculate f0 is:
//   f0 = f0_measured / 2^Q14_BIT_SHIFT
// See the LRA Calibration Support documentation for more details.
static constexpr int32_t Q14_BIT_SHIFT = 14;

// Measured ReDC. The LRA series resistance (ReDC), expressed as follows
// redc(ohms) = redc_measured / 2^Q15_BIT_SHIFT.
// This value represents the unit-specific ReDC input to the click compensation
// algorithm. It can be overwritten at a later time by writing to the redc_stored
// sysfs control.
// See the LRA Calibration Support documentation for more details.
static constexpr int32_t Q15_BIT_SHIFT = 15;

// Measured Q factor, q_measured, is represented by Q8.16 fixed
// point format on cs40l26 devices. The expression to calculate q is:
//   q = q_measured / 2^Q16_BIT_SHIFT
// See the LRA Calibration Support documentation for more details.
static constexpr int32_t Q16_BIT_SHIFT = 16;

static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;

static constexpr uint32_t WT_LEN_CALCD = 0x00800000;
static constexpr uint8_t PWLE_CHIRP_BIT = 0x8;  // Dynamic/static frequency and voltage
static constexpr uint8_t PWLE_BRAKE_BIT = 0x4;
static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;

static constexpr uint8_t PWLE_WT_TYPE = 12;
static constexpr uint8_t PWLE_HEADER_WORD_COUNT = 3;
static constexpr uint8_t PWLE_HEADER_FTR_SHIFT = 8;
static constexpr uint8_t PWLE_SVC_METADATA_WORD_COUNT = 3;
static constexpr uint32_t PWLE_SVC_METADATA_TERMINATOR = 0xFFFFFF;
static constexpr uint8_t PWLE_SEGMENT_WORD_COUNT = 2;
static constexpr uint8_t PWLE_HEADER_WCOUNT_WORD_OFFSET = 2;
static constexpr uint8_t PWLE_WORD_SIZE = sizeof(uint32_t);

static constexpr uint8_t PWLE_SVC_NO_BRAKING = -1;
static constexpr uint8_t PWLE_SVC_CAT_BRAKING = 0;
static constexpr uint8_t PWLE_SVC_OPEN_BRAKING = 1;
static constexpr uint8_t PWLE_SVC_CLOSED_BRAKING = 2;
static constexpr uint8_t PWLE_SVC_MIXED_BRAKING = 3;

static constexpr uint32_t PWLE_SVC_MAX_BRAKING_TIME_MS = 1000;

static constexpr uint8_t PWLE_FTR_BUZZ_BIT = 0x80;
static constexpr uint8_t PWLE_FTR_CLICK_BIT = 0x00;
static constexpr uint8_t PWLE_FTR_DYNAMIC_F0_BIT = 0x10;
static constexpr uint8_t PWLE_FTR_SVC_METADATA_BIT = 0x04;
static constexpr uint8_t PWLE_FTR_DVL_BIT = 0x02;
static constexpr uint8_t PWLE_FTR_LF0T_BIT = 0x01;

static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.0;
static constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0;
static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00;
static constexpr float PWLE_FREQUENCY_MAX_HZ = 1000.00;
static constexpr float PWLE_BW_MAP_SIZE =
        1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);

/*
 * [15] Edge, 0:Falling, 1:Rising
 * [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI)
 * [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 + RAM + Not buzzgen
 */
static constexpr uint16_t GPIO_TRIGGER_CONFIG = 0x9100;

static uint16_t amplitudeToScale(float amplitude, float maximum) {
    float ratio = 100; /* Unit: % */
    if (maximum != 0)
        ratio = amplitude / maximum * 100;

    if (maximum == 0 || ratio > 100)
        ratio = 100;

    return std::round(ratio);
}

static float redcToFloat(std::string *caldata) {
    return static_cast<float>(std::stoul(*caldata, nullptr, 16)) / (1 << Q15_BIT_SHIFT);
}

enum WaveformBankID : uint8_t {
    RAM_WVFRM_BANK,
    ROM_WVFRM_BANK,
    OWT_WVFRM_BANK,
};

enum WaveformIndex : uint16_t {
    /* Physical waveform */
    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
    WAVEFORM_RESERVED_INDEX_1 = 1,
    WAVEFORM_CLICK_INDEX = 2,
    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
    WAVEFORM_THUD_INDEX = 4,
    WAVEFORM_SPIN_INDEX = 5,
    WAVEFORM_QUICK_RISE_INDEX = 6,
    WAVEFORM_SLOW_RISE_INDEX = 7,
    WAVEFORM_QUICK_FALL_INDEX = 8,
    WAVEFORM_LIGHT_TICK_INDEX = 9,
    WAVEFORM_LOW_TICK_INDEX = 10,
    WAVEFORM_RESERVED_MFG_1,
    WAVEFORM_RESERVED_MFG_2,
    WAVEFORM_RESERVED_MFG_3,
    WAVEFORM_MAX_PHYSICAL_INDEX,
    /* OWT waveform */
    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
    WAVEFORM_PWLE,
    /*
     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
     * #define FF_GAIN		0x60  // 96 in decimal
     * #define FF_MAX_EFFECTS	FF_GAIN
     */
    WAVEFORM_MAX_INDEX,
};

std::vector<CompositePrimitive> defaultSupportedPrimitives = {
        ndk::enum_range<CompositePrimitive>().begin(), ndk::enum_range<CompositePrimitive>().end()};

enum vibe_state {
    VIBE_STATE_STOPPED = 0,
    VIBE_STATE_HAPTIC,
    VIBE_STATE_ASP,
};


class DspMemChunk {
  private:
    std::unique_ptr<uint8_t[]> head;
    size_t bytes = 0;
    uint8_t waveformType;
    uint8_t *_current;
    const uint8_t *_max;
    uint32_t _cache = 0;
    int _cachebits = 0;

    bool isEnd() const { return _current == _max; }
    int min(int x, int y) { return x < y ? x : y; }

    int write(int nbits, uint32_t val) {
        int nwrite, i;

        nwrite = min(24 - _cachebits, nbits);
        _cache <<= nwrite;
        _cache |= val >> (nbits - nwrite);
        _cachebits += nwrite;
        nbits -= nwrite;

        if (_cachebits == 24) {
            if (isEnd())
                return -ENOSPC;

            _cache &= 0xFFFFFF;
            for (i = 0; i < sizeof(_cache); i++, _cache <<= 8)
                *_current++ = (_cache & 0xFF000000) >> 24;

            bytes += sizeof(_cache);
            _cachebits = 0;
        }

        if (nbits)
            return write(nbits, val);

        return 0;
    }

   int fToU16(float input, uint16_t *output, float scale, float min, float max) {
        if (input < min || input > max)
            return -ERANGE;

        *output = roundf(input * scale);
        return 0;
    }

    void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags,
                              uint32_t vbemfTarget = 0) {
        write(16, delay);
        write(12, amplitude);
        write(12, frequency);
        /* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
        write(8, (flags | 1) << 4);
        if (flags & PWLE_AMP_REG_BIT) {
            write(24, vbemfTarget); /* target back EMF voltage */
        }
    }

  public:
    uint8_t *front() const { return head.get(); }
    uint8_t type() const { return waveformType; }
    size_t size() const { return bytes; }

    DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
        waveformType = type;
        _current = head.get();
        _max = _current + size;

        if (waveformType == WAVEFORM_COMPOSE) {
            write(8, 0); /* Padding */
            write(8, 0); /* nsections placeholder */
            write(8, 0); /* repeat */
        } else if (waveformType == WAVEFORM_PWLE) {
            write(16, (PWLE_FTR_BUZZ_BIT | PWLE_FTR_DVL_BIT)
                              << PWLE_HEADER_FTR_SHIFT); /* Feature flag */
            write(8, PWLE_WT_TYPE);                      /* type12 */
            write(24, PWLE_HEADER_WORD_COUNT);           /* Header word count */
            write(24, 0);                                /* Body word count placeholder */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
            write(24, 0); /* Waveform length placeholder */
            write(8, 0);  /* Repeat */
            write(12, 0); /* Wait time between repeats */
            write(8, 0);  /* nsections placeholder */
#endif
        } else {
            ALOGE("%s: Invalid type: %u", __func__, waveformType);
        }
    }

    int flush() {
        if (!_cachebits)
            return 0;

        return write(24 - _cachebits, 0);
    }

    int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat,
                                uint8_t flags, uint16_t nextEffectDelay) {
        if (waveformType != WAVEFORM_COMPOSE) {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }
        if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
            ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
            return -EINVAL;
        }
        write(8, effectVolLevel);   /* amplitude */
        write(8, effectIndex);      /* index */
        write(8, repeat);           /* repeat */
        write(8, flags);            /* flags */
        write(16, nextEffectDelay); /* delay */
        return 0;
    }

    int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp) {
        uint16_t delay = 0;
        uint16_t amp = 0;
        uint16_t freq = 0;
        uint8_t flags = 0x0;
        if (waveformType != WAVEFORM_PWLE) {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }
        if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
            (fToU16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
            (fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
            ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
            return -ERANGE;
        }
        if (chirp) {
            flags |= PWLE_CHIRP_BIT;
        }
        constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
        return 0;
    }

    int constructBrakingSegment(int duration, Braking brakingType) {
        uint16_t delay = 0;
        uint16_t freq = 0;
        uint8_t flags = 0x00;
        if (waveformType != WAVEFORM_PWLE) {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }
        if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
            ALOGE("%s: Invalid argument: %d", __func__, duration);
            return -ERANGE;
        }
        fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
        if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
            flags |= PWLE_BRAKE_BIT;
        }

        constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
        return 0;
    }

    int updateWLength(uint32_t totalDuration) {
        uint8_t *f = front();
        if (f == nullptr) {
            ALOGE("%s: head does not exist!", __func__);
            return -ENOMEM;
        }
        if (waveformType != WAVEFORM_PWLE) {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }
        if (totalDuration > 0x7FFFF) {
            ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
            return -EINVAL;
        }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
        f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
#endif
        totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
        totalDuration |=
                WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
        *(f + 0) = (totalDuration >> 24) & 0xFF;
        *(f + 1) = (totalDuration >> 16) & 0xFF;
        *(f + 2) = (totalDuration >> 8) & 0xFF;
        *(f + 3) = totalDuration & 0xFF;
        return 0;
    }

    int updateNSection(int segmentIdx) {
        uint8_t *f = front();
        if (f == nullptr) {
            ALOGE("%s: head does not exist!", __func__);
            return -ENOMEM;
        }

        if (waveformType == WAVEFORM_COMPOSE) {
            if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
                ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
                return -EINVAL;
            }
            *(f + 2) = (0xFF & segmentIdx);
        } else if (waveformType == WAVEFORM_PWLE) {
            if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
                ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
                return -EINVAL;
            }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
            f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
#endif
            *(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
            *(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
        } else {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }

        return 0;
    }

    int updateWCount(int segmentCount) {
        uint8_t *f = front();

        if (segmentCount > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
            ALOGE("%s: Invalid argument: %d", __func__, segmentCount);
            return -EINVAL;
        }
        if (f == nullptr) {
            ALOGE("%s: head does not exist!", __func__);
            return -ENOMEM;
        }
        if (waveformType != WAVEFORM_PWLE) {
            ALOGE("%s: Invalid type: %d", __func__, waveformType);
            return -EDOM;
        }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
        f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
#endif
        uint32_t dataSize = segmentCount * PWLE_SEGMENT_WORD_COUNT + PWLE_HEADER_WORD_COUNT;
        *(f + 0) = (dataSize >> 24) & 0xFF;
        *(f + 1) = (dataSize >> 16) & 0xFF;
        *(f + 2) = (dataSize >> 8) & 0xFF;
        *(f + 3) = dataSize & 0xFF;

        return 0;
    }
};

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;

    RECORD("mIsDual = %d", mIsDual);
    // ==================INPUT Devices== Base =================
    const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
    const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
    if ((strstr(inputEventName, "cs40l26") != nullptr) ||
        (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
        glob_t inputEventPaths;
        int fd = -1;
        int ret;
        uint32_t val = 0;
        char str[20] = {0x00};
        for (uint8_t retry = 0; retry < 10; retry++) {
            ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
            if (ret) {
                ALOGE("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));
                    if (fd > 0) {
                        if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 &&
                            (val & (1 << EV_FF)) && ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
                            strstr(str, inputEventName) != nullptr) {
                            mInputFd.reset(fd);
                            ALOGI("Control %s through %s", inputEventName,
                                  inputEventPaths.gl_pathv[i]);
                            break;
                        }
                        close(fd);
                    }
                }
            }

            if (ret == 0) {
                globfree(&inputEventPaths);
            }
            if (mInputFd.ok()) {
                break;
            }

            sleep(1);
            ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
        }

        if (!mInputFd.ok()) {
            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, 9, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000,
    }; /* 11+3 waveforms. The duration must < UINT16_MAX */
    mEffectBrakingDurations.resize(WAVEFORM_MAX_INDEX);
    mEffectCustomData.reserve(WAVEFORM_MAX_INDEX);

    uint8_t effectIndex;
    uint16_t numBytes = 0;
    for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
        if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
            /* Initialize physical waveforms. */
            mEffectCustomData.push_back({RAM_WVFRM_BANK, effectIndex});
            mFfEffects[effectIndex] = {
                    .type = FF_PERIODIC,
                    .id = -1,
                    // Length == 0 to allow firmware control of the duration
                    .replay.length = 0,
                    .u.periodic.waveform = FF_CUSTOM,
                    .u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
                    .u.periodic.custom_len =
                            static_cast<uint32_t>(mEffectCustomData[effectIndex].size()),
            };
            // Bypass the waveform update due to different input name
            if ((strstr(inputEventName, "cs40l26") != nullptr) ||
                (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
                // Let the firmware control the playback duration to avoid
                // cutting any effect that is played short
                if (!mHwApiDef->setFFEffect(
                            mInputFd, &mFfEffects[effectIndex],
                            mEffectDurations[effectIndex])) {
                    ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
                }
            }
            if (mFfEffects[effectIndex].id != effectIndex) {
                ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffects[effectIndex].id);
            }

            if (mHwApiDef->hasEffectBrakingTimeBank()) {
                mHwApiDef->setEffectBrakingTimeIndex(effectIndex);
                mHwApiDef->getEffectBrakingTimeMs(&mEffectBrakingDurations[effectIndex]);
            }
        } else {
            /* Initiate placeholders for OWT effects. */
            numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
                                                       : FF_CUSTOM_DATA_LEN_MAX_PWLE;
            std::vector<int16_t> tempVec(numBytes, 0);
            mEffectCustomData.push_back(std::move(tempVec));
            mFfEffects[effectIndex] = {
                    .type = FF_PERIODIC,
                    .id = -1,
                    .replay.length = 0,
                    .u.periodic.waveform = FF_CUSTOM,
                    .u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
                    .u.periodic.custom_len = 0,
            };
        }
    }

    // ====================HAL internal effect table== Flip ==================================
    if (mIsDual) {
        mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
        mEffectCustomDataDual.reserve(WAVEFORM_MAX_INDEX);

        for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
            if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
                /* Initialize physical waveforms. */
                mEffectCustomDataDual.push_back({RAM_WVFRM_BANK, effectIndex});
                mFfEffectsDual[effectIndex] = {
                        .type = FF_PERIODIC,
                        .id = -1,
                        // Length == 0 to allow firmware control of the duration
                        .replay.length = 0,
                        .u.periodic.waveform = FF_CUSTOM,
                        .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
                        .u.periodic.custom_len =
                                static_cast<uint32_t>(mEffectCustomDataDual[effectIndex].size()),
                };
                // Bypass the waveform update due to different input name
                if ((strstr(inputEventName, "cs40l26") != nullptr) ||
                    (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
                    // Let the firmware control the playback duration to avoid
                    // cutting any effect that is played short
                    if (!mHwApiDual->setFFEffect(
                                mInputFdDual, &mFfEffectsDual[effectIndex],
                                mEffectDurations[effectIndex])) {
                        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. */
                numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
                                                       : FF_CUSTOM_DATA_LEN_MAX_PWLE;
                std::vector<int16_t> tempVec(numBytes, 0);
                mEffectCustomDataDual.push_back(std::move(tempVec));
                mFfEffectsDual[effectIndex] = {
                        .type = FF_PERIODIC,
                        .id = -1,
                        .replay.length = 0,
                        .u.periodic.waveform = FF_CUSTOM,
                        .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
                        .u.periodic.custom_len = 0,
                };
            }
        }
    }
    // ==============Calibration data checking======================================

    if (mHwCalDef->getF0(&caldata)) {
        mHwApiDef->setF0(caldata);
        mResonantFrequency =
                static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT);
    }
    if (mHwCalDef->getRedc(&caldata)) {
        mHwApiDef->setRedc(caldata);
        mRedc = redcToFloat(&caldata);
    }
    if (mHwCalDef->getQ(&caldata)) {
        mHwApiDef->setQ(caldata);
    }

    if (mHwCalDef->getF0SyncOffset(&mF0Offset)) {
        ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: "
              "%u",
              mF0Offset);
    } else {
        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);
    }

    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) {
        mHwCalDef->getTickVolLevels(&(mTickEffectVol));
        mHwCalDef->getClickVolLevels(&(mClickEffectVol));
        mHwCalDef->getLongVolLevels(&(mLongEffectVol));
    } else {
        ALOGW("Unsupported calibration version! Using the default calibration value");
        mHwCalDef->getTickVolLevels(&(mTickEffectVol));
        mHwCalDef->getClickVolLevels(&(mClickEffectVol));
        mHwCalDef->getLongVolLevels(&(mLongEffectVol));
    }

    // ================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;

    // =============== Compose PWLE check =====================================
    mIsChirpEnabled = mHwCalDef->isChirpEnabled();

    mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits);
    if (mSupportedPrimitivesBits > 0) {
        for (auto e : defaultSupportedPrimitives) {
            if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
                mSupportedPrimitives.emplace_back(e);
            }
        }
    } else {
        for (auto e : defaultSupportedPrimitives) {
            mSupportedPrimitivesBits |= (1 << uint32_t(e));
        }
        mSupportedPrimitives = defaultSupportedPrimitives;
    }

    mPrimitiveMaxScale.resize(WAVEFORM_MAX_INDEX, 100);
    mPrimitiveMaxScale[WAVEFORM_CLICK_INDEX] = 95;
    mPrimitiveMaxScale[WAVEFORM_THUD_INDEX] = 75;
    mPrimitiveMaxScale[WAVEFORM_SPIN_INDEX] = 90;
    mPrimitiveMaxScale[WAVEFORM_LIGHT_TICK_INDEX] = 75;
    mPrimitiveMaxScale[WAVEFORM_LOW_TICK_INDEX] = 75;

    mPrimitiveMinScale.resize(WAVEFORM_MAX_INDEX, 0);
    mPrimitiveMinScale[WAVEFORM_CLICK_INDEX] = 1;
    mPrimitiveMinScale[WAVEFORM_THUD_INDEX] = 11;
    mPrimitiveMinScale[WAVEFORM_SPIN_INDEX] = 23;
    mPrimitiveMinScale[WAVEFORM_SLOW_RISE_INDEX] = 25;
    mPrimitiveMinScale[WAVEFORM_QUICK_FALL_INDEX] = 2;
    mPrimitiveMinScale[WAVEFORM_LIGHT_TICK_INDEX] = 3;
    mPrimitiveMinScale[WAVEFORM_LOW_TICK_INDEX] = 16;

    // ====== 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) {
    ATRACE_NAME("Vibrator::getCapabilities");

    int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
                  IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
                  IVibrator::CAP_GET_Q_FACTOR;
    if (hasHapticAlsaDevice()) {
        ret |= IVibrator::CAP_EXTERNAL_CONTROL;
    } else {
        ALOGE("No haptics ALSA device");
    }
    if (mHwApiDef->hasOwtFreeSpace()) {
        ret |= IVibrator::CAP_COMPOSE_EFFECTS;
        if (mIsChirpEnabled) {
            ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
        }
    }
    *_aidl_return = ret;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::off() {
    ATRACE_NAME("Vibrator::off");
    bool ret{true};
    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);

    RECORD("mActiveId = %d", mActiveId);

    if (mActiveId >= 0) {
        ALOGD("Off: Stop the active effect: %d", mActiveId);
        /* Stop the active effect. */
        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;
        }

        if (!mHwGPIO->setGPIOOutput(false)) {
            ALOGE("Off: Failed to reset GPIO(%d): %s", errno, strerror(errno));
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
        }
    } else {
        ALOGD("Off: Vibrator is already off");
    }

    setGlobalAmplitude(false);
    if (mF0Offset) {
        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);
    }
}

ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
                                const std::shared_ptr<IVibratorCallback> &callback) {
    ATRACE_NAME("Vibrator::on");
    ALOGD("Vibrator::on");
    RECORD("timeoutMs = %d", timeoutMs);

    if (timeoutMs > MAX_TIME_MS) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    const uint16_t index = (timeoutMs < WAVEFORM_LONG_VIBRATION_THRESHOLD_MS)
                                   ? WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX
                                   : WAVEFORM_LONG_VIBRATION_EFFECT_INDEX;
    if (MAX_COLD_START_LATENCY_MS <= MAX_TIME_MS - timeoutMs) {
        timeoutMs += MAX_COLD_START_LATENCY_MS;
    }
    setGlobalAmplitude(true);
    if (mF0Offset) {
        mHwApiDef->setF0Offset(mF0Offset);
        if (mIsDual && mF0OffsetDual) {
            mHwApiDual->setF0Offset(mF0OffsetDual);
        }
    }
    return on(timeoutMs, index, nullptr /*ignored*/, callback);
}

ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
                                     const std::shared_ptr<IVibratorCallback> &callback,
                                     int32_t *_aidl_return) {
    ATRACE_NAME("Vibrator::perform");
    ALOGD("Vibrator::perform");
    RECORD("effect = %s, strength = %s",
                                toString(effect).c_str(), toString(strength).c_str());
    return performEffect(effect, strength, callback, _aidl_return);
}

ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_return) {
    *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK,
                     Effect::DOUBLE_CLICK};
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
    ATRACE_NAME("Vibrator::setAmplitude");

    if (amplitude <= 0.0f || amplitude > 1.0f) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    mLongEffectScale = amplitude;
    if (!isUnderExternalControl()) {
        return setGlobalAmplitude(true);
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
    ATRACE_NAME("Vibrator::setExternalControl");

    setGlobalAmplitude(enabled);

    if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
        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);
        }
    } else {
        ALOGE("No haptics ALSA device");
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }

    mIsUnderExternalControl = enabled;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t *maxDelayMs) {
    ATRACE_NAME("Vibrator::getCompositionDelayMax");
    *maxDelayMs = COMPOSE_DELAY_MAX_MS;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) {
    ATRACE_NAME("Vibrator::getCompositionSizeMax");
    *maxSize = COMPOSE_SIZE_MAX;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive> *supported) {
    *supported = mSupportedPrimitives;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
                                                  int32_t *durationMs) {
    ndk::ScopedAStatus status;
    uint32_t effectIndex;
    if (primitive != CompositePrimitive::NOOP) {
        status = getPrimitiveDetails(primitive, &effectIndex);
        if (!status.isOk()) {
            return status;
        }

        *durationMs = mEffectDurations[effectIndex] + mEffectBrakingDurations[effectIndex];
    } else {
        *durationMs = 0;
    }
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
                                     const std::shared_ptr<IVibratorCallback> &callback) {
    ATRACE_NAME("Vibrator::compose");
    ALOGD("Vibrator::compose");
    RECORD_COMPOSE(composite);

    uint16_t size;
    uint16_t nextEffectDelay;

    if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    /* Check if there is a wait before the first effect. */
    nextEffectDelay = composite.front().delayMs;
    if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    } else if (nextEffectDelay > 0) {
        size = composite.size() + 1;
    } else {
        size = composite.size();
    }

    DspMemChunk ch(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
    const uint8_t header_count = ch.size();

    /* Insert 1 section for a wait before the first effect. */
    if (nextEffectDelay) {
        ch.constructComposeSegment(0 /*amplitude*/, 0 /*index*/, 0 /*repeat*/, 0 /*flags*/,
                                   nextEffectDelay /*delay*/);
    }

    for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
        auto &e_curr = composite[i_curr];
        uint32_t effectIndex = 0;
        uint32_t effectVolLevel = 0;
        float effectScale = e_curr.scale;
        if (effectScale < 0.0f || effectScale > 1.0f) {
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }

        if (e_curr.primitive != CompositePrimitive::NOOP) {
            ndk::ScopedAStatus status;
            status = getPrimitiveDetails(e_curr.primitive, &effectIndex);
            if (!status.isOk()) {
                return status;
            }
            effectVolLevel = intensityToVolLevel(effectScale, effectIndex);
        }

        /* Fetch the next composite effect delay and fill into the current section */
        nextEffectDelay = 0;
        if (i_next < composite.size()) {
            auto &e_next = composite[i_next];
            int32_t delay = e_next.delayMs;

            if (delay > COMPOSE_DELAY_MAX_MS || delay < 0) {
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
            }
            nextEffectDelay = delay;
        }

        if (effectIndex == 0 && nextEffectDelay == 0) {
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }

        nextEffectDelay += mEffectBrakingDurations[effectIndex];

        ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/,
                                   nextEffectDelay /*delay*/);
    }

    ch.flush();
    if (ch.updateNSection(size) < 0) {
        ALOGE("%s: Failed to update the section count", __func__);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (header_count == ch.size()) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    } else {
        // Composition duration should be 0 to allow firmware to play the whole effect
        mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
        if (mIsDual) {
            mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
        }
        return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
                             callback);
    }
}

ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const DspMemChunk *ch,
                                const std::shared_ptr<IVibratorCallback> &callback) {
    ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();

    if (effectIndex >= FF_MAX_EFFECTS) {
        ALOGE("Invalid waveform index %d", effectIndex);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) {
        ALOGE("Previous vibration pending: prev: %d, curr: %d", mActiveId, effectIndex);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }

    if (ch) {
        /* Upload OWT effect. */
        if (ch->front() == nullptr) {
            ALOGE("Invalid OWT bank");
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }

        if (ch->type() != WAVEFORM_PWLE && ch->type() != WAVEFORM_COMPOSE) {
            ALOGE("Invalid OWT type");
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }
        effectIndex = ch->type();

        uint32_t freeBytes;
        mHwApiDef->getOwtFreeSpace(&freeBytes);
        if (ch->size() > freeBytes) {
            ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes);
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }
        if (mIsDual) {
            mHwApiDual->getOwtFreeSpace(&freeBytes);
            if (ch-> size() > freeBytes) {
                ALOGE("Invalid OWT length in flip: Effect %d: %zu > %d!", effectIndex,
                      ch-> size(), freeBytes);
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
            }
        }

        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->front(), ch->size(), &mFfEffects[effectIndex],
                                        &effectIndex, &errorStatus)) {
            ALOGE("Invalid uploadOwtEffect");
            return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
        }
        if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->front(), ch->size(),
                                                    &mFfEffectsDual[effectIndex], &effectIndex,
                                                    &errorStatus)) {
            ALOGE("Invalid uploadOwtEffect in flip");
            return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
        }

    } 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 (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->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);
        }
        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);
            }
        }
    }

    {
        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 (!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();
}

ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
    uint8_t amplitude = set ? roundf(mLongEffectScale * mLongEffectVol[1]) : VOLTAGE_SCALE_MAX;
    if (!set) {
        mLongEffectScale = 1.0;  // Reset the scale for the later new effect.
    }

    return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
}

ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}

ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/,
                                            EffectStrength /*strength*/) {
    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}

ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
    *resonantFreqHz = mResonantFrequency;

    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
    std::string caldata{8, '0'};
    if (!mHwCalDef->getQ(&caldata)) {
        ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }
    *qFactor = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q16_BIT_SHIFT);

    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
        *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
        *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
    // TODO(b/170919640): complete implementation
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
        std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, 1.0);
        *_aidl_return = bandwidthAmplitudeMap;
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        *maxSize = COMPOSE_PWLE_SIZE_MAX_DEFAULT;
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
    int32_t capabilities;
    Vibrator::getCapabilities(&capabilities);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        *supported = {
                Braking::NONE,
        };
        return ndk::ScopedAStatus::ok();
    } else {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
}

static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude,
                                                  float *prevEndFrequency) {
    const float reset = -1.0;
    *prevEndAmplitude = reset;
    *prevEndFrequency = reset;
}

static void incrementIndex(int *index) {
    *index += 1;
}

Vibrator::~Vibrator() {
    if (isUnderExternalControl()) {
        ALOGD("Disabling external control");
        setExternalControl(false);
    }
    ALOGD("Turning off the vibrator");
    off();
    ALOGD("Waiting for mAsyncHandle to complete");
    mAsyncHandle.wait();
}

ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
                                         const std::shared_ptr<IVibratorCallback> &callback) {
    ATRACE_NAME("Vibrator::composePwle");
    int32_t capabilities;

    Vibrator::getCapabilities(&capabilities);
    if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) == 0) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    if (composite.empty() || composite.size() > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    std::vector<Braking> supported;
    Vibrator::getSupportedBraking(&supported);
    bool isClabSupported =
            std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();

    int segmentIdx = 0;
    uint32_t totalDuration = 0;
    float prevEndAmplitude;
    float prevEndFrequency;
    resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
    DspMemChunk ch(WAVEFORM_PWLE, FF_CUSTOM_DATA_LEN_MAX_PWLE);
    bool chirp = false;

    for (auto &e : composite) {
        switch (e.getTag()) {
            case PrimitivePwle::active: {
                auto active = e.get<PrimitivePwle::active>();
                if (active.duration < 0 ||
                    active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }
                if (active.startAmplitude < PWLE_LEVEL_MIN ||
                    active.startAmplitude > PWLE_LEVEL_MAX ||
                    active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }
                if (active.startAmplitude > CS40L26_PWLE_LEVEL_MAX) {
                    active.startAmplitude = CS40L26_PWLE_LEVEL_MAX;
                }
                if (active.endAmplitude > CS40L26_PWLE_LEVEL_MAX) {
                    active.endAmplitude = CS40L26_PWLE_LEVEL_MAX;
                }

                if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
                    active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
                    active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
                    active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }

                if (!((active.startAmplitude == prevEndAmplitude) &&
                      (active.startFrequency == prevEndFrequency))) {
                    if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency,
                                                  false) < 0) {
                        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                    }
                    incrementIndex(&segmentIdx);
                }

                if (active.startFrequency != active.endFrequency) {
                    chirp = true;
                }
                if (ch.constructActiveSegment(active.duration, active.endAmplitude,
                                              active.endFrequency, chirp) < 0) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }
                incrementIndex(&segmentIdx);

                prevEndAmplitude = active.endAmplitude;
                prevEndFrequency = active.endFrequency;
                totalDuration += active.duration;
                chirp = false;
                break;
            }
            case PrimitivePwle::braking: {
                auto braking = e.get<PrimitivePwle::braking>();
                if (braking.braking > Braking::CLAB) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                } else if (!isClabSupported && (braking.braking == Braking::CLAB)) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }

                if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }

                if (ch.constructBrakingSegment(0, braking.braking) < 0) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }
                incrementIndex(&segmentIdx);

                if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) {
                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                }
                incrementIndex(&segmentIdx);

                resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
                totalDuration += braking.duration;
                break;
            }
        }

        if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
            ALOGE("Too many PrimitivePwle section!");
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        }
    }
    ch.flush();

    /* Update wlength */
    totalDuration += MAX_COLD_START_LATENCY_MS;
    if (totalDuration > 0x7FFFF) {
        ALOGE("Total duration is too long (%d)!", totalDuration);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    /* Update word count */
    if (ch.updateWCount(segmentIdx) < 0) {
        ALOGE("%s: Failed to update the waveform word count", __func__);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    /* Update waveform length */
    if (ch.updateWLength(totalDuration) < 0) {
        ALOGE("%s: Failed to update the waveform length length", __func__);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    /* Update nsections */
    if (ch.updateNSection(segmentIdx) < 0) {
        ALOGE("%s: Failed to update the section count", __func__);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
                         callback);
}

bool Vibrator::isUnderExternalControl() {
    return mIsUnderExternalControl;
}

// BnCInterface APIs

binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
    if (fd < 0) {
        ALOGE("Called debug() with invalid fd.");
        return STATUS_OK;
    }

    (void)args;
    (void)numArgs;

    dprintf(fd, "AIDL:\n");

    dprintf(fd, "  Active Effect ID: %" PRId32 "\n", mActiveId);
    dprintf(fd, "  F0: %.02f\n", mResonantFrequency);
    dprintf(fd, "  F0 Offset: base: %" PRIu32 " flip: %" PRIu32 "\n", mF0Offset, mF0OffsetDual);
    dprintf(fd, "  Redc: %.02f\n", mRedc);

    dprintf(fd, "  Voltage Levels:\n");
    dprintf(fd, "    Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
            mTickEffectVol[1]);
    dprintf(fd, "    Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
            mClickEffectVol[1]);
    dprintf(fd, "    Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
            mLongEffectVol[1]);

    uint8_t effectId;
    dprintf(fd, "  Scales\n");
    dprintf(fd, "\tId\tMinScale\tMaxScale\n");
    for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
        dprintf(fd, "\t%d\t%d\t\t%d\n", effectId, mPrimitiveMinScale[effectId],
            mPrimitiveMaxScale[effectId]);
    }

    dprintf(fd, "  Base FF effect:\n");
    dprintf(fd, "    Physical waveform:\n");
    dprintf(fd, "\tId\tIndex\tt   ->\tt'\tBrake\ttrigger button\n");
    for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
        dprintf(fd, "\t%d\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, mEffectBrakingDurations[effectId],
                mFfEffects[effectId].trigger.button);
    }
    dprintf(fd, "    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 << " ";
        for (int i = 0; i < numBytes; i++) {
            ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex
               << (uint16_t)(*(
                          reinterpret_cast<uint8_t *>(mFfEffects[effectId].u.periodic.custom_data) +
                          i))
               << " ";
        }
        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 FF effect:\n");
        dprintf(fd, "    Physical waveform:\n");
        dprintf(fd, "\tId\tIndex\tt   ->\tt'\tBrake\ttrigger button\n");
        for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
            dprintf(fd, "\t%d\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, mEffectBrakingDurations[effectId],
                    mFfEffectsDual[effectId].trigger.button);
        }
        dprintf(fd, "    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, "Versions:\n");
    const std::vector<std::pair<std::string, std::string>> moduleFolderNames = {
        {"cs40l26_core", "Haptics"}, {"cl_dsp_core", "DSP"}};
    const std::string firmwareFolder = "/vendor/firmware/";
    const std::string waveformName = "cs40l26.bin";
    const std::array<std::string, 2> firmwareFileNames = {"cs40l26.wmfw", "cs40l26-calib.wmfw"};
    const std::array<std::string, 4> tuningFileNames = {"cs40l26-svc.bin", "cs40l26-calib.bin",
                                                        "cs40l26-dvl.bin", "cs40l26-dbc.bin"};
    std::ifstream verFile;
    const auto verBinFileMode = std::ifstream::in | std::ifstream::binary;
    std::string ver;
    for (const auto &[folder, logTag] : moduleFolderNames) {
        verFile.open("/sys/module/" + folder + "/version");
        if (verFile.is_open()) {
            getline(verFile, ver);
            dprintf(fd, "  %s Driver: %s\n", logTag.c_str(), ver.c_str());
            verFile.close();
        }
    }
    for (auto &name : firmwareFileNames) {
        verFile.open(firmwareFolder + name, verBinFileMode);
        if (verFile.is_open()) {
            verFile.seekg(113);
            dprintf(fd, "  %s: %d.%d.%d\n", name.c_str(), verFile.get(), verFile.get(),
                    verFile.get());
            verFile.close();
        }
    }
    verFile.open(firmwareFolder + waveformName, verBinFileMode);
    if (verFile.is_open()) {
        while (getline(verFile, ver)) {
            auto pos = ver.find("Date: ");
            if (pos != std::string::npos) {
                ver = ver.substr(pos + 6, pos + 15);
                dprintf(fd, "  %s: %s\n", waveformName.c_str(), ver.c_str());
                break;
            }
        }
        verFile.close();
    }
    for (auto &name : tuningFileNames) {
        verFile.open(firmwareFolder + name, verBinFileMode);
        if (verFile.is_open()) {
            verFile.seekg(36);
            getline(verFile, ver);
            ver = ver.substr(0, ver.find(".bin") + 4);
            ver = ver.substr(ver.rfind('\\') + 1);
            dprintf(fd, "  %s: %s\n", name.c_str(), ver.c_str());
            verFile.close();
        }
    }

    dprintf(fd, "\n");

    mHwApiDef->debug(fd);

    dprintf(fd, "\n");

    mHwCalDef->debug(fd);

    dprintf(fd, "\n");

    if (mIsDual) {
        mHwApiDual->debug(fd);
        dprintf(fd, "\n");
        mHwCalDual->debug(fd);
    }

    fsync(fd);
    return STATUS_OK;
}

bool Vibrator::hasHapticAlsaDevice() {
    // We need to call findHapticAlsaDevice once only. Calling in the
    // constructor is too early in the boot process and the pcm file contents
    // are empty. Hence we make the call here once only right before we need to.
    if (!mConfigHapticAlsaDeviceDone) {
        if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) {
            mHasHapticAlsaDevice = true;
            mConfigHapticAlsaDeviceDone = true;
        } else {
            ALOGE("Haptic ALSA device not supported");
        }
    } else {
        ALOGD("Haptic ALSA device configuration done.");
    }
    return mHasHapticAlsaDevice;
}

ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength,
                                              uint32_t *outEffectIndex, uint32_t *outTimeMs,
                                              uint32_t *outVolLevel) {
    uint32_t effectIndex;
    uint32_t timeMs;
    float intensity;
    uint32_t volLevel;
    switch (strength) {
        case EffectStrength::LIGHT:
            intensity = 0.5f;
            break;
        case EffectStrength::MEDIUM:
            intensity = 0.7f;
            break;
        case EffectStrength::STRONG:
            intensity = 1.0f;
            break;
        default:
            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    switch (effect) {
        case Effect::TEXTURE_TICK:
            effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
            intensity *= 0.5f;
            break;
        case Effect::TICK:
            effectIndex = WAVEFORM_CLICK_INDEX;
            intensity *= 0.5f;
            break;
        case Effect::CLICK:
            effectIndex = WAVEFORM_CLICK_INDEX;
            intensity *= 0.7f;
            break;
        case Effect::HEAVY_CLICK:
            effectIndex = WAVEFORM_CLICK_INDEX;
            intensity *= 1.0f;
            break;
        default:
            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    volLevel = intensityToVolLevel(intensity, effectIndex);
    timeMs = mEffectDurations[effectIndex] + MAX_COLD_START_LATENCY_MS;

    *outEffectIndex = effectIndex;
    *outTimeMs = timeMs;
    *outVolLevel = volLevel;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
                                                uint32_t *outTimeMs, DspMemChunk *outCh) {
    ndk::ScopedAStatus status;
    uint32_t timeMs = 0;
    uint32_t thisEffectIndex;
    uint32_t thisTimeMs;
    uint32_t thisVolLevel;
    switch (effect) {
        case Effect::DOUBLE_CLICK:
            status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs,
                                      &thisVolLevel);
            if (!status.isOk()) {
                return status;
            }
            timeMs += thisTimeMs;
            outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
                                           WAVEFORM_DOUBLE_CLICK_SILENCE_MS);

            timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS;

            status = getSimpleDetails(Effect::HEAVY_CLICK, strength, &thisEffectIndex, &thisTimeMs,
                                      &thisVolLevel);
            if (!status.isOk()) {
                return status;
            }
            timeMs += thisTimeMs;

            outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
                                           0 /*delay*/);
            outCh->flush();
            if (outCh->updateNSection(2) < 0) {
                ALOGE("%s: Failed to update the section count", __func__);
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
            }

            break;
        default:
            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    *outTimeMs = timeMs;
    // Compositions should have 0 duration
    mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
    if (mIsDual) {
        mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
    }

    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive,
                                                 uint32_t *outEffectIndex) {
    uint32_t effectIndex;
    uint32_t primitiveBit = 1 << int32_t(primitive);
    if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    switch (primitive) {
        case CompositePrimitive::NOOP:
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        case CompositePrimitive::CLICK:
            effectIndex = WAVEFORM_CLICK_INDEX;
            break;
        case CompositePrimitive::THUD:
            effectIndex = WAVEFORM_THUD_INDEX;
            break;
        case CompositePrimitive::SPIN:
            effectIndex = WAVEFORM_SPIN_INDEX;
            break;
        case CompositePrimitive::QUICK_RISE:
            effectIndex = WAVEFORM_QUICK_RISE_INDEX;
            break;
        case CompositePrimitive::SLOW_RISE:
            effectIndex = WAVEFORM_SLOW_RISE_INDEX;
            break;
        case CompositePrimitive::QUICK_FALL:
            effectIndex = WAVEFORM_QUICK_FALL_INDEX;
            break;
        case CompositePrimitive::LIGHT_TICK:
            effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
            break;
        case CompositePrimitive::LOW_TICK:
            effectIndex = WAVEFORM_LOW_TICK_INDEX;
            break;
        default:
            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }

    *outEffectIndex = effectIndex;

    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
                                           const std::shared_ptr<IVibratorCallback> &callback,
                                           int32_t *outTimeMs) {
    ndk::ScopedAStatus status;
    uint32_t effectIndex;
    uint32_t timeMs = 0;
    uint32_t volLevel;
    std::optional<DspMemChunk> maybeCh;
    switch (effect) {
        case Effect::TEXTURE_TICK:
            // fall-through
        case Effect::TICK:
            // fall-through
        case Effect::CLICK:
            // fall-through
        case Effect::HEAVY_CLICK:
            status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
            break;
        case Effect::DOUBLE_CLICK:
            maybeCh.emplace(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
            status = getCompoundDetails(effect, strength, &timeMs, &*maybeCh);
            volLevel = VOLTAGE_SCALE_MAX;
            break;
        default:
            status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
            break;
    }
    if (status.isOk()) {
        DspMemChunk *ch = maybeCh ? &*maybeCh : nullptr;
        status = performEffect(effectIndex, volLevel, ch, callback);
    }

    *outTimeMs = timeMs;
    return status;
}

ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
                                           const DspMemChunk *ch,
                                           const std::shared_ptr<IVibratorCallback> &callback) {
    setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX);

    return on(MAX_TIME_MS, effectIndex, ch, callback);
}

void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
    ALOGD("waitForComplete: Callback status in waitForComplete(): callBack: %d",
          (callback != nullptr));

    // Bypass checking flip part's haptic state
    if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
        ALOGD("Failed to get state \"Haptic\"");
    }

    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) {
            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 {
            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");
            }
        }
    }

    if (callback) {
        auto ret = callback->onComplete();
        if (!ret.isOk()) {
            ALOGE("Failed completion callback: %d", ret.getExceptionCode());
        }
    }
    ALOGD("waitForComplete: Done.");
}

uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
    uint32_t volLevel;
    auto calc = [](float intst, std::array<uint32_t, 2> v) -> uint32_t {
        return std::lround(intst * (v[1] - v[0])) + v[0];
    };

    switch (effectIndex) {
        case WAVEFORM_LIGHT_TICK_INDEX:
            volLevel = calc(intensity, mTickEffectVol);
            break;
        case WAVEFORM_QUICK_RISE_INDEX:
            // fall-through
        case WAVEFORM_QUICK_FALL_INDEX:
            volLevel = calc(intensity, mLongEffectVol);
            break;
        case WAVEFORM_CLICK_INDEX:
            // fall-through
        case WAVEFORM_THUD_INDEX:
            // fall-through
        case WAVEFORM_SPIN_INDEX:
            // fall-through
        case WAVEFORM_SLOW_RISE_INDEX:
            // fall-through
        default:
            volLevel = calc(intensity, mClickEffectVol);
            break;
    }
    // The waveform being played must fall within the allowable scale range
    if (effectIndex < WAVEFORM_MAX_INDEX) {
        if (volLevel > mPrimitiveMaxScale[effectIndex]) {
            volLevel = mPrimitiveMaxScale[effectIndex];
        }
        if (volLevel < mPrimitiveMinScale[effectIndex]) {
            volLevel = mPrimitiveMinScale[effectIndex];
        }
    }

    return volLevel;
}

}  // namespace vibrator
}  // namespace hardware
}  // namespace android
}  // namespace aidl
