Skip to content

Commit cfe1e76

Browse files
Treehugger RobotGerrit Code Review
authored andcommitted
Merge "Fix haptics scaling to match VibrationEffect function" into main
2 parents 0b3be83 + 1d88585 commit cfe1e76

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)