diff --git a/api/current.txt b/api/current.txt index cc51943ae5db5..92961eab177d1 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; @@ -40001,8 +40008,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 f1c9f676fd573..cf13d1c63db57 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); + } }