From e13866164bd4ed143fdaadeb057e7868a175787b Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 12 Mar 2021 18:04:04 -0500 Subject: [PATCH] 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 --- .../NotificationManagerService.java | 23 ++++++++++--------- .../notification/BuzzBeepBlinkTest.java | 11 +++++++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0cc9f9e150c6b..d5a9e3c0d4f8b 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7376,15 +7376,7 @@ public class NotificationManagerService extends SystemService { // so need to check the notification still valide for vibrate. synchronized (mNotificationLock) { if (mNotificationsByKey.get(record.getKey()) != null) { - // Vibrator checks the appops for the op package, not the caller, - // 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()); + vibrate(record, effect, true); } else { Slog.e(TAG, "No vibration for canceled notification : " + record.getKey()); @@ -7392,8 +7384,7 @@ public class NotificationManagerService extends SystemService { } }).start(); } else { - mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(), - effect, "Notification", record.getAudioAttributes()); + vibrate(record, effect, false); } return true; } 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) { final int currentUser; final long token = Binder.clearCallingIdentity(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index e510b4fbfdd54..5462f47e3a4ca 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -61,6 +61,7 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.VibrationEffect; @@ -81,10 +82,12 @@ import com.android.internal.logging.InstanceIdSequenceFake; import com.android.internal.util.IntPair; import com.android.server.UiServiceTestCase; import com.android.server.lights.LogicalLight; +import com.android.server.pm.PackageManagerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mockito; @@ -412,12 +415,16 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } private void verifyVibrate() { + ArgumentCaptor captor = ArgumentCaptor.forClass(AudioAttributes.class); 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) { - 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)); }