Add scale factor field to HapticScale

Add extra field scale factor to the ExtenalVibratorService result to
support continuous scaling haptic channels based on a float scale.

The new updated scale function still converges to 1.0 when it's scaling
up the haptic data, but it no longer applies distortion of the vibration
waveform when it scales down the amplitude.

The new scaling is defined as:

(scale_factor * amplitude)
  when scale_factor <= 1

(scale_factor * amplitude) / (1 + (scale_factor - 1) * amplitude^2)
  when scale_factor > 1

Bug: 356407385
Flag: android.os.vibrator.haptics_scale_v2_enabled
Test: libvibrator_tests
Change-Id: Ibde6699bf23614e27b50e1ff79e364d942499187
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index c97e496..cae2de2 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -93,8 +93,8 @@
                   externalVibrationScale.scaleLevel);
     }
 
-    return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/
-                      externalVibrationScale.adaptiveHapticsScale};
+    return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor,
+                           externalVibrationScale.adaptiveHapticsScale);
 }
 
 } // namespace os
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 706f3d7..54afb71 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "ExternalVibrationUtils"
+
 #include <cstring>
 
 #include <android_os_vibrator.h>
@@ -20,6 +22,7 @@
 #include <algorithm>
 #include <math.h>
 
+#include <log/log.h>
 #include <vibrator/ExternalVibrationUtils.h>
 
 namespace android::os {
@@ -29,6 +32,7 @@
 static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
 static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
 static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
+static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN
 
 float getOldHapticScaleGamma(HapticLevel level) {
     switch (level) {
@@ -60,9 +64,34 @@
     }
 }
 
-/* Same as VibrationScaler.SCALE_LEVEL_* */
-float getHapticScaleFactor(HapticLevel level) {
-    switch (level) {
+/* Same as VibrationScaler.getScaleFactor */
+float getHapticScaleFactor(HapticScale scale) {
+    if (android_os_vibrator_haptics_scale_v2_enabled()) {
+        if (scale.getScaleFactor() >= 0) {
+            // ExternalVibratorService provided the scale factor, use it.
+            return scale.getScaleFactor();
+        }
+
+        HapticLevel level = scale.getLevel();
+        switch (level) {
+            case HapticLevel::MUTE:
+                return 0.0f;
+            case HapticLevel::NONE:
+                return 1.0f;
+            default:
+                float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level));
+                if (scaleFactor <= 0) {
+                    ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0",
+                          scaleFactor, static_cast<int32_t>(level));
+                    scaleFactor = 1.0f;
+                }
+                return scaleFactor;
+        }
+    }
+    // Same as VibrationScaler.SCALE_FACTOR_*
+    switch (scale.getLevel()) {
+        case HapticLevel::MUTE:
+            return 0.0f;
         case HapticLevel::VERY_LOW:
             return 0.6f;
         case HapticLevel::LOW:
@@ -83,6 +112,14 @@
 }
 
 float applyNewHapticScale(float value, float scaleFactor) {
+    if (android_os_vibrator_haptics_scale_v2_enabled()) {
+        if (scaleFactor <= 1 || value == 0) {
+            return value * scaleFactor;
+        } else {
+            // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
+            return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value);
+        }
+    }
     float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
     if (scaleFactor <= 1) {
         // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
@@ -115,14 +152,15 @@
         return;
     }
     HapticLevel hapticLevel = scale.getLevel();
-    float scaleFactor = getHapticScaleFactor(hapticLevel);
+    float scaleFactor = getHapticScaleFactor(scale);
     float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
     float oldGamma = getOldHapticScaleGamma(hapticLevel);
     float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
 
     for (size_t i = 0; i < length; i++) {
         if (hapticLevel != HapticLevel::NONE) {
-            if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) {
+            if (android_os_vibrator_fix_audio_coupled_haptics_scaling() ||
+                android_os_vibrator_haptics_scale_v2_enabled()) {
                 buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
             } else {
                 buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index d9a2b81..322a2ac 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,9 +17,13 @@
 #ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
 #define ANDROID_EXTERNAL_VIBRATION_UTILS_H
 
+#include <cstring>
+#include <sstream>
+#include <string>
+
 namespace android::os {
 
-enum class HapticLevel {
+enum class HapticLevel : int32_t {
     MUTE = -100,
     VERY_LOW = -2,
     LOW = -1,
@@ -31,32 +35,42 @@
 class HapticScale {
 private:
 HapticLevel mLevel = HapticLevel::NONE;
+float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor
 float mAdaptiveScaleFactor = 1.0f;
 
 public:
-constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor)
-    : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
-constexpr HapticScale(HapticLevel level) : mLevel(level) {}
-constexpr HapticScale() {}
+    explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor)
+          : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
+    explicit HapticScale(HapticLevel level) : mLevel(level) {}
+    constexpr HapticScale() {}
 
-HapticLevel getLevel() const { return mLevel; }
-float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
+    HapticLevel getLevel() const { return mLevel; }
+    float getScaleFactor() const { return mScaleFactor; }
+    float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
 
-bool operator==(const HapticScale& other) const {
-    return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
-}
+    bool operator==(const HapticScale& other) const {
+        return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor &&
+                mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
+    }
 
 bool isScaleNone() const {
-    return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f;
+    return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f;
 }
 
-bool isScaleMute() const {
-    return mLevel == HapticLevel::MUTE;
+bool isScaleMute() const { return mLevel == HapticLevel::MUTE || mScaleFactor == 0; }
+
+std::string toString() const {
+    std::ostringstream os;
+    os << "HapticScale { level: " << static_cast<int>(mLevel);
+    os << ", scaleFactor: " << mScaleFactor;
+    os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor;
+    os << "}";
+    return os.str();
 }
 
-static HapticScale mute() {
-    return {/*level=*/os::HapticLevel::MUTE};
-}
+static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); }
+
+static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); }
 };
 
 bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp
index 3141380..4133836 100644
--- a/libs/vibrator/tests/ExternalVibrationTest.cpp
+++ b/libs/vibrator/tests/ExternalVibrationTest.cpp
@@ -57,11 +57,14 @@
     originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
     originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
     originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE;
+
     sp<TestVibrationController> vibrationController = new TestVibrationController();
     ASSERT_NE(vibrationController, nullptr);
+
     sp<os::ExternalVibration> original =
             new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController);
     ASSERT_NE(original, nullptr);
+
     EXPECT_EQ(original->getUid(), uid);
     EXPECT_EQ(original->getPackage(), pkg);
     EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type);
@@ -69,18 +72,22 @@
     EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source);
     EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags);
     EXPECT_EQ(original->getController(), vibrationController);
+
     audio_attributes_t defaultAttrs;
     defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
     defaultAttrs.usage = AUDIO_USAGE_UNKNOWN;
     defaultAttrs.source = AUDIO_SOURCE_DEFAULT;
     defaultAttrs.flags = AUDIO_FLAG_NONE;
+
     sp<os::ExternalVibration> parceled =
             new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr);
     ASSERT_NE(parceled, nullptr);
+
     Parcel parcel;
     original->writeToParcel(&parcel);
     parcel.setDataPosition(0);
     parceled->readFromParcel(&parcel);
+
     EXPECT_EQ(parceled->getUid(), uid);
     EXPECT_EQ(parceled->getPackage(), pkg);
     EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type);
@@ -93,12 +100,17 @@
 TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
     os::ExternalVibrationScale externalVibrationScale;
     externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
+    externalVibrationScale.scaleFactor = 0.5f;
     externalVibrationScale.adaptiveHapticsScale = 0.8f;
+
     os::HapticScale hapticScale =
             os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
-    // Check scale factor is forwarded.
+
+    // Check scale factors are forwarded.
     EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
+    EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f);
     EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f);
+
     // Check conversion for all levels.
     EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE);
     EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW);
diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
index 3d8dd9c..7adc9c3 100644
--- a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
+++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
@@ -41,7 +41,7 @@
 
 protected:
     void scaleBuffer(HapticLevel hapticLevel) {
-        scaleBuffer(HapticScale(hapticLevel), 0 /* limit */);
+        scaleBuffer(HapticScale(hapticLevel));
     }
 
     void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) {
@@ -49,7 +49,11 @@
     }
 
     void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) {
-        scaleBuffer(HapticScale(hapticLevel, adaptiveScaleFactor), limit);
+        scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit);
+    }
+
+    void scaleBuffer(HapticScale hapticScale) {
+        scaleBuffer(hapticScale, 0 /* limit */);
     }
 
     void scaleBuffer(HapticScale hapticScale, float limit) {
@@ -60,11 +64,19 @@
     float mBuffer[TEST_BUFFER_LENGTH];
 };
 
-TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestLegacyScaleMute,
-        REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+                                          ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    float expected[TEST_BUFFER_LENGTH];
+    std::fill(std::begin(expected), std::end(expected), 0);
+
+    scaleBuffer(HapticLevel::MUTE);
+    EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expected[TEST_BUFFER_LENGTH];
     std::fill(std::begin(expected), std::end(expected), 0);
 
@@ -73,10 +85,9 @@
 }
 
 TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestFixedScaleMute,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+        ExternalVibrationUtilsTest, TestScaleV2Mute,
+        // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expected[TEST_BUFFER_LENGTH];
     std::fill(std::begin(expected), std::end(expected), 0);
 
@@ -84,11 +95,19 @@
     EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
 
-TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestLegacyScaleNone,
-        REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+                                          ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    float expected[TEST_BUFFER_LENGTH];
+    std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+    scaleBuffer(HapticLevel::NONE);
+    EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expected[TEST_BUFFER_LENGTH];
     std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
 
@@ -97,10 +116,9 @@
 }
 
 TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestFixedScaleNone,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+        ExternalVibrationUtilsTest, TestScaleV2None,
+        // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expected[TEST_BUFFER_LENGTH];
     std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
 
@@ -108,11 +126,9 @@
     EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
 
-TEST_F_WITH_FLAGS(
-    ExternalVibrationUtilsTest,
-    TestLegacyScaleToHapticLevel,
-    REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+                                          ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f };
     scaleBuffer(HapticLevel::VERY_HIGH);
     EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
@@ -130,11 +146,9 @@
     EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
 
-TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestFixedScaleToHapticLevel,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f };
     scaleBuffer(HapticLevel::VERY_HIGH);
     EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
@@ -153,10 +167,52 @@
 }
 
 TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestAdaptiveScaleFactorAppliedAfterLegacyScale,
-        REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+        ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel,
+        // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f };
+    scaleBuffer(HapticLevel::VERY_HIGH);
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f };
+    scaleBuffer(HapticLevel::HIGH);
+    EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f };
+    scaleBuffer(HapticLevel::LOW);
+    EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f };
+    scaleBuffer(HapticLevel::VERY_LOW);
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+        ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel,
+    // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+    REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    constexpr float adaptiveScaleNone = 1.0f;
+
+    float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f };
+    scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone));
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f };
+    scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone));
+    EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f };
+    scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone));
+    EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f };
+    scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone));
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+                                          ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     // Haptic level scale up then adaptive scale down
     float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f };
     scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
@@ -178,11 +234,9 @@
     EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
 
-TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestAdaptiveScaleFactorAppliedAfterFixedScale,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     // Haptic level scale up then adaptive scale down
     float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f };
     scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
@@ -205,10 +259,33 @@
 }
 
 TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestLimitAppliedAfterLegacyScale,
-        REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+        ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2,
+        // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    // Haptic level scale up then adaptive scale down
+    float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f };
+    scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    // Haptic level scale up then adaptive scale up
+    float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f };
+    scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    // Haptic level scale down then adaptive scale down
+    float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+    scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    // Haptic level scale down then adaptive scale up
+    float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f };
+    scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+                                          ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
     float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f };
     scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
@@ -220,11 +297,9 @@
     EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
 
-TEST_F_WITH_FLAGS(
-        ExternalVibrationUtilsTest,
-        TestLimitAppliedAfterFixedScale,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
     // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
     float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
     scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
@@ -235,3 +310,18 @@
     scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
     EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
 }
+
+TEST_F_WITH_FLAGS(
+        ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2,
+        // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+    // Scaled = { 0.2, -0.2, 0.15f, -0.07f };
+    float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+    scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+    // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f }
+    float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f };
+    scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+    EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}