Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c00c97a authored by Lais Andrade's avatar Lais Andrade
Browse files

Fix external vibration behavior on system update

Fix vibrator service to consistently handle ongoing vibrations.

This means that system updates like user settings changes,
screen off events, ringer mode and battery-saver mode updates
will also affect ongoing external vibrations.

Fix: 372241975
Test: VibratorManagerServiceTest
Flag: android.os.vibrator.fix_external_vibration_system_update_aware
Change-Id: Ief72ff646503b0983ea20473a87b44577375f1d3
parent 94fba95e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
    }
}
+26 −12
Original line number Diff line number Diff line
@@ -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,7 +2337,14 @@ 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 (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);
@@ -2344,6 +2354,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                mCurrentSession.requestEnd(endStatus);
            }
        }
    }

    /**
     * Waits until the current vibration finished processing, timing out after the given
@@ -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 {
+127 −6
Original line number Diff line number Diff line
@@ -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);
@@ -2780,6 +2878,29 @@ public class VibratorManagerServiceTest {
        verify(externalVibrationControllerMock, never()).mute();
    }

    @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 {