Merge "Add scale factor field to HapticScale" into main
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);
+}