/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <aidl/android/hardware/vibrator/IVibratorManager.h>

#include <android-base/properties.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <android/persistable_bundle_aidl.h>

#include <cmath>
#include <cstdlib>
#include <ctime>
#include <future>
#include <iomanip>
#include <iostream>
#include <random>

#include "persistable_bundle_utils.h"
#include "pwle_v2_utils.h"
#include "test_utils.h"

using aidl::android::hardware::vibrator::ActivePwle;
using aidl::android::hardware::vibrator::BnVibratorCallback;
using aidl::android::hardware::vibrator::Braking;
using aidl::android::hardware::vibrator::BrakingPwle;
using aidl::android::hardware::vibrator::CompositeEffect;
using aidl::android::hardware::vibrator::CompositePrimitive;
using aidl::android::hardware::vibrator::CompositePwleV2;
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::IVibratorManager;
using aidl::android::hardware::vibrator::PrimitivePwle;
using aidl::android::hardware::vibrator::PwleV2Primitive;
using aidl::android::hardware::vibrator::VendorEffect;
using aidl::android::os::PersistableBundle;
using std::chrono::high_resolution_clock;

using namespace ::std::chrono_literals;

namespace pwle_v2_utils = aidl::android::hardware::vibrator::testing::pwlev2;

const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
                                   ndk::enum_range<Effect>().end()};
const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
                                                   ndk::enum_range<EffectStrength>().end()};

const std::vector<Effect> kInvalidEffects = {
    static_cast<Effect>(static_cast<int32_t>(kEffects.front()) - 1),
    static_cast<Effect>(static_cast<int32_t>(kEffects.back()) + 1),
};

const std::vector<EffectStrength> kInvalidEffectStrengths = {
    static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.front()) - 1),
    static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.back()) + 1),
};

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

const std::vector<CompositePrimitive> kRequiredPrimitives = {
        CompositePrimitive::CLICK,      CompositePrimitive::LIGHT_TICK,
        CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
        CompositePrimitive::QUICK_FALL,
};

const std::vector<CompositePrimitive> kInvalidPrimitives = {
    static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.front()) - 1),
    static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.back()) + 1),
};

// Timeout to wait for vibration callback completion.
static const std::chrono::milliseconds VIBRATION_CALLBACK_TIMEOUT =
        300ms * android::base::HwTimeoutMultiplier();

static constexpr int32_t VENDOR_EFFECTS_MIN_VERSION = 3;
static constexpr int32_t PWLE_V2_MIN_VERSION = 3;

static std::vector<std::string> findVibratorManagerNames() {
    std::vector<std::string> names;
    constexpr auto callback = [](const char* instance, void* context) {
        auto fullName = std::string(IVibratorManager::descriptor) + "/" + instance;
        static_cast<std::vector<std::string>*>(context)->emplace_back(fullName);
    };
    AServiceManager_forEachDeclaredInstance(IVibratorManager::descriptor,
                                            static_cast<void*>(&names), callback);
    return names;
}

static std::vector<std::string> findUnmanagedVibratorNames() {
    std::vector<std::string> names;
    constexpr auto callback = [](const char* instance, void* context) {
        auto fullName = std::string(IVibrator::descriptor) + "/" + instance;
        static_cast<std::vector<std::string>*>(context)->emplace_back(fullName);
    };
    AServiceManager_forEachDeclaredInstance(IVibrator::descriptor, static_cast<void*>(&names),
                                            callback);
    return names;
}

class CompletionCallback : public BnVibratorCallback {
  public:
    ndk::ScopedAStatus onComplete() override {
        completionPromise.set_value();
        return ndk::ScopedAStatus::ok();
    }

    std::future_status wait_for(const std::chrono::milliseconds& timeout) {
        return completionFuture.wait_for(timeout);
    }

  private:
    std::promise<void> completionPromise;
    std::future<void> completionFuture{completionPromise.get_future()};
};

class VibratorAidl : public testing::TestWithParam<std::tuple<int32_t, int32_t>> {
  public:
    virtual void SetUp() override {
        int32_t managerIdx = std::get<0>(GetParam());
        int32_t vibratorId = std::get<1>(GetParam());

        if (managerIdx < 0) {
            // Testing a unmanaged vibrator, using vibratorId as index from registered HALs
            std::vector<std::string> vibratorNames = findUnmanagedVibratorNames();
            ASSERT_LT(vibratorId, vibratorNames.size());
            vibrator = IVibrator::fromBinder(ndk::SpAIBinder(
                    AServiceManager_waitForService(vibratorNames[vibratorId].c_str())));
        } else {
            // Testing a managed vibrator, using vibratorId to retrieve it from the manager
            std::vector<std::string> managerNames = findVibratorManagerNames();
            ASSERT_LT(managerIdx, managerNames.size());
            auto vibratorManager = IVibratorManager::fromBinder(ndk::SpAIBinder(
                    AServiceManager_waitForService(managerNames[managerIdx].c_str())));
            EXPECT_OK(vibratorManager->getVibrator(vibratorId, &vibrator))
                    << "\n  For vibrator id: " << vibratorId;
        }

        ASSERT_NE(vibrator, nullptr);
        EXPECT_OK(vibrator->getInterfaceVersion(&version));
        EXPECT_OK(vibrator->getCapabilities(&capabilities));
    }

    virtual void TearDown() override {
        // Reset vibrator state between tests.
        EXPECT_OK(vibrator->off());
    }

    std::shared_ptr<IVibrator> vibrator;
    int32_t version;
    int32_t capabilities;
};

static float getResonantFrequencyHz(const std::shared_ptr<IVibrator>& vibrator,
                                    int32_t capabilities) {
    float resonantFrequencyHz;
    ndk::ScopedAStatus status = vibrator->getResonantFrequency(&resonantFrequencyHz);
    if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
        EXPECT_OK(std::move(status));
        EXPECT_GT(resonantFrequencyHz, 0);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
    return resonantFrequencyHz;
}

static bool shouldValidateLegacyFrequencyControlResult(int32_t capabilities, int32_t version,
                                                       ndk::ScopedAStatus& status) {
    bool hasFrequencyControl = capabilities & IVibrator::CAP_FREQUENCY_CONTROL;
    // Legacy frequency control APIs deprecated with PWLE V2 feature.
    bool isDeprecated = version >= PWLE_V2_MIN_VERSION;
    bool isUnknownOrUnsupported = status.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
                                  status.getStatus() == STATUS_UNKNOWN_TRANSACTION;

    // Validate if older HAL or if result is provided, even after deprecation.
    return hasFrequencyControl && (!isDeprecated || !isUnknownOrUnsupported);
}

static float getFrequencyResolutionHz(const std::shared_ptr<IVibrator>& vibrator,
                                      int32_t capabilities, int32_t version) {
    float freqResolutionHz = -1;
    ndk::ScopedAStatus status = vibrator->getFrequencyResolution(&freqResolutionHz);
    if (shouldValidateLegacyFrequencyControlResult(capabilities, version, status)) {
        EXPECT_OK(std::move(status));
        EXPECT_GT(freqResolutionHz, 0);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
    return freqResolutionHz;
}

static float getFrequencyMinimumHz(const std::shared_ptr<IVibrator>& vibrator, int32_t capabilities,
                                   int32_t version) {
    float freqMinimumHz;
    ndk::ScopedAStatus status = vibrator->getFrequencyMinimum(&freqMinimumHz);
    if (shouldValidateLegacyFrequencyControlResult(capabilities, version, status)) {
        EXPECT_OK(std::move(status));

        float resonantFrequencyHz = getResonantFrequencyHz(vibrator, capabilities);

        EXPECT_GT(freqMinimumHz, 0);
        EXPECT_LE(freqMinimumHz, resonantFrequencyHz);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
    return freqMinimumHz;
}

static float getFrequencyMaximumHz(const std::shared_ptr<IVibrator>& vibrator, int32_t capabilities,
                                   int32_t version) {
    std::vector<float> bandwidthAmplitudeMap;
    ndk::ScopedAStatus status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
    if (shouldValidateLegacyFrequencyControlResult(capabilities, version, status)) {
        EXPECT_OK(std::move(status));
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }

    float freqMaximumHz = ((bandwidthAmplitudeMap.size() - 1) *
                           getFrequencyResolutionHz(vibrator, capabilities, version)) +
                          getFrequencyMinimumHz(vibrator, capabilities, version);
    return freqMaximumHz;
}

static float getAmplitudeMin() {
    return 0.0;
}

static float getAmplitudeMax() {
    return 1.0;
}

static ActivePwle composeValidActivePwle(const std::shared_ptr<IVibrator>& vibrator,
                                         int32_t capabilities, int32_t version) {
    float frequencyHz;
    if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
        frequencyHz = getResonantFrequencyHz(vibrator, capabilities);
    } else if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
        if (version < PWLE_V2_MIN_VERSION) {
            frequencyHz = getFrequencyMinimumHz(vibrator, capabilities, version);
        } else {
            frequencyHz = pwle_v2_utils::getPwleV2FrequencyMinHz(vibrator);
        }
    } else {
        frequencyHz = 150.0;  // default value commonly used
    }

    ActivePwle active;
    active.startAmplitude = (getAmplitudeMin() + getAmplitudeMax()) / 2;
    active.startFrequency = frequencyHz;
    active.endAmplitude = (getAmplitudeMin() + getAmplitudeMax()) / 2;
    active.endFrequency = frequencyHz;
    vibrator->getPwlePrimitiveDurationMax(&(active.duration));

    return active;
}

TEST_P(VibratorAidl, OnThenOffBeforeTimeout) {
    EXPECT_OK(vibrator->on(2000, nullptr /*callback*/));
    sleep(1);
    EXPECT_OK(vibrator->off());
}

TEST_P(VibratorAidl, OnWithCallback) {
    if (!(capabilities & IVibrator::CAP_ON_CALLBACK))
        return;

    auto callback = ndk::SharedRefBase::make<CompletionCallback>();
    uint32_t durationMs = 250;
    auto timeout = std::chrono::milliseconds(durationMs) + VIBRATION_CALLBACK_TIMEOUT;
    EXPECT_OK(vibrator->on(durationMs, callback));
    EXPECT_EQ(callback->wait_for(timeout), std::future_status::ready);
    EXPECT_OK(vibrator->off());
}

TEST_P(VibratorAidl, OnCallbackNotSupported) {
    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) {
        auto callback = ndk::SharedRefBase::make<CompletionCallback>();
        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->on(250, callback));
    }
}

TEST_P(VibratorAidl, ValidateEffect) {
    std::vector<Effect> supported;
    EXPECT_OK(vibrator->getSupportedEffects(&supported));

    for (Effect effect : kEffects) {
        bool isEffectSupported =
            std::find(supported.begin(), supported.end(), effect) != supported.end();

        for (EffectStrength strength : kEffectStrengths) {
            int32_t lengthMs = 0;
            ndk::ScopedAStatus status =
                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);

            if (isEffectSupported) {
                EXPECT_OK(std::move(status))
                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
                EXPECT_GT(lengthMs, 0);
                usleep(lengthMs * 1000);
                EXPECT_OK(vibrator->off());
            } else {
                EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
            }
        }
    }
}

TEST_P(VibratorAidl, ValidateEffectWithCallback) {
    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK))
        return;

    std::vector<Effect> supported;
    EXPECT_OK(vibrator->getSupportedEffects(&supported));

    for (Effect effect : kEffects) {
        bool isEffectSupported =
            std::find(supported.begin(), supported.end(), effect) != supported.end();

        for (EffectStrength strength : kEffectStrengths) {
            auto callback = ndk::SharedRefBase::make<CompletionCallback>();
            int lengthMs = 0;
            ndk::ScopedAStatus status = vibrator->perform(effect, strength, callback, &lengthMs);

            if (isEffectSupported) {
                EXPECT_OK(std::move(status))
                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
                EXPECT_GT(lengthMs, 0);
            } else {
                EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
            }

            if (lengthMs <= 0) continue;

            auto timeout = std::chrono::milliseconds(lengthMs) + VIBRATION_CALLBACK_TIMEOUT;
            EXPECT_EQ(callback->wait_for(timeout), std::future_status::ready);

            EXPECT_OK(vibrator->off());
        }
    }
}

TEST_P(VibratorAidl, ValidateEffectWithCallbackNotSupported) {
    if (capabilities & IVibrator::CAP_PERFORM_CALLBACK)
        return;

    for (Effect effect : kEffects) {
        for (EffectStrength strength : kEffectStrengths) {
            auto callback = ndk::SharedRefBase::make<CompletionCallback>();
            int lengthMs;
            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->perform(effect, strength, callback, &lengthMs))
                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
        }
    }
}

TEST_P(VibratorAidl, InvalidEffectsUnsupported) {
    for (Effect effect : kInvalidEffects) {
        for (EffectStrength strength : kEffectStrengths) {
            int32_t lengthMs;
            EXPECT_UNKNOWN_OR_UNSUPPORTED(
                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs))
                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
        }
    }
    for (Effect effect : kEffects) {
        for (EffectStrength strength : kInvalidEffectStrengths) {
            int32_t lengthMs;
            EXPECT_UNKNOWN_OR_UNSUPPORTED(
                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs))
                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
        }
    }
}

TEST_P(VibratorAidl, PerformVendorEffectSupported) {
    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;

    float scale = 0.0f;
    float vendorScale = 0.0f;
    for (EffectStrength strength : kEffectStrengths) {
        PersistableBundle vendorData;
        ::aidl::android::hardware::vibrator::testing::fillBasicData(&vendorData);

        PersistableBundle nestedData;
        ::aidl::android::hardware::vibrator::testing::fillBasicData(&nestedData);
        vendorData.putPersistableBundle("test_nested_bundle", nestedData);

        VendorEffect effect;
        effect.vendorData = vendorData;
        effect.strength = strength;
        effect.scale = scale;
        effect.vendorScale = vendorScale;
        scale += 0.5f;
        vendorScale += 0.2f;

        auto callback = ndk::SharedRefBase::make<CompletionCallback>();
        ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);

        // No expectations on the actual status, the effect might be refused with illegal argument
        // or the vendor might return a service-specific error code.
        EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
                    status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
                << status << "\n For vendor effect with strength" << toString(strength)
                << " and scale " << effect.scale;

        if (status.isOk()) {
            // Generic vendor data should not trigger vibrations, but if it does trigger one
            // then we make sure the vibrator is reset by triggering off().
            EXPECT_OK(vibrator->off());
        }
    }
}

TEST_P(VibratorAidl, PerformVendorEffectStability) {
    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;

    // Run some iterations of performVendorEffect with randomized vendor data to check basic
    // stability of the implementation.
    uint8_t iterations = 200;

    for (EffectStrength strength : kEffectStrengths) {
        float scale = 0.5f;
        float vendorScale = 0.2f;
        for (uint8_t i = 0; i < iterations; i++) {
            PersistableBundle vendorData;
            ::aidl::android::hardware::vibrator::testing::fillRandomData(&vendorData);

            VendorEffect effect;
            effect.vendorData = vendorData;
            effect.strength = strength;
            effect.scale = scale;
            effect.vendorScale = vendorScale;
            scale *= 2;
            vendorScale *= 1.5f;

            auto callback = ndk::SharedRefBase::make<CompletionCallback>();
            ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);

            // No expectations on the actual status, the effect might be refused with illegal
            // argument or the vendor might return a service-specific error code.
            EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
                        status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
                    << status << "\n For random vendor effect with strength " << toString(strength)
                    << " and scale " << effect.scale;

            if (status.isOk()) {
                // Random vendor data should not trigger vibrations, but if it does trigger one
                // then we make sure the vibrator is reset by triggering off().
                EXPECT_OK(vibrator->off());
            }
        }
    }
}

TEST_P(VibratorAidl, PerformVendorEffectEmptyVendorData) {
    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;

    for (EffectStrength strength : kEffectStrengths) {
        VendorEffect effect;
        effect.strength = strength;
        effect.scale = 1.0f;
        effect.vendorScale = 1.0f;

        ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, nullptr /*callback*/);

        EXPECT_TRUE(status.getExceptionCode() == EX_SERVICE_SPECIFIC)
                << status << "\n For vendor effect with strength " << toString(strength)
                << " and scale " << effect.scale;
    }
}

TEST_P(VibratorAidl, PerformVendorEffectInvalidScale) {
    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;

    VendorEffect effect;
    effect.strength = EffectStrength::MEDIUM;

    effect.scale = -1.0f;
    effect.vendorScale = 1.0f;
    EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));

    effect.scale = 1.0f;
    effect.vendorScale = -1.0f;
    EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));
}

TEST_P(VibratorAidl, PerformVendorEffectUnsupported) {
    if (version < VENDOR_EFFECTS_MIN_VERSION) {
        EXPECT_EQ(capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS, 0)
                << "Vibrator version " << version << " should not report vendor effects capability";
    }
    if (capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) return;

    for (EffectStrength strength : kEffectStrengths) {
        VendorEffect effect;
        effect.strength = strength;
        effect.scale = 1.0f;
        effect.vendorScale = 1.0f;

        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->performVendorEffect(effect, nullptr /*callback*/))
                << "\n  For vendor effect with strength " << toString(strength);
    }
}

TEST_P(VibratorAidl, ChangeVibrationAmplitude) {
    if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
        EXPECT_OK(vibrator->setAmplitude(0.1f));
        EXPECT_OK(vibrator->on(2000, nullptr /*callback*/));
        EXPECT_OK(vibrator->setAmplitude(0.5f));
        sleep(1);
        EXPECT_OK(vibrator->setAmplitude(1.0f));
        sleep(1);
        EXPECT_OK(vibrator->off());
    }
}

TEST_P(VibratorAidl, AmplitudeOutsideRangeFails) {
    if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(-1));
        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(0));
        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(1.1));
    }
}

TEST_P(VibratorAidl, AmplitudeReturnsUnsupportedMatchingCapabilities) {
    if ((capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) == 0) {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setAmplitude(1));
    }
}

TEST_P(VibratorAidl, ChangeVibrationExternalControl) {
    if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
        EXPECT_OK(vibrator->setExternalControl(true));
        sleep(1);
        EXPECT_OK(vibrator->setExternalControl(false));
        sleep(1);
    }
}

TEST_P(VibratorAidl, ExternalAmplitudeControl) {
    const bool supportsExternalAmplitudeControl =
        (capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0;

    if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
        EXPECT_OK(vibrator->setExternalControl(true));

        if (supportsExternalAmplitudeControl) {
            EXPECT_OK(vibrator->setAmplitude(0.5));
        } else {
            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setAmplitude(0.5));
        }

        EXPECT_OK(vibrator->setExternalControl(false));
    } else {
        EXPECT_FALSE(supportsExternalAmplitudeControl);
    }
}

TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) {
    if ((capabilities & IVibrator::CAP_EXTERNAL_CONTROL) == 0) {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setExternalControl(true));
    }
}

TEST_P(VibratorAidl, GetSupportedPrimitives) {
    if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
        std::vector<CompositePrimitive> supported;
        EXPECT_OK(vibrator->getSupportedPrimitives(&supported));

        for (CompositePrimitive primitive : kCompositePrimitives) {
            bool isPrimitiveSupported =
                std::find(supported.begin(), supported.end(), primitive) != supported.end();
            bool isPrimitiveRequired =
                    std::find(kRequiredPrimitives.begin(), kRequiredPrimitives.end(), primitive) !=
                    kRequiredPrimitives.end();

            EXPECT_TRUE(isPrimitiveSupported || !isPrimitiveRequired) << toString(primitive);
        }
    }
}

TEST_P(VibratorAidl, GetPrimitiveDuration) {
    if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
        std::vector<CompositePrimitive> supported;
        EXPECT_OK(vibrator->getSupportedPrimitives(&supported));

        for (CompositePrimitive primitive : kCompositePrimitives) {
            bool isPrimitiveSupported =
                std::find(supported.begin(), supported.end(), primitive) != supported.end();
            int32_t duration;

            if (isPrimitiveSupported) {
                EXPECT_OK(vibrator->getPrimitiveDuration(primitive, &duration))
                        << "\n  For primitive: " << toString(primitive) << " " << duration;
                if (primitive != CompositePrimitive::NOOP) {
                    ASSERT_GT(duration, 0)
                            << "\n  For primitive: " << toString(primitive) << " " << duration;
                }
            } else {
                EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->getPrimitiveDuration(primitive, &duration))
                        << "\n  For primitive: " << toString(primitive);
            }
        }
    }
}

TEST_P(VibratorAidl, ComposeValidPrimitives) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    std::vector<CompositePrimitive> supported;
    int32_t maxDelay, maxSize;

    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
    EXPECT_OK(vibrator->getCompositionDelayMax(&maxDelay));
    EXPECT_OK(vibrator->getCompositionSizeMax(&maxSize));

    std::vector<CompositeEffect> composite;

    for (int i = 0; i < supported.size(); i++) {
        CompositePrimitive primitive = supported[i];
        float t = static_cast<float>(i + 1) / supported.size();
        CompositeEffect effect;

        effect.delayMs = maxDelay * t;
        effect.primitive = primitive;
        effect.scale = t;

        if (composite.size() == maxSize) {
            break;
        }
    }

    if (composite.size() != 0) {
        EXPECT_OK(vibrator->compose(composite, nullptr));
        EXPECT_OK(vibrator->off());
    }
}

TEST_P(VibratorAidl, ComposeUnsupportedPrimitives) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    std::vector<CompositePrimitive> unsupported(kInvalidPrimitives);
    std::vector<CompositePrimitive> supported;

    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));

    for (CompositePrimitive primitive : kCompositePrimitives) {
        bool isPrimitiveSupported =
                std::find(supported.begin(), supported.end(), primitive) != supported.end();

        if (!isPrimitiveSupported) {
            unsupported.push_back(primitive);
        }
    }

    for (CompositePrimitive primitive : unsupported) {
        std::vector<CompositeEffect> composite(1);

        for (CompositeEffect& effect : composite) {
            effect.delayMs = 0;
            effect.primitive = primitive;
            effect.scale = 1.0f;
        }
        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->compose(composite, nullptr));
    }
}

TEST_P(VibratorAidl, ComposeScaleBoundary) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    std::vector<CompositeEffect> composite(1);
    CompositeEffect& effect = composite[0];

    effect.delayMs = 0;
    effect.primitive = CompositePrimitive::CLICK;

    effect.scale = std::nextafter(0.0f, -1.0f);
    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));

    effect.scale = 0.0f;
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    effect.scale = 1.0f;
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    effect.scale = std::nextafter(1.0f, 2.0f);
    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
}

TEST_P(VibratorAidl, ComposeDelayBoundary) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    int32_t maxDelay;

    EXPECT_OK(vibrator->getCompositionDelayMax(&maxDelay));

    std::vector<CompositeEffect> composite(1);
    CompositeEffect& effect = composite[0];

    effect.primitive = CompositePrimitive::CLICK;
    effect.scale = 1.0f;

    effect.delayMs = 0;
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    effect.delayMs = 1;
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    effect.delayMs = maxDelay;
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    effect.delayMs = maxDelay + 1;
    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
}

TEST_P(VibratorAidl, ComposeSizeBoundary) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    int32_t maxSize;

    EXPECT_OK(vibrator->getCompositionSizeMax(&maxSize));

    std::vector<CompositeEffect> composite(maxSize);
    CompositeEffect effect;

    effect.delayMs = 1;
    effect.primitive = CompositePrimitive::CLICK;
    effect.scale = 1.0f;

    std::fill(composite.begin(), composite.end(), effect);
    EXPECT_OK(vibrator->compose(composite, nullptr));
    EXPECT_OK(vibrator->off());

    composite.emplace_back(effect);
    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
}

TEST_P(VibratorAidl, ComposeCallback) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_EFFECTS)) {
        GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
    }

    std::vector<CompositePrimitive> supported;
    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
    if (supported.empty()) {
        return;
    }

    int32_t maxSize;
    EXPECT_OK(vibrator->getCompositionSizeMax(&maxSize));
    int32_t maxDelay;
    EXPECT_OK(vibrator->getCompositionDelayMax(&maxDelay));

    std::map<CompositePrimitive, int32_t> primitiveDurations;
    for (const auto& primitive : supported) {
        int32_t durationMs = 0;
        EXPECT_OK(vibrator->getPrimitiveDuration(primitive, &durationMs));
        primitiveDurations[primitive] = durationMs;
    }

    auto testComposition = [&](const std::vector<CompositeEffect>& composite,
                               const std::string& message = "") {
        if (composite.empty()) {
            return;
        }

        int32_t expectedDurationMs = 0;
        for (const auto& effect : composite) {
            expectedDurationMs += effect.delayMs + primitiveDurations.at(effect.primitive);
        }

        auto callback = ndk::SharedRefBase::make<CompletionCallback>();
        auto expectedDuration = std::chrono::milliseconds(expectedDurationMs);
        auto start = high_resolution_clock::now();

        EXPECT_OK(vibrator->compose(composite, callback)) << message;

        EXPECT_EQ(callback->wait_for(expectedDuration + VIBRATION_CALLBACK_TIMEOUT),
                  std::future_status::ready)
                << message;
        auto end = high_resolution_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

        EXPECT_GE(elapsed.count(), expectedDuration.count()) << message;
        EXPECT_OK(vibrator->off()) << message;
    };

    for (CompositePrimitive primitive : supported) {
        if (primitive == CompositePrimitive::NOOP) {
            continue;
        }

        // Test individual primitives
        CompositeEffect effect;
        effect.delayMs = 0;
        effect.primitive = primitive;
        effect.scale = 1.0f;
        testComposition({effect}, "\n  For primitive: " + toString(primitive));

        // Test a composition of multiple effects
        const size_t compositeSize = std::min((size_t)maxSize, (size_t)5);

        std::vector<CompositeEffect> composite;
        for (size_t i = 0; i < compositeSize; i++) {
            CompositeEffect effect;
            effect.primitive = primitive;
            effect.scale = 1.0f;
            composite.push_back(effect);
        }

        // Effect with no delay
        for (auto& effect : composite) {
            effect.delayMs = 0;
        }
        testComposition(composite, "\n  For 0ms composite with primitive: " + toString(primitive));

        // Effect with 10ms delay
        if (maxDelay < 10) {
            return;
        }
        for (auto& effect : composite) {
            effect.delayMs = 10;
        }
        testComposition(composite, "\n  For 10ms composite with primitive: " + toString(primitive));

        // Effect with 100ms delay
        if (maxDelay < 100) {
            return;
        }
        for (auto& effect : composite) {
            effect.delayMs = 100;
        }
        testComposition(composite,
                        "\n  For 100ms composite with primitive: " + toString(primitive));
    }
}

TEST_P(VibratorAidl, AlwaysOn) {
    if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) {
        std::vector<Effect> supported;
        EXPECT_OK(vibrator->getSupportedAlwaysOnEffects(&supported));

        for (Effect effect : kEffects) {
            bool isEffectSupported =
                std::find(supported.begin(), supported.end(), effect) != supported.end();

            for (EffectStrength strength : kEffectStrengths) {
                ndk::ScopedAStatus status = vibrator->alwaysOnEnable(0, effect, strength);

                if (isEffectSupported) {
                    EXPECT_OK(std::move(status))
                            << "\n  For effect: " << toString(effect) << " " << toString(strength);
                } else {
                    EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
                            << "\n  For effect: " << toString(effect) << " " << toString(strength);
                }
            }
        }

        EXPECT_OK(vibrator->alwaysOnDisable(0));
    }
}

TEST_P(VibratorAidl, GetResonantFrequency) {
    getResonantFrequencyHz(vibrator, capabilities);
}

TEST_P(VibratorAidl, GetQFactor) {
    float qFactor;
    ndk::ScopedAStatus status = vibrator->getQFactor(&qFactor);
    if (capabilities & IVibrator::CAP_GET_Q_FACTOR) {
        EXPECT_OK(std::move(status));
        ASSERT_GT(qFactor, 0);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
}

TEST_P(VibratorAidl, GetFrequencyResolution) {
    getFrequencyResolutionHz(vibrator, capabilities, version);
}

TEST_P(VibratorAidl, GetFrequencyMinimum) {
    getFrequencyMinimumHz(vibrator, capabilities, version);
}

TEST_P(VibratorAidl, GetBandwidthAmplitudeMap) {
    std::vector<float> bandwidthAmplitudeMap;
    ndk::ScopedAStatus status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);

    if (shouldValidateLegacyFrequencyControlResult(capabilities, version, status)) {
        EXPECT_OK(std::move(status));
        ASSERT_FALSE(bandwidthAmplitudeMap.empty());

        int minMapSize = (getResonantFrequencyHz(vibrator, capabilities) -
                          getFrequencyMinimumHz(vibrator, capabilities, version)) /
                         getFrequencyResolutionHz(vibrator, capabilities, version);
        ASSERT_GT(bandwidthAmplitudeMap.size(), minMapSize);

        for (float e : bandwidthAmplitudeMap) {
            ASSERT_GE(e, 0.0);
            ASSERT_LE(e, 1.0);
        }
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
}

TEST_P(VibratorAidl, GetPwlePrimitiveDurationMax) {
    int32_t durationMs;
    ndk::ScopedAStatus status = vibrator->getPwlePrimitiveDurationMax(&durationMs);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        EXPECT_OK(std::move(status));
        ASSERT_NE(durationMs, 0);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
}

TEST_P(VibratorAidl, GetPwleCompositionSizeMax) {
    int32_t maxSize;
    ndk::ScopedAStatus status = vibrator->getPwleCompositionSizeMax(&maxSize);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        EXPECT_OK(std::move(status));
        ASSERT_NE(maxSize, 0);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
}

TEST_P(VibratorAidl, GetSupportedBraking) {
    std::vector<Braking> supported;
    ndk::ScopedAStatus status = vibrator->getSupportedBraking(&supported);
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        bool isDefaultNoneSupported =
            std::find(supported.begin(), supported.end(), Braking::NONE) != supported.end();
        EXPECT_OK(std::move(status));
        ASSERT_TRUE(isDefaultNoneSupported);
    } else {
        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
    }
}

TEST_P(VibratorAidl, ComposeValidPwle) {
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        ActivePwle firstActive = composeValidActivePwle(vibrator, capabilities, version);

        std::vector<Braking> supported;
        EXPECT_OK(vibrator->getSupportedBraking(&supported));
        bool isClabSupported =
            std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
        BrakingPwle firstBraking;
        firstBraking.braking = isClabSupported ? Braking::CLAB : Braking::NONE;
        firstBraking.duration = 100;

        ActivePwle secondActive = composeValidActivePwle(vibrator, capabilities, version);
        if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
            float minFrequencyHz = getFrequencyMinimumHz(vibrator, capabilities, version);
            float maxFrequencyHz = getFrequencyMaximumHz(vibrator, capabilities, version);
            float freqResolutionHz = getFrequencyResolutionHz(vibrator, capabilities, version);
            // As of API 16 these APIs are deprecated and no longer required to be implemented
            //  with frequency control capability.
            if (minFrequencyHz >= 0 && maxFrequencyHz >= 0 && freqResolutionHz >= 0) {
                secondActive.startFrequency = minFrequencyHz + (freqResolutionHz / 2.0f);
                secondActive.endFrequency = maxFrequencyHz - (freqResolutionHz / 3.0f);
            }
        }
        BrakingPwle secondBraking;
        secondBraking.braking = Braking::NONE;
        secondBraking.duration = 10;

        std::vector<PrimitivePwle> pwleQueue = {firstActive, firstBraking, secondActive,
                                                secondBraking};

        EXPECT_OK(vibrator->composePwle(pwleQueue, nullptr));
        EXPECT_OK(vibrator->off());
    }
}

TEST_P(VibratorAidl, ComposeValidPwleWithCallback) {
    if (!((capabilities & IVibrator::CAP_ON_CALLBACK) &&
          (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS)))
        return;

    auto callback = ndk::SharedRefBase::make<CompletionCallback>();
    int32_t segmentDurationMaxMs;
    vibrator->getPwlePrimitiveDurationMax(&segmentDurationMaxMs);
    uint32_t durationMs = segmentDurationMaxMs * 2 + 100;  // Sum of 2 active and 1 braking below
    auto timeout = std::chrono::milliseconds(durationMs) + VIBRATION_CALLBACK_TIMEOUT;

    ActivePwle active = composeValidActivePwle(vibrator, capabilities, version);

    std::vector<Braking> supported;
    EXPECT_OK(vibrator->getSupportedBraking(&supported));
    bool isClabSupported =
        std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
    BrakingPwle braking;
    braking.braking = isClabSupported ? Braking::CLAB : Braking::NONE;
    braking.duration = 100;

    std::vector<PrimitivePwle> pwleQueue = {active, braking, active};

    EXPECT_OK(vibrator->composePwle(pwleQueue, callback));
    EXPECT_EQ(callback->wait_for(timeout), std::future_status::ready);
    EXPECT_OK(vibrator->off());
}

TEST_P(VibratorAidl, ComposePwleSegmentBoundary) {
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        std::vector<PrimitivePwle> pwleQueue;
        // test empty queue
        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));

        ActivePwle active = composeValidActivePwle(vibrator, capabilities, version);

        PrimitivePwle pwle;
        pwle = active;
        int segmentCountMax;
        vibrator->getPwleCompositionSizeMax(&segmentCountMax);

        // Create PWLE queue with more segments than allowed
        for (int i = 0; i < segmentCountMax + 10; i++) {
            pwleQueue.emplace_back(std::move(pwle));
        }

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));
    }
}

TEST_P(VibratorAidl, ComposePwleAmplitudeParameterBoundary) {
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        ActivePwle active = composeValidActivePwle(vibrator, capabilities, version);
        active.startAmplitude = getAmplitudeMax() + 1.0;  // Amplitude greater than allowed
        active.endAmplitude = getAmplitudeMax() + 1.0;    // Amplitude greater than allowed

        std::vector<PrimitivePwle> pwleQueueGreater = {active};

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueGreater, nullptr));

        active.startAmplitude = getAmplitudeMin() - 1.0;  // Amplitude less than allowed
        active.endAmplitude = getAmplitudeMin() - 1.0;    // Amplitude less than allowed

        std::vector<PrimitivePwle> pwleQueueLess = {active};

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueLess, nullptr));
    }
}

TEST_P(VibratorAidl, ComposePwleFrequencyParameterBoundary) {
    if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) &&
        (capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
        float freqMinimumHz = getFrequencyMinimumHz(vibrator, capabilities, version);
        float freqMaximumHz = getFrequencyMaximumHz(vibrator, capabilities, version);
        float freqResolutionHz = getFrequencyResolutionHz(vibrator, capabilities, version);

        // As of API 16 these APIs are deprecated and no longer required to be implemented with
        // frequency control capability.
        if (freqMinimumHz < 0 || freqMaximumHz < 0 || freqResolutionHz < 0) {
            GTEST_SKIP() << "PWLE V1 is not supported, skipping test";
            return;
        }

        ActivePwle active = composeValidActivePwle(vibrator, capabilities, version);
        active.startFrequency =
            freqMaximumHz + freqResolutionHz;                    // Frequency greater than allowed
        active.endFrequency = freqMaximumHz + freqResolutionHz;  // Frequency greater than allowed

        std::vector<PrimitivePwle> pwleQueueGreater = {active};

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueGreater, nullptr));

        active.startFrequency = freqMinimumHz - freqResolutionHz;  // Frequency less than allowed
        active.endFrequency = freqMinimumHz - freqResolutionHz;    // Frequency less than allowed

        std::vector<PrimitivePwle> pwleQueueLess = {active};

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueLess, nullptr));
    }
}

TEST_P(VibratorAidl, ComposePwleSegmentDurationBoundary) {
    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
        ActivePwle active = composeValidActivePwle(vibrator, capabilities, version);

        int32_t segmentDurationMaxMs;
        vibrator->getPwlePrimitiveDurationMax(&segmentDurationMaxMs);
        active.duration = segmentDurationMaxMs + 10;  // Segment duration greater than allowed

        std::vector<PrimitivePwle> pwleQueue = {active};

        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));
    }
}

TEST_P(VibratorAidl, FrequencyToOutputAccelerationMapHasValidFrequencyRange) {
    if (version < PWLE_V2_MIN_VERSION || !(capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
        GTEST_SKIP() << "Frequency control is not supported, skipping test";
        return;
    }

    std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
    ndk::ScopedAStatus status =
            vibrator->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
    EXPECT_OK(std::move(status));
    ASSERT_FALSE(frequencyToOutputAccelerationMap.empty());
    auto sharpnessRange =
            pwle_v2_utils::getPwleV2SharpnessRange(vibrator, frequencyToOutputAccelerationMap);
    // Validate the curve provides a usable sharpness range, which is a range of frequencies
    // that are supported by the device.
    ASSERT_TRUE(sharpnessRange.first >= 0);
    // Validate that the sharpness range is a valid interval, not a single point.
    ASSERT_TRUE(sharpnessRange.first < sharpnessRange.second);
}

TEST_P(VibratorAidl, FrequencyToOutputAccelerationMapUnsupported) {
    if ((capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) return;

    std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;

    EXPECT_UNKNOWN_OR_UNSUPPORTED(
            vibrator->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap));
}

TEST_P(VibratorAidl, GetPwleV2PrimitiveDurationMaxMillis) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    int32_t durationMs;
    ndk::ScopedAStatus status = vibrator->getPwleV2PrimitiveDurationMaxMillis(&durationMs);
    EXPECT_OK(std::move(status));
    ASSERT_GT(durationMs, 0);  // Ensure greater than zero
    ASSERT_GE(durationMs, pwle_v2_utils::COMPOSE_PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS);
}

TEST_P(VibratorAidl, GetPwleV2CompositionSizeMax) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    int32_t maxSize;
    ndk::ScopedAStatus status = vibrator->getPwleV2CompositionSizeMax(&maxSize);
    EXPECT_OK(std::move(status));
    ASSERT_GT(maxSize, 0);  // Ensure greater than zero
    ASSERT_GE(maxSize, pwle_v2_utils::COMPOSE_PWLE_V2_MIN_REQUIRED_SIZE);
}

TEST_P(VibratorAidl, GetPwleV2PrimitiveDurationMinMillis) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    int32_t durationMs;
    ndk::ScopedAStatus status = vibrator->getPwleV2PrimitiveDurationMinMillis(&durationMs);
    EXPECT_OK(std::move(status));
    ASSERT_GT(durationMs, 0);  // Ensure greater than zero
    ASSERT_LE(durationMs, pwle_v2_utils::COMPOSE_PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS);
}

TEST_P(VibratorAidl, ValidatePwleV2DependencyOnFrequencyControl) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    // Check if frequency control is supported
    bool hasFrequencyControl = (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) != 0;
    ASSERT_TRUE(hasFrequencyControl) << "Frequency control MUST be supported when PWLE V2 is.";
}

TEST_P(VibratorAidl, ComposeValidPwleV2Effect) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    EXPECT_OK(vibrator->composePwleV2(pwle_v2_utils::composeValidPwleV2Effect(vibrator), nullptr));
    EXPECT_OK(vibrator->off());
}

TEST_P(VibratorAidl, ComposePwleV2Unsupported) {
    if (version < PWLE_V2_MIN_VERSION) {
        EXPECT_EQ(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2, 0)
                << "Vibrator version " << version << " should not report PWLE V2 capability.";
    }
    if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) return;

    CompositePwleV2 composite;
    composite.pwlePrimitives.emplace_back(/*amplitude=*/1.0f, /*frequencyHz=*/100.0f,
                                          /*timeMillis=*/50);

    EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->composePwleV2(composite, nullptr));
}

TEST_P(VibratorAidl, ComposeValidPwleV2EffectWithCallback) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    auto callback = ndk::SharedRefBase::make<CompletionCallback>();
    int32_t minDuration;
    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDuration));
    auto timeout = std::chrono::milliseconds(minDuration) + VIBRATION_CALLBACK_TIMEOUT;
    float minFrequency = pwle_v2_utils::getPwleV2FrequencyMinHz(vibrator);

    CompositePwleV2 composite;
    composite.pwlePrimitives.emplace_back(/*amplitude=*/0.5, minFrequency, minDuration);

    EXPECT_OK(vibrator->composePwleV2(composite, callback));
    EXPECT_EQ(callback->wait_for(timeout), std::future_status::ready);
    EXPECT_OK(vibrator->off());
}

TEST_P(VibratorAidl, composePwleV2EffectWithTooManyPoints) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(
            pwle_v2_utils::composePwleV2EffectWithTooManyPoints(vibrator), nullptr));
}

TEST_P(VibratorAidl, composeInvalidPwleV2Effect) {
    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
        return;
    }

    // Retrieve min and max durations
    int32_t minDurationMs, maxDurationMs;
    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs));

    CompositePwleV2 composePwle;

    // Negative amplitude
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/-0.8f, /*frequency=*/100, minDurationMs));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with negative amplitude should fail";
    composePwle.pwlePrimitives.clear();

    // Amplitude exceeding 1.0
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/1.2f, /*frequency=*/100, minDurationMs));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with amplitude greater than 1.0 should fail";
    composePwle.pwlePrimitives.clear();

    // Duration exceeding maximum
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/0.2f, /*frequency=*/100, maxDurationMs + 10));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with duration exceeding maximum should fail";
    composePwle.pwlePrimitives.clear();

    // Negative duration
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/0.2f, /*frequency=*/100, /*time=*/-1));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with negative duration should fail";
    composePwle.pwlePrimitives.clear();

    // Frequency below minimum
    float minFrequency = pwle_v2_utils::getPwleV2FrequencyMinHz(vibrator);
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/0.2f, minFrequency - 1, minDurationMs));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with frequency below minimum should fail";
    composePwle.pwlePrimitives.clear();

    // Frequency above maximum
    float maxFrequency = pwle_v2_utils::getPwleV2FrequencyMaxHz(vibrator);
    composePwle.pwlePrimitives.push_back(
            PwleV2Primitive(/*amplitude=*/0.2f, maxFrequency + 1, minDurationMs));
    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
            << "Composing PWLE V2 effect with frequency above maximum should fail";
}

std::vector<std::tuple<int32_t, int32_t>> GenerateVibratorMapping() {
    std::vector<std::tuple<int32_t, int32_t>> tuples;

    std::vector<std::string> managerNames = findVibratorManagerNames();
    std::vector<int32_t> vibratorIds;
    for (int i = 0; i < managerNames.size(); i++) {
        auto vibratorManager = IVibratorManager::fromBinder(
                ndk::SpAIBinder(AServiceManager_waitForService(managerNames[i].c_str())));
        if (vibratorManager->getVibratorIds(&vibratorIds).isOk()) {
            for (int32_t vibratorId : vibratorIds) {
                tuples.emplace_back(i, vibratorId);
            }
        }
    }

    std::vector<std::string> vibratorNames = findUnmanagedVibratorNames();
    for (int i = 0; i < vibratorNames.size(); i++) {
        tuples.emplace_back(-1, i);
    }

    return tuples;
}

std::string PrintGeneratedTest(const testing::TestParamInfo<VibratorAidl::ParamType> &info) {
    const auto &[managerIdx, vibratorId] = info.param;
    if (managerIdx < 0) {
        return std::string("TOP_LEVEL_VIBRATOR_") + std::to_string(vibratorId);
    }
    return std::string("MANAGER_") + std::to_string(managerIdx) + "_VIBRATOR_ID_" +
           std::to_string(vibratorId);
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(GenerateVibratorMapping()),
                         PrintGeneratedTest);

int main(int argc, char **argv) {
    // Random values are used in the implementation.
    std::srand(std::time(nullptr));

    ::testing::InitGoogleTest(&argc, argv);
    ABinderProcess_setThreadPoolMaxThreadCount(1);
    ABinderProcess_startThreadPool();
    return RUN_ALL_TESTS();
}
