diff options
3 files changed, 163 insertions, 18 deletions
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index 0615578935a1..414f27498414 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -155,3 +155,13 @@ flag { 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 3f5fc338ee3b..ce91e63b4849 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 b248218b6cef..132a3247dad2 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { } @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,10 +2701,11 @@ public class VibratorManagerServiceTest { } @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); - createSystemReadyService(); + VibratorManagerService service = createSystemReadyService(); IExternalVibrationController externalVibrationControllerMock = mock(IExternalVibrationController.class); @@ -2711,13 +2716,59 @@ public class VibratorManagerServiceTest { 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(); + + 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, never()).mute(); } @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 @@ public class VibratorManagerServiceTest { } @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 @@ public class VibratorManagerServiceTest { } @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 @@ public class VibratorManagerServiceTest { } @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); |