Loading services/core/java/com/android/server/vibrator/Vibration.java +1 −2 Original line number Diff line number Diff line Loading @@ -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, } Loading services/core/java/com/android/server/vibrator/VibratorManagerService.java +34 −16 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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 Loading Loading @@ -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. * Loading services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +57 −29 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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 Loading Loading
services/core/java/com/android/server/vibrator/Vibration.java +1 −2 Original line number Diff line number Diff line Loading @@ -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, } Loading
services/core/java/com/android/server/vibrator/VibratorManagerService.java +34 −16 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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 Loading Loading @@ -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. * Loading
services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +57 −29 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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 Loading