Add Chirp APIs for PWLE sequence creation

Add the necessary Chirp APIs required to allow for piecewise-linear
equation sequencing of haptic waveforms. These APIs will allow
developers to create a fully customizable sequence of haptics waveforms
for playback.

Bug: 162859057
Test: verify pwle sequences can be created and played using idlcli
command. Also verify using atest.

Change-Id: I7fec224b7090e482bbcd1c94a3799ec232cc547f
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index bf61bfe..c6682b3 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -26,9 +26,16 @@
 
 static constexpr int32_t kComposeDelayMaxMs = 1000;
 static constexpr int32_t kComposeSizeMax = 256;
+static constexpr int32_t kComposePwleSizeMax = 127;
 
 static constexpr float kResonantFrequency = 150.0;
 static constexpr float kQFactor = 11.0;
+static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+static constexpr float PWLE_LEVEL_MIN = 0.0;
+static constexpr float PWLE_LEVEL_MAX = 0.98256;
+static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
+static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
+static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
 
 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
     LOG(INFO) << "Vibrator reporting capabilities";
@@ -36,7 +43,8 @@
                     IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
                     IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
                     IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
-                    IVibrator::CAP_GET_Q_FACTOR;
+                    IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
+                    IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
     return ndk::ScopedAStatus::ok();
 }
 
@@ -215,6 +223,169 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
+    *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
+    *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
+    // A valid array should be of size:
+    //     (PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ
+    *_aidl_return = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10,
+                     0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20};
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
+    *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
+    *maxSize = kComposePwleSizeMax;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
+    *supported = {
+            Braking::NONE,
+            Braking::CLAB,
+    };
+    return ndk::ScopedAStatus::ok();
+}
+
+void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
+    const float reset = -1.0;
+    prevEndAmplitude = reset;
+    prevEndFrequency = reset;
+}
+
+void incrementIndex(int &index) {
+    index += 1;
+}
+
+void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
+    pwleBuilder << ",C" << segmentIdx << ":1";
+    pwleBuilder << ",B" << segmentIdx << ":0";
+    pwleBuilder << ",AR" << segmentIdx << ":0";
+    pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
+                            float amplitude, float frequency) {
+    pwleBuilder << ",T" << segmentIdx << ":" << duration;
+    pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
+    pwleBuilder << ",F" << segmentIdx << ":" << frequency;
+    constructActiveDefaults(pwleBuilder, segmentIdx);
+}
+
+void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
+                             Braking brakingType) {
+    pwleBuilder << ",T" << segmentIdx << ":" << duration;
+    pwleBuilder << ",L" << segmentIdx << ":" << 0;
+    pwleBuilder << ",F" << segmentIdx << ":" << 0;
+    pwleBuilder << ",C" << segmentIdx << ":0";
+    pwleBuilder << ",B" << segmentIdx << ":"
+                << static_cast<std::underlying_type<Braking>::type>(brakingType);
+    pwleBuilder << ",AR" << segmentIdx << ":0";
+    pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
+                                         const std::shared_ptr<IVibratorCallback> &callback) {
+    std::ostringstream pwleBuilder;
+    std::string pwleQueue;
+
+    int compositionSizeMax;
+    getPwleCompositionSizeMax(&compositionSizeMax);
+    if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    float prevEndAmplitude;
+    float prevEndFrequency;
+    resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
+
+    int segmentIdx = 0;
+    uint32_t totalDuration = 0;
+
+    pwleBuilder << "S:0,WF:4,RP:0,WT:0";
+
+    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.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))) {
+                    constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
+                                           active.startFrequency);
+                    incrementIndex(segmentIdx);
+                }
+
+                constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
+                                       active.endAmplitude, active.endFrequency);
+                incrementIndex(segmentIdx);
+
+                prevEndAmplitude = active.endAmplitude;
+                prevEndFrequency = active.endFrequency;
+                totalDuration += active.duration;
+                break;
+            }
+            case PrimitivePwle::braking: {
+                auto braking = e.get<PrimitivePwle::braking>();
+                if (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);
+                }
+
+                constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
+                incrementIndex(segmentIdx);
+
+                constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
+                incrementIndex(segmentIdx);
+
+                resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
+                totalDuration += braking.duration;
+                break;
+            }
+        }
+    }
+
+    std::thread([=] {
+        LOG(INFO) << "Starting composePwle on another thread";
+        usleep(totalDuration * 1000);
+        if (callback != nullptr) {
+            LOG(INFO) << "Notifying compose PWLE complete";
+            callback->onComplete();
+        }
+    }).detach();
+
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace vibrator
 }  // namespace hardware
 }  // namespace android