Apply VIBRATE_ON settings in vibrator service

Apply main switch toggle from "Vibration & haptics" settings screeen to
disable all vibrations except for accessibility usage and the ones with
bypass flag (e.g. broadcasted alers from CellBroadcastAlertAudio.

Fix: 30028435
Test: VibrationSettingsTest
Change-Id: Ia8a0e5e952ce2e1055b6c63fc5801546b635d39a
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index fbe2170..2f2158d 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -97,7 +97,7 @@
     optional int32 status = 6;
 }
 
-// Next id: 24
+// Next id: 25
 message VibratorManagerServiceDumpProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     repeated int32 vibrator_ids = 1;
@@ -106,6 +106,7 @@
     optional VibrationProto current_external_vibration = 4;
     optional bool vibrator_under_external_control = 5;
     optional bool low_power_mode = 6;
+    optional bool vibrate_on = 24;
     optional int32 alarm_intensity = 18;
     optional int32 alarm_default_intensity = 19;
     optional int32 haptic_feedback_intensity = 7;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index c54d490..6c5d952 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -97,6 +97,15 @@
                     USAGE_ALARM,
                     USAGE_COMMUNICATION_REQUEST));
 
+    /**
+     * Usage allowed for vibrations when {@link Settings.System#VIBRATE_ON} is disabled.
+     *
+     * <p>The only allowed usage is accessibility, which is applied when the user enables talkback.
+     * Other usages that must ignore this setting should use
+     * {@link VibrationAttributes#FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF}.
+     */
+    private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;
+
     /** Listener for changes on vibration settings. */
     interface OnVibratorSettingsChanged {
         /** Callback triggered when any of the vibrator settings change. */
@@ -127,6 +136,8 @@
     private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
     @GuardedBy("mLock")
     private boolean mBatterySaverMode;
+    @GuardedBy("mLock")
+    private boolean mVibrateOn;
 
     VibrationSettings(Context context, Handler handler) {
         this(context, handler, new VibrationConfig(context.getResources()));
@@ -199,6 +210,7 @@
 
         // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
+        registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
         registerSettingsObserver(Settings.System.getUriFor(
@@ -314,11 +326,14 @@
                 return Vibration.Status.IGNORED_FOR_POWER;
             }
 
-            int intensity = getCurrentIntensity(usage);
-            if ((intensity == Vibrator.VIBRATION_INTENSITY_OFF)
-                    && !attrs.isFlagSet(
-                            VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
-                return Vibration.Status.IGNORED_FOR_SETTINGS;
+            if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
+                if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
+                    return Vibration.Status.IGNORED_FOR_SETTINGS;
+                }
+
+                if (getCurrentIntensity(usage) == Vibrator.VIBRATION_INTENSITY_OFF) {
+                    return Vibration.Status.IGNORED_FOR_SETTINGS;
+                }
             }
 
             if (!shouldVibrateForRingerModeLocked(usage)) {
@@ -357,6 +372,7 @@
     void updateSettings() {
         synchronized (mLock) {
             mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
 
             int alarmIntensity = toIntensity(
                     loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
@@ -437,8 +453,9 @@
                     + "mVibratorConfig=" + mVibrationConfig
                     + ", mVibrateInputDevices=" + mVibrateInputDevices
                     + ", mBatterySaverMode=" + mBatterySaverMode
-                    + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+                    + ", mVibrateOn=" + mVibrateOn
                     + ", mVibrationIntensities=" + vibrationIntensitiesString
+                    + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
                     + '}';
         }
     }
@@ -446,6 +463,8 @@
     /** Write current settings into given {@link ProtoOutputStream}. */
     public void dumpProto(ProtoOutputStream proto) {
         synchronized (mLock) {
+            proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
+            proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
             proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
                     getCurrentIntensity(USAGE_ALARM));
             proto.write(VibratorManagerServiceDumpProto.ALARM_DEFAULT_INTENSITY,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 2c22419..5d4ffbb 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -319,6 +319,34 @@
         }
     }
 
+
+    @Test
+    public void shouldIgnoreVibration_vibrateOnDisabled_ignoresUsagesNotAccessibility() {
+        setUserSetting(Settings.System.VIBRATE_ON, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_ACCESSIBILITY) {
+                assertVibrationNotIgnoredForUsage(usage);
+            } else {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
+    }
+
+    @Test
+    public void shouldIgnoreVibration_vibrateOnEnabledOrUnset_allowsAnyUsage() {
+        deleteUserSetting(Settings.System.VIBRATE_ON);
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+
+        setUserSetting(Settings.System.VIBRATE_ON, 1);
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+    }
     @Test
     public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() {
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
@@ -560,10 +588,17 @@
         when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
     }
 
+    private void deleteUserSetting(String settingName) {
+        Settings.System.putStringForUser(
+                mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+        mVibrationSettings.updateSettings();
+    }
+
     private void setUserSetting(String settingName, int value) {
         Settings.System.putIntForUser(
                 mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
-        // FakeSettingsProvider don't support testing triggering ContentObserver yet.
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
         mVibrationSettings.updateSettings();
     }