Squash frequency control capability for merged multi-vibrators when

missing frequency profile.

Some CTS tests for frequency data assume that it exists (i.e. crash when
it doesn't) when a device claims it hasFrequencyControl. Note that the
API is currently non-public.

Bug: 257032261
Test: atest VibratorTest
Change-Id: Id1c1ae555f42009cb394a6cef664624054b575ac
This commit is contained in:
Simon Bowden
2022-11-10 19:20:23 +00:00
parent 8d6647d473
commit 69eaf72cf1
2 changed files with 69 additions and 11 deletions

View File

@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.vibrator.IVibrator;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
@@ -313,8 +314,14 @@ public class SystemVibrator extends Vibrator {
private static final float EPSILON = 1e-5f;
public MultiVibratorInfo(VibratorInfo[] vibrators) {
// Need to use an extra constructor to share the computation in super initialization.
this(vibrators, frequencyProfileIntersection(vibrators));
}
private MultiVibratorInfo(VibratorInfo[] vibrators,
VibratorInfo.FrequencyProfile mergedProfile) {
super(/* id= */ -1,
capabilitiesIntersection(vibrators),
capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
supportedEffectsIntersection(vibrators),
supportedBrakingIntersection(vibrators),
supportedPrimitivesAndDurationsIntersection(vibrators),
@@ -323,14 +330,19 @@ public class SystemVibrator extends Vibrator {
integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
frequencyProfileIntersection(vibrators));
mergedProfile);
}
private static int capabilitiesIntersection(VibratorInfo[] infos) {
private static int capabilitiesIntersection(VibratorInfo[] infos,
boolean frequencyProfileIsEmpty) {
int intersection = ~0;
for (VibratorInfo info : infos) {
intersection &= info.getCapabilities();
}
if (frequencyProfileIsEmpty) {
// Revoke frequency control if the merged frequency profile ended up empty.
intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL;
}
return intersection;
}

View File

@@ -246,10 +246,12 @@ public class VibratorTest {
@Test
public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(1f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(2f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
.build();
@@ -258,6 +260,7 @@ public class VibratorTest {
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
assertEmptyFrequencyProfileAndControl(info);
// One vibrator with values undefined.
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
@@ -266,16 +269,19 @@ public class VibratorTest {
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 10, 0.5f, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 5, 1, null))
@@ -285,113 +291,131 @@ public class VibratorTest {
assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
// No frequency range defined.
assertTrue(info.getFrequencyProfile().isEmpty());
assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
@Test
public void getFrequencyProfile_noVibrator_returnsEmpty() {
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentResonantFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_missingValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingResonantFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMinFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
assertTrue(info.getFrequencyProfile().isEmpty());
assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0.5f, 1, 1, 0.5f }))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 1, 1, 1 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
.setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0.8f, 1, 0.8f, 0.5f }))
.build();
@@ -401,6 +425,20 @@ public class VibratorTest {
assertEquals(
new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
info.getFrequencyProfile());
assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
// Third vibrator without frequency control capability.
thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0.8f, 1, 0.8f, 0.5f }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
assertEquals(
new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
info.getFrequencyProfile());
assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
@Test
@@ -547,4 +585,12 @@ public class VibratorTest {
VibrationAttributes vibrationAttributes = captor.getValue();
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
/**
* Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
*/
void assertEmptyFrequencyProfileAndControl(VibratorInfo info) {
assertTrue(info.getFrequencyProfile().isEmpty());
assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
}