Check for the vibrate notification key on delayed vibrations
Notifications played with sound have a delayed vibration, in order to
match the vibration and sound effects. If the effects are cleared before
the vibration starts, or a new one takes its place, then the vibration
hould be cancelled because it no longer will be tracked by the
NotificationManagerService.mVibrateNotificationKey.
This can cause repeating vibrations, like incoming calls, to start and
continue vibrating even after the notification was handled, (if the user
opens the notification panel before it actually starts vibrating, for
example).
Fix: 202752953
Test: NotificationManagerServiceTest
Change-Id: If3ed4a741e78058a321691acebe735c735542f16
Merged-In: If3ed4a741e78058a321691acebe735c735542f16
(cherry picked from commit 2bf7f52086)
This commit is contained in:
@@ -7699,7 +7699,10 @@ public class NotificationManagerService extends SystemService {
|
||||
final int waitMs = mAudioManager.getFocusRampTimeMs(
|
||||
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
|
||||
record.getAudioAttributes());
|
||||
if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
|
||||
if (DBG) {
|
||||
Slog.v(TAG, "Delaying vibration for notification "
|
||||
+ record.getKey() + " by " + waitMs + "ms");
|
||||
}
|
||||
try {
|
||||
Thread.sleep(waitMs);
|
||||
} catch (InterruptedException e) { }
|
||||
@@ -7707,9 +7710,17 @@ public class NotificationManagerService extends SystemService {
|
||||
// so need to check the notification still valide for vibrate.
|
||||
synchronized (mNotificationLock) {
|
||||
if (mNotificationsByKey.get(record.getKey()) != null) {
|
||||
vibrate(record, effect, true);
|
||||
if (record.getKey().equals(mVibrateNotificationKey)) {
|
||||
vibrate(record, effect, true);
|
||||
} else {
|
||||
if (DBG) {
|
||||
Slog.v(TAG, "No vibration for notification "
|
||||
+ record.getKey() + ": a new notification is "
|
||||
+ "vibrating, or effects were cleared while waiting");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "No vibration for canceled notification : "
|
||||
Slog.w(TAG, "No vibration for canceled notification "
|
||||
+ record.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
@@ -40,6 +39,7 @@ import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -48,6 +48,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Notification;
|
||||
@@ -73,7 +74,6 @@ import android.provider.Settings;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Slog;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.IAccessibilityManager;
|
||||
@@ -102,6 +102,7 @@ import java.util.Objects;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
|
||||
public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
||||
|
||||
@Mock AudioManager mAudioManager;
|
||||
@@ -156,6 +157,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
||||
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
|
||||
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
|
||||
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
|
||||
when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
|
||||
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
|
||||
when(mVibrator.hasFrequencyControl()).thenReturn(false);
|
||||
when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
|
||||
@@ -444,6 +446,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
||||
timeout(MAX_VIBRATION_DELAY).times(1));
|
||||
}
|
||||
|
||||
private void verifyDelayedNeverVibrate() {
|
||||
verify(mVibrator, after(MAX_VIBRATION_DELAY).never()).vibrate(anyInt(), anyString(), any(),
|
||||
anyString(), any(AudioAttributes.class));
|
||||
}
|
||||
|
||||
private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
|
||||
VerificationMode verification) {
|
||||
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
|
||||
@@ -1588,8 +1595,51 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
||||
// beep wasn't reset
|
||||
verifyNeverBeep();
|
||||
verifyNeverVibrate();
|
||||
verify(mRingtonePlayer, never()).stopAsync();
|
||||
verify(mVibrator, never()).cancel();
|
||||
verifyNeverStopAudio();
|
||||
verifyNeverStopVibrate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtoneInsistentBeep_clearEffectsStopsSoundAndVibration() throws Exception {
|
||||
NotificationChannel ringtoneChannel =
|
||||
new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
|
||||
ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
|
||||
new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
|
||||
ringtoneChannel.enableVibration(true);
|
||||
NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
|
||||
mService.addNotification(ringtoneNotification);
|
||||
assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
|
||||
mService.buzzBeepBlinkLocked(ringtoneNotification);
|
||||
verifyBeepLooped();
|
||||
verifyDelayedVibrateLooped();
|
||||
|
||||
mService.clearSoundLocked();
|
||||
mService.clearVibrateLocked();
|
||||
|
||||
verifyStopAudio();
|
||||
verifyStopVibrate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtoneInsistentBeep_neverVibratesWhenEffectsClearedBeforeDelay()
|
||||
throws Exception {
|
||||
NotificationChannel ringtoneChannel =
|
||||
new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
|
||||
ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
|
||||
new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
|
||||
ringtoneChannel.enableVibration(true);
|
||||
NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
|
||||
mService.addNotification(ringtoneNotification);
|
||||
assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
|
||||
mService.buzzBeepBlinkLocked(ringtoneNotification);
|
||||
verifyBeepLooped();
|
||||
verifyNeverVibrate();
|
||||
|
||||
mService.clearSoundLocked();
|
||||
mService.clearVibrateLocked();
|
||||
|
||||
verifyStopAudio();
|
||||
verifyDelayedNeverVibrate();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user