Merge "Fix SystemVibrator constant support checks on device without vibrator" into sc-dev
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index fd8948c..0c3debb1 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -26,6 +26,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.Objects;
@@ -254,11 +255,14 @@
      * <p>This uses the first vibrator on the list as the default one for all hardware spec, but
      * uses an intersection of all vibrators to decide the capabilities and effect/primitive
      * support.
+     *
+     * @hide
      */
-    private static class AllVibratorsInfo extends VibratorInfo {
+    @VisibleForTesting
+    public static class AllVibratorsInfo extends VibratorInfo {
         private final VibratorInfo[] mVibratorInfos;
 
-        AllVibratorsInfo(VibratorInfo[] vibrators) {
+        public AllVibratorsInfo(VibratorInfo[] vibrators) {
             super(/* id= */ -1, capabilitiesIntersection(vibrators),
                     vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
             mVibratorInfos = vibrators;
@@ -266,6 +270,9 @@
 
         @Override
         public int isEffectSupported(int effectId) {
+            if (mVibratorInfos.length == 0) {
+                return Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+            }
             int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
             for (VibratorInfo info : mVibratorInfos) {
                 int effectSupported = info.isEffectSupported(effectId);
@@ -280,6 +287,9 @@
 
         @Override
         public boolean isPrimitiveSupported(int primitiveId) {
+            if (mVibratorInfos.length == 0) {
+                return false;
+            }
             for (VibratorInfo info : mVibratorInfos) {
                 if (!info.isPrimitiveSupported(primitiveId)) {
                     return false;
@@ -288,6 +298,19 @@
             return true;
         }
 
+        @Override
+        public int getPrimitiveDuration(int primitiveId) {
+            int maxDuration = 0;
+            for (VibratorInfo info : mVibratorInfos) {
+                int duration = info.getPrimitiveDuration(primitiveId);
+                if (duration == 0) {
+                    return 0;
+                }
+                maxDuration = Math.max(maxDuration, duration);
+            }
+            return maxDuration;
+        }
+
         private static int capabilitiesIntersection(VibratorInfo[] infos) {
             if (infos.length == 0) {
                 return 0;
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 8f9168b..0ece793 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertEquals;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -26,6 +28,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
 import android.platform.test.annotations.Presubmit;
 
@@ -70,6 +73,54 @@
     }
 
     @Test
+    public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[0]);
+        assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
+                info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+    }
+
+    @Test
+    public void areEffectsSupported_unsupportedInOneVibrator_returnsNo() {
+        VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .build();
+        VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
+                .setSupportedEffects(new int[0])
+                .build();
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+        assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
+                info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+    }
+
+    @Test
+    public void areEffectsSupported_unknownInOneVibrator_returnsUnknown() {
+        VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .build();
+        VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
+        assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
+                info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+    }
+
+    @Test
+    public void arePrimitivesSupported_supportedInAllVibrators_returnsYes() {
+        VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .build();
+        VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .build();
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{firstVibrator, secondVibrator});
+        assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
+                info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+    }
+
+    @Test
     public void arePrimitivesSupported_returnsArrayOfSameSize() {
         assertEquals(0, mVibratorSpy.arePrimitivesSupported(new int[0]).length);
         assertEquals(1, mVibratorSpy.arePrimitivesSupported(
@@ -80,6 +131,40 @@
     }
 
     @Test
+    public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[0]);
+        assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
+    public void arePrimitivesSupported_unsupportedInOneVibrator_returnsFalse() {
+        VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .build();
+        VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+        assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
+    public void arePrimitivesSupported_supportedInAllVibrators_returnsTrue() {
+        VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .build();
+        VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
+                .build();
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{firstVibrator, secondVibrator});
+        assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
     public void getPrimitivesDurations_returnsArrayOfSameSize() {
         assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
         assertEquals(1, mVibratorSpy.getPrimitiveDurations(
@@ -90,6 +175,40 @@
     }
 
     @Test
+    public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[0]);
+        assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
+    public void getPrimitivesDurations_unsupportedInOneVibrator_returnsZero() {
+        VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .build();
+        VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+        assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
+    public void getPrimitivesDurations_supportedInAllVibrators_returnsMaxDuration() {
+        VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .build();
+        VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+                .build();
+        SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+                new VibratorInfo[]{firstVibrator, secondVibrator});
+        assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+    }
+
+    @Test
     public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
         AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(