From ccc6ae64ff1dd957fabb24b3c889a69d2d42765d Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 1 Mar 2018 16:24:49 -0500 Subject: [PATCH] Expand the visual effects that DND can suppress. Additionally default to disabling all visual effects when DND is on, to gather dogfood feedback. The 'dnd has changed' notification will be reposted after this CL and has updated text. Test: runtest systemui-notification Bug: 74075050 Change-Id: I5bec8ccc6456d98112907b0264ecd52734983984 --- api/current.txt | 15 +- .../java/android/app/NotificationManager.java | 130 ++++++++++++++++- .../NotificationListenerService.java | 9 +- .../service/notification/ZenModeConfig.java | 45 +++--- core/res/res/values/config.xml | 1 + core/res/res/values/strings.xml | 4 +- core/res/res/xml/default_zen_mode_config.xml | 4 +- .../server/notification/BadgeExtractor.java | 8 +- .../NotificationManagerService.java | 103 ++++++++++++-- .../server/notification/ZenModeExtractor.java | 11 +- .../server/notification/ZenModeFiltering.java | 15 ++ .../server/notification/ZenModeHelper.java | 9 +- .../notification/BadgeExtractorTest.java | 51 +++++++ .../NotificationManagerServiceTest.java | 132 ++++++++++++++++++ .../notification/ZenModeExtractorTest.java | 16 ++- .../notification/ZenModeFilteringTest.java | 120 ++++++++++++++++ .../notification/ZenModeHelperTest.java | 95 +++++++++++++ 17 files changed, 709 insertions(+), 59 deletions(-) create mode 100644 services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java diff --git a/api/current.txt b/api/current.txt index 2113d5d54f599..6fdc103ef0824 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5752,8 +5752,15 @@ package android.app { field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0 field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1 field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2 - field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 - field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_AMBIENT = 128; // 0x80 + field public static final int SUPPRESSED_EFFECT_BADGE = 64; // 0x40 + field public static final int SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 4; // 0x4 + field public static final int SUPPRESSED_EFFECT_LIGHTS = 8; // 0x8 + field public static final int SUPPRESSED_EFFECT_NOTIFICATION_LIST = 256; // 0x100 + field public static final int SUPPRESSED_EFFECT_PEEK = 16; // 0x10 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_STATUS_BAR = 32; // 0x20 field public final int priorityCallSenders; field public final int priorityCategories; field public final int priorityMessageSenders; @@ -40000,8 +40007,8 @@ package android.service.notification { field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 field public static final int REASON_USER_STOPPED = 6; // 0x6 field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; - field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 - field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 } public static class NotificationListenerService.Ranking { diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index b207d57689c40..46d1264f952d2 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1072,20 +1072,77 @@ public class NotificationManager { * @hide */ public static final int SUPPRESSED_EFFECTS_UNSET = -1; + /** * Whether notifications suppressed by DND should not interrupt visually (e.g. with * notification lights or by turning the screen on) when the screen is off. + * + * @deprecated use {@link #SUPPRESSED_EFFECT_FULL_SCREEN_INTENT} and + * {@link #SUPPRESSED_EFFECT_AMBIENT} and {@link #SUPPRESSED_EFFECT_LIGHTS} individually. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0; /** * Whether notifications suppressed by DND should not interrupt visually when the screen * is on (e.g. by peeking onto the screen). + * + * @deprecated use {@link #SUPPRESSED_EFFECT_PEEK}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1; + /** + * Whether {@link Notification#fullScreenIntent full screen intents} from + * notifications intercepted by DND are blocked. + */ + public static final int SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 1 << 2; + + /** + * Whether {@link NotificationChannel#shouldShowLights() notification lights} from + * notifications intercepted by DND are blocked. + */ + public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 3; + + /** + * Whether notifications intercepted by DND are prevented from peeking. + */ + public static final int SUPPRESSED_EFFECT_PEEK = 1 << 4; + + /** + * Whether notifications intercepted by DND are prevented from appearing in the status bar, + * on devices that support status bars. + */ + public static final int SUPPRESSED_EFFECT_STATUS_BAR = 1 << 5; + + /** + * Whether {@link NotificationChannel#canShowBadge() badges} from + * notifications intercepted by DND are blocked on devices that support badging. + */ + public static final int SUPPRESSED_EFFECT_BADGE = 1 << 6; + + /** + * Whether notification intercepted by DND are prevented from appearing on ambient displays + * on devices that support ambient display. + */ + public static final int SUPPRESSED_EFFECT_AMBIENT = 1 << 7; + + /** + * Whether notification intercepted by DND are prevented from appearing in notification + * list views like the notification shade or lockscreen on devices that support those + * views. + */ + public static final int SUPPRESSED_EFFECT_NOTIFICATION_LIST = 1 << 8; + private static final int[] ALL_SUPPRESSED_EFFECTS = { SUPPRESSED_EFFECT_SCREEN_OFF, SUPPRESSED_EFFECT_SCREEN_ON, + SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, + SUPPRESSED_EFFECT_LIGHTS, + SUPPRESSED_EFFECT_PEEK, + SUPPRESSED_EFFECT_STATUS_BAR, + SUPPRESSED_EFFECT_BADGE, + SUPPRESSED_EFFECT_AMBIENT, + SUPPRESSED_EFFECT_NOTIFICATION_LIST }; /** @@ -1097,6 +1154,12 @@ public class NotificationManager { /** * Constructs a policy for Do Not Disturb priority mode behavior. * + *

+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot + * change user-designated values to allow or disallow + * {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and + * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd. + * * @param priorityCategories bitmask of categories of notifications that can bypass DND. * @param priorityCallSenders which callers can bypass DND. * @param priorityMessageSenders which message senders can bypass DND. @@ -1109,6 +1172,26 @@ public class NotificationManager { /** * Constructs a policy for Do Not Disturb priority mode behavior. * + *

+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot + * change user-designated values to allow or disallow + * {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and + * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd. + *

+ * Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can + * only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and + * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} bits of the suppressed visual effects field. + * All other suppressed effects will be ignored and reconstituted from the screen on + * and screen off values. + *

+ * Apps that target {@link Build.VERSION_CODES#P} or above can set any + * suppressed visual effects. However, if any suppressed effects > + * {@link #SUPPRESSED_EFFECT_SCREEN_ON} are set, {@link #SUPPRESSED_EFFECT_SCREEN_ON} + * and {@link #SUPPRESSED_EFFECT_SCREEN_OFF} will be ignored and reconstituted from + * the more specific suppressed visual effect bits. Apps should migrate to targeting + * specific effects instead of the deprecated {@link #SUPPRESSED_EFFECT_SCREEN_ON} and + * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} effects. + * * @param priorityCategories bitmask of categories of notifications that can bypass DND. * @param priorityCallSenders which callers can bypass DND. * @param priorityMessageSenders which message senders can bypass DND. @@ -1190,6 +1273,30 @@ public class NotificationManager { } } + /** + * @hide + */ + public static int getAllSuppressedVisualEffects() { + int effects = 0; + for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) { + effects |= ALL_SUPPRESSED_EFFECTS[i]; + } + return effects; + } + + /** + * @hide + */ + public static boolean areAllVisualEffectsSuppressed(int effects) { + for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) { + final int effect = ALL_SUPPRESSED_EFFECTS[i]; + if ((effects & effect) == 0) { + return false; + } + } + return true; + } + public static String suppressedEffectsToString(int effects) { if (effects <= 0) return ""; final StringBuilder sb = new StringBuilder(); @@ -1228,9 +1335,26 @@ public class NotificationManager { private static String effectToString(int effect) { switch (effect) { - case SUPPRESSED_EFFECT_SCREEN_OFF: return "SUPPRESSED_EFFECT_SCREEN_OFF"; - case SUPPRESSED_EFFECT_SCREEN_ON: return "SUPPRESSED_EFFECT_SCREEN_ON"; - case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET"; + case SUPPRESSED_EFFECT_FULL_SCREEN_INTENT: + return "SUPPRESSED_EFFECT_FULL_SCREEN_INTENT"; + case SUPPRESSED_EFFECT_LIGHTS: + return "SUPPRESSED_EFFECT_LIGHTS"; + case SUPPRESSED_EFFECT_PEEK: + return "SUPPRESSED_EFFECT_PEEK"; + case SUPPRESSED_EFFECT_STATUS_BAR: + return "SUPPRESSED_EFFECT_STATUS_BAR"; + case SUPPRESSED_EFFECT_BADGE: + return "SUPPRESSED_EFFECT_BADGE"; + case SUPPRESSED_EFFECT_AMBIENT: + return "SUPPRESSED_EFFECT_AMBIENT"; + case SUPPRESSED_EFFECT_NOTIFICATION_LIST: + return "SUPPRESSED_EFFECT_NOTIFICATION_LIST"; + case SUPPRESSED_EFFECT_SCREEN_OFF: + return "SUPPRESSED_EFFECT_SCREEN_OFF"; + case SUPPRESSED_EFFECT_SCREEN_ON: + return "SUPPRESSED_EFFECT_SCREEN_ON"; + case SUPPRESSED_EFFECTS_UNSET: + return "SUPPRESSED_EFFECTS_UNSET"; default: return "UNKNOWN_" + effect; } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index eebd22ae64bb9..ea10ae7b38080 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -149,13 +149,19 @@ public abstract class NotificationListenerService extends Service { /** * Whether notification suppressed by DND should not interruption visually when the screen is * off. + * + * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_OFF = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; /** * Whether notification suppressed by DND should not interruption visually when the screen is * on. + * + * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_ON = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; @@ -1453,7 +1459,8 @@ public abstract class NotificationListenerService extends Service { /** * Returns the type(s) of visual effects that should be suppressed for this notification. - * See {@link #SUPPRESSED_EFFECT_SCREEN_OFF}, {@link #SUPPRESSED_EFFECT_SCREEN_ON}. + * See {@link NotificationManager.Policy}, e.g. + * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}. */ public int getSuppressedVisualEffects() { return mSuppressedVisualEffects; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index b61919eba2507..740a387e078d7 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -92,10 +92,12 @@ public class ZenModeConfig implements Parcelable { private static final boolean DEFAULT_ALLOW_REMINDERS = false; private static final boolean DEFAULT_ALLOW_EVENTS = false; private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false; - private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true; - private static final boolean DEFAULT_ALLOW_SCREEN_ON = true; + private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false; + private static final boolean DEFAULT_ALLOW_SCREEN_ON = false; + private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = + Policy.getAllSuppressedVisualEffects(); - public static final int XML_VERSION = 4; + public static final int XML_VERSION = 5; public static final String ZEN_TAG = "zen"; private static final String ZEN_ATT_VERSION = "version"; private static final String ZEN_ATT_USER = "user"; @@ -113,6 +115,8 @@ public class ZenModeConfig implements Parcelable { private static final String ALLOW_ATT_EVENTS = "events"; private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff"; private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn"; + private static final String DISALLOW_TAG = "disallow"; + private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects"; private static final String CONDITION_ATT_ID = "id"; private static final String CONDITION_ATT_SUMMARY = "summary"; @@ -146,6 +150,7 @@ public class ZenModeConfig implements Parcelable { public int allowCallsFrom = DEFAULT_SOURCE; public int allowMessagesFrom = DEFAULT_SOURCE; public int user = UserHandle.USER_SYSTEM; + public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS; public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF; public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON; public int version; @@ -180,6 +185,7 @@ public class ZenModeConfig implements Parcelable { allowAlarms = source.readInt() == 1; allowMedia = source.readInt() == 1; allowSystem = source.readInt() == 1; + suppressedVisualEffects = source.readInt(); } @Override @@ -212,6 +218,7 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(allowAlarms ? 1 : 0); dest.writeInt(allowMedia ? 1 : 0); dest.writeInt(allowSystem ? 1 : 0); + dest.writeInt(suppressedVisualEffects); } @Override @@ -230,6 +237,7 @@ public class ZenModeConfig implements Parcelable { .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) .append(",allowWhenScreenOff=").append(allowWhenScreenOff) .append(",allowWhenScreenOn=").append(allowWhenScreenOn) + .append(",suppressedVisualEffects=").append(suppressedVisualEffects) .append(",automaticRules=").append(automaticRules) .append(",manualRule=").append(manualRule) .append(']').toString(); @@ -279,6 +287,10 @@ public class ZenModeConfig implements Parcelable { if (allowWhenScreenOn != to.allowWhenScreenOn) { d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn); } + if (suppressedVisualEffects != to.suppressedVisualEffects) { + d.addLine("suppressedVisualEffects", suppressedVisualEffects, + to.suppressedVisualEffects); + } final ArraySet allRules = new ArraySet<>(); addKeys(allRules, automaticRules); addKeys(allRules, to.automaticRules); @@ -383,7 +395,8 @@ public class ZenModeConfig implements Parcelable { && other.allowWhenScreenOn == allowWhenScreenOn && other.user == user && Objects.equals(other.automaticRules, automaticRules) - && Objects.equals(other.manualRule, manualRule); + && Objects.equals(other.manualRule, manualRule) + && other.suppressedVisualEffects == suppressedVisualEffects; } @Override @@ -391,7 +404,8 @@ public class ZenModeConfig implements Parcelable { return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, - allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule); + allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule, + suppressedVisualEffects); } private static String toDayList(int[] days) { @@ -474,6 +488,8 @@ public class ZenModeConfig implements Parcelable { rt.allowCallsFrom = DEFAULT_SOURCE; rt.allowMessagesFrom = DEFAULT_SOURCE; } + // continue to read even though we now have suppressedVisualEffects, in case + // we need to revert to users' previous settings rt.allowWhenScreenOff = safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF); rt.allowWhenScreenOn = @@ -482,6 +498,9 @@ public class ZenModeConfig implements Parcelable { rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA, DEFAULT_ALLOW_MEDIA); rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM); + } else if (DISALLOW_TAG.equals(tag)) { + rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS, + DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); } else if (AUTOMATIC_TAG.equals(tag)) { @@ -517,6 +536,10 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem)); out.endTag(null, ALLOW_TAG); + out.startTag(null, DISALLOW_TAG); + out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects)); + out.endTag(null, DISALLOW_TAG); + if (manualRule != null) { out.startTag(null, MANUAL_TAG); writeRuleXml(manualRule, out); @@ -701,13 +724,6 @@ public class ZenModeConfig implements Parcelable { if (allowRepeatCallers) { priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; } - int suppressedVisualEffects = 0; - if (!allowWhenScreenOff) { - suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; - } - if (!allowWhenScreenOn) { - suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; - } if (allowAlarms) { priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; } @@ -770,10 +786,7 @@ public class ZenModeConfig implements Parcelable { allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders, allowMessagesFrom); if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { - allowWhenScreenOff = - (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_OFF) == 0; - allowWhenScreenOn = - (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0; + suppressedVisualEffects = policy.suppressedVisualEffects; } } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fa9cab59135fd..a606595deaf00 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2396,6 +2396,7 @@ com.android.server.notification.NotificationIntrusivenessExtractor com.android.server.notification.VisibilityExtractor + com.android.server.notification.BadgeExtractor diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 15dffc0ba52c3..a5bd179826b14 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4883,7 +4883,7 @@ System changes - Do Not Disturb has changed + Do Not Disturb is hiding notifications to help you focus - Tap to check your behavior settings for interruptions + This is a new feature from the latest system update. Tap to change. diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml index 5e463d866b4df..2d3cd1cd5d29d 100644 --- a/core/res/res/xml/default_zen_mode_config.xml +++ b/core/res/res/xml/default_zen_mode_config.xml @@ -18,7 +18,9 @@ --> - + + + diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java index d8a30ae7c53f8..d91d5410d0042 100644 --- a/services/core/java/com/android/server/notification/BadgeExtractor.java +++ b/services/core/java/com/android/server/notification/BadgeExtractor.java @@ -15,6 +15,8 @@ */ package com.android.server.notification; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; + import android.content.Context; import android.util.Slog; @@ -54,6 +56,11 @@ public class BadgeExtractor implements NotificationSignalExtractor { } } + if (record.isIntercepted() + && (record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_BADGE) != 0) { + record.setShowBadge(false); + } + return null; } @@ -64,6 +71,5 @@ public class BadgeExtractor implements NotificationSignalExtractor { @Override public void setZenHelper(ZenModeHelper helper) { - } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cd387b017c53e..f0b60ec2fe137 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -22,6 +22,16 @@ import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -60,8 +70,6 @@ import static android.service.notification.NotificationListenerService.REASON_SN import static android.service.notification.NotificationListenerService.REASON_TIMEOUT; import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED; import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED; -import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF; -import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON; import static android.service.notification.NotificationListenerService.TRIM_FULL; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.Display.DEFAULT_DISPLAY; @@ -1342,6 +1350,7 @@ public class NotificationManagerService extends SystemService { @Override void onPolicyChanged() { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); + mRankingHandler.requestSort(); } }); mRankingHelper = new RankingHelper(getContext(), @@ -1781,6 +1790,74 @@ public class NotificationManagerService extends SystemService { UsageEvents.Event.NOTIFICATION_SEEN); } + protected int calculateSuppressedVisualEffects(Policy incomingPolicy, Policy currPolicy, + int targetSdkVersion) { + if (incomingPolicy.suppressedVisualEffects == SUPPRESSED_EFFECTS_UNSET) { + return incomingPolicy.suppressedVisualEffects; + } + final int[] effectsIntroducedInP = { + SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, + SUPPRESSED_EFFECT_LIGHTS, + SUPPRESSED_EFFECT_PEEK, + SUPPRESSED_EFFECT_STATUS_BAR, + SUPPRESSED_EFFECT_BADGE, + SUPPRESSED_EFFECT_AMBIENT, + SUPPRESSED_EFFECT_NOTIFICATION_LIST + }; + + int newSuppressedVisualEffects = incomingPolicy.suppressedVisualEffects; + if (targetSdkVersion <= Build.VERSION_CODES.O_MR1) { + // unset higher order bits introduced in P, maintain the user's higher order bits + for (int i = 0; i < effectsIntroducedInP.length ; i++) { + newSuppressedVisualEffects &= ~effectsIntroducedInP[i]; + newSuppressedVisualEffects |= + (currPolicy.suppressedVisualEffects & effectsIntroducedInP[i]); + } + // set higher order bits according to lower order bits + if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS; + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; + } + if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK; + } + } else { + boolean hasNewEffects = (newSuppressedVisualEffects + - SUPPRESSED_EFFECT_SCREEN_ON - SUPPRESSED_EFFECT_SCREEN_OFF) > 0; + // if any of the new effects introduced in P are set + if (hasNewEffects) { + // clear out the deprecated effects + newSuppressedVisualEffects &= ~ (SUPPRESSED_EFFECT_SCREEN_ON + | SUPPRESSED_EFFECT_SCREEN_OFF); + + // set the deprecated effects according to the new more specific effects + if ((newSuppressedVisualEffects & Policy.SUPPRESSED_EFFECT_PEEK) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_SCREEN_ON; + } + if ((newSuppressedVisualEffects & Policy.SUPPRESSED_EFFECT_LIGHTS) != 0 + && (newSuppressedVisualEffects + & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0 + && (newSuppressedVisualEffects + & Policy.SUPPRESSED_EFFECT_AMBIENT) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_SCREEN_OFF; + } + } else { + // set higher order bits according to lower order bits + if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS; + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; + } + if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) { + newSuppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK; + } + } + } + + return newSuppressedVisualEffects; + } + /** * Report to usage stats that the notification was clicked. * @param r notification record @@ -3063,10 +3140,9 @@ public class NotificationManagerService extends SystemService { try { final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg, 0, UserHandle.getUserId(MY_UID)); + Policy currPolicy = mZenModeHelper.getNotificationPolicy(); if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1) { - Policy currPolicy = mZenModeHelper.getNotificationPolicy(); - int priorityCategories = policy.priorityCategories; // ignore alarm and media values from new policy priorityCategories &= ~Policy.PRIORITY_CATEGORY_ALARMS; @@ -3084,11 +3160,12 @@ public class NotificationManagerService extends SystemService { policy.priorityCallSenders, policy.priorityMessageSenders, policy.suppressedVisualEffects); } + int newVisualEffects = calculateSuppressedVisualEffects( + policy, currPolicy, applicationInfo.targetSdkVersion); + policy = new Policy(policy.priorityCategories, + policy.priorityCallSenders, policy.priorityMessageSenders, + newVisualEffects); - Slog.i(TAG, "setNotificationPolicy pkg=" + pkg - + " targetSdk=" + applicationInfo.targetSdkVersion - + " policy=" - + Policy.priorityCategoriesToString(policy.priorityCategories)); mZenModeHelper.setNotificationPolicy(policy); } catch (RemoteException e) { } finally { @@ -4465,8 +4542,7 @@ public class NotificationManagerService extends SystemService { // release the light boolean wasShowLights = mLights.remove(key); if (record.getLight() != null && aboveThreshold - && ((record.getSuppressedVisualEffects() - & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) { + && ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) { mLights.add(key); updateLightsLocked(); if (mUseAttentionLight) { @@ -4864,11 +4940,8 @@ public class NotificationManagerService extends SystemService { private void applyZenModeLocked(NotificationRecord record) { record.setIntercepted(mZenModeHelper.shouldIntercept(record)); if (record.isIntercepted()) { - int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff() - ? SUPPRESSED_EFFECT_SCREEN_OFF : 0) - | (mZenModeHelper.shouldSuppressWhenScreenOn() - ? SUPPRESSED_EFFECT_SCREEN_ON : 0); - record.setSuppressedVisualEffects(suppressed); + record.setSuppressedVisualEffects( + mZenModeHelper.getNotificationPolicy().suppressedVisualEffects); } else { record.setSuppressedVisualEffects(0); } diff --git a/services/core/java/com/android/server/notification/ZenModeExtractor.java b/services/core/java/com/android/server/notification/ZenModeExtractor.java index 74f9806410811..a0aa1c32645ff 100644 --- a/services/core/java/com/android/server/notification/ZenModeExtractor.java +++ b/services/core/java/com/android/server/notification/ZenModeExtractor.java @@ -16,8 +16,8 @@ package com.android.server.notification; -import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF; -import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import android.content.Context; import android.util.Log; @@ -49,11 +49,8 @@ public class ZenModeExtractor implements NotificationSignalExtractor { record.setIntercepted(mZenModeHelper.shouldIntercept(record)); if (record.isIntercepted()) { - int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff() - ? SUPPRESSED_EFFECT_SCREEN_OFF : 0) - | (mZenModeHelper.shouldSuppressWhenScreenOn() - ? SUPPRESSED_EFFECT_SCREEN_ON : 0); - record.setSuppressedVisualEffects(suppressed); + record.setSuppressedVisualEffects( + mZenModeHelper.getNotificationPolicy().suppressedVisualEffects); } else { record.setSuppressedVisualEffects(0); } diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java index a0003a56e537a..a7e0c51081885 100644 --- a/services/core/java/com/android/server/notification/ZenModeFiltering.java +++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java @@ -16,7 +16,10 @@ package com.android.server.notification; +import static android.provider.Settings.Global.ZEN_MODE_OFF; + import android.app.Notification; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.media.AudioAttributes; @@ -30,6 +33,8 @@ import android.telecom.TelecomManager; import android.util.ArrayMap; import android.util.Slog; +import com.android.internal.messages.nano.SystemMessageProto; + import java.io.PrintWriter; import java.util.Date; import java.util.Objects; @@ -104,6 +109,16 @@ public class ZenModeFiltering { } public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) { + if (zen == ZEN_MODE_OFF) { + return false; + } + // Make an exception to policy for the notification saying that policy has changed + if (NotificationManager.Policy.areAllVisualEffectsSuppressed(config.suppressedVisualEffects) + && "android".equals(record.sbn.getPackageName()) + && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.sbn.getId()) { + ZenLog.traceNotIntercepted(record, "systemDndChangedNotification"); + return false; + } switch (zen) { case Global.ZEN_MODE_NO_INTERRUPTIONS: // #notevenalarms diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 28d692ad624ad..aa1f7d95845f5 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -602,7 +602,8 @@ public class ZenModeHelper { pw.println(config); return; } - pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s," + pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b," + + "messages=%b,messagesFrom=%s," + "events=%b,reminders=%b,whenScreenOff=%b,whenScreenOn=%b)\n", config.allowAlarms, config.allowMedia, config.allowSystem, config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom), @@ -610,6 +611,7 @@ public class ZenModeHelper { ZenModeConfig.sourceToString(config.allowMessagesFrom), config.allowEvents, config.allowReminders, config.allowWhenScreenOff, config.allowWhenScreenOn); + pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects); pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule); if (config.automaticRules.isEmpty()) return; final int N = config.automaticRules.size(); @@ -623,7 +625,7 @@ public class ZenModeHelper { throws XmlPullParserException, IOException { final ZenModeConfig config = ZenModeConfig.readXml(parser); if (config != null) { - if (config.version < ZenModeConfig.XML_VERSION) { + if (config.version < ZenModeConfig.XML_VERSION || forRestore) { Settings.Global.putInt(mContext.getContentResolver(), Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1); } @@ -1176,8 +1178,7 @@ public class ZenModeHelper { @VisibleForTesting protected Notification createZenUpgradeNotification() { - Intent intent = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS) - .setPackage("com.android.settings") + Intent intent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java index 142041a4f7058..cfc7430cc8f4c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java @@ -18,10 +18,13 @@ package com.android.server.notification; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import android.app.ActivityManager; import android.app.Notification; @@ -149,4 +152,52 @@ public class BadgeExtractorTest extends UiServiceTestCase { assertFalse(r.canShowBadge()); } + + @Test + public void testDndOverridesYes() { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.badgingEnabled(mUser)).thenReturn(true); + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED); + r.setIntercepted(true); + r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_BADGE); + + extractor.process(r); + + assertFalse(r.canShowBadge()); + } + + @Test + public void testDndOConsidersInterception() { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.badgingEnabled(mUser)).thenReturn(true); + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED); + r.setIntercepted(false); + r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_BADGE); + + extractor.process(r); + + assertTrue(r.canShowBadge()); + } + + @Test + public void testDndConsidersSuppressedVisualEffects() { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.badgingEnabled(mUser)).thenReturn(true); + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true); + NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED); + r.setIntercepted(true); + r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS); + + extractor.process(r); + + assertTrue(r.canShowBadge()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 7b2c040db5abf..4fe54b9f15ef1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -22,8 +22,19 @@ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.os.Build.VERSION_CODES.O_MR1; +import static android.os.Build.VERSION_CODES.P; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -2530,4 +2541,125 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()), anyInt(), anyInt()); } + + @Test + public void testSetNotificationPolicy_preP_setOldFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); + + int expected = SUPPRESSED_EFFECT_BADGE + | SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF + | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, O_MR1); + + assertEquals(expected, actual); + } + + @Test + public void testSetNotificationPolicy_preP_setNewFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_NOTIFICATION_LIST); + + int expected = SUPPRESSED_EFFECT_BADGE; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, O_MR1); + + assertEquals(expected, actual); + } + + @Test + public void testSetNotificationPolicy_preP_setOldNewFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR); + + int expected = + SUPPRESSED_EFFECT_BADGE | SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, O_MR1); + + assertEquals(expected, actual); + } + + @Test + public void testSetNotificationPolicy_P_setOldFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); + + int expected = SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF + | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, P); + + assertEquals(expected, actual); + } + + @Test + public void testSetNotificationPolicy_P_setNewFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); + + int expected = SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_SCREEN_OFF + | SUPPRESSED_EFFECT_AMBIENT | SUPPRESSED_EFFECT_LIGHTS + | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, P); + + assertEquals(expected, actual); + } + + @Test + public void testSetNotificationPolicy_P_setOldNewFields() { + ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); + mService.mZenModeHelper = mZenModeHelper; + NotificationManager.Policy userPolicy = + new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE); + when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy); + + NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR); + + int expected = SUPPRESSED_EFFECT_STATUS_BAR; + int actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, P); + + assertEquals(expected, actual); + + appPolicy = new NotificationManager.Policy(0, 0, 0, + SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); + + expected = SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + actual = mService.calculateSuppressedVisualEffects(appPolicy, userPolicy, P); + + assertEquals(expected, actual); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java index faba6b6f55b77..beff0d11ef3e8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java @@ -17,6 +17,8 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -57,6 +59,8 @@ public class ZenModeExtractorTest extends UiServiceTestCase { assertFalse(r.isIntercepted()); when(mZenModeHelper.shouldIntercept(any())).thenReturn(true); + when(mZenModeHelper.getNotificationPolicy()).thenReturn( + new NotificationManager.Policy(0,0,0)); extractor.process(r); @@ -70,7 +74,8 @@ public class ZenModeExtractorTest extends UiServiceTestCase { NotificationRecord r = generateRecord(); when(mZenModeHelper.shouldIntercept(any())).thenReturn(false); - when(mZenModeHelper.shouldSuppressWhenScreenOff()).thenReturn(false); + when(mZenModeHelper.getNotificationPolicy()).thenReturn( + new NotificationManager.Policy(0,0,0)); extractor.process(r); @@ -84,13 +89,14 @@ public class ZenModeExtractorTest extends UiServiceTestCase { NotificationRecord r = generateRecord(); when(mZenModeHelper.shouldIntercept(any())).thenReturn(true); - when(mZenModeHelper.shouldSuppressWhenScreenOff()).thenReturn(true); - when(mZenModeHelper.shouldSuppressWhenScreenOn()).thenReturn(true); + when(mZenModeHelper.getNotificationPolicy()).thenReturn( + new NotificationManager.Policy(0,0,0, SUPPRESSED_EFFECT_PEEK + | SUPPRESSED_EFFECT_NOTIFICATION_LIST)); extractor.process(r); - assertEquals(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF - | NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON, + assertEquals(NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK + | NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST, r.getSuppressedVisualEffects()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java new file mode 100644 index 0000000000000..1936439240426 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distriZenbuted on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static android.provider.Settings.Global.ZEN_MODE_ALARMS; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS; +import static android.provider.Settings.Global.ZEN_MODE_OFF; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.service.notification.StatusBarNotification; +import android.service.notification.ZenModeConfig; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class ZenModeFilteringTest extends UiServiceTestCase { + + private ZenModeFiltering mZenModeFiltering; + + @Before + public void setUp() { + mZenModeFiltering = new ZenModeFiltering(mContext); + } + + private NotificationRecord getNotificationRecord() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getNotification()).thenReturn(mock(Notification.class)); + return new NotificationRecord(mContext, sbn, mock(NotificationChannel.class)); + } + + @Test + public void testSuppressDNDInfo_yes_VisEffectsAllowed() { + NotificationRecord r = getNotificationRecord(); + when(r.sbn.getPackageName()).thenReturn("android"); + when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE); + ZenModeConfig config = mock(ZenModeConfig.class); + config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects() + - SUPPRESSED_EFFECT_STATUS_BAR; + + assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r)); + } + + @Test + public void testSuppressDNDInfo_yes_WrongId() { + NotificationRecord r = getNotificationRecord(); + when(r.sbn.getPackageName()).thenReturn("android"); + when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION); + ZenModeConfig config = mock(ZenModeConfig.class); + config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects(); + + assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r)); + } + + @Test + public void testSuppressDNDInfo_yes_WrongPackage() { + NotificationRecord r = getNotificationRecord(); + when(r.sbn.getPackageName()).thenReturn("android2"); + when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE); + ZenModeConfig config = mock(ZenModeConfig.class); + config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects(); + + assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r)); + } + + @Test + public void testSuppressDNDInfo_no() { + NotificationRecord r = getNotificationRecord(); + when(r.sbn.getPackageName()).thenReturn("android"); + when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE); + ZenModeConfig config = mock(ZenModeConfig.class); + config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects(); + + assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r)); + assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, config, r)); + assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, config, r)); + } + + @Test + public void testSuppressAnything_yes_ZenModeOff() { + NotificationRecord r = getNotificationRecord(); + when(r.sbn.getPackageName()).thenReturn("bananas"); + ZenModeConfig config = mock(ZenModeConfig.class); + + assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, config, r)); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 5bfa15a7b8c04..9008803c2c2f9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertTrue; @@ -34,6 +36,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.NotificationManager; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; @@ -48,8 +51,11 @@ import android.service.notification.ZenModeConfig; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.Xml; +import com.android.internal.R; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; import android.util.Slog; @@ -58,6 +64,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -80,12 +93,27 @@ public class ZenModeHelperTest extends UiServiceTestCase { mContext = spy(getContext()); mContentResolver = mContext.getContentResolver(); when(mContext.getResources()).thenReturn(mResources); + when(mResources.getString(R.string.zen_mode_default_every_night_name)).thenReturn("night"); + when(mResources.getString(R.string.zen_mode_default_events_name)).thenReturn("events"); when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager); mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(), mConditionProviders)); } + private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup) + throws Exception { + XmlSerializer serializer = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mZenModeHelperSpy.writeXml(serializer, forBackup); + serializer.endDocument(); + serializer.flush(); + mZenModeHelperSpy.setConfig(new ZenModeConfig(), "writing xml"); + return baos; + } + @Test public void testZenOff_NoMuteApplied() { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF; @@ -497,4 +525,71 @@ public class ZenModeHelperTest extends UiServiceTestCase { verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelperSpy.TAG); } + + @Test + public void testParcelConfig() { + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowAlarms = false; + mZenModeHelperSpy.mConfig.allowMedia = false; + mZenModeHelperSpy.mConfig.allowSystem = false; + mZenModeHelperSpy.mConfig.allowReminders = true; + mZenModeHelperSpy.mConfig.allowCalls = true; + mZenModeHelperSpy.mConfig.allowMessages = true; + mZenModeHelperSpy.mConfig.allowEvents = true; + mZenModeHelperSpy.mConfig.allowRepeatCallers= true; + mZenModeHelperSpy.mConfig.allowWhenScreenOff = true; + mZenModeHelperSpy.mConfig.allowWhenScreenOn = true; + mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; + mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule(); + mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a"); + mZenModeHelperSpy.mConfig.manualRule.enabled = true; + mZenModeHelperSpy.mConfig.manualRule.snoozing = true; + + ZenModeConfig actual = mZenModeHelperSpy.mConfig.copy(); + + assertEquals(mZenModeHelperSpy.mConfig, actual); + } + + @Test + public void testWriteXml() throws Exception { + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowAlarms = false; + mZenModeHelperSpy.mConfig.allowMedia = false; + mZenModeHelperSpy.mConfig.allowSystem = false; + mZenModeHelperSpy.mConfig.allowReminders = true; + mZenModeHelperSpy.mConfig.allowCalls = true; + mZenModeHelperSpy.mConfig.allowMessages = true; + mZenModeHelperSpy.mConfig.allowEvents = true; + mZenModeHelperSpy.mConfig.allowRepeatCallers= true; + mZenModeHelperSpy.mConfig.allowWhenScreenOff = true; + mZenModeHelperSpy.mConfig.allowWhenScreenOn = true; + mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; + mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule(); + mZenModeHelperSpy.mConfig.manualRule.zenMode = + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a"); + mZenModeHelperSpy.mConfig.manualRule.enabled = true; + mZenModeHelperSpy.mConfig.manualRule.snoozing = true; + + ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); + + ByteArrayOutputStream baos = writeXmlAndPurge(false); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + mZenModeHelperSpy.readXml(parser, false); + + assertEquals(expected, mZenModeHelperSpy.mConfig); + } + + @Test + public void testPolicyReadsSuppressedEffects() { + mZenModeHelperSpy.mConfig.allowWhenScreenOff = true; + mZenModeHelperSpy.mConfig.allowWhenScreenOn = true; + mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; + + NotificationManager.Policy policy = mZenModeHelperSpy.getNotificationPolicy(); + assertEquals(SUPPRESSED_EFFECT_BADGE, policy.suppressedVisualEffects); + } }