Delay notification vibration to synchronize with notif sound

Delay the vibration associated with a notification by the length
  of the ramp time preceding a notification sound. Do this only
  for notifications that play a sound.
Tests: verify that calls to Vibrator.vibrate() method are within the
  max delay that could be applied to a notification sound/vibration.

Test: play notification with vibration, verify vibration syncs with sound
Test: runtest systemui-notification
Bug: 35152866
Change-Id: I571eb5620d654265d19f45aca2073e8ccc4799df
This commit is contained in:
Jean-Michel Trivi
2017-05-25 18:32:40 -07:00
parent d2638591d9
commit ea0eb5f581
2 changed files with 43 additions and 17 deletions

View File

@@ -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);
}

View File

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