1 /**
2  * Copyright (c) 2014, The Android Open Source Project
3  *
4  * Licensed under the Apache License,  2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.notification;
18 
19 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
20 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
21 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
22 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
23 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
25 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
26 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
27 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
28 import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
29 import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
30 import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
31 import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE;
32 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
33 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
34 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
35 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
36 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
37 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
38 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
39 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
40 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
41 import static android.service.notification.ZenPolicy.STATE_ALLOW;
42 import static android.service.notification.ZenPolicy.STATE_DISALLOW;
43 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
44 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
45 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
46 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
47 
48 import android.annotation.FlaggedApi;
49 import android.annotation.IntDef;
50 import android.annotation.NonNull;
51 import android.annotation.Nullable;
52 import android.app.ActivityManager;
53 import android.app.AlarmManager;
54 import android.app.AutomaticZenRule;
55 import android.app.Flags;
56 import android.app.NotificationManager;
57 import android.app.NotificationManager.Policy;
58 import android.compat.annotation.UnsupportedAppUsage;
59 import android.content.ComponentName;
60 import android.content.Context;
61 import android.content.pm.ApplicationInfo;
62 import android.content.pm.PackageManager;
63 import android.content.res.Resources;
64 import android.net.Uri;
65 import android.os.Build;
66 import android.os.Parcel;
67 import android.os.Parcelable;
68 import android.os.UserHandle;
69 import android.provider.Settings.Global;
70 import android.text.TextUtils;
71 import android.text.format.DateFormat;
72 import android.util.ArrayMap;
73 import android.util.PluralsMessageFormatter;
74 import android.util.Slog;
75 import android.util.proto.ProtoOutputStream;
76 
77 import com.android.internal.R;
78 import com.android.internal.annotations.VisibleForTesting;
79 import com.android.internal.util.XmlUtils;
80 import com.android.modules.utils.TypedXmlPullParser;
81 import com.android.modules.utils.TypedXmlSerializer;
82 
83 import org.xmlpull.v1.XmlPullParser;
84 import org.xmlpull.v1.XmlPullParserException;
85 
86 import java.io.IOException;
87 import java.lang.annotation.Retention;
88 import java.lang.annotation.RetentionPolicy;
89 import java.time.Instant;
90 import java.util.ArrayList;
91 import java.util.Arrays;
92 import java.util.Calendar;
93 import java.util.Date;
94 import java.util.GregorianCalendar;
95 import java.util.HashMap;
96 import java.util.HashSet;
97 import java.util.List;
98 import java.util.Locale;
99 import java.util.Map;
100 import java.util.Objects;
101 import java.util.Set;
102 import java.util.TimeZone;
103 import java.util.UUID;
104 import java.util.regex.Pattern;
105 
106 /**
107  * Persisted configuration for zen mode.
108  *
109  * @hide
110  */
111 public class ZenModeConfig implements Parcelable {
112     private static final String TAG = "ZenModeConfig";
113 
114     /**
115      * The {@link ZenModeConfig} is being updated because of an unknown reason.
116      */
117     public static final int UPDATE_ORIGIN_UNKNOWN = 0;
118 
119     /**
120      * The {@link ZenModeConfig} is being updated because of system initialization (i.e. load from
121      * storage, on device boot).
122      */
123     public static final int UPDATE_ORIGIN_INIT = 1;
124 
125     /** The {@link ZenModeConfig} is being updated (replaced) because of a user switch or unlock. */
126     public static final int UPDATE_ORIGIN_INIT_USER = 2;
127 
128     /** The {@link ZenModeConfig} is being updated because of a user action, for example:
129      * <ul>
130      *     <li>{@link NotificationManager#setAutomaticZenRuleState} with a
131      *     {@link Condition#source} equal to {@link Condition#SOURCE_USER_ACTION}.</li>
132      *     <li>Adding, updating, or removing a rule from Settings.</li>
133      *     <li>Directly activating or deactivating/snoozing a rule through some UI affordance (e.g.
134      *     Quick Settings).</li>
135      * </ul>
136      */
137     public static final int UPDATE_ORIGIN_USER = 3;
138 
139     /**
140      * The {@link ZenModeConfig} is being "independently" updated by an app, and not as a result of
141      * a user's action inside that app (for example, activating an {@link AutomaticZenRule} based on
142      * a previously set schedule).
143      */
144     public static final int UPDATE_ORIGIN_APP = 4;
145 
146     /**
147      * The {@link ZenModeConfig} is being updated by the System or SystemUI. Note that this only
148      * includes cases where the call is coming from the System/SystemUI but the change is not due to
149      * a user action (e.g. automatically activating a schedule-based rule). If the change is a
150      * result of a user action (e.g. activating a rule by tapping on its QS tile) then
151      * {@link #UPDATE_ORIGIN_USER} is used instead.
152      */
153     public static final int UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI = 5;
154 
155     /**
156      * The {@link ZenModeConfig} is being updated (replaced) because the user's DND configuration
157      * is being restored from a backup.
158      */
159     public static final int UPDATE_ORIGIN_RESTORE_BACKUP = 6;
160 
161     @IntDef(prefix = { "UPDATE_ORIGIN_" }, value = {
162             UPDATE_ORIGIN_UNKNOWN,
163             UPDATE_ORIGIN_INIT,
164             UPDATE_ORIGIN_INIT_USER,
165             UPDATE_ORIGIN_USER,
166             UPDATE_ORIGIN_APP,
167             UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
168             UPDATE_ORIGIN_RESTORE_BACKUP
169     })
170     @Retention(RetentionPolicy.SOURCE)
171     public @interface ConfigChangeOrigin {}
172 
173     public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
174     public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
175     public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
176     public static final int MAX_SOURCE = SOURCE_STAR;
177     private static final int DEFAULT_SOURCE = SOURCE_STAR;
178     private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
179 
180     public static final String MANUAL_RULE_ID = "MANUAL_RULE";
181     public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
182     public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
183     public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
184             EVENTS_DEFAULT_RULE_ID);
185 
186     public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
187             Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
188 
189     public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
190     private static final int SECONDS_MS = 1000;
191     private static final int MINUTES_MS = 60 * SECONDS_MS;
192     private static final int DAY_MINUTES = 24 * 60;
193     private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
194 
195     // Default allow categories set in readXml() from default_zen_mode_config.xml,
196     // fallback/upgrade values:
197     private static final boolean DEFAULT_ALLOW_ALARMS = true;
198     private static final boolean DEFAULT_ALLOW_MEDIA = true;
199     private static final boolean DEFAULT_ALLOW_SYSTEM = false;
200     private static final boolean DEFAULT_ALLOW_CALLS = true;
201     private static final boolean DEFAULT_ALLOW_MESSAGES = true;
202     private static final boolean DEFAULT_ALLOW_REMINDERS = false;
203     private static final boolean DEFAULT_ALLOW_EVENTS = false;
204     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
205     private static final boolean DEFAULT_ALLOW_CONV = true;
206     private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
207     private static final boolean DEFAULT_ALLOW_PRIORITY_CHANNELS = true;
208     private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
209     // Default setting here is 010011101 = 157
210     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
211             SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
212                     | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
213 
214     // ZenModeConfig XML versions distinguishing key changes.
215     public static final int XML_VERSION_ZEN_UPGRADE = 8;
216     public static final int XML_VERSION_MODES_API = 11;
217 
218     // TODO: b/310620812 - Update XML_VERSION and update default_zen_config.xml accordingly when
219     //       modes_api is inlined.
220     private static final int XML_VERSION = 10;
221     public static final String ZEN_TAG = "zen";
222     private static final String ZEN_ATT_VERSION = "version";
223     private static final String ZEN_ATT_USER = "user";
224     private static final String ALLOW_TAG = "allow";
225     private static final String ALLOW_ATT_ALARMS = "alarms";
226     private static final String ALLOW_ATT_MEDIA = "media";
227     private static final String ALLOW_ATT_SYSTEM = "system";
228     private static final String ALLOW_ATT_CALLS = "calls";
229     private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
230     private static final String ALLOW_ATT_MESSAGES = "messages";
231     private static final String ALLOW_ATT_FROM = "from";
232     private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
233     private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
234     private static final String ALLOW_ATT_REMINDERS = "reminders";
235     private static final String ALLOW_ATT_EVENTS = "events";
236     private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
237     private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
238     private static final String ALLOW_ATT_CONV = "convos";
239     private static final String ALLOW_ATT_CONV_FROM = "convosFrom";
240     private static final String ALLOW_ATT_CHANNELS = "priorityChannelsAllowed";
241     private static final String POLICY_USER_MODIFIED_FIELDS = "policyUserModifiedFields";
242     private static final String DISALLOW_TAG = "disallow";
243     private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
244     private static final String STATE_TAG = "state";
245     private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
246 
247     // zen policy visual effects attributes
248     private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent";
249     private static final String SHOW_ATT_LIGHTS = "showLights";
250     private static final String SHOW_ATT_PEEK = "shoePeek";
251     private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons";
252     private static final String SHOW_ATT_BADGES = "showBadges";
253     private static final String SHOW_ATT_AMBIENT = "showAmbient";
254     private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList";
255 
256     private static final String CONDITION_ATT_ID = "id";
257     private static final String CONDITION_ATT_SUMMARY = "summary";
258     private static final String CONDITION_ATT_LINE1 = "line1";
259     private static final String CONDITION_ATT_LINE2 = "line2";
260     private static final String CONDITION_ATT_ICON = "icon";
261     private static final String CONDITION_ATT_STATE = "state";
262     private static final String CONDITION_ATT_SOURCE = "source";
263     private static final String CONDITION_ATT_FLAGS = "flags";
264 
265     private static final String ZEN_POLICY_TAG = "zen_policy";
266 
267     private static final String MANUAL_TAG = "manual";
268     private static final String AUTOMATIC_TAG = "automatic";
269     private static final String AUTOMATIC_DELETED_TAG = "deleted";
270 
271     private static final String RULE_ATT_ID = "ruleId";
272     private static final String RULE_ATT_ENABLED = "enabled";
273     private static final String RULE_ATT_SNOOZING = "snoozing";
274     private static final String RULE_ATT_NAME = "name";
275     private static final String RULE_ATT_PKG = "pkg";
276     private static final String RULE_ATT_COMPONENT = "component";
277     private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
278     private static final String RULE_ATT_ZEN = "zen";
279     private static final String RULE_ATT_CONDITION_ID = "conditionId";
280     private static final String RULE_ATT_CREATION_TIME = "creationTime";
281     private static final String RULE_ATT_ENABLER = "enabler";
282     private static final String RULE_ATT_MODIFIED = "modified";
283     private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable";
284     private static final String RULE_ATT_TYPE = "type";
285     private static final String RULE_ATT_USER_MODIFIED_FIELDS = "userModifiedFields";
286     private static final String RULE_ATT_ICON = "rule_icon";
287     private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
288     private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant";
289 
290     private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
291     private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
292             "zdeSuppressAmbientDisplay";
293     private static final String DEVICE_EFFECT_DIM_WALLPAPER = "zdeDimWallpaper";
294     private static final String DEVICE_EFFECT_USE_NIGHT_MODE = "zdeUseNightMode";
295     private static final String DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS = "zdeDisableAutoBrightness";
296     private static final String DEVICE_EFFECT_DISABLE_TAP_TO_WAKE = "zdeDisableTapToWake";
297     private static final String DEVICE_EFFECT_DISABLE_TILT_TO_WAKE = "zdeDisableTiltToWake";
298     private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch";
299     private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage";
300     private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze";
301     private static final String DEVICE_EFFECT_EXTRAS = "zdeExtraEffects";
302     private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields";
303 
304     private static final String ITEM_SEPARATOR = ",";
305     private static final String ITEM_SEPARATOR_ESCAPE = "\\";
306     private static final Pattern ITEM_SPLITTER_REGEX = Pattern.compile("(?<!\\\\),");
307 
308     @UnsupportedAppUsage
309     public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
310     public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
311     public boolean allowSystem = DEFAULT_ALLOW_SYSTEM;
312     public boolean allowCalls = DEFAULT_ALLOW_CALLS;
313     public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
314     public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
315     public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
316     public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
317     public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
318     public int allowMessagesFrom = DEFAULT_SOURCE;
319     public boolean allowConversations = DEFAULT_ALLOW_CONV;
320     public int allowConversationsFrom = DEFAULT_ALLOW_CONV_FROM;
321     public int user = UserHandle.USER_SYSTEM;
322     public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
323     // Note that when the modes_api flag is true, the areChannelsBypassingDnd boolean only tracks
324     // whether the current user has any priority channels. These channels may bypass DND when
325     // allowPriorityChannels is true.
326     // TODO: b/310620812 - Rename to be more accurate when modes_api flag is inlined.
327     public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
328     public boolean allowPriorityChannels = DEFAULT_ALLOW_PRIORITY_CHANNELS;
329     public int version;
330 
331     public ZenRule manualRule;
332     @UnsupportedAppUsage
333     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
334 
335     // Note: Map is *pkg|conditionId* (see deletedRuleKey()) -> ZenRule,
336     // unlike automaticRules (which is id -> rule).
337     public final ArrayMap<String, ZenRule> deletedRules = new ArrayMap<>();
338 
339     @UnsupportedAppUsage
ZenModeConfig()340     public ZenModeConfig() {
341         if (Flags.modesUi()) {
342             ensureManualZenRule();
343         }
344     }
345 
ZenModeConfig(Parcel source)346     public ZenModeConfig(Parcel source) {
347         if (!Flags.modesUi()) {
348             allowCalls = source.readInt() == 1;
349             allowRepeatCallers = source.readInt() == 1;
350             allowMessages = source.readInt() == 1;
351             allowReminders = source.readInt() == 1;
352             allowEvents = source.readInt() == 1;
353             allowCallsFrom = source.readInt();
354             allowMessagesFrom = source.readInt();
355         }
356         user = source.readInt();
357         manualRule = source.readParcelable(null, ZenRule.class);
358         readRulesFromParcel(automaticRules, source);
359         if (Flags.modesApi()) {
360             readRulesFromParcel(deletedRules, source);
361         }
362         if (!Flags.modesUi()) {
363             allowAlarms = source.readInt() == 1;
364             allowMedia = source.readInt() == 1;
365             allowSystem = source.readInt() == 1;
366             suppressedVisualEffects = source.readInt();
367         }
368         areChannelsBypassingDnd = source.readInt() == 1;
369         if (!Flags.modesUi()) {
370             allowConversations = source.readBoolean();
371             allowConversationsFrom = source.readInt();
372             if (Flags.modesApi()) {
373                 allowPriorityChannels = source.readBoolean();
374             }
375         }
376     }
377 
getDefaultZenPolicy()378     public static ZenPolicy getDefaultZenPolicy() {
379         ZenPolicy policy = new ZenPolicy.Builder()
380                 .allowAlarms(true)
381                 .allowMedia(true)
382                 .allowSystem(false)
383                 .allowCalls(PEOPLE_TYPE_STARRED)
384                 .allowMessages(PEOPLE_TYPE_STARRED)
385                 .allowReminders(false)
386                 .allowEvents(false)
387                 .allowRepeatCallers(true)
388                 .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
389                 .showAllVisualEffects()
390                 .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false)
391                 .showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
392                 .showVisualEffect(VISUAL_EFFECT_PEEK, false)
393                 .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
394                 .allowPriorityChannels(true)
395                 .build();
396         return policy;
397     }
398 
getDefaultConfig()399     public static ZenModeConfig getDefaultConfig() {
400         ZenModeConfig config = new ZenModeConfig();
401 
402         EventInfo eventInfo = new EventInfo();
403         eventInfo.reply = REPLY_YES_OR_MAYBE;
404         ZenRule events = new ZenRule();
405         events.id = EVENTS_DEFAULT_RULE_ID;
406         events.conditionId = toEventConditionId(eventInfo);
407         events.component = ComponentName.unflattenFromString(
408                 "android/com.android.server.notification.EventConditionProvider");
409         events.enabled = false;
410         events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
411         events.pkg = "android";
412         config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events);
413 
414         ScheduleInfo scheduleInfo = new ScheduleInfo();
415         scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7};
416         scheduleInfo.startHour = 22;
417         scheduleInfo.endHour = 7;
418         scheduleInfo.exitAtAlarm = true;
419         ZenRule sleeping = new ZenRule();
420         sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID;
421         sleeping.conditionId = toScheduleConditionId(scheduleInfo);
422         sleeping.component = ComponentName.unflattenFromString(
423                 "android/com.android.server.notification.ScheduleConditionProvider");
424         sleeping.enabled = false;
425         sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
426         sleeping.pkg = "android";
427         config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping);
428 
429         return config;
430     }
431 
ensureManualZenRule()432     void ensureManualZenRule() {
433         if (manualRule == null) {
434             final ZenRule newRule = new ZenRule();
435             newRule.type = AutomaticZenRule.TYPE_OTHER;
436             newRule.enabled = true;
437             newRule.conditionId = Uri.EMPTY;
438             newRule.allowManualInvocation = true;
439             newRule.zenPolicy = getDefaultZenPolicy();
440             newRule.pkg = "android";
441             manualRule = newRule;
442         }
443     }
444 
readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source)445     private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) {
446         final int len = source.readInt();
447         if (len > 0) {
448             final String[] ids = new String[len];
449             final ZenRule[] rules = new ZenRule[len];
450             source.readStringArray(ids);
451             source.readTypedArray(rules, ZenRule.CREATOR);
452             for (int i = 0; i < len; i++) {
453                 ruleMap.put(ids[i], rules[i]);
454             }
455         }
456     }
457 
458     @Override
writeToParcel(Parcel dest, int flags)459     public void writeToParcel(Parcel dest, int flags) {
460         if (!Flags.modesUi()) {
461             dest.writeInt(allowCalls ? 1 : 0);
462             dest.writeInt(allowRepeatCallers ? 1 : 0);
463             dest.writeInt(allowMessages ? 1 : 0);
464             dest.writeInt(allowReminders ? 1 : 0);
465             dest.writeInt(allowEvents ? 1 : 0);
466             dest.writeInt(allowCallsFrom);
467             dest.writeInt(allowMessagesFrom);
468         }
469         dest.writeInt(user);
470         dest.writeParcelable(manualRule, 0);
471         writeRulesToParcel(automaticRules, dest);
472         if (Flags.modesApi()) {
473             writeRulesToParcel(deletedRules, dest);
474         }
475         if (!Flags.modesUi()) {
476             dest.writeInt(allowAlarms ? 1 : 0);
477             dest.writeInt(allowMedia ? 1 : 0);
478             dest.writeInt(allowSystem ? 1 : 0);
479             dest.writeInt(suppressedVisualEffects);
480         }
481         dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
482         if (!Flags.modesUi()) {
483             dest.writeBoolean(allowConversations);
484             dest.writeInt(allowConversationsFrom);
485             if (Flags.modesApi()) {
486                 dest.writeBoolean(allowPriorityChannels);
487             }
488         }
489     }
490 
writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest)491     private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) {
492         if (!ruleMap.isEmpty()) {
493             final int len = ruleMap.size();
494             final String[] ids = new String[len];
495             final ZenRule[] rules = new ZenRule[len];
496             for (int i = 0; i < len; i++) {
497                 ids[i] = ruleMap.keyAt(i);
498                 rules[i] = ruleMap.valueAt(i);
499             }
500             dest.writeInt(len);
501             dest.writeStringArray(ids);
502             dest.writeTypedArray(rules, 0);
503         } else {
504             dest.writeInt(0);
505         }
506     }
507 
508     @Override
toString()509     public String toString() {
510         StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
511                 .append("user=").append(user);
512         if (!Flags.modesUi()) {
513             sb.append(",allowAlarms=").append(allowAlarms)
514                     .append(",allowMedia=").append(allowMedia)
515                     .append(",allowSystem=").append(allowSystem)
516                     .append(",allowReminders=").append(allowReminders)
517                     .append(",allowEvents=").append(allowEvents)
518                     .append(",allowCalls=").append(allowCalls)
519                     .append(",allowRepeatCallers=").append(allowRepeatCallers)
520                     .append(",allowMessages=").append(allowMessages)
521                     .append(",allowConversations=").append(allowConversations)
522                     .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
523                     .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
524                     .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
525                             (allowConversationsFrom))
526                     .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects);
527         }
528         if (Flags.modesApi()) {
529             sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd);
530             sb.append(",allowPriorityChannels=").append(allowPriorityChannels);
531         } else {
532             sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd);
533         }
534         sb.append(",\nautomaticRules=").append(rulesToString(automaticRules));
535         sb.append(",\nmanualRule=").append(manualRule);
536         if (Flags.modesApi()) {
537             sb.append(",\ndeletedRules=").append(rulesToString(deletedRules));
538         }
539         return sb.append(']').toString();
540     }
541 
isAllowPriorityChannels()542     public boolean isAllowPriorityChannels() {
543         if (Flags.modesUi()) {
544             throw new IllegalStateException("can't be used with modesUI flag");
545         }
546         return allowPriorityChannels;
547     }
548 
setAllowPriorityChannels(boolean allowPriorityChannels)549     public void setAllowPriorityChannels(boolean allowPriorityChannels) {
550         if (Flags.modesUi()) {
551             throw new IllegalStateException("can't be used with modesUI flag");
552         } else {
553             this.allowPriorityChannels = allowPriorityChannels;
554         }
555     }
556 
getSuppressedVisualEffects()557     public int getSuppressedVisualEffects() {
558         if (Flags.modesUi()) {
559             throw new IllegalStateException("can't be used with modesUI flag");
560         } else {
561             return this.suppressedVisualEffects;
562         }
563     }
564 
setSuppressedVisualEffects(int suppressedVisualEffects)565     public void setSuppressedVisualEffects(int suppressedVisualEffects) {
566         if (Flags.modesUi()) {
567             throw new IllegalStateException("can't be used with modesUI flag");
568         } else {
569             this.suppressedVisualEffects = suppressedVisualEffects;
570         }
571     }
572 
getAllowConversationsFrom()573     public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() {
574         if (Flags.modesUi()) {
575             return manualRule.zenPolicy.getPriorityConversationSenders();
576         }
577         return allowConversationsFrom;
578     }
579 
setAllowConversationsFrom( @enPolicy.ConversationSenders int allowConversationsFrom)580     public void setAllowConversationsFrom(
581             @ZenPolicy.ConversationSenders int allowConversationsFrom) {
582         if (Flags.modesUi()) {
583             throw new IllegalStateException("can't be used with modesUI flag");
584         } else {
585             this.allowConversationsFrom = allowConversationsFrom;
586         }
587     }
588 
setAllowConversations(boolean allowConversations)589     public void setAllowConversations(boolean allowConversations) {
590         if (Flags.modesUi()) {
591             throw new IllegalStateException("can't be used with modesUI flag");
592         } else {
593             this.allowConversations = allowConversations;
594         }
595     }
596 
isAllowConversations()597     public boolean isAllowConversations() {
598         if (Flags.modesUi()) {
599             throw new IllegalStateException("can't be used with modesUI flag");
600         }
601         return allowConversations;
602     }
603 
getAllowMessagesFrom()604     public @Policy.PrioritySenders int getAllowMessagesFrom() {
605         if (Flags.modesUi()) {
606             throw new IllegalStateException("can't be used with modesUI flag");
607         }
608         return allowMessagesFrom;
609     }
610 
setAllowMessagesFrom(@olicy.PrioritySenders int allowMessagesFrom)611     public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) {
612         if (Flags.modesUi()) {
613             throw new IllegalStateException("can't be used with modesUI flag");
614         } else {
615             this.allowMessagesFrom = allowMessagesFrom;
616         }
617     }
618 
setAllowMessages(boolean allowMessages)619     public void setAllowMessages(boolean allowMessages) {
620         if (Flags.modesUi()) {
621             throw new IllegalStateException("can't be used with modesUI flag");
622         }
623         this.allowMessages = allowMessages;
624     }
625 
getAllowCallsFrom()626     public @Policy.PrioritySenders int getAllowCallsFrom() {
627         if (Flags.modesUi()) {
628             return peopleTypeToPrioritySenders(
629                     manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
630         }
631         return allowCallsFrom;
632     }
633 
setAllowCallsFrom(@olicy.PrioritySenders int allowCallsFrom)634     public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) {
635         if (Flags.modesUi()) {
636             manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy)
637                     .allowCalls(prioritySendersToPeopleType(allowCallsFrom))
638                     .build();
639         } else {
640             this.allowCallsFrom = allowCallsFrom;
641         }
642     }
643 
setAllowCalls(boolean allowCalls)644     public void setAllowCalls(boolean allowCalls) {
645         if (Flags.modesUi()) {
646             throw new IllegalStateException("can't be used with modesUI flag");
647         }
648         this.allowCalls = allowCalls;
649     }
650 
isAllowEvents()651     public boolean isAllowEvents() {
652         if (Flags.modesUi()) {
653             return manualRule.zenPolicy.isCategoryAllowed(
654                     ZenPolicy.PRIORITY_CATEGORY_EVENTS, false);
655         }
656         return allowEvents;
657     }
658 
setAllowEvents(boolean allowEvents)659     public void setAllowEvents(boolean allowEvents) {
660         if (Flags.modesUi()) {
661             throw new IllegalStateException("can't be used with modesUI flag");
662         } else {
663             this.allowEvents = allowEvents;
664         }
665     }
666 
isAllowReminders()667     public boolean isAllowReminders() {
668         if (Flags.modesUi()) {
669             throw new IllegalStateException("can't be used with modesUI flag");
670         }
671         return allowReminders;
672     }
673 
setAllowReminders(boolean allowReminders)674     public void setAllowReminders(boolean allowReminders) {
675         if (Flags.modesUi()) {
676             throw new IllegalStateException("can't be used with modesUI flag");
677         } else {
678             this.allowReminders = allowReminders;
679         }
680     }
681 
isAllowMessages()682     public boolean isAllowMessages() {
683         if (Flags.modesUi()) {
684             throw new IllegalStateException("can't be used with modesUI flag");
685         }
686         return allowMessages;
687     }
688 
isAllowRepeatCallers()689     public boolean isAllowRepeatCallers() {
690         if (Flags.modesUi()) {
691             throw new IllegalStateException("can't be used with modesUI flag");
692         }
693         return allowRepeatCallers;
694     }
695 
setAllowRepeatCallers(boolean allowRepeatCallers)696     public void setAllowRepeatCallers(boolean allowRepeatCallers) {
697         if (Flags.modesUi()) {
698             throw new IllegalStateException("can't be used with modesUI flag");
699         } else {
700             this.allowRepeatCallers = allowRepeatCallers;
701         }
702     }
703 
isAllowSystem()704     public boolean isAllowSystem() {
705         if (Flags.modesUi()) {
706             throw new IllegalStateException("can't be used with modesUI flag");
707         }
708         return allowSystem;
709     }
710 
setAllowSystem(boolean allowSystem)711     public void setAllowSystem(boolean allowSystem) {
712         if (Flags.modesUi()) {
713             throw new IllegalStateException("can't be used with modesUI flag");
714         } else {
715             this.allowSystem = allowSystem;
716         }
717     }
718 
isAllowMedia()719     public boolean isAllowMedia() {
720         if (Flags.modesUi()) {
721             throw new IllegalStateException("can't be used with modesUI flag");
722         }
723         return allowMedia;
724     }
725 
setAllowMedia(boolean allowMedia)726     public void setAllowMedia(boolean allowMedia) {
727         if (Flags.modesUi()) {
728             throw new IllegalStateException("can't be used with modesUI flag");
729         } else {
730             this.allowMedia = allowMedia;
731         }
732     }
733 
isAllowAlarms()734     public boolean isAllowAlarms() {
735         if (Flags.modesUi()) {
736             throw new IllegalStateException("can't be used with modesUI flag");
737         }
738         return allowAlarms;
739     }
740 
setAllowAlarms(boolean allowAlarms)741     public void setAllowAlarms(boolean allowAlarms) {
742         if (Flags.modesUi()) {
743             throw new IllegalStateException("can't be used with modesUI flag");
744         } else {
745             this.allowAlarms = allowAlarms;
746         }
747     }
748 
isAllowCalls()749     public boolean isAllowCalls() {
750         if (Flags.modesUi()) {
751             throw new IllegalStateException("can't be used with modesUI flag");
752         }
753         return allowCalls;
754     }
755 
rulesToString(ArrayMap<String, ZenRule> ruleList)756     private static String rulesToString(ArrayMap<String, ZenRule> ruleList) {
757         if (ruleList.isEmpty()) {
758             return "{}";
759         }
760 
761         StringBuilder buffer = new StringBuilder(ruleList.size() * 28);
762         buffer.append("{\n");
763         for (int i = 0; i < ruleList.size(); i++) {
764             if (i > 0) {
765                 buffer.append(",\n");
766             }
767             Object value = ruleList.valueAt(i);
768             buffer.append(value);
769         }
770         buffer.append('}');
771         return buffer.toString();
772     }
773 
isValid()774     public boolean isValid() {
775         if (!isValidManualRule(manualRule)) return false;
776         final int N = automaticRules.size();
777         for (int i = 0; i < N; i++) {
778             if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
779         }
780         return true;
781     }
782 
isValidManualRule(ZenRule rule)783     private static boolean isValidManualRule(ZenRule rule) {
784         return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
785     }
786 
isValidAutomaticRule(ZenRule rule)787     private static boolean isValidAutomaticRule(ZenRule rule) {
788         return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
789                 && rule.conditionId != null && sameCondition(rule);
790     }
791 
sameCondition(ZenRule rule)792     private static boolean sameCondition(ZenRule rule) {
793         if (rule == null) return false;
794         if (rule.conditionId == null) {
795             return rule.condition == null;
796         } else {
797             return rule.condition == null || rule.conditionId.equals(rule.condition.id);
798         }
799     }
800 
generateMinuteBuckets()801     private static int[] generateMinuteBuckets() {
802         final int maxHrs = 12;
803         final int[] buckets = new int[maxHrs + 3];
804         buckets[0] = 15;
805         buckets[1] = 30;
806         buckets[2] = 45;
807         for (int i = 1; i <= maxHrs; i++) {
808             buckets[2 + i] = 60 * i;
809         }
810         return buckets;
811     }
812 
sourceToString(int source)813     public static String sourceToString(int source) {
814         switch (source) {
815             case SOURCE_ANYONE:
816                 return "anyone";
817             case SOURCE_CONTACT:
818                 return "contacts";
819             case SOURCE_STAR:
820                 return "stars";
821             default:
822                 return "UNKNOWN";
823         }
824     }
825 
826     @Override
equals(@ullable Object o)827     public boolean equals(@Nullable Object o) {
828         if (!(o instanceof ZenModeConfig)) return false;
829         if (o == this) return true;
830         final ZenModeConfig other = (ZenModeConfig) o;
831         // The policy fields that live on config are compared directly because the fields will
832         // contain data until MODES_UI is rolled out/cleaned up.
833         boolean eq = other.allowAlarms == allowAlarms
834                 && other.allowMedia == allowMedia
835                 && other.allowSystem == allowSystem
836                 && other.allowCalls == allowCalls
837                 && other.allowRepeatCallers == allowRepeatCallers
838                 && other.allowMessages == allowMessages
839                 && other.allowCallsFrom == allowCallsFrom
840                 && other.allowMessagesFrom == allowMessagesFrom
841                 && other.allowReminders == allowReminders
842                 && other.allowEvents == allowEvents
843                 && other.user == user
844                 && Objects.equals(other.automaticRules, automaticRules)
845                 && Objects.equals(other.manualRule, manualRule)
846                 && other.suppressedVisualEffects == suppressedVisualEffects
847                 && other.areChannelsBypassingDnd == areChannelsBypassingDnd
848                 && other.allowConversations == allowConversations
849                 && other.allowConversationsFrom == allowConversationsFrom;
850         if (Flags.modesApi()) {
851             return eq
852                     && Objects.equals(other.deletedRules, deletedRules)
853                     && other.allowPriorityChannels == allowPriorityChannels;
854         }
855         return eq;
856     }
857 
858     @Override
hashCode()859     public int hashCode() {
860         // The policy fields that live on config are compared directly because the fields will
861         // contain data until MODES_UI is rolled out/cleaned up.
862         if (Flags.modesApi()) {
863             return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
864                     allowRepeatCallers, allowMessages,
865                     allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
866                     user, automaticRules, manualRule,
867                     suppressedVisualEffects, areChannelsBypassingDnd, allowConversations,
868                     allowConversationsFrom, allowPriorityChannels);
869         }
870         return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
871                 allowRepeatCallers, allowMessages,
872                 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
873                 user, automaticRules, manualRule,
874                 suppressedVisualEffects, areChannelsBypassingDnd, allowConversations,
875                 allowConversationsFrom);
876     }
877 
toDayList(int[] days)878     private static String toDayList(int[] days) {
879         if (days == null || days.length == 0) return "";
880         final StringBuilder sb = new StringBuilder();
881         for (int i = 0; i < days.length; i++) {
882             if (i > 0) sb.append('.');
883             sb.append(days[i]);
884         }
885         return sb.toString();
886     }
887 
tryParseDayList(String dayList, String sep)888     private static int[] tryParseDayList(String dayList, String sep) {
889         if (dayList == null) return null;
890         final String[] tokens = dayList.split(sep);
891         if (tokens.length == 0) return null;
892         final int[] rt = new int[tokens.length];
893         for (int i = 0; i < tokens.length; i++) {
894             final int day = tryParseInt(tokens[i], -1);
895             if (day == -1) return null;
896             rt[i] = day;
897         }
898         return rt;
899     }
900 
tryParseInt(String value, int defValue)901     private static int tryParseInt(String value, int defValue) {
902         if (TextUtils.isEmpty(value)) return defValue;
903         try {
904             return Integer.parseInt(value);
905         } catch (NumberFormatException e) {
906             return defValue;
907         }
908     }
909 
tryParseLong(String value, long defValue)910     private static long tryParseLong(String value, long defValue) {
911         if (TextUtils.isEmpty(value)) return defValue;
912         try {
913             return Long.parseLong(value);
914         } catch (NumberFormatException e) {
915             return defValue;
916         }
917     }
918 
tryParseLong(String value, Long defValue)919     private static Long tryParseLong(String value, Long defValue) {
920         if (TextUtils.isEmpty(value)) return defValue;
921         try {
922             return Long.parseLong(value);
923         } catch (NumberFormatException e) {
924             return defValue;
925         }
926     }
927 
getCurrentXmlVersion()928     public static int getCurrentXmlVersion() {
929         return Flags.modesApi() ? XML_VERSION_MODES_API : XML_VERSION;
930     }
931 
readXml(TypedXmlPullParser parser)932     public static ZenModeConfig readXml(TypedXmlPullParser parser)
933             throws XmlPullParserException, IOException {
934         int type = parser.getEventType();
935         if (type != XmlPullParser.START_TAG) return null;
936         String tag = parser.getName();
937         if (!ZEN_TAG.equals(tag)) return null;
938         final ZenModeConfig rt = new ZenModeConfig();
939         rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion());
940         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
941         boolean readSuppressedEffects = false;
942         boolean readManualRule = false;
943         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
944             tag = parser.getName();
945             if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
946                 if (Flags.modesUi() && !readManualRule) {
947                     // migrate from fields on config into manual rule
948                     rt.manualRule.zenPolicy = rt.toZenPolicy();
949                 }
950                 return rt;
951             }
952             if (type == XmlPullParser.START_TAG) {
953                 if (ALLOW_TAG.equals(tag)) {
954                     rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
955                             DEFAULT_ALLOW_CALLS);
956                     rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
957                             DEFAULT_ALLOW_REPEAT_CALLERS);
958                     rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
959                             DEFAULT_ALLOW_MESSAGES);
960                     rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
961                             DEFAULT_ALLOW_REMINDERS);
962                     rt.allowConversations = safeBoolean(parser, ALLOW_ATT_CONV, DEFAULT_ALLOW_CONV);
963                     rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
964                     final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
965                     final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
966                     final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
967                     rt.allowConversationsFrom = safeInt(parser, ALLOW_ATT_CONV_FROM,
968                             DEFAULT_ALLOW_CONV_FROM);
969                     if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
970                         rt.allowCallsFrom = callsFrom;
971                         rt.allowMessagesFrom = messagesFrom;
972                     } else if (isValidSource(from)) {
973                         Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
974                         rt.allowCallsFrom = from;
975                         rt.allowMessagesFrom = from;
976                     } else {
977                         rt.allowCallsFrom = DEFAULT_CALLS_SOURCE;
978                         rt.allowMessagesFrom = DEFAULT_SOURCE;
979                     }
980                     rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
981                     rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
982                             DEFAULT_ALLOW_MEDIA);
983                     rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
984                     if (Flags.modesApi()) {
985                         rt.allowPriorityChannels = safeBoolean(parser, ALLOW_ATT_CHANNELS,
986                                 DEFAULT_ALLOW_PRIORITY_CHANNELS);
987                     }
988 
989                     // migrate old suppressed visual effects fields, if they still exist in the xml
990                     Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
991                     Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
992                     if (allowWhenScreenOff != null || allowWhenScreenOn != null) {
993                         // If either setting exists, then reset the suppressed visual effects field
994                         // to 0 (all allowed) so that only the relevant bits are disallowed by
995                         // the migrated settings.
996                         readSuppressedEffects = true;
997                         rt.suppressedVisualEffects = 0;
998                     }
999                     if (allowWhenScreenOff != null) {
1000                         if (!allowWhenScreenOff) {
1001                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
1002                                     | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
1003                                     | SUPPRESSED_EFFECT_AMBIENT;
1004                         }
1005                     }
1006                     if (allowWhenScreenOn != null) {
1007                         if (!allowWhenScreenOn) {
1008                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
1009                         }
1010                     }
1011                     if (readSuppressedEffects) {
1012                         Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects);
1013                     }
1014                 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) {
1015                     // only read from suppressed visual effects field if we haven't just migrated
1016                     // the values from allowOn/allowOff, lest we wipe out those settings
1017                     rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
1018                             DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
1019                 } else if (MANUAL_TAG.equals(tag)) {
1020                     rt.manualRule = readRuleXml(parser);
1021                     if (rt.manualRule != null) {
1022                         readManualRule = true;
1023                     }
1024                 } else if (AUTOMATIC_TAG.equals(tag)
1025                         || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
1026                     final String id = parser.getAttributeValue(null, RULE_ATT_ID);
1027                     final ZenRule automaticRule = readRuleXml(parser);
1028                     if (id != null && automaticRule != null) {
1029                         automaticRule.id = id;
1030                         if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
1031                             String deletedRuleKey = deletedRuleKey(automaticRule);
1032                             if (deletedRuleKey != null) {
1033                                 rt.deletedRules.put(deletedRuleKey, automaticRule);
1034                             }
1035                         } else if (AUTOMATIC_TAG.equals(tag)) {
1036                             rt.automaticRules.put(id, automaticRule);
1037                         }
1038                     }
1039                 } else if (STATE_TAG.equals(tag)) {
1040                     rt.areChannelsBypassingDnd = safeBoolean(parser,
1041                             STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
1042                 }
1043             }
1044         }
1045         throw new IllegalStateException("Failed to reach END_DOCUMENT");
1046     }
1047 
1048     /** Generates the map key used for a {@link ZenRule} in {@link #deletedRules}. */
1049     @Nullable
deletedRuleKey(ZenRule rule)1050     public static String deletedRuleKey(ZenRule rule) {
1051         if (rule.pkg != null && rule.conditionId != null) {
1052             return rule.pkg + "|" + rule.conditionId.toString();
1053         } else {
1054             return null;
1055         }
1056     }
1057 
1058     /**
1059      * Writes XML of current ZenModeConfig
1060      * @param out serializer
1061      * @param version uses the current XML version if version is null
1062      * @throws IOException
1063      */
1064 
writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)1065     public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)
1066             throws IOException {
1067         int xmlVersion = getCurrentXmlVersion();
1068         out.startTag(null, ZEN_TAG);
1069         out.attribute(null, ZEN_ATT_VERSION, version == null
1070                 ? Integer.toString(xmlVersion) : Integer.toString(version));
1071         out.attributeInt(null, ZEN_ATT_USER, user);
1072         out.startTag(null, ALLOW_TAG);
1073         // From MODES_UI these fields are only read if the flag has transitioned from off to on
1074         // However, we will continue to write these fields until the flag is cleaned up so it's
1075         // possible to turn the flag off without losing user data
1076         out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls);
1077         out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers);
1078         out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages);
1079         out.attributeBoolean(null, ALLOW_ATT_REMINDERS, allowReminders);
1080         out.attributeBoolean(null, ALLOW_ATT_EVENTS, allowEvents);
1081         out.attributeInt(null, ALLOW_ATT_CALLS_FROM, allowCallsFrom);
1082         out.attributeInt(null, ALLOW_ATT_MESSAGES_FROM, allowMessagesFrom);
1083         out.attributeBoolean(null, ALLOW_ATT_ALARMS, allowAlarms);
1084         out.attributeBoolean(null, ALLOW_ATT_MEDIA, allowMedia);
1085         out.attributeBoolean(null, ALLOW_ATT_SYSTEM, allowSystem);
1086         out.attributeBoolean(null, ALLOW_ATT_CONV, allowConversations);
1087         out.attributeInt(null, ALLOW_ATT_CONV_FROM, allowConversationsFrom);
1088         if (Flags.modesApi()) {
1089             out.attributeBoolean(null, ALLOW_ATT_CHANNELS, allowPriorityChannels);
1090         }
1091         out.endTag(null, ALLOW_TAG);
1092 
1093         out.startTag(null, DISALLOW_TAG);
1094         out.attributeInt(null, DISALLOW_ATT_VISUAL_EFFECTS, suppressedVisualEffects);
1095         out.endTag(null, DISALLOW_TAG);
1096 
1097         if (manualRule != null) {
1098             out.startTag(null, MANUAL_TAG);
1099             writeRuleXml(manualRule, out);
1100             out.endTag(null, MANUAL_TAG);
1101         }
1102         final int N = automaticRules.size();
1103         for (int i = 0; i < N; i++) {
1104             final String id = automaticRules.keyAt(i);
1105             final ZenRule automaticRule = automaticRules.valueAt(i);
1106             out.startTag(null, AUTOMATIC_TAG);
1107             out.attribute(null, RULE_ATT_ID, id);
1108             writeRuleXml(automaticRule, out);
1109             out.endTag(null, AUTOMATIC_TAG);
1110         }
1111         if (Flags.modesApi() && !forBackup) {
1112             for (int i = 0; i < deletedRules.size(); i++) {
1113                 final ZenRule deletedRule = deletedRules.valueAt(i);
1114                 out.startTag(null, AUTOMATIC_DELETED_TAG);
1115                 out.attribute(null, RULE_ATT_ID, deletedRule.id);
1116                 writeRuleXml(deletedRule, out);
1117                 out.endTag(null, AUTOMATIC_DELETED_TAG);
1118             }
1119         }
1120 
1121         out.startTag(null, STATE_TAG);
1122         out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd);
1123         out.endTag(null, STATE_TAG);
1124 
1125         out.endTag(null, ZEN_TAG);
1126     }
1127 
readRuleXml(TypedXmlPullParser parser)1128     public static ZenRule readRuleXml(TypedXmlPullParser parser) {
1129         final ZenRule rt = new ZenRule();
1130         rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
1131         rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
1132         final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
1133         rt.zenMode = tryParseZenMode(zen, -1);
1134         if (rt.zenMode == -1) {
1135             Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
1136             return null;
1137         }
1138         rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
1139         rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
1140         rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
1141         rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG);
1142         if (rt.pkg == null) {
1143             // backfill from component, if present. configActivity is not safe to backfill from
1144             rt.pkg = rt.component != null ? rt.component.getPackageName() : null;
1145         }
1146         rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
1147         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
1148         rt.condition = readConditionXml(parser);
1149 
1150         if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS
1151                 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
1152             // all default rules and user created rules updated to zenMode important interruptions
1153             Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
1154             rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
1155         }
1156         rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
1157         rt.zenPolicy = readZenPolicyXml(parser);
1158         if (Flags.modesApi()) {
1159             rt.zenDeviceEffects = readZenDeviceEffectsXml(parser);
1160             rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false);
1161             rt.iconResName = parser.getAttributeValue(null, RULE_ATT_ICON);
1162             rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
1163             rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN);
1164             rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0);
1165             rt.zenPolicyUserModifiedFields = safeInt(parser, POLICY_USER_MODIFIED_FIELDS, 0);
1166             rt.zenDeviceEffectsUserModifiedFields = safeInt(parser,
1167                     DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0);
1168             Long deletionInstant = tryParseLong(
1169                     parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null);
1170             if (deletionInstant != null) {
1171                 rt.deletionInstant = Instant.ofEpochMilli(deletionInstant);
1172             }
1173         }
1174         return rt;
1175     }
1176 
writeRuleXml(ZenRule rule, TypedXmlSerializer out)1177     public static void writeRuleXml(ZenRule rule, TypedXmlSerializer out) throws IOException {
1178         out.attributeBoolean(null, RULE_ATT_ENABLED, rule.enabled);
1179         if (rule.name != null) {
1180             out.attribute(null, RULE_ATT_NAME, rule.name);
1181         }
1182         out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode);
1183         if (rule.pkg != null) {
1184             out.attribute(null, RULE_ATT_PKG, rule.pkg);
1185         }
1186         if (rule.component != null) {
1187             out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
1188         }
1189         if (rule.configurationActivity != null) {
1190             out.attribute(null, RULE_ATT_CONFIG_ACTIVITY,
1191                     rule.configurationActivity.flattenToString());
1192         }
1193         if (rule.conditionId != null) {
1194             out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
1195         }
1196         out.attributeLong(null, RULE_ATT_CREATION_TIME, rule.creationTime);
1197         if (rule.enabler != null) {
1198             out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
1199         }
1200         if (rule.condition != null) {
1201             writeConditionXml(rule.condition, out);
1202         }
1203         if (rule.zenPolicy != null) {
1204             writeZenPolicyXml(rule.zenPolicy, out);
1205         }
1206         if (Flags.modesApi() && rule.zenDeviceEffects != null) {
1207             writeZenDeviceEffectsXml(rule.zenDeviceEffects, out);
1208         }
1209         out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified);
1210         if (Flags.modesApi()) {
1211             out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation);
1212             if (rule.iconResName != null) {
1213                 out.attribute(null, RULE_ATT_ICON, rule.iconResName);
1214             }
1215             if (rule.triggerDescription != null) {
1216                 out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription);
1217             }
1218             out.attributeInt(null, RULE_ATT_TYPE, rule.type);
1219             out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields);
1220             out.attributeInt(null, POLICY_USER_MODIFIED_FIELDS, rule.zenPolicyUserModifiedFields);
1221             out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS,
1222                     rule.zenDeviceEffectsUserModifiedFields);
1223             if (rule.deletionInstant != null) {
1224                 out.attributeLong(null, RULE_ATT_DELETION_INSTANT,
1225                         rule.deletionInstant.toEpochMilli());
1226             }
1227         }
1228     }
1229 
readConditionXml(TypedXmlPullParser parser)1230     public static Condition readConditionXml(TypedXmlPullParser parser) {
1231         final Uri id = safeUri(parser, CONDITION_ATT_ID);
1232         if (id == null) return null;
1233         final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
1234         final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
1235         final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
1236         final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
1237         final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
1238         final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
1239         try {
1240             if (Flags.modesApi()) {
1241                 final int source = safeInt(parser, CONDITION_ATT_SOURCE, Condition.SOURCE_UNKNOWN);
1242                 return new Condition(id, summary, line1, line2, icon, state, source, flags);
1243             } else {
1244                 return new Condition(id, summary, line1, line2, icon, state, flags);
1245             }
1246         } catch (IllegalArgumentException e) {
1247             Slog.w(TAG, "Unable to read condition xml", e);
1248             return null;
1249         }
1250     }
1251 
writeConditionXml(Condition c, TypedXmlSerializer out)1252     public static void writeConditionXml(Condition c, TypedXmlSerializer out) throws IOException {
1253         out.attribute(null, CONDITION_ATT_ID, c.id.toString());
1254         out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
1255         out.attribute(null, CONDITION_ATT_LINE1, c.line1);
1256         out.attribute(null, CONDITION_ATT_LINE2, c.line2);
1257         out.attributeInt(null, CONDITION_ATT_ICON, c.icon);
1258         out.attributeInt(null, CONDITION_ATT_STATE, c.state);
1259         if (Flags.modesApi()) {
1260             out.attributeInt(null, CONDITION_ATT_SOURCE, c.source);
1261         }
1262         out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags);
1263     }
1264 
1265     /**
1266      * Read the zen policy from xml
1267      * Returns null if no zen policy exists
1268      */
readZenPolicyXml(TypedXmlPullParser parser)1269     public static ZenPolicy readZenPolicyXml(TypedXmlPullParser parser) {
1270         boolean policySet = false;
1271 
1272         ZenPolicy.Builder builder = new ZenPolicy.Builder();
1273         final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
1274         final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
1275         final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET);
1276         final int conversations = safeInt(parser, ALLOW_ATT_CONV_FROM,
1277                 ZenPolicy.CONVERSATION_SENDERS_UNSET);
1278         final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET);
1279         final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET);
1280         final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET);
1281         final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET);
1282         final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET);
1283         if (Flags.modesApi()) {
1284             final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET);
1285             if (channels != ZenPolicy.STATE_UNSET) {
1286                 builder.allowPriorityChannels(channels == STATE_ALLOW);
1287                 policySet = true;
1288             }
1289         }
1290 
1291         if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
1292             builder.allowCalls(calls);
1293             policySet = true;
1294         }
1295         if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) {
1296             builder.allowMessages(messages);
1297             policySet = true;
1298         }
1299         if (repeatCallers != ZenPolicy.STATE_UNSET) {
1300             builder.allowRepeatCallers(repeatCallers == STATE_ALLOW);
1301             policySet = true;
1302         }
1303         if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) {
1304             builder.allowConversations(conversations);
1305             policySet = true;
1306         }
1307         if (alarms != ZenPolicy.STATE_UNSET) {
1308             builder.allowAlarms(alarms == STATE_ALLOW);
1309             policySet = true;
1310         }
1311         if (media != ZenPolicy.STATE_UNSET) {
1312             builder.allowMedia(media == STATE_ALLOW);
1313             policySet = true;
1314         }
1315         if (system != ZenPolicy.STATE_UNSET) {
1316             builder.allowSystem(system == STATE_ALLOW);
1317             policySet = true;
1318         }
1319         if (events != ZenPolicy.STATE_UNSET) {
1320             builder.allowEvents(events == STATE_ALLOW);
1321             policySet = true;
1322         }
1323         if (reminders != ZenPolicy.STATE_UNSET) {
1324             builder.allowReminders(reminders == STATE_ALLOW);
1325             policySet = true;
1326         }
1327 
1328         final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT,
1329                 ZenPolicy.STATE_UNSET);
1330         final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET);
1331         final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET);
1332         final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET);
1333         final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET);
1334         final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET);
1335         final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST,
1336                 ZenPolicy.STATE_UNSET);
1337 
1338         if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
1339             builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW);
1340             policySet = true;
1341         }
1342         if (lights != ZenPolicy.STATE_UNSET) {
1343             builder.showLights(lights == STATE_ALLOW);
1344             policySet = true;
1345         }
1346         if (peek != ZenPolicy.STATE_UNSET) {
1347             builder.showPeeking(peek == STATE_ALLOW);
1348             policySet = true;
1349         }
1350         if (statusBar != ZenPolicy.STATE_UNSET) {
1351             builder.showStatusBarIcons(statusBar == STATE_ALLOW);
1352             policySet = true;
1353         }
1354         if (badges != ZenPolicy.STATE_UNSET) {
1355             builder.showBadges(badges == STATE_ALLOW);
1356             policySet = true;
1357         }
1358         if (ambient != ZenPolicy.STATE_UNSET) {
1359             builder.showInAmbientDisplay(ambient == STATE_ALLOW);
1360             policySet = true;
1361         }
1362         if (notificationList != ZenPolicy.STATE_UNSET) {
1363             builder.showInNotificationList(notificationList == STATE_ALLOW);
1364             policySet = true;
1365         }
1366 
1367         if (policySet) {
1368             return builder.build();
1369         }
1370         return null;
1371     }
1372 
1373     /**
1374      * Writes zen policy to xml
1375      */
writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out)1376     public static void writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out)
1377             throws IOException {
1378         writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
1379         writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
1380         writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(),
1381                 out);
1382         writeZenPolicyState(ALLOW_ATT_CONV_FROM, policy.getPriorityConversationSenders(), out);
1383         writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out);
1384         writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out);
1385         writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out);
1386         writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out);
1387         writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out);
1388 
1389         writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(),
1390                 out);
1391         writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out);
1392         writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out);
1393         writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out);
1394         writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out);
1395         writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out);
1396         writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(),
1397                 out);
1398 
1399         if (Flags.modesApi()) {
1400             writeZenPolicyState(ALLOW_ATT_CHANNELS, policy.getPriorityChannelsAllowed(), out);
1401         }
1402     }
1403 
writeZenPolicyState(String attr, int val, TypedXmlSerializer out)1404     private static void writeZenPolicyState(String attr, int val, TypedXmlSerializer out)
1405             throws IOException {
1406         if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
1407                 || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
1408             if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
1409                 out.attributeInt(null, attr, val);
1410             }
1411         } else if (Objects.equals(attr, ALLOW_ATT_CONV_FROM)) {
1412             if (val != ZenPolicy.CONVERSATION_SENDERS_UNSET) {
1413                 out.attributeInt(null, attr, val);
1414             }
1415         } else if (Flags.modesApi() && Objects.equals(attr, ALLOW_ATT_CHANNELS)) {
1416             if (val != ZenPolicy.STATE_UNSET) {
1417                 out.attributeInt(null, attr, val);
1418             }
1419         } else {
1420             if (val != ZenPolicy.STATE_UNSET) {
1421                 out.attributeInt(null, attr, val);
1422             }
1423         }
1424     }
1425 
1426     @FlaggedApi(Flags.FLAG_MODES_API)
1427     @Nullable
readZenDeviceEffectsXml(TypedXmlPullParser parser)1428     private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) {
1429         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
1430                 .setShouldDisplayGrayscale(
1431                         safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
1432                 .setShouldSuppressAmbientDisplay(
1433                         safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
1434                 .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
1435                 .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
1436                 .setShouldDisableAutoBrightness(
1437                         safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
1438                 .setShouldDisableTapToWake(
1439                         safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
1440                 .setShouldDisableTiltToWake(
1441                         safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
1442                 .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
1443                 .setShouldMinimizeRadioUsage(
1444                         safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
1445                 .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
1446                 .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS))
1447                 .build();
1448 
1449         return deviceEffects.hasEffects() ? deviceEffects : null;
1450     }
1451 
1452     @FlaggedApi(Flags.FLAG_MODES_API)
writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects, TypedXmlSerializer out)1453     private static void writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects,
1454             TypedXmlSerializer out) throws IOException {
1455         writeBooleanIfTrue(out, DEVICE_EFFECT_DISPLAY_GRAYSCALE,
1456                 deviceEffects.shouldDisplayGrayscale());
1457         writeBooleanIfTrue(out, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY,
1458                 deviceEffects.shouldSuppressAmbientDisplay());
1459         writeBooleanIfTrue(out, DEVICE_EFFECT_DIM_WALLPAPER, deviceEffects.shouldDimWallpaper());
1460         writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_MODE, deviceEffects.shouldUseNightMode());
1461         writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS,
1462                 deviceEffects.shouldDisableAutoBrightness());
1463         writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE,
1464                 deviceEffects.shouldDisableTapToWake());
1465         writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE,
1466                 deviceEffects.shouldDisableTiltToWake());
1467         writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TOUCH, deviceEffects.shouldDisableTouch());
1468         writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
1469                 deviceEffects.shouldMinimizeRadioUsage());
1470         writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
1471         writeStringSet(out, DEVICE_EFFECT_EXTRAS, deviceEffects.getExtraEffects());
1472     }
1473 
writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)1474     private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)
1475             throws IOException {
1476         if (value) {
1477             out.attributeBoolean(null, att, true);
1478         }
1479     }
1480 
writeStringSet(TypedXmlSerializer out, String att, Set<String> values)1481     private static void writeStringSet(TypedXmlSerializer out, String att, Set<String> values)
1482             throws IOException {
1483         if (values.isEmpty()) {
1484             return;
1485         }
1486         // We escape each item  by replacing "\" by "\\" and "," by "\,". Then we concatenate with
1487         // "," as separator. Reading performs the same operations in the opposite order.
1488         List<String> escapedItems = new ArrayList<>();
1489         for (String item : values) {
1490             escapedItems.add(
1491                     item
1492                             .replace(ITEM_SEPARATOR_ESCAPE,
1493                                     ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR_ESCAPE)
1494                             .replace(ITEM_SEPARATOR,
1495                                     ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR));
1496         }
1497         String serialized = String.join(ITEM_SEPARATOR, escapedItems);
1498         out.attribute(null, att, serialized);
1499     }
1500 
isValidHour(int val)1501     public static boolean isValidHour(int val) {
1502         return val >= 0 && val < 24;
1503     }
1504 
isValidMinute(int val)1505     public static boolean isValidMinute(int val) {
1506         return val >= 0 && val < 60;
1507     }
1508 
isValidSource(int source)1509     private static boolean isValidSource(int source) {
1510         return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
1511     }
1512 
unsafeBoolean(TypedXmlPullParser parser, String att)1513     private static Boolean unsafeBoolean(TypedXmlPullParser parser, String att) {
1514         try {
1515             return parser.getAttributeBoolean(null, att);
1516         } catch (Exception e) {
1517             return null;
1518         }
1519     }
1520 
safeBoolean(TypedXmlPullParser parser, String att, boolean defValue)1521     private static boolean safeBoolean(TypedXmlPullParser parser, String att, boolean defValue) {
1522         return parser.getAttributeBoolean(null, att, defValue);
1523     }
1524 
safeBoolean(String val, boolean defValue)1525     private static boolean safeBoolean(String val, boolean defValue) {
1526         if (TextUtils.isEmpty(val)) return defValue;
1527         return Boolean.parseBoolean(val);
1528     }
1529 
safeInt(TypedXmlPullParser parser, String att, int defValue)1530     private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
1531         return parser.getAttributeInt(null, att, defValue);
1532     }
1533 
safeComponentName(TypedXmlPullParser parser, String att)1534     private static ComponentName safeComponentName(TypedXmlPullParser parser, String att) {
1535         final String val = parser.getAttributeValue(null, att);
1536         if (TextUtils.isEmpty(val)) return null;
1537         return ComponentName.unflattenFromString(val);
1538     }
1539 
safeUri(TypedXmlPullParser parser, String att)1540     private static Uri safeUri(TypedXmlPullParser parser, String att) {
1541         final String val = parser.getAttributeValue(null, att);
1542         if (val == null) return null;
1543         return Uri.parse(val);
1544     }
1545 
safeLong(TypedXmlPullParser parser, String att, long defValue)1546     private static long safeLong(TypedXmlPullParser parser, String att, long defValue) {
1547         final String val = parser.getAttributeValue(null, att);
1548         return tryParseLong(val, defValue);
1549     }
1550 
1551     @NonNull
safeStringSet(TypedXmlPullParser parser, String att)1552     private static Set<String> safeStringSet(TypedXmlPullParser parser, String att) {
1553         Set<String> values = new HashSet<>();
1554 
1555         String serialized = parser.getAttributeValue(null, att);
1556         if (!TextUtils.isEmpty(serialized)) {
1557             // We split on every "," that is *not preceded* by the escape character "\".
1558             // Then we reverse the escaping done on each individual item.
1559             String[] escapedItems = ITEM_SPLITTER_REGEX.split(serialized);
1560             for (String escapedItem : escapedItems) {
1561                 values.add(escapedItem
1562                         .replace(ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR_ESCAPE,
1563                                 ITEM_SEPARATOR_ESCAPE)
1564                         .replace(ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR,
1565                                 ITEM_SEPARATOR));
1566             }
1567         }
1568         return values;
1569     }
1570 
1571     @Override
describeContents()1572     public int describeContents() {
1573         return 0;
1574     }
1575 
copy()1576     public ZenModeConfig copy() {
1577         final Parcel parcel = Parcel.obtain();
1578         try {
1579             writeToParcel(parcel, 0);
1580             parcel.setDataPosition(0);
1581             return new ZenModeConfig(parcel);
1582         } finally {
1583             parcel.recycle();
1584         }
1585     }
1586 
1587     public static final @android.annotation.NonNull Parcelable.Creator<ZenModeConfig> CREATOR
1588             = new Parcelable.Creator<ZenModeConfig>() {
1589         @Override
1590         public ZenModeConfig createFromParcel(Parcel source) {
1591             return new ZenModeConfig(source);
1592         }
1593 
1594         @Override
1595         public ZenModeConfig[] newArray(int size) {
1596             return new ZenModeConfig[size];
1597         }
1598     };
1599 
getZenPolicy()1600     public ZenPolicy getZenPolicy() {
1601         return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy();
1602     }
1603 
1604     /**
1605      * Converts a ZenModeConfig to a ZenPolicy
1606      */
1607     @VisibleForTesting
toZenPolicy()1608     ZenPolicy toZenPolicy() {
1609         ZenPolicy.Builder builder = new ZenPolicy.Builder()
1610                 .allowCalls(allowCalls
1611                         ? prioritySendersToPeopleType(allowCallsFrom)
1612                         : ZenPolicy.PEOPLE_TYPE_NONE)
1613                 .allowRepeatCallers(allowRepeatCallers)
1614                 .allowMessages(allowMessages
1615                         ? prioritySendersToPeopleType(allowMessagesFrom)
1616                         : ZenPolicy.PEOPLE_TYPE_NONE)
1617                 .allowReminders(allowReminders)
1618                 .allowEvents(allowEvents)
1619                 .allowAlarms(allowAlarms)
1620                 .allowMedia(allowMedia)
1621                 .allowSystem(allowSystem)
1622                 .allowConversations(allowConversations ? allowConversationsFrom
1623                         : ZenPolicy.CONVERSATION_SENDERS_NONE);
1624         if (suppressedVisualEffects == 0) {
1625             builder.showAllVisualEffects();
1626         } else {
1627             // configs don't have an unset state: wither true or false.
1628             builder.showFullScreenIntent(
1629                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0);
1630             builder.showLights(
1631                     (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0);
1632             builder.showPeeking(
1633                     (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0);
1634             builder.showStatusBarIcons(
1635                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0);
1636             builder.showBadges(
1637                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
1638             builder.showInAmbientDisplay(
1639                     (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0);
1640             builder.showInNotificationList(
1641                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
1642         }
1643 
1644         if (Flags.modesApi()) {
1645             builder.allowPriorityChannels(allowPriorityChannels);
1646         }
1647         return builder.build();
1648     }
1649 
1650     /**
1651      * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
1652      * defaults for all unset values in zenPolicy
1653      */
toNotificationPolicy(ZenPolicy zenPolicy)1654     public Policy toNotificationPolicy(ZenPolicy zenPolicy) {
1655         NotificationManager.Policy defaultPolicy = toNotificationPolicy();
1656         int priorityCategories = 0;
1657         int suppressedVisualEffects = 0;
1658         int callSenders = defaultPolicy.priorityCallSenders;
1659         int messageSenders = defaultPolicy.priorityMessageSenders;
1660         int conversationSenders = defaultPolicy.priorityConversationSenders;
1661 
1662         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
1663                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
1664             priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
1665         }
1666 
1667         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
1668                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) {
1669             priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
1670         }
1671 
1672         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
1673                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
1674             priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
1675             messageSenders = peopleTypeToPrioritySenders(
1676                     zenPolicy.getPriorityMessageSenders(), messageSenders);
1677         }
1678 
1679         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS,
1680                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) {
1681             priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
1682             conversationSenders = zenPolicyConversationSendersToNotificationPolicy(
1683                     zenPolicy.getPriorityConversationSenders(), conversationSenders);
1684         } else {
1685             conversationSenders = CONVERSATION_SENDERS_NONE;
1686         }
1687 
1688         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
1689                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
1690             priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
1691             callSenders = peopleTypeToPrioritySenders(
1692                     zenPolicy.getPriorityCallSenders(), callSenders);
1693         }
1694 
1695         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
1696                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS,
1697                         defaultPolicy))) {
1698             priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
1699         }
1700 
1701         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS,
1702                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) {
1703             priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
1704         }
1705 
1706         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA,
1707                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) {
1708             priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
1709         }
1710 
1711         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM,
1712                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) {
1713             priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
1714         }
1715 
1716         boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed(
1717                 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
1718                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
1719                         defaultPolicy));
1720 
1721         boolean suppressLights = !zenPolicy.isVisualEffectAllowed(
1722                 ZenPolicy.VISUAL_EFFECT_LIGHTS,
1723                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
1724                         defaultPolicy));
1725 
1726         boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
1727                 ZenPolicy.VISUAL_EFFECT_AMBIENT,
1728                 isVisualEffectAllowed(SUPPRESSED_EFFECT_AMBIENT,
1729                         defaultPolicy));
1730 
1731         if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
1732             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
1733         }
1734 
1735         if (suppressFullScreenIntent) {
1736             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
1737         }
1738 
1739         if (suppressLights) {
1740             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
1741         }
1742 
1743         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
1744                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
1745                         defaultPolicy))) {
1746             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
1747             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
1748         }
1749 
1750         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
1751                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR,
1752                         defaultPolicy))) {
1753             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
1754         }
1755 
1756         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
1757                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE,
1758                         defaultPolicy))) {
1759             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
1760         }
1761 
1762         if (suppressAmbient) {
1763             suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
1764         }
1765 
1766         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
1767                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST,
1768                         defaultPolicy))) {
1769             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
1770         }
1771 
1772         int state = defaultPolicy.state;
1773         if (Flags.modesApi()) {
1774             state = Policy.policyState(defaultPolicy.hasPriorityChannels(),
1775                     ZenPolicy.stateToBoolean(zenPolicy.getPriorityChannelsAllowed(),
1776                             DEFAULT_ALLOW_PRIORITY_CHANNELS));
1777         }
1778 
1779         return new NotificationManager.Policy(priorityCategories, callSenders,
1780                 messageSenders, suppressedVisualEffects, state, conversationSenders);
1781     }
1782 
isPriorityCategoryEnabled(int categoryType, Policy policy)1783     private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
1784         return (policy.priorityCategories & categoryType) != 0;
1785     }
1786 
isVisualEffectAllowed(int visualEffect, Policy policy)1787     private boolean isVisualEffectAllowed(int visualEffect, Policy policy) {
1788         return (policy.suppressedVisualEffects & visualEffect) == 0;
1789     }
1790 
isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect)1791     private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) {
1792         return (suppressedVisualEffects & visualEffect) == 0;
1793     }
1794 
toNotificationPolicy()1795     public Policy toNotificationPolicy() {
1796         int priorityCategories = 0;
1797         int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
1798         int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
1799         int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT;
1800         int state = 0;
1801         int suppressedVisualEffects = 0;
1802 
1803         if (Flags.modesUi()) {
1804             if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) {
1805                 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
1806             }
1807             if (manualRule.zenPolicy.isCategoryAllowed(
1808                     ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) {
1809                 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
1810             }
1811             if (manualRule.zenPolicy.isCategoryAllowed(
1812                     ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) {
1813                 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
1814             }
1815             if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) {
1816                 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
1817             }
1818             if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) {
1819                 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
1820             }
1821             if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) {
1822                 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
1823             }
1824 
1825             if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) {
1826                 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
1827             }
1828             priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
1829                     manualRule.zenPolicy.getPriorityConversationSenders(),
1830                     CONVERSATION_SENDERS_NONE);
1831             if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) {
1832                 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
1833             }
1834             priorityCallSenders = peopleTypeToPrioritySenders(
1835                     manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
1836             if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) {
1837                 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
1838             }
1839             priorityMessageSenders = peopleTypeToPrioritySenders(
1840                     manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE);
1841 
1842             state = Policy.policyState(areChannelsBypassingDnd,
1843                     manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW);
1844 
1845             boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed(
1846                     ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
1847                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1848                             ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT));
1849 
1850             boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed(
1851                     ZenPolicy.VISUAL_EFFECT_LIGHTS,
1852                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1853                             ZenPolicy.VISUAL_EFFECT_LIGHTS));
1854 
1855             boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed(
1856                     ZenPolicy.VISUAL_EFFECT_AMBIENT,
1857                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1858                             ZenPolicy.VISUAL_EFFECT_AMBIENT));
1859 
1860             if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
1861                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
1862             }
1863 
1864             if (suppressFullScreenIntent) {
1865                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
1866             }
1867 
1868             if (suppressLights) {
1869                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
1870             }
1871 
1872             if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
1873                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1874                             ZenPolicy.VISUAL_EFFECT_PEEK))) {
1875                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
1876                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
1877             }
1878 
1879             if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
1880                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1881                             ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) {
1882                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
1883             }
1884 
1885             if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
1886                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1887                             ZenPolicy.VISUAL_EFFECT_BADGE))) {
1888                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
1889             }
1890 
1891             if (suppressAmbient) {
1892                 suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
1893             }
1894 
1895             if (!manualRule.zenPolicy.isVisualEffectAllowed(
1896                     ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
1897                     isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
1898                             ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) {
1899                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
1900             }
1901         } else {
1902             if (isAllowConversations()) {
1903                 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
1904             }
1905             if (isAllowCalls()) {
1906                 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
1907             }
1908             if (isAllowMessages()) {
1909                 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
1910             }
1911             if (isAllowEvents()) {
1912                 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
1913             }
1914             if (isAllowReminders()) {
1915                 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
1916             }
1917             if (isAllowRepeatCallers()) {
1918                 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
1919             }
1920             if (isAllowAlarms()) {
1921                 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
1922             }
1923             if (isAllowMedia()) {
1924                 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
1925             }
1926             if (isAllowSystem()) {
1927                 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
1928             }
1929             priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders);
1930             priorityMessageSenders = sourceToPrioritySenders(
1931                     getAllowMessagesFrom(), priorityMessageSenders);
1932             priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
1933                     getAllowConversationsFrom(), priorityConversationSenders);
1934 
1935             state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
1936             if (Flags.modesApi()) {
1937                 state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels);
1938             }
1939             suppressedVisualEffects = getSuppressedVisualEffects();
1940         }
1941 
1942         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
1943                 suppressedVisualEffects, state, priorityConversationSenders);
1944     }
1945 
1946     /**
1947      * Creates scheduleCalendar from a condition id
1948      * @param conditionId
1949      * @return ScheduleCalendar with info populated with conditionId
1950      */
toScheduleCalendar(Uri conditionId)1951     public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
1952         final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
1953         if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
1954         final ScheduleCalendar sc = new ScheduleCalendar();
1955         sc.setSchedule(schedule);
1956         sc.setTimeZone(TimeZone.getDefault());
1957         return sc;
1958     }
1959 
sourceToPrioritySenders(int source, int def)1960     private static int sourceToPrioritySenders(int source, int def) {
1961         switch (source) {
1962             case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
1963             case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
1964             case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
1965             default: return def;
1966         }
1967     }
1968 
normalizePrioritySenders(int prioritySenders, int def)1969     private static int normalizePrioritySenders(int prioritySenders, int def) {
1970         if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS
1971                 || prioritySenders == Policy.PRIORITY_SENDERS_STARRED
1972                 || prioritySenders == Policy.PRIORITY_SENDERS_ANY)) {
1973             return def;
1974         }
1975         return prioritySenders;
1976     }
1977 
normalizeConversationSenders(boolean allowed, int senders, int def)1978     private static int normalizeConversationSenders(boolean allowed, int senders, int def) {
1979         if (!allowed) {
1980             return CONVERSATION_SENDERS_NONE;
1981         }
1982         if (!(senders == CONVERSATION_SENDERS_ANYONE
1983                 || senders == CONVERSATION_SENDERS_IMPORTANT
1984                 || senders == CONVERSATION_SENDERS_NONE)) {
1985             return def;
1986         }
1987         return senders;
1988     }
1989 
applyNotificationPolicy(Policy policy)1990     public void applyNotificationPolicy(Policy policy) {
1991         if (policy == null) return;
1992         if (Flags.modesUi()) {
1993             manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
1994         } else {
1995             setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0);
1996             allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
1997             allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
1998             allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1999             allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
2000             allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
2001             allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
2002             allowRepeatCallers =
2003                     (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
2004                             != 0;
2005             allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
2006             allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
2007                     allowMessagesFrom);
2008             if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
2009                 suppressedVisualEffects = policy.suppressedVisualEffects;
2010             }
2011             allowConversations = (policy.priorityCategories
2012                     & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
2013             allowConversationsFrom = normalizeConversationSenders(allowConversations,
2014                     policy.priorityConversationSenders,
2015                     allowConversationsFrom);
2016             if (policy.state != Policy.STATE_UNSET) {
2017                 if (Flags.modesApi()) {
2018                     setAllowPriorityChannels(policy.allowPriorityChannels());
2019                 }
2020             }
2021         }
2022         if (policy.state != Policy.STATE_UNSET) {
2023             areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
2024         }
2025     }
2026 
toTimeCondition(Context context, int minutesFromNow, int userHandle)2027     public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
2028         return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
2029     }
2030 
toTimeCondition(Context context, int minutesFromNow, int userHandle, boolean shortVersion)2031     public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
2032             boolean shortVersion) {
2033         final long now = System.currentTimeMillis();
2034         final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
2035         return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
2036     }
2037 
toTimeCondition(Context context, long time, int minutes, int userHandle, boolean shortVersion)2038     public static Condition toTimeCondition(Context context, long time, int minutes,
2039             int userHandle, boolean shortVersion) {
2040         final int num;
2041         String summary, line1, line2;
2042         final CharSequence formattedTime =
2043                 getFormattedTime(context, time, isToday(time), userHandle);
2044         final Resources res = context.getResources();
2045         final Map<String, Object> arguments = new HashMap<>();
2046         if (minutes < 60) {
2047             // display as minutes
2048             num = minutes;
2049             int summaryResId = shortVersion ? R.string.zen_mode_duration_minutes_summary_short
2050                     : R.string.zen_mode_duration_minutes_summary;
2051             arguments.put("count", num);
2052             arguments.put("formattedTime", formattedTime);
2053             summary = PluralsMessageFormatter.format(res, arguments, summaryResId);
2054             int line1ResId = shortVersion ? R.string.zen_mode_duration_minutes_short
2055                     : R.string.zen_mode_duration_minutes;
2056             line1 = PluralsMessageFormatter.format(res, arguments, line1ResId);
2057             line2 = res.getString(R.string.zen_mode_until, formattedTime);
2058         } else if (minutes < DAY_MINUTES) {
2059             // display as hours
2060             num =  Math.round(minutes / 60f);
2061             int summaryResId = shortVersion ? R.string.zen_mode_duration_hours_summary_short
2062                     : R.string.zen_mode_duration_hours_summary;
2063             arguments.put("count", num);
2064             arguments.put("formattedTime", formattedTime);
2065             summary = PluralsMessageFormatter.format(res, arguments, summaryResId);
2066             int line1ResId = shortVersion ? R.string.zen_mode_duration_hours_short
2067                     : R.string.zen_mode_duration_hours;
2068             line1 = PluralsMessageFormatter.format(res, arguments, line1ResId);
2069             line2 = res.getString(R.string.zen_mode_until, formattedTime);
2070         } else {
2071             // display as day/time
2072             summary = line1 = line2 = res.getString(R.string.zen_mode_until_next_day,
2073                     formattedTime);
2074         }
2075         final Uri id = toCountdownConditionId(time, false);
2076         return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
2077                 Condition.FLAG_RELEVANT_NOW);
2078     }
2079 
2080     /**
2081      * Converts countdown to alarm parameters into a condition with user facing summary
2082      */
toNextAlarmCondition(Context context, long alarm, int userHandle)2083     public static Condition toNextAlarmCondition(Context context, long alarm,
2084             int userHandle) {
2085         boolean isSameDay = isToday(alarm);
2086         final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
2087         final Resources res = context.getResources();
2088         final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
2089         final Uri id = toCountdownConditionId(alarm, true);
2090         return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
2091                 Condition.FLAG_RELEVANT_NOW);
2092     }
2093 
2094     /**
2095      * Creates readable time from time in milliseconds
2096      */
getFormattedTime(Context context, long time, boolean isSameDay, int userHandle)2097     public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
2098             int userHandle) {
2099         String skeleton = (!isSameDay ? "EEE " : "")
2100                 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
2101         final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
2102         return DateFormat.format(pattern, time);
2103     }
2104 
2105     /**
2106      * Determines whether a time in milliseconds is today or not
2107      */
isToday(long time)2108     public static boolean isToday(long time) {
2109         GregorianCalendar now = new GregorianCalendar();
2110         GregorianCalendar endTime = new GregorianCalendar();
2111         endTime.setTimeInMillis(time);
2112         if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
2113                 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
2114                 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
2115             return true;
2116         }
2117         return false;
2118     }
2119 
2120     // ==== Built-in system conditions ====
2121 
2122     public static final String SYSTEM_AUTHORITY = "android";
2123 
2124     // ==== Built-in system condition: countdown ====
2125 
2126     public static final String COUNTDOWN_PATH = "countdown";
2127 
2128     public static final String IS_ALARM_PATH = "alarm";
2129 
2130     /**
2131      * Converts countdown condition parameters into a condition id.
2132      */
toCountdownConditionId(long time, boolean alarm)2133     public static Uri toCountdownConditionId(long time, boolean alarm) {
2134         return new Uri.Builder().scheme(Condition.SCHEME)
2135                 .authority(SYSTEM_AUTHORITY)
2136                 .appendPath(COUNTDOWN_PATH)
2137                 .appendPath(Long.toString(time))
2138                 .appendPath(IS_ALARM_PATH)
2139                 .appendPath(Boolean.toString(alarm))
2140                 .build();
2141     }
2142 
tryParseCountdownConditionId(Uri conditionId)2143     public static long tryParseCountdownConditionId(Uri conditionId) {
2144         if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
2145         if (conditionId.getPathSegments().size() < 2
2146                 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
2147         try {
2148             return Long.parseLong(conditionId.getPathSegments().get(1));
2149         } catch (RuntimeException e) {
2150             Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
2151             return 0;
2152         }
2153     }
2154 
2155     /**
2156      * Returns whether this condition is a countdown condition.
2157      */
isValidCountdownConditionId(Uri conditionId)2158     public static boolean isValidCountdownConditionId(Uri conditionId) {
2159         return tryParseCountdownConditionId(conditionId) != 0;
2160     }
2161 
2162     /**
2163      * Returns whether this condition is a countdown to an alarm.
2164      */
isValidCountdownToAlarmConditionId(Uri conditionId)2165     public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
2166         if (tryParseCountdownConditionId(conditionId) != 0) {
2167             if (conditionId.getPathSegments().size() < 4
2168                     || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
2169                 return false;
2170             }
2171             try {
2172                 return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
2173             } catch (RuntimeException e) {
2174                 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
2175                 return false;
2176             }
2177         }
2178         return false;
2179     }
2180 
2181     // ==== Built-in system condition: schedule ====
2182 
2183     public static final String SCHEDULE_PATH = "schedule";
2184 
toScheduleConditionId(ScheduleInfo schedule)2185     public static Uri toScheduleConditionId(ScheduleInfo schedule) {
2186         return new Uri.Builder().scheme(Condition.SCHEME)
2187                 .authority(SYSTEM_AUTHORITY)
2188                 .appendPath(SCHEDULE_PATH)
2189                 .appendQueryParameter("days", toDayList(schedule.days))
2190                 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
2191                 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
2192                 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
2193                 .build();
2194     }
2195 
isValidScheduleConditionId(Uri conditionId)2196     public static boolean isValidScheduleConditionId(Uri conditionId) {
2197         ScheduleInfo info;
2198         try {
2199             info = tryParseScheduleConditionId(conditionId);
2200         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
2201             return false;
2202         }
2203 
2204         if (info == null || info.days == null || info.days.length == 0) {
2205             return false;
2206         }
2207         return true;
2208     }
2209 
2210     /**
2211      * Returns whether the conditionId is a valid ScheduleCondition.
2212      * If allowNever is true, this will return true even if the ScheduleCondition never occurs.
2213      */
isValidScheduleConditionId(Uri conditionId, boolean allowNever)2214     public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) {
2215         ScheduleInfo info;
2216         try {
2217             info = tryParseScheduleConditionId(conditionId);
2218         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
2219             return false;
2220         }
2221 
2222         if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) {
2223             return false;
2224         }
2225         return true;
2226     }
2227 
2228     /**
2229      * Returns the {@link ScheduleInfo} encoded in the condition id, or {@code null} if it could not
2230      * be decoded.
2231      */
2232     @UnsupportedAppUsage
2233     @Nullable
tryParseScheduleConditionId(Uri conditionId)2234     public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
2235         final boolean isSchedule =  conditionId != null
2236                 && Condition.SCHEME.equals(conditionId.getScheme())
2237                 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
2238                 && conditionId.getPathSegments().size() == 1
2239                 && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
2240         if (!isSchedule) return null;
2241         final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
2242         final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
2243         if (start == null || end == null) return null;
2244         final ScheduleInfo rt = new ScheduleInfo();
2245         rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
2246         rt.startHour = start[0];
2247         rt.startMinute = start[1];
2248         rt.endHour = end[0];
2249         rt.endMinute = end[1];
2250         rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
2251         return rt;
2252     }
2253 
getScheduleConditionProvider()2254     public static ComponentName getScheduleConditionProvider() {
2255         return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
2256     }
2257 
2258     public static class ScheduleInfo {
2259         @UnsupportedAppUsage
2260         public int[] days;
2261         @UnsupportedAppUsage
2262         public int startHour;
2263         @UnsupportedAppUsage
2264         public int startMinute;
2265         @UnsupportedAppUsage
2266         public int endHour;
2267         @UnsupportedAppUsage
2268         public int endMinute;
2269         public boolean exitAtAlarm;
2270         public long nextAlarm;
2271 
2272         @Override
hashCode()2273         public int hashCode() {
2274             return 0;
2275         }
2276 
2277         @Override
equals(@ullable Object o)2278         public boolean equals(@Nullable Object o) {
2279             if (!(o instanceof ScheduleInfo)) return false;
2280             final ScheduleInfo other = (ScheduleInfo) o;
2281             return toDayList(days).equals(toDayList(other.days))
2282                     && startHour == other.startHour
2283                     && startMinute == other.startMinute
2284                     && endHour == other.endHour
2285                     && endMinute == other.endMinute
2286                     && exitAtAlarm == other.exitAtAlarm;
2287         }
2288 
copy()2289         public ScheduleInfo copy() {
2290             final ScheduleInfo rt = new ScheduleInfo();
2291             if (days != null) {
2292                 rt.days = new int[days.length];
2293                 System.arraycopy(days, 0, rt.days, 0, days.length);
2294             }
2295             rt.startHour = startHour;
2296             rt.startMinute = startMinute;
2297             rt.endHour = endHour;
2298             rt.endMinute = endMinute;
2299             rt.exitAtAlarm = exitAtAlarm;
2300             rt.nextAlarm = nextAlarm;
2301             return rt;
2302         }
2303 
2304         @Override
toString()2305         public String toString() {
2306             return "ScheduleInfo{" +
2307                     "days=" + Arrays.toString(days) +
2308                     ", startHour=" + startHour +
2309                     ", startMinute=" + startMinute +
2310                     ", endHour=" + endHour +
2311                     ", endMinute=" + endMinute +
2312                     ", exitAtAlarm=" + exitAtAlarm +
2313                     ", nextAlarm=" + ts(nextAlarm) +
2314                     '}';
2315         }
2316 
ts(long time)2317         protected static String ts(long time) {
2318             return new Date(time) + " (" + time + ")";
2319         }
2320     }
2321 
2322     // ==== Built-in system condition: event ====
2323 
2324     public static final String EVENT_PATH = "event";
2325 
toEventConditionId(EventInfo event)2326     public static Uri toEventConditionId(EventInfo event) {
2327         return new Uri.Builder().scheme(Condition.SCHEME)
2328                 .authority(SYSTEM_AUTHORITY)
2329                 .appendPath(EVENT_PATH)
2330                 .appendQueryParameter("userId", Long.toString(event.userId))
2331                 .appendQueryParameter("calendar", event.calName != null ? event.calName : "")
2332                 .appendQueryParameter("calendarId", event.calendarId != null
2333                         ? event.calendarId.toString() : "")
2334                 .appendQueryParameter("reply", Integer.toString(event.reply))
2335                 .build();
2336     }
2337 
isValidEventConditionId(Uri conditionId)2338     public static boolean isValidEventConditionId(Uri conditionId) {
2339         return tryParseEventConditionId(conditionId) != null;
2340     }
2341 
2342     /**
2343      * Returns the {@link EventInfo} encoded in the condition id, or {@code null} if it could not be
2344      * decoded.
2345      */
2346     @Nullable
tryParseEventConditionId(Uri conditionId)2347     public static EventInfo tryParseEventConditionId(Uri conditionId) {
2348         final boolean isEvent = conditionId != null
2349                 && Condition.SCHEME.equals(conditionId.getScheme())
2350                 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
2351                 && conditionId.getPathSegments().size() == 1
2352                 && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
2353         if (!isEvent) return null;
2354         final EventInfo rt = new EventInfo();
2355         rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
2356         rt.calName = conditionId.getQueryParameter("calendar");
2357         if (TextUtils.isEmpty(rt.calName)) {
2358             rt.calName = null;
2359         }
2360         rt.calendarId = tryParseLong(conditionId.getQueryParameter("calendarId"), null);
2361         rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
2362         return rt;
2363     }
2364 
getEventConditionProvider()2365     public static ComponentName getEventConditionProvider() {
2366         return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
2367     }
2368 
2369     public static class EventInfo {
2370         public static final int REPLY_ANY_EXCEPT_NO = 0;
2371         public static final int REPLY_YES_OR_MAYBE = 1;
2372         public static final int REPLY_YES = 2;
2373 
2374         public int userId = UserHandle.USER_NULL;  // USER_NULL = unspecified - use current user
2375         public String calName;  // CalendarContract.Calendars.DISPLAY_NAME, or null for any
2376         public Long calendarId; // Calendars._ID, or null if restored from < Q calendar
2377         public int reply;
2378 
2379         @Override
hashCode()2380         public int hashCode() {
2381             return Objects.hash(userId, calName, calendarId, reply);
2382         }
2383 
2384         @Override
equals(@ullable Object o)2385         public boolean equals(@Nullable Object o) {
2386             if (!(o instanceof EventInfo)) return false;
2387             final EventInfo other = (EventInfo) o;
2388             return userId == other.userId
2389                     && Objects.equals(calName, other.calName)
2390                     && reply == other.reply
2391                     && Objects.equals(calendarId, other.calendarId);
2392         }
2393 
copy()2394         public EventInfo copy() {
2395             final EventInfo rt = new EventInfo();
2396             rt.userId = userId;
2397             rt.calName = calName;
2398             rt.reply = reply;
2399             rt.calendarId = calendarId;
2400             return rt;
2401         }
2402 
resolveUserId(int userId)2403         public static int resolveUserId(int userId) {
2404             return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
2405         }
2406     }
2407 
2408     // ==== End built-in system conditions ====
2409 
tryParseHourAndMinute(String value)2410     private static int[] tryParseHourAndMinute(String value) {
2411         if (TextUtils.isEmpty(value)) return null;
2412         final int i = value.indexOf('.');
2413         if (i < 1 || i >= value.length() - 1) return null;
2414         final int hour = tryParseInt(value.substring(0, i), -1);
2415         final int minute = tryParseInt(value.substring(i + 1), -1);
2416         return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
2417     }
2418 
tryParseZenMode(String value, int defValue)2419     private static int tryParseZenMode(String value, int defValue) {
2420         final int rt = tryParseInt(value, defValue);
2421         return Global.isValidZenMode(rt) ? rt : defValue;
2422     }
2423 
newRuleId()2424     public static String newRuleId() {
2425         return UUID.randomUUID().toString().replace("-", "");
2426     }
2427 
2428     /**
2429      * Gets the name of the app associated with owner
2430      */
getOwnerCaption(Context context, String owner)2431     public static String getOwnerCaption(Context context, String owner) {
2432         final PackageManager pm = context.getPackageManager();
2433         try {
2434             final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
2435             if (info != null) {
2436                 final CharSequence seq = info.loadLabel(pm);
2437                 if (seq != null) {
2438                     final String str = seq.toString().trim();
2439                     if (str.length() > 0) {
2440                         return str;
2441                     }
2442                 }
2443             }
2444         } catch (Throwable e) {
2445             Slog.w(TAG, "Error loading owner caption", e);
2446         }
2447         return "";
2448     }
2449 
isManualActive()2450     public boolean isManualActive() {
2451         if (!Flags.modesUi()) {
2452             return manualRule != null;
2453         }
2454         return manualRule != null && manualRule.isAutomaticActive();
2455     }
2456 
2457     public static class ZenRule implements Parcelable {
2458         @UnsupportedAppUsage
2459         public boolean enabled;
2460         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2461         public boolean snoozing;         // user manually disabled this instance
2462         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2463         public String name;              // required for automatic
2464         @UnsupportedAppUsage
2465         public int zenMode;             // ie: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
2466         @UnsupportedAppUsage
2467         public Uri conditionId;          // required for automatic
2468         public Condition condition;      // optional
2469         public ComponentName component;  // optional
2470         public ComponentName configurationActivity; // optional
2471         public String id;                // required for automatic (unique)
2472         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2473         public long creationTime;        // required for automatic
2474         // package name, only used for manual rules when they have turned DND on.
2475         public String enabler;
2476         public ZenPolicy zenPolicy;
2477         @FlaggedApi(Flags.FLAG_MODES_API)
2478         @Nullable public ZenDeviceEffects zenDeviceEffects;
2479         public boolean modified;    // rule has been modified from initial creation
2480         public String pkg;
2481         @AutomaticZenRule.Type
2482         public int type = AutomaticZenRule.TYPE_UNKNOWN;
2483         public String triggerDescription;
2484         public String iconResName;
2485         public boolean allowManualInvocation;
2486         @AutomaticZenRule.ModifiableField public int userModifiedFields;
2487         @ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields;
2488         @ZenDeviceEffects.ModifiableField public int zenDeviceEffectsUserModifiedFields;
2489         @Nullable public Instant deletionInstant; // Only set on deleted rules.
2490 
ZenRule()2491         public ZenRule() { }
2492 
ZenRule(Parcel source)2493         public ZenRule(Parcel source) {
2494             enabled = source.readInt() == 1;
2495             snoozing = source.readInt() == 1;
2496             if (source.readInt() == 1) {
2497                 name = source.readString();
2498             }
2499             zenMode = source.readInt();
2500             conditionId = source.readParcelable(null, android.net.Uri.class);
2501             condition = source.readParcelable(null, android.service.notification.Condition.class);
2502             component = source.readParcelable(null, android.content.ComponentName.class);
2503             configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
2504             if (source.readInt() == 1) {
2505                 id = source.readString();
2506             }
2507             creationTime = source.readLong();
2508             if (source.readInt() == 1) {
2509                 enabler = source.readString();
2510             }
2511             zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
2512             if (Flags.modesApi()) {
2513                 zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
2514             }
2515             modified = source.readInt() == 1;
2516             pkg = source.readString();
2517             if (Flags.modesApi()) {
2518                 allowManualInvocation = source.readBoolean();
2519                 iconResName = source.readString();
2520                 triggerDescription = source.readString();
2521                 type = source.readInt();
2522                 userModifiedFields = source.readInt();
2523                 zenPolicyUserModifiedFields = source.readInt();
2524                 zenDeviceEffectsUserModifiedFields = source.readInt();
2525                 if (source.readInt() == 1) {
2526                     deletionInstant = Instant.ofEpochMilli(source.readLong());
2527                 }
2528             }
2529         }
2530 
2531         /**
2532          * Whether this ZenRule can be updated by an app. In general, rules that have been
2533          * customized by the user cannot be further updated by an app, with some exceptions:
2534          * <ul>
2535          *     <li>Non user-configurable fields, like type, icon, configurationActivity, etc.
2536          *     <li>Name, if the name was not specifically modified by the user (to support language
2537          *          switches).
2538          * </ul>
2539          */
2540         @FlaggedApi(Flags.FLAG_MODES_API)
canBeUpdatedByApp()2541         public boolean canBeUpdatedByApp() {
2542             // The rule is considered updateable if its bitmask has no user modifications, and
2543             // the bitmasks of the policy and device effects have no modification.
2544             return userModifiedFields == 0
2545                     && zenPolicyUserModifiedFields == 0
2546                     && zenDeviceEffectsUserModifiedFields == 0;
2547         }
2548 
2549         @Override
describeContents()2550         public int describeContents() {
2551             return 0;
2552         }
2553 
2554         @Override
writeToParcel(Parcel dest, int flags)2555         public void writeToParcel(Parcel dest, int flags) {
2556             dest.writeInt(enabled ? 1 : 0);
2557             dest.writeInt(snoozing ? 1 : 0);
2558             if (name != null) {
2559                 dest.writeInt(1);
2560                 dest.writeString(name);
2561             } else {
2562                 dest.writeInt(0);
2563             }
2564             dest.writeInt(zenMode);
2565             dest.writeParcelable(conditionId, 0);
2566             dest.writeParcelable(condition, 0);
2567             dest.writeParcelable(component, 0);
2568             dest.writeParcelable(configurationActivity, 0);
2569             if (id != null) {
2570                 dest.writeInt(1);
2571                 dest.writeString(id);
2572             } else {
2573                 dest.writeInt(0);
2574             }
2575             dest.writeLong(creationTime);
2576             if (enabler != null) {
2577                 dest.writeInt(1);
2578                 dest.writeString(enabler);
2579             } else {
2580                 dest.writeInt(0);
2581             }
2582             dest.writeParcelable(zenPolicy, 0);
2583             if (Flags.modesApi()) {
2584                 dest.writeParcelable(zenDeviceEffects, 0);
2585             }
2586             dest.writeInt(modified ? 1 : 0);
2587             dest.writeString(pkg);
2588             if (Flags.modesApi()) {
2589                 dest.writeBoolean(allowManualInvocation);
2590                 dest.writeString(iconResName);
2591                 dest.writeString(triggerDescription);
2592                 dest.writeInt(type);
2593                 dest.writeInt(userModifiedFields);
2594                 dest.writeInt(zenPolicyUserModifiedFields);
2595                 dest.writeInt(zenDeviceEffectsUserModifiedFields);
2596                 if (deletionInstant != null) {
2597                     dest.writeInt(1);
2598                     dest.writeLong(deletionInstant.toEpochMilli());
2599                 } else {
2600                     dest.writeInt(0);
2601                 }
2602             }
2603         }
2604 
2605         @Override
toString()2606         public String toString() {
2607             StringBuilder sb = new StringBuilder(ZenRule.class.getSimpleName()).append('[')
2608                     .append("id=").append(id)
2609                     .append(",state=").append(condition == null ? "STATE_FALSE"
2610                             : Condition.stateToString(condition.state))
2611                     .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
2612                     .append(",snoozing=").append(snoozing)
2613                     .append(",name=").append(name)
2614                     .append(",zenMode=").append(Global.zenModeToString(zenMode))
2615                     .append(",conditionId=").append(conditionId)
2616                     .append(",pkg=").append(pkg)
2617                     .append(",component=").append(component)
2618                     .append(",configActivity=").append(configurationActivity)
2619                     .append(",creationTime=").append(creationTime)
2620                     .append(",enabler=").append(enabler)
2621                     .append(",zenPolicy=").append(zenPolicy)
2622                     .append(",modified=").append(modified)
2623                     .append(",condition=").append(condition);
2624 
2625             if (Flags.modesApi()) {
2626                 sb.append(",deviceEffects=").append(zenDeviceEffects)
2627                         .append(",allowManualInvocation=").append(allowManualInvocation)
2628                         .append(",iconResName=").append(iconResName)
2629                         .append(",triggerDescription=").append(triggerDescription)
2630                         .append(",type=").append(type);
2631                 if (userModifiedFields != 0) {
2632                     sb.append(",userModifiedFields=")
2633                             .append(AutomaticZenRule.fieldsToString(userModifiedFields));
2634                 }
2635                 if (zenPolicyUserModifiedFields != 0) {
2636                     sb.append(",zenPolicyUserModifiedFields=")
2637                             .append(ZenPolicy.fieldsToString(zenPolicyUserModifiedFields));
2638                 }
2639                 if (zenDeviceEffectsUserModifiedFields != 0) {
2640                     sb.append(",zenDeviceEffectsUserModifiedFields=")
2641                             .append(ZenDeviceEffects.fieldsToString(
2642                                     zenDeviceEffectsUserModifiedFields));
2643                 }
2644                 if (deletionInstant != null) {
2645                     sb.append(",deletionInstant=").append(deletionInstant);
2646                 }
2647             }
2648 
2649             return sb.append(']').toString();
2650         }
2651 
2652         /** @hide */
2653         // TODO: add configuration activity
dumpDebug(ProtoOutputStream proto, long fieldId)2654         public void dumpDebug(ProtoOutputStream proto, long fieldId) {
2655             final long token = proto.start(fieldId);
2656 
2657             proto.write(ZenRuleProto.ID, id);
2658             proto.write(ZenRuleProto.NAME, name);
2659             proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
2660             proto.write(ZenRuleProto.ENABLED, enabled);
2661             proto.write(ZenRuleProto.ENABLER, enabler);
2662             proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
2663             proto.write(ZenRuleProto.ZEN_MODE, zenMode);
2664             if (conditionId != null) {
2665                 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
2666             }
2667             if (condition != null) {
2668                 condition.dumpDebug(proto, ZenRuleProto.CONDITION);
2669             }
2670             if (component != null) {
2671                 component.dumpDebug(proto, ZenRuleProto.COMPONENT);
2672             }
2673             if (zenPolicy != null) {
2674                 zenPolicy.dumpDebug(proto, ZenRuleProto.ZEN_POLICY);
2675             }
2676             proto.write(ZenRuleProto.MODIFIED, modified);
2677             proto.end(token);
2678         }
2679 
2680         @Override
equals(@ullable Object o)2681         public boolean equals(@Nullable Object o) {
2682             if (!(o instanceof ZenRule)) return false;
2683             if (o == this) return true;
2684             final ZenRule other = (ZenRule) o;
2685             boolean finalEquals = other.enabled == enabled
2686                     && other.snoozing == snoozing
2687                     && Objects.equals(other.name, name)
2688                     && other.zenMode == zenMode
2689                     && Objects.equals(other.conditionId, conditionId)
2690                     && Objects.equals(other.condition, condition)
2691                     && Objects.equals(other.component, component)
2692                     && Objects.equals(other.configurationActivity, configurationActivity)
2693                     && Objects.equals(other.id, id)
2694                     && Objects.equals(other.enabler, enabler)
2695                     && Objects.equals(other.zenPolicy, zenPolicy)
2696                     && Objects.equals(other.pkg, pkg)
2697                     && other.modified == modified;
2698 
2699             if (Flags.modesApi()) {
2700                 return finalEquals
2701                         && Objects.equals(other.zenDeviceEffects, zenDeviceEffects)
2702                         && other.allowManualInvocation == allowManualInvocation
2703                         && Objects.equals(other.iconResName, iconResName)
2704                         && Objects.equals(other.triggerDescription, triggerDescription)
2705                         && other.type == type
2706                         && other.userModifiedFields == userModifiedFields
2707                         && other.zenPolicyUserModifiedFields == zenPolicyUserModifiedFields
2708                         && other.zenDeviceEffectsUserModifiedFields
2709                             == zenDeviceEffectsUserModifiedFields
2710                         && Objects.equals(other.deletionInstant, deletionInstant);
2711             }
2712 
2713             return finalEquals;
2714         }
2715 
2716         @Override
hashCode()2717         public int hashCode() {
2718             if (Flags.modesApi()) {
2719                 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
2720                         component, configurationActivity, pkg, id, enabler, zenPolicy,
2721                         zenDeviceEffects, modified, allowManualInvocation, iconResName,
2722                         triggerDescription, type, userModifiedFields, zenPolicyUserModifiedFields,
2723                         zenDeviceEffectsUserModifiedFields, deletionInstant);
2724             }
2725             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
2726                     component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
2727         }
2728 
2729         /** Returns a deep copy of the {@link ZenRule}. */
copy()2730         public ZenRule copy() {
2731             final Parcel parcel = Parcel.obtain();
2732             try {
2733                 writeToParcel(parcel, 0);
2734                 parcel.setDataPosition(0);
2735                 return new ZenRule(parcel);
2736             } finally {
2737                 parcel.recycle();
2738             }
2739         }
2740 
isAutomaticActive()2741         public boolean isAutomaticActive() {
2742             return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
2743         }
2744 
getPkg()2745         public String getPkg() {
2746             return !TextUtils.isEmpty(pkg)
2747                     ? pkg
2748                     : (component != null)
2749                             ? component.getPackageName()
2750                             : (configurationActivity != null)
2751                                     ? configurationActivity.getPackageName()
2752                                     : null;
2753         }
2754 
isTrueOrUnknown()2755         public boolean isTrueOrUnknown() {
2756             return condition != null && (condition.state == Condition.STATE_TRUE
2757                     || condition.state == Condition.STATE_UNKNOWN);
2758         }
2759 
2760         public static final @android.annotation.NonNull Parcelable.Creator<ZenRule> CREATOR
2761                 = new Parcelable.Creator<ZenRule>() {
2762             @Override
2763             public ZenRule createFromParcel(Parcel source) {
2764                 return new ZenRule(source);
2765             }
2766             @Override
2767             public ZenRule[] newArray(int size) {
2768                 return new ZenRule[size];
2769             }
2770         };
2771     }
2772 
2773     /**
2774      * Determines whether dnd behavior should mute all ringer-controlled sounds
2775      * This includes notification, ringer and system sounds
2776      */
areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy policy)2777     public static boolean areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy
2778             policy) {
2779         boolean allowReminders = (policy.priorityCategories
2780                 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
2781         boolean allowCalls = (policy.priorityCategories
2782                 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
2783         boolean allowMessages = (policy.priorityCategories
2784                 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
2785         boolean allowEvents = (policy.priorityCategories
2786                 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
2787         boolean allowRepeatCallers = (policy.priorityCategories
2788                 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
2789         boolean allowConversations = (policy.priorityConversationSenders
2790                 & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
2791         boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
2792         if (Flags.modesApi()) {
2793             areChannelsBypassingDnd = policy.hasPriorityChannels()
2794                     && policy.allowPriorityChannels();
2795         }
2796         boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
2797         return !allowReminders && !allowCalls && !allowMessages && !allowEvents
2798                 && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem
2799                 && !allowConversations;
2800     }
2801 
2802     /**
2803      * Determines whether dnd behavior should mute all sounds
2804      */
areAllZenBehaviorSoundsMuted(NotificationManager.Policy policy)2805     public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy
2806             policy) {
2807         boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
2808         boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
2809         return !allowAlarms && !allowMedia && areAllPriorityOnlyRingerSoundsMuted(policy);
2810     }
2811 
2812     /**
2813      * Determines if DND is currently overriding the ringer
2814      */
isZenOverridingRinger(int zen, Policy consolidatedPolicy)2815     public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) {
2816         return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
2817                 || zen == Global.ZEN_MODE_ALARMS
2818                 || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS
2819                 && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy));
2820     }
2821 
2822     /**
2823      * Determines whether dnd behavior should mute all ringer-controlled sounds
2824      * This includes notification, ringer and system sounds
2825      */
areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config)2826     public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) {
2827         if (Flags.modesUi()) {
2828             final ZenPolicy policy = config.manualRule.zenPolicy;
2829             return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false)
2830                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false)
2831                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false)
2832                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false)
2833                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false)
2834                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false)
2835                     && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed()
2836                     == STATE_ALLOW);
2837 
2838         } else {
2839             boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd;
2840             if (Flags.modesApi()) {
2841                 areChannelsBypassingDnd = config.areChannelsBypassingDnd
2842                         && config.isAllowPriorityChannels();
2843             }
2844             return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages()
2845                     && !config.isAllowEvents() && !config.isAllowRepeatCallers()
2846                     && !areChannelsBypassingDnd && !config.isAllowSystem();
2847         }
2848     }
2849 
2850     /**
2851      * Determines whether dnd mutes all sounds
2852      */
areAllZenBehaviorSoundsMuted(ZenModeConfig config)2853     public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
2854         if (Flags.modesUi()) {
2855             final ZenPolicy policy = config.manualRule.zenPolicy;
2856             return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false)
2857                     && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false)
2858                     && areAllPriorityOnlyRingerSoundsMuted(config);
2859         }
2860         return !config.isAllowAlarms()  && !config.isAllowMedia()
2861                 && areAllPriorityOnlyRingerSoundsMuted(config);
2862     }
2863 
2864     /**
2865      * Returns a description of the current do not disturb settings from config.
2866      * - If turned on manually and end time is known, returns end time.
2867      * - If turned on manually and end time is on forever until turned off, return null if
2868      * describeForeverCondition is false, else return String describing indefinite behavior
2869      * - If turned on by an automatic rule, returns the automatic rule name.
2870      * - If on due to an app, returns the app name.
2871      * - If there's a combination of rules/apps that trigger, then shows the one that will
2872      *  last the longest if applicable.
2873      * @return null if DND is off or describeForeverCondition is false and
2874      * DND is on forever (until turned off)
2875      */
getDescription(Context context, boolean zenOn, ZenModeConfig config, boolean describeForeverCondition)2876     public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
2877             boolean describeForeverCondition) {
2878         if (!zenOn || config == null) {
2879             return null;
2880         }
2881 
2882         String secondaryText = "";
2883         long latestEndTime = -1;
2884 
2885         // DND turned on by manual rule
2886         if (config.manualRule != null) {
2887             final Uri id = config.manualRule.conditionId;
2888             if (config.manualRule.enabler != null) {
2889                 // app triggered manual rule
2890                 String appName = getOwnerCaption(context, config.manualRule.enabler);
2891                 if (!appName.isEmpty()) {
2892                     secondaryText = appName;
2893                 }
2894             } else {
2895                 if (id == null) {
2896                     // Do not disturb manually triggered to remain on forever until turned off
2897                     if (describeForeverCondition) {
2898                         return context.getString(R.string.zen_mode_forever);
2899                     } else {
2900                         return null;
2901                     }
2902                 } else {
2903                     latestEndTime = tryParseCountdownConditionId(id);
2904                     if (latestEndTime > 0) {
2905                         final CharSequence formattedTime = getFormattedTime(context,
2906                                 latestEndTime, isToday(latestEndTime),
2907                                 context.getUserId());
2908                         secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
2909                     }
2910                 }
2911             }
2912         }
2913 
2914         // DND turned on by an automatic rule
2915         for (ZenRule automaticRule : config.automaticRules.values()) {
2916             if (automaticRule.isAutomaticActive()) {
2917                 if (isValidEventConditionId(automaticRule.conditionId)
2918                         || isValidScheduleConditionId(automaticRule.conditionId)) {
2919                     // set text if automatic rule end time is the latest active rule end time
2920                     long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
2921                     if (endTime > latestEndTime) {
2922                         latestEndTime = endTime;
2923                         secondaryText = automaticRule.name;
2924                     }
2925                 } else {
2926                     // set text if 3rd party rule
2927                     return automaticRule.name;
2928                 }
2929             }
2930         }
2931 
2932         return !secondaryText.equals("") ? secondaryText : null;
2933     }
2934 
parseAutomaticRuleEndTime(Context context, Uri id)2935     private static long parseAutomaticRuleEndTime(Context context, Uri id) {
2936         if (isValidEventConditionId(id)) {
2937             // cannot look up end times for events
2938             return Long.MAX_VALUE;
2939         }
2940 
2941         if (isValidScheduleConditionId(id)) {
2942             ScheduleCalendar schedule = toScheduleCalendar(id);
2943             long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
2944 
2945             // check if automatic rule will end on next alarm
2946             if (schedule.exitAtAlarm()) {
2947                 long nextAlarm = getNextAlarm(context);
2948                 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
2949                 if (schedule.shouldExitForAlarm(endTimeMs)) {
2950                     return nextAlarm;
2951                 }
2952             }
2953 
2954             return endTimeMs;
2955         }
2956 
2957         return -1;
2958     }
2959 
getNextAlarm(Context context)2960     private static long getNextAlarm(Context context) {
2961         final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2962         final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
2963         return info != null ? info.getTriggerTime() : 0;
2964     }
2965 }
2966