Merge "Be more strict about triggering notification lights" into pi-dev

This commit is contained in:
Julia Reynolds
2018-07-10 12:49:16 +00:00
committed by Android (Google) Code Review
2 changed files with 236 additions and 10 deletions

View File

@@ -329,6 +329,8 @@ public class NotificationManagerService extends SystemService {
private long[] mFallbackVibrationPattern; private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight; private boolean mUseAttentionLight;
boolean mHasLight = true;
boolean mLightEnabled;
boolean mSystemReady; boolean mSystemReady;
private boolean mDisableNotificationEffects; private boolean mDisableNotificationEffects;
@@ -343,9 +345,9 @@ public class NotificationManagerService extends SystemService {
private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN; private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
// for enabling and disabling notification pulse behavior // for enabling and disabling notification pulse behavior
private boolean mScreenOn = true; boolean mScreenOn = true;
protected boolean mInCall = false; protected boolean mInCall = false;
private boolean mNotificationPulseEnabled; boolean mNotificationPulseEnabled;
private Uri mInCallNotificationUri; private Uri mInCallNotificationUri;
private AudioAttributes mInCallNotificationAudioAttributes; private AudioAttributes mInCallNotificationAudioAttributes;
@@ -1198,7 +1200,8 @@ public class NotificationManagerService extends SystemService {
ContentResolver resolver = getContext().getContentResolver(); ContentResolver resolver = getContext().getContentResolver();
if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
boolean pulseEnabled = Settings.System.getIntForUser(resolver, boolean pulseEnabled = Settings.System.getIntForUser(resolver,
Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0; Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
!= 0;
if (mNotificationPulseEnabled != pulseEnabled) { if (mNotificationPulseEnabled != pulseEnabled) {
mNotificationPulseEnabled = pulseEnabled; mNotificationPulseEnabled = pulseEnabled;
updateNotificationPulse(); updateNotificationPulse();
@@ -1460,6 +1463,8 @@ public class NotificationManagerService extends SystemService {
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume); mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
mHasLight =
resources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
// Don't start allowing notifications until the setup wizard has run once. // Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on. // After that, including subsequent boots, init with notifications turned on.
@@ -3825,6 +3830,7 @@ public class NotificationManagerService extends SystemService {
pw.println(" "); pw.println(" ");
} }
pw.println(" mUseAttentionLight=" + mUseAttentionLight); pw.println(" mUseAttentionLight=" + mUseAttentionLight);
pw.println(" mHasLight=" + mHasLight);
pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey); pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
@@ -4822,8 +4828,7 @@ public class NotificationManagerService extends SystemService {
// light // light
// release the light // release the light
boolean wasShowLights = mLights.remove(key); boolean wasShowLights = mLights.remove(key);
if (record.getLight() != null && aboveThreshold if (canShowLightsLocked(record, aboveThreshold)) {
&& ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) {
mLights.add(key); mLights.add(key);
updateLightsLocked(); updateLightsLocked();
if (mUseAttentionLight) { if (mUseAttentionLight) {
@@ -4834,7 +4839,19 @@ public class NotificationManagerService extends SystemService {
updateLightsLocked(); updateLightsLocked();
} }
if (buzz || beep || blink) { if (buzz || beep || blink) {
// Ignore summary updates because we don't display most of the information.
if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
Log.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
}
} else {
if (DEBUG_INTERRUPTIVENESS) {
Log.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is interruptive: alerted");
}
record.setInterruptive(true); record.setInterruptive(true);
}
MetricsLogger.action(record.getLogMaker() MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT) .setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN) .setType(MetricsEvent.TYPE_OPEN)
@@ -4843,12 +4860,50 @@ public class NotificationManagerService extends SystemService {
} }
} }
@GuardedBy("mNotificationLock")
boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
// device lacks light
if (!mHasLight) {
return false;
}
// user turned lights off globally
if (!mNotificationPulseEnabled) {
return false;
}
// the notification/channel has no light
if (record.getLight() == null) {
return false;
}
// unimportant notification
if (!aboveThreshold) {
return false;
}
// suppressed due to DND
if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
return false;
}
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
return false;
}
// Suppressed because another notification in its group handles alerting
if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
return false;
}
// not if in call or the screen's on
if (mInCall || mScreenOn) {
return false;
}
return true;
}
@GuardedBy("mNotificationLock") @GuardedBy("mNotificationLock")
boolean shouldMuteNotificationLocked(final NotificationRecord record) { boolean shouldMuteNotificationLocked(final NotificationRecord record) {
// Suppressed because it's a silent update // Suppressed because it's a silent update
final Notification notification = record.getNotification(); final Notification notification = record.getNotification();
if(record.isUpdate if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
return true; return true;
} }

View File

@@ -19,7 +19,9 @@ import static android.app.Notification.GROUP_ALERT_ALL;
import static android.app.Notification.GROUP_ALERT_CHILDREN; import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY; import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
@@ -149,6 +151,9 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN); mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
mService.setUsageStats(mUsageStats); mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager); mService.setAccessibilityManager(accessibilityManager);
mService.mScreenOn = false;
mService.mInCall = false;
mService.mNotificationPulseEnabled = true;
} }
// //
@@ -216,8 +221,13 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
} }
private NotificationRecord getLightsNotification() { private NotificationRecord getLightsNotification() {
return getNotificationRecord(mId, false /* insistent */, false /* once */,
false /* noisy */, false /* buzzy*/, true /* lights */);
}
private NotificationRecord getLightsOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */, return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, true /* buzzy*/, true /* lights */); false /* noisy */, false /* buzzy*/, true /* lights */);
} }
private NotificationRecord getCustomLightsNotification() { private NotificationRecord getCustomLightsNotification() {
@@ -244,6 +254,12 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
groupKey, groupAlertBehavior, false); groupKey, groupAlertBehavior, false);
} }
private NotificationRecord getLightsNotificationRecord(String groupKey,
int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
true, groupKey, groupAlertBehavior, false);
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration, boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior, boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
@@ -369,6 +385,10 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
verify(mVibrator, never()).cancel(); verify(mVibrator, never()).cancel();
} }
private void verifyNeverLights() {
verify(mLight, never()).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
private void verifyLights() { private void verifyLights() {
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt()); verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
} }
@@ -712,7 +732,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(summary); mService.buzzBeepBlinkLocked(summary);
verifyBeepLooped(); verifyBeepLooped();
assertTrue(summary.isInterruptive()); // summaries are never interruptive for notification counts
assertFalse(summary.isInterruptive());
} }
@Test @Test
@@ -990,6 +1011,156 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
} }
@Test
public void testLightsScreenOn() {
mService.mScreenOn = true;
NotificationRecord r = getLightsNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsInCall() {
mService.mInCall = true;
NotificationRecord r = getLightsNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsSilentUpdate() {
NotificationRecord r = getLightsOnceNotification();
mService.buzzBeepBlinkLocked(r);
verifyLights();
assertTrue(r.isInterruptive());
r = getLightsOnceNotification();
r.isUpdate = true;
mService.buzzBeepBlinkLocked(r);
// checks that lights happened once, i.e. this new call didn't trigger them again
verifyLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsUnimportant() {
NotificationRecord r = getLightsNotification();
r.setImportance(IMPORTANCE_LOW, "testing");
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsNoLights() {
NotificationRecord r = getQuietNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsNoLightOnDevice() {
mService.mHasLight = false;
NotificationRecord r = getLightsNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsLightsOffGlobally() {
mService.mNotificationPulseEnabled = false;
NotificationRecord r = getLightsNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testLightsDndIntercepted() {
NotificationRecord r = getLightsNotification();
r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
}
@Test
public void testGroupAlertSummaryNoLightsChild() {
NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
mService.buzzBeepBlinkLocked(child);
verifyNeverLights();
assertFalse(child.isInterruptive());
}
@Test
public void testGroupAlertSummaryLightsSummary() {
NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
mService.buzzBeepBlinkLocked(summary);
verifyLights();
// summaries should never count for interruptiveness counts
assertFalse(summary.isInterruptive());
}
@Test
public void testGroupAlertSummaryLightsNonGroupChild() {
NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_SUMMARY);
mService.buzzBeepBlinkLocked(nonGroup);
verifyLights();
assertTrue(nonGroup.isInterruptive());
}
@Test
public void testGroupAlertChildNoLightsSummary() {
NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
mService.buzzBeepBlinkLocked(summary);
verifyNeverLights();
assertFalse(summary.isInterruptive());
}
@Test
public void testGroupAlertChildLightsChild() {
NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
mService.buzzBeepBlinkLocked(child);
verifyLights();
assertTrue(child.isInterruptive());
}
@Test
public void testGroupAlertChildLightsNonGroupSummary() {
NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_CHILDREN);
mService.buzzBeepBlinkLocked(nonGroup);
verifyLights();
assertTrue(nonGroup.isInterruptive());
}
@Test
public void testGroupAlertAllLightsGroup() {
NotificationRecord group = getLightsNotificationRecord("a", GROUP_ALERT_ALL);
mService.buzzBeepBlinkLocked(group);
verifyLights();
assertTrue(group.isInterruptive());
}
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> { static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex; private final int mRepeatIndex;