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

Commit e1386616 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Vibrate as system instead of as an app

When DND is on, apps aren't allowed to play vibrations on
blocked usages. However, we still need to play vibrations for
notifications from those apps that meet DND criteria.

Previously we accomplished this by passing in the bypass
DND flag, however this flag is also being used to bypass camera
audio restrictions, which we don't want.

So now we'll play vibrations as "android", which is always
exception from DND restrictions and isn't exempt from the
camera restriction. In order to maintain debugging from bugreports,
the package name and uid that we're vibrating for are now included
in the vibration reason.

Test: BuzzBeepBlinkTest
Test: trigger notification vibration
Test: trigger notification vibration, DND on, notif allowed
Test: fail to trigger notification vibration, DND on, notif intercepted
Test: fail to trigger notification vibration, camera is recording video
Test: check bugreport output

Bug: 171946151
Change-Id: I8958852403827341480351ee0c60a4fd30f5280d
parent f6f51162
Loading
Loading
Loading
Loading
+12 −11
Original line number Original line Diff line number Diff line
@@ -7376,15 +7376,7 @@ public class NotificationManagerService extends SystemService {
                    // so need to check the notification still valide for vibrate.
                    // so need to check the notification still valide for vibrate.
                    synchronized (mNotificationLock) {
                    synchronized (mNotificationLock) {
                        if (mNotificationsByKey.get(record.getKey()) != null) {
                        if (mNotificationsByKey.get(record.getKey()) != null) {
                            // Vibrator checks the appops for the op package, not the caller,
                            vibrate(record, effect, true);
                            // so we need to add the bypass dnd flag to be heard. it's ok to
                            // always add this flag here because we've already checked that we can
                            // bypass dnd
                            AudioAttributes.Builder aab =
                                    new AudioAttributes.Builder(record.getAudioAttributes())
                                    .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
                            mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
                                    effect, "Notification (delayed)", aab.build());
                        } else {
                        } else {
                            Slog.e(TAG, "No vibration for canceled notification : "
                            Slog.e(TAG, "No vibration for canceled notification : "
                                    + record.getKey());
                                    + record.getKey());
@@ -7392,8 +7384,7 @@ public class NotificationManagerService extends SystemService {
                    }
                    }
                }).start();
                }).start();
            } else {
            } else {
                mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
                vibrate(record, effect, false);
                        effect, "Notification", record.getAudioAttributes());
            }
            }
            return true;
            return true;
        } finally{
        } finally{
@@ -7401,6 +7392,16 @@ public class NotificationManagerService extends SystemService {
        }
        }
    }
    }


    private void vibrate(NotificationRecord record, VibrationEffect effect, boolean delayed) {
        // We need to vibrate as "android" so we can breakthrough DND. VibratorManagerService
        // doesn't have a concept of vibrating on an app's behalf, so add the app information
        // to the reason so we can still debug from bugreports
        String reason = "Notification (" + record.getSbn().getOpPkg() + " "
                + record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
        mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
                effect, reason, record.getAudioAttributes());
    }

    private boolean isNotificationForCurrentUser(NotificationRecord record) {
    private boolean isNotificationForCurrentUser(NotificationRecord record) {
        final int currentUser;
        final int currentUser;
        final long token = Binder.clearCallingIdentity();
        final long token = Binder.clearCallingIdentity();
+9 −2
Original line number Original line Diff line number Diff line
@@ -61,6 +61,7 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManager;
import android.net.Uri;
import android.net.Uri;
import android.os.Handler;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.VibrationEffect;
@@ -81,10 +82,12 @@ import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.internal.util.IntPair;
import com.android.internal.util.IntPair;
import com.android.server.UiServiceTestCase;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LogicalLight;
import com.android.server.lights.LogicalLight;
import com.android.server.pm.PackageManagerService;


import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Mockito;
@@ -412,12 +415,16 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
    }
    }


    private void verifyVibrate() {
    private void verifyVibrate() {
        ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
        verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
        verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
                anyString(), any(AudioAttributes.class));
                anyString(), captor.capture());
        assertEquals(0, (captor.getValue().getAllFlags()
                & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
    }
    }


    private void verifyVibrate(int times) {
    private void verifyVibrate(int times) {
        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(),
        verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
                eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
                any(AudioAttributes.class));
                any(AudioAttributes.class));
    }
    }