Merge "Fix external vibration behavior on system update" into main
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 0615578..414f2749 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -155,3 +155,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "haptics"
+ name: "fix_external_vibration_system_update_aware"
+ description: "Fix the audio-coupled haptics handling of system updates."
+ bug: "372241975"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 3f5fc33..ce91e63 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1015,9 +1015,12 @@
updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i));
}
- // TODO(b/372241975): investigate why external vibrations were not handled here before
- if (mCurrentSession == null
- || (mCurrentSession instanceof ExternalVibrationSession)) {
+ if (mCurrentSession == null) {
+ return;
+ }
+
+ if (!Flags.fixExternalVibrationSystemUpdateAware()
+ && (mCurrentSession instanceof ExternalVibrationSession)) {
return;
}
@@ -1025,7 +1028,7 @@
if (inputDevicesChanged || (ignoreStatus != null)) {
if (DEBUG) {
Slog.d(TAG, "Canceling vibration because settings changed: "
- + (inputDevicesChanged ? "input devices changed" : ignoreStatus));
+ + (ignoreStatus == null ? "input devices changed" : ignoreStatus));
}
mCurrentSession.requestEnd(Status.CANCELLED_BY_SETTINGS_UPDATE);
}
@@ -2334,14 +2337,22 @@
@GuardedBy("mLock")
private void maybeClearCurrentAndNextSessionsLocked(
Predicate<VibrationSession> shouldEndSessionPredicate, Status endStatus) {
- // TODO(b/372241975): investigate why external vibrations were not handled here before
- if (!(mNextSession instanceof ExternalVibrationSession)
- && shouldEndSessionPredicate.test(mNextSession)) {
- clearNextSessionLocked(endStatus);
- }
- if (!(mCurrentSession instanceof ExternalVibrationSession)
- && shouldEndSessionPredicate.test(mCurrentSession)) {
- mCurrentSession.requestEnd(endStatus);
+ if (Flags.fixExternalVibrationSystemUpdateAware()) {
+ if (shouldEndSessionPredicate.test(mNextSession)) {
+ clearNextSessionLocked(endStatus);
+ }
+ if (shouldEndSessionPredicate.test(mCurrentSession)) {
+ mCurrentSession.requestEnd(endStatus);
+ }
+ } else {
+ if (!(mNextSession instanceof ExternalVibrationSession)
+ && shouldEndSessionPredicate.test(mNextSession)) {
+ clearNextSessionLocked(endStatus);
+ }
+ if (!(mCurrentSession instanceof ExternalVibrationSession)
+ && shouldEndSessionPredicate.test(mCurrentSession)) {
+ mCurrentSession.requestEnd(endStatus);
+ }
}
}
@@ -2535,6 +2546,9 @@
Slog.d(TAG, "Stopping external vibration: " + vib);
}
mCurrentSession.requestEnd(Status.FINISHED);
+ } else if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, "VibrationSession invalid on external vibration stop."
+ + " currentSession=" + mCurrentSession + ", received=" + vib);
}
}
} finally {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index b248218..132a324 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -147,6 +147,9 @@
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build();
private static final AudioAttributes AUDIO_NOTIFICATION_ATTRS =
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build();
+ private static final AudioAttributes AUDIO_HAPTIC_FEEDBACK_ATTRS =
+ new AudioAttributes.Builder().setUsage(
+ AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build();
private static final VibrationAttributes ALARM_ATTRS =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
@@ -2674,7 +2677,8 @@
}
@Test
- public void onExternalVibration_thenDeniedAppOps_doNotCancelVibration() throws Throwable {
+ @DisableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_legacyDeniedAppOps_doNotCancelVibration() throws Throwable {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
@@ -2697,7 +2701,32 @@
}
@Test
- public void onExternalVibration_thenPowerModeChanges_doNotCancelVibration() throws Exception {
+ @EnableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_thenDeniedAppOps_cancelVibration() throws Throwable {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ when(mAppOpsManagerMock.checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+ service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
+
+ verify(externalVibrationControllerMock).mute();
+ }
+
+ @Test
+ @DisableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_legacyPowerModeChanges_doNotCancelVibration() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
createSystemReadyService();
@@ -2705,7 +2734,7 @@
IExternalVibrationController externalVibrationControllerMock =
mock(IExternalVibrationController.class);
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
- AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ AUDIO_HAPTIC_FEEDBACK_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
externalVibration);
@@ -2717,7 +2746,29 @@
}
@Test
- public void onExternalVibration_thenSettingsChange_doNotCancelVibration() throws Exception {
+ @EnableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_thenPowerModeChanges_cancelVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_HAPTIC_FEEDBACK_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ verify(externalVibrationControllerMock).mute();
+ }
+
+ @Test
+ @DisableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_legacySettingsChange_doNotCancelVibration() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
@@ -2739,7 +2790,31 @@
}
@Test
- public void onExternalVibration_thenScreenTurnsOff_doNotCancelVibration() throws Throwable {
+ @EnableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_thenSettingsChange_cancelVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+ service.mVibrationSettings.mSettingObserver.onChange(false);
+ service.updateServiceState();
+
+ verify(externalVibrationControllerMock).mute();
+ }
+
+ @Test
+ @DisableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_legacyScreenTurnsOff_doNotCancelVibration() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
@@ -2759,7 +2834,30 @@
}
@Test
- public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable {
+ @EnableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_thenScreenTurnsOff_cancelVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(externalVibrationControllerMock).mute();
+ }
+
+ @Test
+ @DisableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_legacyFgUserRequestsMute_doNotCancelVibration()
+ throws Exception {
assumeTrue(UserManagerInternal.shouldShowNotificationForBackgroundUserSounds());
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -2781,6 +2879,29 @@
}
@Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_FIX_EXTERNAL_VIBRATION_SYSTEM_UPDATE_AWARE)
+ public void onExternalVibration_thenFgUserRequestsMute_cancelVibration() throws Exception {
+ assumeTrue(UserManagerInternal.shouldShowNotificationForBackgroundUserSounds());
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ service.mIntentReceiver.onReceive(mContextSpy, new Intent(
+ BackgroundUserSoundNotifier.ACTION_MUTE_SOUND));
+
+ verify(externalVibrationControllerMock).mute();
+ }
+
+ @Test
@DisableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void startVibrationSession_withoutFeatureFlag_throwsException() throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);