diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3667e161b7af0..13bde3cf7f1ae 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3714,7 +3714,7 @@ public class NotificationManagerService extends SystemService { if (!mInCall && hasValidVibrate && !ringerModeSilent) { mVibrateNotificationKey = key; - buzz = playVibration(record, vibration); + buzz = playVibration(record, vibration, hasValidSound); } } } @@ -3788,22 +3788,41 @@ public class NotificationManagerService extends SystemService { return false; } - private boolean playVibration(final NotificationRecord record, long[] vibration) { + private boolean playVibration(final NotificationRecord record, long[] vibration, + boolean delayVibForSound) { // Escalate privileges so we can use the vibrator even if the // notifying app does not have the VIBRATE permission. long identity = Binder.clearCallingIdentity(); try { - final boolean insistent = - (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0; - final VibrationEffect effect = VibrationEffect.createWaveform( - vibration, insistent ? 0 : -1 /*repeatIndex*/); - mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), - effect, record.getAudioAttributes()); + final VibrationEffect effect; + try { + final boolean insistent = + (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0; + effect = VibrationEffect.createWaveform( + vibration, insistent ? 0 : -1 /*repeatIndex*/); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Error creating vibration waveform with pattern: " + + Arrays.toString(vibration)); + return false; + } + if (delayVibForSound) { + new Thread(() -> { + // delay the vibration by the same amount as the notification sound + final int waitMs = mAudioManager.getFocusRampTimeMs( + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, + record.getAudioAttributes()); + if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms"); + try { + Thread.sleep(waitMs); + } catch (InterruptedException e) { } + mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), + effect, record.getAudioAttributes()); + }).start(); + } else { + mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), + effect, record.getAudioAttributes()); + } return true; - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Error creating vibration waveform with pattern: " + - Arrays.toString(vibration)); - return false; } finally{ Binder.restoreCallingIdentity(identity); } diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index 39caa3ce147c8..ec0887489fba4 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -61,6 +61,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +103,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { private static final long[] FALLBACK_VIBRATION_PATTERN = new long[] {100, 100, 100}; private static final VibrationEffect FALLBACK_VIBRATION = VibrationEffect.createWaveform(FALLBACK_VIBRATION_PATTERN, -1); + private static final int MAX_VIBRATION_DELAY = 1000; @Before public void setUp() { @@ -309,6 +311,11 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { (AudioAttributes) anyObject()); } + private void verifyDelayedVibrateLooped() { + verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), + argThat(mVibrateLoopMatcher), (AudioAttributes) anyObject()); + } + private void verifyStopVibrate() { verify(mVibrator, times(1)).cancel(); } @@ -506,8 +513,8 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { VibrationEffect effect = VibrationEffect.createWaveform(r.getVibration(), -1); - verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), eq(effect), - (AudioAttributes) anyObject()); + verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), + eq(effect), (AudioAttributes) anyObject()); } @Test @@ -521,8 +528,8 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { mService.buzzBeepBlinkLocked(r); - verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), eq(FALLBACK_VIBRATION), - (AudioAttributes) anyObject()); + verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), + eq(FALLBACK_VIBRATION), (AudioAttributes) anyObject()); verify(mRingtonePlayer, never()).playAsync (anyObject(), anyObject(), anyBoolean(), anyObject()); } @@ -539,7 +546,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { mService.buzzBeepBlinkLocked(r); - verifyVibrateLooped(); + verifyDelayedVibrateLooped(); } @Test