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