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

Commit 88efc6f7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update vibration cancellation policy for notifications" into tm-qpr-dev

parents 4f9b4bec 8b1ce21a
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