Skip to content

Commit 1d88585

Browse files
laissandradeShunkai Yao
authored andcommitted
Fix haptics scaling to match VibrationEffect function
Fix the scale function in ExternalVibrationUtils to match the one used by VibrationEffect. Add flagged fix with new tests for the module libvibrator. Bug: 356144312 Flag: android.os.vibrator.fix_audio_coupled_haptics_scaling Test: libvibrator_test Change-Id: I56116536d7cddab29448f0e436ee752c7181eb45 Merged-In: I56116536d7cddab29448f0e436ee752c7181eb45
1 parent 351a8df commit 1d88585

7 files changed

Lines changed: 510 additions & 7 deletions

File tree

libs/vibrator/Android.bp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ package {
2424
cc_defaults {
2525
name: "libvibrator_defaults",
2626

27+
defaults: [
28+
"aconfig_lib_cc_shared_link.defaults",
29+
],
30+
2731
cflags: [
2832
"-Wall",
2933
"-Werror",
@@ -50,9 +54,11 @@ cc_library {
5054
"libbinder",
5155
"liblog",
5256
"libutils",
57+
"server_configurable_flags",
5358
],
5459

5560
whole_static_libs: [
61+
"android.os.vibrator.flags-aconfig-cc",
5662
"libvibratorutils",
5763
],
5864

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

88+
static_libs: [
89+
"android.os.vibrator.flags-aconfig-cc",
90+
],
91+
8292
shared_libs: [
93+
"liblog",
8394
"libutils",
95+
"server_configurable_flags",
8496
],
8597

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

90102
visibility: [
91103
"//frameworks/native/libs/vibrator",
104+
"//frameworks/native/libs/vibrator/tests",
92105
"//frameworks/av/media/libeffects/hapticgenerator",
93106
],
94107
}

libs/vibrator/ExternalVibrationUtils.cpp

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
#include <cstring>
1717

18+
#include <android_os_vibrator.h>
19+
20+
#include <algorithm>
1821
#include <math.h>
1922

2023
#include <vibrator/ExternalVibrationUtils.h>
@@ -25,8 +28,9 @@ namespace {
2528
static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
2629
static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
2730
static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
31+
static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
2832

29-
float getHapticScaleGamma(HapticLevel level) {
33+
float getOldHapticScaleGamma(HapticLevel level) {
3034
switch (level) {
3135
case HapticLevel::VERY_LOW:
3236
return 2.0f;
@@ -41,7 +45,7 @@ float getHapticScaleGamma(HapticLevel level) {
4145
}
4246
}
4347

44-
float getHapticMaxAmplitudeRatio(HapticLevel level) {
48+
float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
4549
switch (level) {
4650
case HapticLevel::VERY_LOW:
4751
return HAPTIC_SCALE_VERY_LOW_RATIO;
@@ -56,6 +60,52 @@ float getHapticMaxAmplitudeRatio(HapticLevel level) {
5660
}
5761
}
5862

63+
/* Same as VibrationScaler.SCALE_LEVEL_* */
64+
float getHapticScaleFactor(HapticLevel level) {
65+
switch (level) {
66+
case HapticLevel::VERY_LOW:
67+
return 0.6f;
68+
case HapticLevel::LOW:
69+
return 0.8f;
70+
case HapticLevel::HIGH:
71+
return 1.2f;
72+
case HapticLevel::VERY_HIGH:
73+
return 1.4f;
74+
default:
75+
return 1.0f;
76+
}
77+
}
78+
79+
float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
80+
float sign = value >= 0 ? 1.0 : -1.0;
81+
return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
82+
* maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
83+
}
84+
85+
float applyNewHapticScale(float value, float scaleFactor) {
86+
float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
87+
if (scaleFactor <= 1) {
88+
// Scale down is simply a gamma corrected application of scaleFactor to the intensity.
89+
// Scale up requires a different curve to ensure the intensity will not become > 1.
90+
return value * scale;
91+
}
92+
93+
float sign = value >= 0 ? 1.0f : -1.0f;
94+
float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
95+
float x = fabsf(value) * scale * extraScale;
96+
float maxX = scale * extraScale; // scaled x for intensity == 1
97+
98+
float expX = expf(x);
99+
float expMaxX = expf(maxX);
100+
101+
// Using f = tanh as the scale up function so the max value will converge.
102+
// a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
103+
float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
104+
float fx = (expX - 1.0f) / (expX + 1.0f);
105+
106+
return sign * std::clamp(a * fx, 0.0f, 1.0f);
107+
}
108+
59109
void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
60110
if (scale.isScaleMute()) {
61111
memset(buffer, 0, length * sizeof(float));
@@ -65,15 +115,18 @@ void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
65115
return;
66116
}
67117
HapticLevel hapticLevel = scale.getLevel();
118+
float scaleFactor = getHapticScaleFactor(hapticLevel);
68119
float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
69-
float gamma = getHapticScaleGamma(hapticLevel);
70-
float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel);
120+
float oldGamma = getOldHapticScaleGamma(hapticLevel);
121+
float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
71122

72123
for (size_t i = 0; i < length; i++) {
73124
if (hapticLevel != HapticLevel::NONE) {
74-
float sign = buffer[i] >= 0 ? 1.0 : -1.0;
75-
buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
76-
* maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
125+
if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) {
126+
buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
127+
} else {
128+
buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
129+
}
77130
}
78131

79132
if (adaptiveScaleFactor != 1.0f) {

libs/vibrator/TEST_MAPPING

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"postsubmit": [
3+
{
4+
"name": "libvibrator_test"
5+
}
6+
]
7+
}

libs/vibrator/tests/Android.bp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (C) 2024 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package {
15+
default_team: "trendy_team_haptics_framework",
16+
// See: http://go/android-license-faq
17+
// A large-scale-change added 'default_applicable_licenses' to import
18+
// all of the 'license_kinds' from "frameworks_native_license"
19+
// to get the below license kinds:
20+
// SPDX-license-identifier-Apache-2.0
21+
default_applicable_licenses: ["frameworks_native_license"],
22+
}
23+
24+
cc_test {
25+
name: "libvibrator_test",
26+
test_suites: ["general-tests"],
27+
defaults: [
28+
"aconfig_lib_cc_shared_link.defaults",
29+
],
30+
srcs: [
31+
"ExternalVibrationTest.cpp",
32+
"ExternalVibrationUtilsTest.cpp",
33+
],
34+
cflags: [
35+
"-Wall",
36+
"-Werror",
37+
"-Wextra",
38+
],
39+
static_libs: [
40+
"android.os.vibrator.flags-aconfig-cc",
41+
"libflagtest",
42+
"libgtest",
43+
"liblog",
44+
"libvibrator",
45+
"libvibratorutils",
46+
],
47+
shared_libs: [
48+
"libbase",
49+
"libbinder",
50+
"libutils",
51+
"server_configurable_flags",
52+
],
53+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (C) 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <binder/Parcel.h>
18+
#include <gtest/gtest.h>
19+
#include <vibrator/ExternalVibration.h>
20+
21+
using namespace android;
22+
using namespace testing;
23+
24+
using HapticLevel = os::HapticLevel;
25+
using ScaleLevel = os::ExternalVibrationScale::ScaleLevel;
26+
27+
class TestVibrationController : public os::IExternalVibrationController {
28+
public:
29+
explicit TestVibrationController() {}
30+
IBinder *onAsBinder() override { return nullptr; }
31+
binder::Status mute(/*out*/ bool *ret) override {
32+
*ret = false;
33+
return binder::Status::ok();
34+
};
35+
binder::Status unmute(/*out*/ bool *ret) override {
36+
*ret = false;
37+
return binder::Status::ok();
38+
};
39+
};
40+
41+
class ExternalVibrationTest : public Test {
42+
protected:
43+
HapticLevel toHapticLevel(ScaleLevel level) {
44+
os::ExternalVibrationScale externalVibrationScale;
45+
externalVibrationScale.scaleLevel = level;
46+
os::HapticScale hapticScale =
47+
os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
48+
return hapticScale.getLevel();
49+
}
50+
};
51+
52+
TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) {
53+
int32_t uid = 1;
54+
std::string pkg("package.name");
55+
audio_attributes_t originalAttrs;
56+
originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
57+
originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
58+
originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
59+
originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE;
60+
sp<TestVibrationController> vibrationController = new TestVibrationController();
61+
ASSERT_NE(vibrationController, nullptr);
62+
sp<os::ExternalVibration> original =
63+
new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController);
64+
ASSERT_NE(original, nullptr);
65+
EXPECT_EQ(original->getUid(), uid);
66+
EXPECT_EQ(original->getPackage(), pkg);
67+
EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type);
68+
EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage);
69+
EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source);
70+
EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags);
71+
EXPECT_EQ(original->getController(), vibrationController);
72+
audio_attributes_t defaultAttrs;
73+
defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
74+
defaultAttrs.usage = AUDIO_USAGE_UNKNOWN;
75+
defaultAttrs.source = AUDIO_SOURCE_DEFAULT;
76+
defaultAttrs.flags = AUDIO_FLAG_NONE;
77+
sp<os::ExternalVibration> parceled =
78+
new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr);
79+
ASSERT_NE(parceled, nullptr);
80+
Parcel parcel;
81+
original->writeToParcel(&parcel);
82+
parcel.setDataPosition(0);
83+
parceled->readFromParcel(&parcel);
84+
EXPECT_EQ(parceled->getUid(), uid);
85+
EXPECT_EQ(parceled->getPackage(), pkg);
86+
EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type);
87+
EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage);
88+
EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source);
89+
EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags);
90+
// TestVibrationController does not implement onAsBinder, skip controller parcel in this test.
91+
}
92+
93+
TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
94+
os::ExternalVibrationScale externalVibrationScale;
95+
externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
96+
externalVibrationScale.adaptiveHapticsScale = 0.8f;
97+
os::HapticScale hapticScale =
98+
os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
99+
// Check scale factor is forwarded.
100+
EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
101+
EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f);
102+
// Check conversion for all levels.
103+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE);
104+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW);
105+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW);
106+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE);
107+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH);
108+
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH);
109+
}

0 commit comments

Comments
 (0)