Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfe1e76d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Fix haptics scaling to match VibrationEffect function" into main

parents 0b3be83f 1d88585f
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ package {
cc_defaults {
    name: "libvibrator_defaults",

    defaults: [
        "aconfig_lib_cc_shared_link.defaults",
    ],

    cflags: [
        "-Wall",
        "-Werror",
@@ -50,9 +54,11 @@ cc_library {
        "libbinder",
        "liblog",
        "libutils",
        "server_configurable_flags",
    ],

    whole_static_libs: [
        "android.os.vibrator.flags-aconfig-cc",
        "libvibratorutils",
    ],

@@ -79,8 +85,14 @@ cc_library {
    vendor_available: true,
    double_loadable: true,

    static_libs: [
        "android.os.vibrator.flags-aconfig-cc",
    ],

    shared_libs: [
        "liblog",
        "libutils",
        "server_configurable_flags",
    ],

    srcs: [
@@ -89,6 +101,7 @@ cc_library {

    visibility: [
        "//frameworks/native/libs/vibrator",
        "//frameworks/native/libs/vibrator/tests",
        "//frameworks/av/media/libeffects/hapticgenerator",
    ],
}
+60 −7
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
#include <cstring>

#include <android_os_vibrator.h>

#include <algorithm>
#include <math.h>

#include <vibrator/ExternalVibrationUtils.h>
@@ -25,8 +28,9 @@ namespace {
static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
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

float getHapticScaleGamma(HapticLevel level) {
float getOldHapticScaleGamma(HapticLevel level) {
    switch (level) {
    case HapticLevel::VERY_LOW:
        return 2.0f;
@@ -41,7 +45,7 @@ float getHapticScaleGamma(HapticLevel level) {
    }
}

float getHapticMaxAmplitudeRatio(HapticLevel level) {
float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
    switch (level) {
    case HapticLevel::VERY_LOW:
        return HAPTIC_SCALE_VERY_LOW_RATIO;
@@ -56,6 +60,52 @@ float getHapticMaxAmplitudeRatio(HapticLevel level) {
    }
}

/* Same as VibrationScaler.SCALE_LEVEL_* */
float getHapticScaleFactor(HapticLevel level) {
    switch (level) {
        case HapticLevel::VERY_LOW:
            return 0.6f;
        case HapticLevel::LOW:
            return 0.8f;
        case HapticLevel::HIGH:
            return 1.2f;
        case HapticLevel::VERY_HIGH:
            return 1.4f;
        default:
            return 1.0f;
    }
}

float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
    float sign = value >= 0 ? 1.0 : -1.0;
    return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
                * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
}

float applyNewHapticScale(float value, float scaleFactor) {
    float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
    if (scaleFactor <= 1) {
        // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
        // Scale up requires a different curve to ensure the intensity will not become > 1.
        return value * scale;
    }

    float sign = value >= 0 ? 1.0f : -1.0f;
    float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
    float x = fabsf(value) * scale * extraScale;
    float maxX = scale * extraScale; // scaled x for intensity == 1

    float expX = expf(x);
    float expMaxX = expf(maxX);

    // Using f = tanh as the scale up function so the max value will converge.
    // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
    float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
    float fx = (expX - 1.0f) / (expX + 1.0f);

    return sign * std::clamp(a * fx, 0.0f, 1.0f);
}

void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
    if (scale.isScaleMute()) {
        memset(buffer, 0, length * sizeof(float));
@@ -65,15 +115,18 @@ void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
        return;
    }
    HapticLevel hapticLevel = scale.getLevel();
    float scaleFactor = getHapticScaleFactor(hapticLevel);
    float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
    float gamma = getHapticScaleGamma(hapticLevel);
    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel);
    float oldGamma = getOldHapticScaleGamma(hapticLevel);
    float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);

    for (size_t i = 0; i < length; i++) {
        if (hapticLevel != HapticLevel::NONE) {
            float sign = buffer[i] >= 0 ? 1.0 : -1.0;
            buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
                        * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
            if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) {
                buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
            } else {
                buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
            }
        }

        if (adaptiveScaleFactor != 1.0f) {
+7 −0
Original line number Diff line number Diff line
{
  "postsubmit": [
    {
      "name": "libvibrator_test"
    }
  ]
}
+53 −0
Original line number Diff line number Diff line
// Copyright (C) 2024 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.
package {
    default_team: "trendy_team_haptics_framework",
    // See: http://go/android-license-faq
    // A large-scale-change added 'default_applicable_licenses' to import
    // all of the 'license_kinds' from "frameworks_native_license"
    // to get the below license kinds:
    //   SPDX-license-identifier-Apache-2.0
    default_applicable_licenses: ["frameworks_native_license"],
}

cc_test {
    name: "libvibrator_test",
    test_suites: ["general-tests"],
    defaults: [
        "aconfig_lib_cc_shared_link.defaults",
    ],
    srcs: [
        "ExternalVibrationTest.cpp",
        "ExternalVibrationUtilsTest.cpp",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
    static_libs: [
        "android.os.vibrator.flags-aconfig-cc",
        "libflagtest",
        "libgtest",
        "liblog",
        "libvibrator",
        "libvibratorutils",
    ],
    shared_libs: [
        "libbase",
        "libbinder",
        "libutils",
        "server_configurable_flags",
    ],
}
+109 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 <binder/Parcel.h>
#include <gtest/gtest.h>
#include <vibrator/ExternalVibration.h>

using namespace android;
using namespace testing;

using HapticLevel = os::HapticLevel;
using ScaleLevel = os::ExternalVibrationScale::ScaleLevel;

class TestVibrationController : public os::IExternalVibrationController {
public:
    explicit TestVibrationController() {}
    IBinder *onAsBinder() override { return nullptr; }
    binder::Status mute(/*out*/ bool *ret) override {
        *ret = false;
        return binder::Status::ok();
    };
    binder::Status unmute(/*out*/ bool *ret) override {
        *ret = false;
        return binder::Status::ok();
    };
};

class ExternalVibrationTest : public Test {
protected:
    HapticLevel toHapticLevel(ScaleLevel level) {
        os::ExternalVibrationScale externalVibrationScale;
        externalVibrationScale.scaleLevel = level;
        os::HapticScale hapticScale =
                os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
        return hapticScale.getLevel();
    }
};

TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) {
    int32_t uid = 1;
    std::string pkg("package.name");
    audio_attributes_t originalAttrs;
    originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
    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);
    EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage);
    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);
    EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage);
    EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source);
    EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags);
    // TestVibrationController does not implement onAsBinder, skip controller parcel in this test.
}

TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
    os::ExternalVibrationScale externalVibrationScale;
    externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
    externalVibrationScale.adaptiveHapticsScale = 0.8f;
    os::HapticScale hapticScale =
            os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
    // Check scale factor is forwarded.
    EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
    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);
    EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW);
    EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE);
    EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH);
    EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH);
}
Loading