Introduce CTS for Vibrator frequency APIs

Add tests to ensure that devices with frequency control will return
non-empty frequency profiles with valid values for output acceleration
and supported frequencies.

Also introduce coverage tests to the new resonant frequency and Q-factor
APIs.

Bug: 211750173
Test: VibratorTest
Change-Id: Id65d5723473201b4b92aec812cbbd45c77a84631
diff --git a/tests/tests/os/src/android/os/cts/VibratorManagerTest.java b/tests/tests/os/src/android/os/cts/VibratorManagerTest.java
index 2ddecab..f0fb42e 100644
--- a/tests/tests/os/src/android/os/cts/VibratorManagerTest.java
+++ b/tests/tests/os/src/android/os/cts/VibratorManagerTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
@@ -35,6 +34,7 @@
 import android.os.Vibrator;
 import android.os.Vibrator.OnVibratorStateChangedListener;
 import android.os.VibratorManager;
+import android.os.vibrator.VibratorFrequencyProfile;
 import android.util.SparseArray;
 
 import androidx.test.filters.LargeTest;
@@ -69,6 +69,12 @@
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
+    private static final float TEST_TOLERANCE = 1e-5f;
+
+    private static final float MINIMUM_ACCEPTED_MEASUREMENT_INTERVAL_FREQUENCY = 1f;
+    private static final float MINIMUM_ACCEPTED_FREQUENCY = 1f;
+    private static final float MAXIMUM_ACCEPTED_FREQUENCY = 2_000f;
+
     private static final long CALLBACK_TIMEOUT_MILLIS = 5_000;
     private static final VibrationAttributes VIBRATION_ATTRIBUTES =
             new VibrationAttributes.Builder()
@@ -242,17 +248,81 @@
     }
 
     @Test
-    public void testVibrator() {
+    public void testSingleVibratorIsPresent() {
         for (int vibratorId : mVibratorManager.getVibratorIds()) {
             Vibrator vibrator = mVibratorManager.getVibrator(vibratorId);
             assertNotNull(vibrator);
             assertEquals(vibratorId, vibrator.getId());
             assertTrue(vibrator.hasVibrator());
+        }
+    }
 
-            // Just check these methods will not crash.
-            // We don't really have a way to test if the device supports each effect or not.
+    @Test
+    public void testSingleVibratorAmplitudeAndFrequencyControls() {
+        for (int vibratorId : mVibratorManager.getVibratorIds()) {
+            Vibrator vibrator = mVibratorManager.getVibrator(vibratorId);
+            assertNotNull(vibrator);
+
+            // Just check this method will not crash.
             vibrator.hasAmplitudeControl();
 
+            // Single vibrators should return the frequency profile when it has frequency control.
+            assertEquals(vibrator.hasFrequencyControl(),
+                    vibrator.getFrequencyProfile() != null);
+        }
+    }
+
+    @Test
+    public void testSingleVibratorFrequencyProfile() {
+        for (int vibratorId : mVibratorManager.getVibratorIds()) {
+            Vibrator vibrator = mVibratorManager.getVibrator(vibratorId);
+            VibratorFrequencyProfile frequencyProfile = vibrator.getFrequencyProfile();
+            if (frequencyProfile == null) {
+                continue;
+            }
+
+            float measurementIntervalHz = frequencyProfile.getMaxAmplitudeMeasurementInterval();
+            assertTrue(measurementIntervalHz >= MINIMUM_ACCEPTED_MEASUREMENT_INTERVAL_FREQUENCY);
+
+            float resonantFrequency = vibrator.getResonantFrequency();
+            float minFrequencyHz = frequencyProfile.getMinFrequency();
+            float maxFrequencyHz = frequencyProfile.getMaxFrequency();
+
+            assertTrue(minFrequencyHz >= MINIMUM_ACCEPTED_FREQUENCY);
+            assertTrue(maxFrequencyHz > minFrequencyHz);
+            assertTrue(maxFrequencyHz <= MAXIMUM_ACCEPTED_FREQUENCY);
+
+            if (!Float.isNaN(resonantFrequency)) {
+                // If the device has a resonant frequency, then it should be within the supported
+                // frequency range described by the profile.
+                assertTrue(resonantFrequency >= minFrequencyHz);
+                assertTrue(resonantFrequency <= maxFrequencyHz);
+            }
+
+            float[] measurements = frequencyProfile.getMaxAmplitudeMeasurements();
+
+            // There should be at least 3 points for a valid profile.
+            assertTrue(measurements.length > 2);
+            assertEquals(maxFrequencyHz,
+                    minFrequencyHz + ((measurements.length - 1) * measurementIntervalHz),
+                    TEST_TOLERANCE);
+
+            boolean hasPositiveMeasurement = false;
+            for (float measurement : measurements) {
+                assertTrue(measurement >= 0);
+                assertTrue(measurement <= 1);
+                hasPositiveMeasurement |= measurement > 0;
+            }
+            assertTrue(hasPositiveMeasurement);
+        }
+    }
+
+    @Test
+    public void testSingleVibratorEffectAndPrimitiveSupport() {
+        for (int vibratorId : mVibratorManager.getVibratorIds()) {
+            Vibrator vibrator = mVibratorManager.getVibrator(vibratorId);
+            assertNotNull(vibrator);
+
             // Just check these methods return valid support arrays.
             // We don't really have a way to test if the device supports each effect or not.
             assertEquals(2, vibrator.areEffectsSupported(
@@ -260,6 +330,14 @@
             assertEquals(2, vibrator.arePrimitivesSupported(
                     VibrationEffect.Composition.PRIMITIVE_CLICK,
                     VibrationEffect.Composition.PRIMITIVE_TICK).length);
+        }
+    }
+
+    @Test
+    public void testSingleVibratorVibrateAndCancel() {
+        for (int vibratorId : mVibratorManager.getVibratorIds()) {
+            Vibrator vibrator = mVibratorManager.getVibrator(vibratorId);
+            assertNotNull(vibrator);
 
             vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
             assertStartsVibrating(vibratorId);
diff --git a/tests/tests/os/src/android/os/cts/VibratorTest.java b/tests/tests/os/src/android/os/cts/VibratorTest.java
index 7085ba5..53fdd21 100644
--- a/tests/tests/os/src/android/os/cts/VibratorTest.java
+++ b/tests/tests/os/src/android/os/cts/VibratorTest.java
@@ -20,6 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.clearInvocations;
@@ -34,6 +36,7 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.Vibrator.OnVibratorStateChangedListener;
+import android.os.vibrator.VibratorFrequencyProfile;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -70,6 +73,12 @@
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
+    private static final float TEST_TOLERANCE = 1e-5f;
+
+    private static final float MINIMUM_ACCEPTED_MEASUREMENT_INTERVAL_FREQUENCY = 1f;
+    private static final float MINIMUM_ACCEPTED_FREQUENCY = 1f;
+    private static final float MAXIMUM_ACCEPTED_FREQUENCY = 2_000f;
+
     private static final AudioAttributes AUDIO_ATTRIBUTES =
             new AudioAttributes.Builder()
                     .setUsage(AudioAttributes.USAGE_MEDIA)
@@ -112,7 +121,6 @@
             VibrationAttributes.USAGE_TOUCH,
     };
 
-
     /**
      * This listener is used for test helper methods like asserting it starts/stops vibrating.
      * It's not strongly required that the interactions with this mock are validated by all tests.
@@ -317,6 +325,13 @@
     }
 
     @Test
+    public void testVibratorHasFrequencyControl() {
+        // Just make sure it doesn't crash when this is called; we don't really have a way to test
+        // if the frequency control works or not.
+        mVibrator.hasFrequencyControl();
+    }
+
+    @Test
     public void testVibratorEffectsAreSupported() {
         // Just make sure it doesn't crash when this is called and that it returns all queries;
         // We don't really have a way to test if the device supports each effect or not.
@@ -365,10 +380,87 @@
     }
 
     @Test
-    public void testVibratorIsVibrating() {
-        if (!mVibrator.hasVibrator()) {
-            return;
+    public void testVibratorResonantFrequency() {
+        // Check that the resonant frequency provided is NaN, or if it's a reasonable value.
+        float resonantFrequency = mVibrator.getResonantFrequency();
+        assertTrue(Float.isNaN(resonantFrequency)
+                || (resonantFrequency > 0 && resonantFrequency < MAXIMUM_ACCEPTED_FREQUENCY));
+    }
+
+    @Test
+    public void testVibratorQFactor() {
+        // Just make sure it doesn't crash when this is called;
+        // We don't really have a way to test if the device provides the Q-factor or not.
+        mVibrator.getQFactor();
+    }
+
+    @Test
+    public void testVibratorVibratorFrequencyProfileFrequencyControl() {
+        assumeNotNull(mVibrator.getFrequencyProfile());
+
+        // If the frequency profile is present then the vibrator must have frequency control.
+        // The other implication is not true if the default vibrator represents multiple vibrators.
+        assertTrue(mVibrator.hasFrequencyControl());
+    }
+
+    @Test
+    public void testVibratorFrequencyProfileMeasurementInterval() {
+        VibratorFrequencyProfile frequencyProfile = mVibrator.getFrequencyProfile();
+        assumeNotNull(frequencyProfile);
+
+        float measurementIntervalHz = frequencyProfile.getMaxAmplitudeMeasurementInterval();
+        assertTrue(measurementIntervalHz >= MINIMUM_ACCEPTED_MEASUREMENT_INTERVAL_FREQUENCY);
+    }
+
+    @Test
+    public void testVibratorFrequencyProfileSupportedFrequencyRange() {
+        VibratorFrequencyProfile frequencyProfile = mVibrator.getFrequencyProfile();
+        assumeNotNull(frequencyProfile);
+
+        float resonantFrequency = mVibrator.getResonantFrequency();
+        float minFrequencyHz = frequencyProfile.getMinFrequency();
+        float maxFrequencyHz = frequencyProfile.getMaxFrequency();
+
+        assertTrue(minFrequencyHz >= MINIMUM_ACCEPTED_FREQUENCY);
+        assertTrue(maxFrequencyHz > minFrequencyHz);
+        assertTrue(maxFrequencyHz <= MAXIMUM_ACCEPTED_FREQUENCY);
+
+        if (!Float.isNaN(resonantFrequency)) {
+            // If the device has a resonant frequency, then it should be within the supported
+            // frequency range described by the profile.
+            assertTrue(resonantFrequency >= minFrequencyHz);
+            assertTrue(resonantFrequency <= maxFrequencyHz);
         }
+    }
+
+    @Test
+    public void testVibratorFrequencyProfileOutputAccelerationMeasurements() {
+        VibratorFrequencyProfile frequencyProfile = mVibrator.getFrequencyProfile();
+        assumeNotNull(frequencyProfile);
+
+        float minFrequencyHz = frequencyProfile.getMinFrequency();
+        float maxFrequencyHz = frequencyProfile.getMaxFrequency();
+        float measurementIntervalHz = frequencyProfile.getMaxAmplitudeMeasurementInterval();
+        float[] measurements = frequencyProfile.getMaxAmplitudeMeasurements();
+
+        // There should be at least 3 points for a valid profile: min, center and max frequencies.
+        assertTrue(measurements.length > 2);
+        assertEquals(maxFrequencyHz,
+                minFrequencyHz + ((measurements.length - 1) * measurementIntervalHz),
+                TEST_TOLERANCE);
+
+        boolean hasPositiveMeasurement = false;
+        for (float measurement : measurements) {
+            assertTrue(measurement >= 0);
+            assertTrue(measurement <= 1);
+            hasPositiveMeasurement |= measurement > 0;
+        }
+        assertTrue(hasPositiveMeasurement);
+    }
+
+    @Test
+    public void testVibratorIsVibrating() {
+        assumeTrue(mVibrator.hasVibrator());
 
         assertFalse(mVibrator.isVibrating());
 
@@ -384,9 +476,7 @@
     @LargeTest
     @Test
     public void testVibratorVibratesNoLongerThanDuration() {
-        if (!mVibrator.hasVibrator()) {
-            return;
-        }
+        assumeTrue(mVibrator.hasVibrator());
 
         mVibrator.vibrate(1000);
         assertStartsVibrating();
@@ -398,9 +488,7 @@
     @LargeTest
     @Test
     public void testVibratorStateCallback() {
-        if (!mVibrator.hasVibrator()) {
-            return;
-        }
+        assumeTrue(mVibrator.hasVibrator());
 
         OnVibratorStateChangedListener listener1 = newMockStateListener();
         OnVibratorStateChangedListener listener2 = newMockStateListener();
@@ -424,9 +512,7 @@
     @LargeTest
     @Test
     public void testVibratorStateCallbackRemoval() {
-        if (!mVibrator.hasVibrator()) {
-            return;
-        }
+        assumeTrue(mVibrator.hasVibrator());
 
         OnVibratorStateChangedListener listener1 = newMockStateListener();
         OnVibratorStateChangedListener listener2 = newMockStateListener();