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

Commit 6bd7a9c7 authored by Lais Andrade's avatar Lais Andrade
Browse files

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 2bf7f520)
parent 2d7f5250
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -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) {
                            if (record.getKey().equals(mVibrateNotificationKey)) {
                                vibrate(record, effect, true);
                            } else {
                            Slog.e(TAG, "No vibration for canceled notification : "
                                if (DBG) {
                                    Slog.v(TAG, "No vibration for notification "
                                            + record.getKey() + ": a new notification is "
                                            + "vibrating, or effects were cleared while waiting");
                                }
                            }
                        } else {
                            Slog.w(TAG, "No vibration for canceled notification "
                                    + record.getKey());
                        }
                    }
+54 −4
Original line number Diff line number Diff line
@@ -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