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

Commit cb0e9370 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Update vibration cancellation policy for notifications" into tm-qpr-dev am: 88efc6f7

parents a8cd6f6b 88efc6f7
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -61,12 +61,11 @@ final class Vibration {
        IGNORED_BACKGROUND,
        IGNORED_UNKNOWN_VIBRATION,
        IGNORED_UNSUPPORTED,
        IGNORED_FOR_ALARM,
        IGNORED_FOR_EXTERNAL,
        IGNORED_FOR_HIGHER_IMPORTANCE,
        IGNORED_FOR_ONGOING,
        IGNORED_FOR_POWER,
        IGNORED_FOR_RINGER_MODE,
        IGNORED_FOR_RINGTONE,
        IGNORED_FOR_SETTINGS,
        IGNORED_SUPERSEDED,
    }
+34 −16
Original line number Diff line number Diff line
@@ -713,14 +713,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            case IGNORED_ERROR_APP_OPS:
                Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
                break;
            case IGNORED_FOR_ALARM:
            case IGNORED_FOR_EXTERNAL:
                if (DEBUG) {
                    Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
                    Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
                }
                break;
            case IGNORED_FOR_EXTERNAL:
            case IGNORED_FOR_HIGHER_IMPORTANCE:
                if (DEBUG) {
                    Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
                    Slog.d(TAG, "Ignoring incoming vibration in favor of ongoing vibration"
                            + " with higher importance");
                }
                break;
            case IGNORED_FOR_ONGOING:
@@ -734,12 +735,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                            + attrs);
                }
                break;
            case IGNORED_FOR_RINGTONE:
                if (DEBUG) {
                    Slog.d(TAG, "Ignoring incoming vibration in favor of ringtone vibration");
                }
                break;

            default:
                if (DEBUG) {
                    Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
@@ -812,20 +807,43 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            return null;
        }

        if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_ALARM) {
            return Vibration.Status.IGNORED_FOR_ALARM;
        }

        if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE) {
            return Vibration.Status.IGNORED_FOR_RINGTONE;
        int currentUsage = currentVibration.attrs.getUsage();
        int newUsage = vib.attrs.getUsage();
        if (getVibrationImportance(currentUsage) > getVibrationImportance(newUsage)) {
            // Current vibration has higher importance than this one and should not be cancelled.
            return Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE;
        }

        if (currentVibration.isRepeating()) {
            // Current vibration is repeating, assume it's more important.
            return Vibration.Status.IGNORED_FOR_ONGOING;
        }

        return null;
    }

    private static int getVibrationImportance(@VibrationAttributes.Usage int usage) {
        switch (usage) {
            case VibrationAttributes.USAGE_RINGTONE:
                return 5;
            case VibrationAttributes.USAGE_ALARM:
                return 4;
            case VibrationAttributes.USAGE_NOTIFICATION:
                return 3;
            case VibrationAttributes.USAGE_COMMUNICATION_REQUEST:
            case VibrationAttributes.USAGE_ACCESSIBILITY:
                return 2;
            case VibrationAttributes.USAGE_HARDWARE_FEEDBACK:
            case VibrationAttributes.USAGE_PHYSICAL_EMULATION:
                return 1;
            case VibrationAttributes.USAGE_MEDIA:
            case VibrationAttributes.USAGE_TOUCH:
            case VibrationAttributes.USAGE_UNKNOWN:
            default:
                return 0;
        }
    }

    /**
     * Check if given vibration should be ignored by this service.
     *
+57 −29
Original line number Diff line number Diff line
@@ -778,8 +778,7 @@ public class VibratorManagerServiceTest {
        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                HAPTIC_FEEDBACK_ATTRS);
        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS);

        // Wait before checking it never played a second effect.
        assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
@@ -793,49 +792,78 @@ public class VibratorManagerServiceTest {
    }

    @Test
    public void vibrate_withOngoingAlarmVibration_ignoresEffect() throws Exception {
    public void vibrate_withNewRepeatingVibration_cancelsOngoingEffect() throws Exception {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
        VibratorManagerService service = createSystemReadyService();

        VibrationEffect alarmEffect = VibrationEffect.createWaveform(
                new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
        vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
                VibrationAttributes.USAGE_ALARM).build());
        vibrate(service, alarmEffect, ALARM_ATTRS);

        // VibrationThread will start this vibration async, so wait before checking it started.
        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                HAPTIC_FEEDBACK_ATTRS);
        VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
                new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
        vibrate(service, repeatingEffect, NOTIFICATION_ATTRS);

        // Wait before checking it never played a second effect.
        assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
                service, /* timeout= */ 50));
        // VibrationThread will start this vibration async, so wait before checking it started.
        assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
                service, TEST_TIMEOUT_MILLIS));

        // The second vibration should have recorded that the vibrators were turned on.
        verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
    }

    @Test
    public void vibrate_withOngoingRingtoneVibration_ignoresEffect() throws Exception {
    public void vibrate_withOngoingHigherImportanceVibration_ignoresEffect() throws Exception {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
        VibratorManagerService service = createSystemReadyService();

        VibrationEffect alarmEffect = VibrationEffect.createWaveform(
        VibrationEffect effect = VibrationEffect.createWaveform(
                new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
        vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
                VibrationAttributes.USAGE_RINGTONE).build());
        vibrate(service, effect, ALARM_ATTRS);

        // VibrationThread will start this vibration async, so wait before checking it started.
        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                HAPTIC_FEEDBACK_ATTRS);
        vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);

        // Wait before checking it never played a second effect.
        assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
                service, /* timeout= */ 50));

        // The second vibration shouldn't have recorded that the vibrators were turned on.
        verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
    }

    @Test
    public void vibrate_withOngoingLowerImportanceVibration_cancelsOngoingEffect()
            throws Exception {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
        VibratorManagerService service = createSystemReadyService();

        VibrationEffect effect = VibrationEffect.createWaveform(
                new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
        vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);

        // VibrationThread will start this vibration async, so wait before checking it started.
        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, effect, RINGTONE_ATTRS);

        // VibrationThread will start this vibration async, so wait before checking it started.
        assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
                service, TEST_TIMEOUT_MILLIS));

        // The second vibration should have recorded that the vibrators were turned on.
        verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
    }

    @Test
@@ -1052,15 +1080,15 @@ public class VibratorManagerServiceTest {
                IVibrator.CAP_COMPOSE_EFFECTS);
        VibratorManagerService service = createSystemReadyService();

        vibrate(service, CombinedVibration.startSequential()
                .addNext(1, VibrationEffect.createOneShot(100, 125))
                .combine(), NOTIFICATION_ATTRS);
        assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, VibrationEffect.startComposition()
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
                .compose(), HAPTIC_FEEDBACK_ATTRS);
        assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
                service, TEST_TIMEOUT_MILLIS));

        vibrate(service, CombinedVibration.startSequential()
                .addNext(1, VibrationEffect.createOneShot(100, 125))
                .combine(), NOTIFICATION_ATTRS);
        assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
                service, TEST_TIMEOUT_MILLIS));

@@ -1070,25 +1098,25 @@ public class VibratorManagerServiceTest {
        assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
                service, TEST_TIMEOUT_MILLIS));

        // Ring vibrations have intensity OFF and are not played.
        vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
        assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3,
                service, TEST_TIMEOUT_MILLIS));
                service, /* timeout= */ 50));

        // Only 3 effects played successfully.
        assertEquals(3, fakeVibrator.getAllEffectSegments().size());

        // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
        assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
                0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(0)).getScale());

        // Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
        assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
                0.6 < fakeVibrator.getAmplitudes().get(0));

        // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
        assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
                0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(1)).getScale());

        // Alarm vibration will be scaled with SCALE_NONE.
        assertEquals(1f,
                ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5);

        // Ring vibrations have intensity OFF and are not played.
    }

    @Test