1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 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 android.annotation.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SuppressLint;
24 import android.annotation.TestApi;
25 import android.app.Flags;
26 import android.app.Notification;
27 import android.app.NotificationChannel;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.proto.ProtoOutputStream;
31 
32 import java.io.ByteArrayOutputStream;
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Objects;
39 
40 /**
41  * ZenPolicy determines whether to allow certain notifications and their corresponding sounds to
42  * play when a device is in Do Not Disturb mode.
43  * ZenPolicy also dictates the visual effects of notifications that are intercepted when
44  * a device is in Do Not Disturb mode.
45  */
46 public final class ZenPolicy implements Parcelable {
47 
48     /**
49      * Enum for the user-modifiable fields in this object.
50      * @hide
51      */
52     @IntDef(flag = true, prefix = { "FIELD_" }, value = {
53             FIELD_MESSAGES,
54             FIELD_CALLS,
55             FIELD_CONVERSATIONS,
56             FIELD_ALLOW_CHANNELS,
57             FIELD_PRIORITY_CATEGORY_REMINDERS,
58             FIELD_PRIORITY_CATEGORY_EVENTS,
59             FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS,
60             FIELD_PRIORITY_CATEGORY_ALARMS,
61             FIELD_PRIORITY_CATEGORY_MEDIA,
62             FIELD_PRIORITY_CATEGORY_SYSTEM,
63             FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT,
64             FIELD_VISUAL_EFFECT_LIGHTS,
65             FIELD_VISUAL_EFFECT_PEEK,
66             FIELD_VISUAL_EFFECT_STATUS_BAR,
67             FIELD_VISUAL_EFFECT_BADGE,
68             FIELD_VISUAL_EFFECT_AMBIENT,
69             FIELD_VISUAL_EFFECT_NOTIFICATION_LIST,
70     })
71     @Retention(RetentionPolicy.SOURCE)
72     public @interface ModifiableField {}
73 
74     /**
75      * Covers modifications to MESSAGE_SENDERS and PRIORITY_CATEGORY_MESSAGES, which are set at
76      * the same time.
77      * @hide
78      */
79     @FlaggedApi(Flags.FLAG_MODES_API)
80     public static final int FIELD_MESSAGES = 1 << 0;
81     /**
82      * Covers modifications to CALL_SENDERS and PRIORITY_CATEGORY_CALLS, which are set at
83      * the same time.
84      * @hide
85      */
86     @FlaggedApi(Flags.FLAG_MODES_API)
87     public static final int FIELD_CALLS = 1 << 1;
88     /**
89      * Covers modifications to CONVERSATION_SENDERS and PRIORITY_CATEGORY_CONVERSATIONS, which are
90      * set at the same time.
91      * @hide
92      */
93     @FlaggedApi(Flags.FLAG_MODES_API)
94     public static final int FIELD_CONVERSATIONS = 1 << 2;
95     /**
96      * @hide
97      */
98     @FlaggedApi(Flags.FLAG_MODES_API)
99     public static final int FIELD_ALLOW_CHANNELS = 1 << 3;
100     /**
101      * @hide
102      */
103     @FlaggedApi(Flags.FLAG_MODES_API)
104     public static final int FIELD_PRIORITY_CATEGORY_REMINDERS = 1 << 4;
105     /**
106      * @hide
107      */
108     @FlaggedApi(Flags.FLAG_MODES_API)
109     public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 1 << 5;
110     /**
111      * @hide
112      */
113     @FlaggedApi(Flags.FLAG_MODES_API)
114     public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 6;
115     /**
116      * @hide
117      */
118     @FlaggedApi(Flags.FLAG_MODES_API)
119     public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 1 << 7;
120     /**
121      * @hide
122      */
123     @FlaggedApi(Flags.FLAG_MODES_API)
124     public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 1 << 8;
125     /**
126      * @hide
127      */
128     @FlaggedApi(Flags.FLAG_MODES_API)
129     public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 1 << 9;
130     /**
131      * @hide
132      */
133     @FlaggedApi(Flags.FLAG_MODES_API)
134     public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1 << 10;
135     /**
136      * @hide
137      */
138     @FlaggedApi(Flags.FLAG_MODES_API)
139     public static final int FIELD_VISUAL_EFFECT_LIGHTS = 1 << 11;
140     /**
141      * @hide
142      */
143     @FlaggedApi(Flags.FLAG_MODES_API)
144     public static final int FIELD_VISUAL_EFFECT_PEEK = 1 << 12;
145     /**
146      * @hide
147      */
148     @FlaggedApi(Flags.FLAG_MODES_API)
149     public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 1 << 13;
150     /**
151      * @hide
152      */
153     @FlaggedApi(Flags.FLAG_MODES_API)
154     public static final int FIELD_VISUAL_EFFECT_BADGE = 1 << 14;
155     /**
156      * @hide
157      */
158     @FlaggedApi(Flags.FLAG_MODES_API)
159     public static final int FIELD_VISUAL_EFFECT_AMBIENT = 1 << 15;
160     /**
161      * @hide
162      */
163     @FlaggedApi(Flags.FLAG_MODES_API)
164     public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 1 << 16;
165 
166     private List<Integer> mPriorityCategories;
167     private List<Integer> mVisualEffects;
168     private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
169     private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
170     private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
171     @FlaggedApi(Flags.FLAG_MODES_API)
172     private @ChannelType int mAllowChannels = CHANNEL_POLICY_UNSET;
173 
174     /** @hide */
175     @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
176             PRIORITY_CATEGORY_REMINDERS,
177             PRIORITY_CATEGORY_EVENTS,
178             PRIORITY_CATEGORY_MESSAGES,
179             PRIORITY_CATEGORY_CALLS,
180             PRIORITY_CATEGORY_REPEAT_CALLERS,
181             PRIORITY_CATEGORY_ALARMS,
182             PRIORITY_CATEGORY_MEDIA,
183             PRIORITY_CATEGORY_SYSTEM,
184             PRIORITY_CATEGORY_CONVERSATIONS,
185     })
186     @Retention(RetentionPolicy.SOURCE)
187     public @interface PriorityCategory {}
188 
189     /** @hide */
190     public static final int PRIORITY_CATEGORY_REMINDERS = 0;
191     /** @hide */
192     public static final int PRIORITY_CATEGORY_EVENTS = 1;
193     /** @hide */
194     public static final int PRIORITY_CATEGORY_MESSAGES = 2;
195     /** @hide */
196     public static final int PRIORITY_CATEGORY_CALLS = 3;
197     /** @hide */
198     public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4;
199     /** @hide */
200     public static final int PRIORITY_CATEGORY_ALARMS = 5;
201     /** @hide */
202     public static final int PRIORITY_CATEGORY_MEDIA = 6;
203     /** @hide */
204     public static final int PRIORITY_CATEGORY_SYSTEM = 7;
205     /** @hide */
206     public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8;
207 
208     /**
209      * Total number of priority categories. Keep updated with any updates to PriorityCategory enum.
210      * @hide
211      */
212     public static final int NUM_PRIORITY_CATEGORIES = 9;
213 
214     /** @hide */
215     @IntDef(prefix = { "VISUAL_EFFECT_" }, value = {
216             VISUAL_EFFECT_FULL_SCREEN_INTENT,
217             VISUAL_EFFECT_LIGHTS,
218             VISUAL_EFFECT_PEEK,
219             VISUAL_EFFECT_STATUS_BAR,
220             VISUAL_EFFECT_BADGE,
221             VISUAL_EFFECT_AMBIENT,
222             VISUAL_EFFECT_NOTIFICATION_LIST,
223     })
224     @Retention(RetentionPolicy.SOURCE)
225     public @interface VisualEffect {}
226 
227     /** @hide */
228     public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0;
229     /** @hide */
230     public static final int VISUAL_EFFECT_LIGHTS = 1;
231     /** @hide */
232     public static final int VISUAL_EFFECT_PEEK = 2;
233     /** @hide */
234     public static final int VISUAL_EFFECT_STATUS_BAR = 3;
235     /** @hide */
236     public static final int VISUAL_EFFECT_BADGE = 4;
237     /** @hide */
238     public static final int VISUAL_EFFECT_AMBIENT = 5;
239     /** @hide */
240     public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6;
241 
242     /**
243      * Total number of visual effects. Keep updated with any updates to VisualEffect enum.
244      * @hide
245      */
246     public static final int NUM_VISUAL_EFFECTS = 7;
247 
248     /** @hide */
249     @IntDef(prefix = { "PEOPLE_TYPE_" }, value = {
250             PEOPLE_TYPE_UNSET,
251             PEOPLE_TYPE_ANYONE,
252             PEOPLE_TYPE_CONTACTS,
253             PEOPLE_TYPE_STARRED,
254             PEOPLE_TYPE_NONE,
255     })
256     @Retention(RetentionPolicy.SOURCE)
257     public @interface PeopleType {}
258 
259     /**
260      * Used to indicate no preference for the type of people that can bypass dnd for either
261      * calls or messages.
262      */
263     public static final int PEOPLE_TYPE_UNSET = 0;
264 
265     /**
266      * Used to indicate all calls or messages can bypass dnd.
267      */
268     public static final int PEOPLE_TYPE_ANYONE = 1;
269 
270     /**
271      * Used to indicate calls or messages from contacts can bypass dnd.
272      */
273     public static final int PEOPLE_TYPE_CONTACTS = 2;
274 
275     /**
276      * Used to indicate calls or messages from starred contacts can bypass dnd.
277      */
278     public static final int PEOPLE_TYPE_STARRED = 3;
279 
280     /**
281      * Used to indicate no calls or messages can bypass dnd.
282      */
283     public static final int PEOPLE_TYPE_NONE = 4;
284 
285 
286     /** @hide */
287     @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
288             CONVERSATION_SENDERS_UNSET,
289             CONVERSATION_SENDERS_ANYONE,
290             CONVERSATION_SENDERS_IMPORTANT,
291             CONVERSATION_SENDERS_NONE,
292     })
293     @Retention(RetentionPolicy.SOURCE)
294     public @interface ConversationSenders {}
295 
296     /**
297      * Used to indicate no preference for the type of conversations that can bypass dnd.
298      */
299     public static final int CONVERSATION_SENDERS_UNSET = 0;
300 
301     /**
302      * Used to indicate all conversations can bypass dnd.
303      */
304     public static final int CONVERSATION_SENDERS_ANYONE = 1;
305 
306     /**
307      * Used to indicate important conversations can bypass dnd.
308      */
309     public static final int CONVERSATION_SENDERS_IMPORTANT = 2;
310 
311     /**
312      * Used to indicate no conversations can bypass dnd.
313      */
314     public static final int CONVERSATION_SENDERS_NONE = 3;
315 
316     /** @hide */
317     @IntDef(prefix = { "STATE_" }, value = {
318             STATE_UNSET,
319             STATE_ALLOW,
320             STATE_DISALLOW,
321     })
322     @Retention(RetentionPolicy.SOURCE)
323     public @interface State {}
324 
325     /**
326      * Indicates no preference for whether a type of sound or visual effect is or isn't allowed
327      * to play/show when DND is active.  Will default to the current set policy.
328      */
329     public static final int STATE_UNSET = 0;
330 
331     /**
332      * Indicates a type of sound or visual effect is allowed to play/show when DND is active.
333      */
334     public static final int STATE_ALLOW = 1;
335 
336     /**
337      * Indicates a type of sound or visual effect is not allowed to play/show when DND is active.
338      */
339     public static final int STATE_DISALLOW = 2;
340 
341     @IntDef(prefix = { "CHANNEL_POLICY_" }, value = {
342             CHANNEL_POLICY_UNSET,
343             CHANNEL_POLICY_PRIORITY,
344             CHANNEL_POLICY_NONE,
345     })
346     @Retention(RetentionPolicy.SOURCE)
347     private @interface ChannelType {}
348 
349     /**
350      * Indicates no explicit setting for which channels may bypass DND when this policy is active.
351      * Defaults to {@link #CHANNEL_POLICY_PRIORITY}.
352      *
353      * @hide
354      */
355     @FlaggedApi(Flags.FLAG_MODES_API)
356     public static final int CHANNEL_POLICY_UNSET = 0;
357 
358     /**
359      * Indicates that channels marked as {@link NotificationChannel#canBypassDnd()} can bypass DND
360      * when this policy is active.
361      *
362      * @hide
363      */
364     @FlaggedApi(Flags.FLAG_MODES_API)
365     public static final int CHANNEL_POLICY_PRIORITY = 1;
366 
367     /**
368      * Indicates that no channels can bypass DND when this policy is active, even those marked as
369      * {@link NotificationChannel#canBypassDnd()}.
370      *
371      * @hide
372      */
373     @FlaggedApi(Flags.FLAG_MODES_API)
374     public static final int CHANNEL_POLICY_NONE = 2;
375 
376     /** @hide */
ZenPolicy()377     public ZenPolicy() {
378         mPriorityCategories = new ArrayList<>(Collections.nCopies(NUM_PRIORITY_CATEGORIES, 0));
379         mVisualEffects = new ArrayList<>(Collections.nCopies(NUM_VISUAL_EFFECTS, 0));
380     }
381 
382     /** @hide */
383     @FlaggedApi(Flags.FLAG_MODES_API)
ZenPolicy(List<Integer> priorityCategories, List<Integer> visualEffects, @PeopleType int priorityMessages, @PeopleType int priorityCalls, @ConversationSenders int conversationSenders, @ChannelType int allowChannels)384     public ZenPolicy(List<Integer> priorityCategories, List<Integer> visualEffects,
385                      @PeopleType int priorityMessages, @PeopleType int priorityCalls,
386                      @ConversationSenders int conversationSenders, @ChannelType int allowChannels) {
387         mPriorityCategories = priorityCategories;
388         mVisualEffects = visualEffects;
389         mPriorityMessages = priorityMessages;
390         mPriorityCalls = priorityCalls;
391         mConversationSenders = conversationSenders;
392         mAllowChannels = allowChannels;
393     }
394 
395     /**
396      * Conversation type that can bypass DND.
397      * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
398      * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
399      */
getPriorityConversationSenders()400     public @ConversationSenders int getPriorityConversationSenders() {
401         return mConversationSenders;
402     }
403 
404     /**
405      * Message senders that can bypass DND.
406      * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
407      * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
408      */
getPriorityMessageSenders()409     public @PeopleType int getPriorityMessageSenders() {
410         return mPriorityMessages;
411     }
412 
413     /**
414      * Callers that can bypass DND.
415      * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
416      * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
417      */
getPriorityCallSenders()418     public @PeopleType int getPriorityCallSenders() {
419         return mPriorityCalls;
420     }
421 
422     /**
423      * Whether this policy wants to allow conversation notifications
424      * (see {@link NotificationChannel#getConversationId()}) to play sounds and visually appear
425      * or to intercept them when DND is active.
426      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
427      */
getPriorityCategoryConversations()428     public @State int getPriorityCategoryConversations() {
429         return mPriorityCategories.get(PRIORITY_CATEGORY_CONVERSATIONS);
430     }
431 
432     /**
433      * Whether this policy wants to allow notifications with category
434      * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear
435      * or to intercept them when DND is active.
436      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
437      */
getPriorityCategoryReminders()438     public @State int getPriorityCategoryReminders() {
439         return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS);
440     }
441 
442     /**
443      * Whether this policy wants to allow notifications with category
444      * {@link Notification#CATEGORY_EVENT} to play sounds and visually appear
445      * or to intercept them when DND is active.
446      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
447      */
getPriorityCategoryEvents()448     public @State int getPriorityCategoryEvents() {
449         return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS);
450     }
451 
452     /**
453      * Whether this policy wants to allow notifications with category
454      * {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear
455      * or to intercept them when DND is active.  Types of message senders that are allowed
456      * are specified by {@link #getPriorityMessageSenders}.
457      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
458      */
getPriorityCategoryMessages()459     public @State int getPriorityCategoryMessages() {
460         return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES);
461     }
462 
463     /**
464      * Whether this policy wants to allow notifications with category
465      * {@link Notification#CATEGORY_CALL} to play sounds and visually appear
466      * or to intercept them when DND is active.  Types of callers that are allowed
467      * are specified by {@link #getPriorityCallSenders()}.
468      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
469      */
getPriorityCategoryCalls()470     public @State int getPriorityCategoryCalls() {
471         return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS);
472     }
473 
474     /**
475      * Whether this policy wants to allow repeat callers (notifications with category
476      * {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and
477      * visually appear or to intercept them when DND is active.
478      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
479      */
getPriorityCategoryRepeatCallers()480     public @State int getPriorityCategoryRepeatCallers() {
481         return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS);
482     }
483 
484     /**
485      * Whether this policy wants to allow notifications with category
486      * {@link Notification#CATEGORY_ALARM} to play sounds and visually appear
487      * or to intercept them when DND is active.
488      * When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND
489      * is active.
490      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
491      */
getPriorityCategoryAlarms()492     public @State int getPriorityCategoryAlarms() {
493         return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS);
494     }
495 
496     /**
497      * Whether this policy wants to allow media notifications to play sounds and visually appear
498      * or to intercept them when DND is active.
499      * When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is
500      * active.
501      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
502      */
getPriorityCategoryMedia()503     public @State int getPriorityCategoryMedia() {
504         return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA);
505     }
506 
507     /**
508      * Whether this policy wants to allow system sounds when DND is active.
509      * When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active.
510      * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
511      */
getPriorityCategorySystem()512     public @State int getPriorityCategorySystem() {
513         return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM);
514     }
515 
516     /**
517      * Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from
518      * notifications intercepted by DND.
519      */
getVisualEffectFullScreenIntent()520     public @State int getVisualEffectFullScreenIntent() {
521         return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT);
522     }
523 
524     /**
525      * Whether this policy allows {@link NotificationChannel#shouldShowLights() notification
526      * lights} from notifications intercepted by DND.
527      */
getVisualEffectLights()528     public @State int getVisualEffectLights() {
529         return mVisualEffects.get(VISUAL_EFFECT_LIGHTS);
530     }
531 
532     /**
533      * Whether this policy allows peeking from notifications intercepted by DND.
534      */
getVisualEffectPeek()535     public @State int getVisualEffectPeek() {
536         return mVisualEffects.get(VISUAL_EFFECT_PEEK);
537     }
538 
539     /**
540      * Whether this policy allows notifications intercepted by DND from appearing in the status bar
541      * on devices that support status bars.
542      */
getVisualEffectStatusBar()543     public @State int getVisualEffectStatusBar() {
544         return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR);
545     }
546 
547     /**
548      * Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from
549      * notifications intercepted by DND on devices that support badging.
550      */
getVisualEffectBadge()551     public @State int getVisualEffectBadge() {
552         return mVisualEffects.get(VISUAL_EFFECT_BADGE);
553     }
554 
555     /**
556      * Whether this policy allows notifications intercepted by DND from appearing on ambient
557      * displays on devices that support ambient display.
558      */
getVisualEffectAmbient()559     public @State int getVisualEffectAmbient() {
560         return mVisualEffects.get(VISUAL_EFFECT_AMBIENT);
561     }
562 
563     /**
564      * Whether this policy allows notifications intercepted by DND from appearing in notification
565      * list views like the notification shade or lockscreen on devices that support those
566      * views.
567      */
getVisualEffectNotificationList()568     public @State int getVisualEffectNotificationList() {
569         return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST);
570     }
571 
572     /**
573      * @hide
574      */
getAllowedChannels()575     public @ChannelType int getAllowedChannels() {
576         return mAllowChannels;
577     }
578 
579     /**
580      * Whether this policy allows {@link NotificationChannel channels} marked as
581      * {@link NotificationChannel#canBypassDnd()} to bypass DND. If {@link #STATE_ALLOW}, these
582      * channels may bypass; if {@link #STATE_DISALLOW}, then even notifications from channels
583      * with {@link NotificationChannel#canBypassDnd()} will be intercepted.
584      */
585     @FlaggedApi(Flags.FLAG_MODES_API)
getPriorityChannelsAllowed()586     public @State int getPriorityChannelsAllowed() {
587         switch (mAllowChannels) {
588             case CHANNEL_POLICY_PRIORITY:
589                 return STATE_ALLOW;
590             case CHANNEL_POLICY_NONE:
591                 return STATE_DISALLOW;
592             default:
593                 return STATE_UNSET;
594         }
595     }
596 
597     /**
598      * Whether this policy hides all visual effects
599      * @hide
600      */
shouldHideAllVisualEffects()601     public boolean shouldHideAllVisualEffects() {
602         for (int i = 0; i < mVisualEffects.size(); i++) {
603             if (mVisualEffects.get(i) != STATE_DISALLOW) {
604                 return false;
605             }
606         }
607         return true;
608     }
609 
610     /**
611      * Whether this policy shows all visual effects
612      * @hide
613      */
shouldShowAllVisualEffects()614     public boolean shouldShowAllVisualEffects() {
615         for (int i = 0; i < mVisualEffects.size(); i++) {
616             if (mVisualEffects.get(i) != STATE_ALLOW) {
617                 return false;
618             }
619         }
620         return true;
621     }
622 
623     /**
624      * Builder class for {@link ZenPolicy} objects.
625      * Provides a convenient way to set the various fields of a {@link ZenPolicy}.  If a field
626      * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
627      */
628     public static final class Builder {
629         private ZenPolicy mZenPolicy;
630 
Builder()631         public Builder() {
632             mZenPolicy = new ZenPolicy();
633         }
634 
635         /**
636          * @hide
637          */
638         @SuppressLint("UnflaggedApi")
639         @TestApi
Builder(@ullable ZenPolicy policy)640         public Builder(@Nullable ZenPolicy policy) {
641             if (policy != null) {
642                 mZenPolicy = policy.copy();
643             } else {
644                 mZenPolicy = new ZenPolicy();
645             }
646         }
647 
648         /**
649          * Builds the current ZenPolicy.
650          */
build()651         public @NonNull ZenPolicy build() {
652             if (Flags.modesApi()) {
653                 return new ZenPolicy(new ArrayList<>(mZenPolicy.mPriorityCategories),
654                         new ArrayList<>(mZenPolicy.mVisualEffects),
655                         mZenPolicy.mPriorityMessages, mZenPolicy.mPriorityCalls,
656                         mZenPolicy.mConversationSenders, mZenPolicy.mAllowChannels);
657             } else {
658                 return mZenPolicy.copy();
659             }
660         }
661 
662         /**
663          * Allows all notifications to bypass DND and unmutes all streams.
664          */
allowAllSounds()665         public @NonNull Builder allowAllSounds() {
666             for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
667                 mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW);
668             }
669             mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE;
670             mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE;
671             mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_ANYONE;
672             return this;
673         }
674 
675         /**
676          * Intercepts all notifications and prevents them from playing sounds
677          * when DND is active. Also mutes alarm, system and media streams.
678          * Notification channels can still play sounds only if they
679          * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND,
680          * the ringer stream is also muted.
681          */
disallowAllSounds()682         public @NonNull Builder disallowAllSounds() {
683             for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
684                 mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW);
685             }
686             mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
687             mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
688             mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE;
689             return this;
690         }
691 
692         /**
693          * Allows notifications intercepted by DND to show on all surfaces when DND is active.
694          */
showAllVisualEffects()695         public @NonNull Builder showAllVisualEffects() {
696             for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
697                 mZenPolicy.mVisualEffects.set(i, STATE_ALLOW);
698             }
699             return this;
700         }
701 
702         /**
703          * Disallows notifications intercepted by DND from showing when DND is active.
704          */
hideAllVisualEffects()705         public @NonNull Builder hideAllVisualEffects() {
706             for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
707                 mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW);
708             }
709             return this;
710         }
711 
712         /**
713          * Unsets a priority category, neither allowing or disallowing. When applying this policy,
714          * unset categories will default to the current applied policy.
715          * @hide
716          */
unsetPriorityCategory(@riorityCategory int category)717         public @NonNull Builder unsetPriorityCategory(@PriorityCategory int category) {
718             mZenPolicy.mPriorityCategories.set(category, STATE_UNSET);
719 
720             if (category == PRIORITY_CATEGORY_MESSAGES) {
721                 mZenPolicy.mPriorityMessages = PEOPLE_TYPE_UNSET;
722             } else if (category == PRIORITY_CATEGORY_CALLS) {
723                 mZenPolicy.mPriorityCalls = PEOPLE_TYPE_UNSET;
724             } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
725                 mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_UNSET;
726             }
727 
728             return this;
729         }
730 
731         /**
732          * Unsets a visual effect, neither allowing or disallowing. When applying this policy,
733          * unset effects will default to the current applied policy.
734          * @hide
735          */
unsetVisualEffect(@isualEffect int effect)736         public @NonNull Builder unsetVisualEffect(@VisualEffect int effect) {
737             mZenPolicy.mVisualEffects.set(effect, STATE_UNSET);
738             return this;
739         }
740 
741         /**
742          * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER}
743          * to play sounds and visually appear or to intercept them when DND is active.
744          */
allowReminders(boolean allow)745         public @NonNull Builder allowReminders(boolean allow) {
746             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS,
747                     allow ? STATE_ALLOW : STATE_DISALLOW);
748             return this;
749         }
750 
751         /**
752          * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT}
753          * to play sounds and visually appear or to intercept them when DND is active.
754          */
allowEvents(boolean allow)755         public @NonNull Builder allowEvents(boolean allow) {
756             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS,
757                     allow ? STATE_ALLOW : STATE_DISALLOW);
758             return this;
759         }
760 
761         /**
762          * Whether to allow conversation notifications
763          * (see {@link NotificationChannel#setConversationId(String, String)})
764          * that match audienceType to play sounds and visually appear or to intercept
765          * them when DND is active.
766          * @param audienceType callers that are allowed to bypass DND
767          */
allowConversations(@onversationSenders int audienceType)768         public @NonNull  Builder allowConversations(@ConversationSenders int audienceType) {
769             if (audienceType == STATE_UNSET) {
770                 return unsetPriorityCategory(PRIORITY_CATEGORY_CONVERSATIONS);
771             }
772 
773             if (audienceType == CONVERSATION_SENDERS_NONE) {
774                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_DISALLOW);
775             } else if (audienceType == CONVERSATION_SENDERS_ANYONE
776                     || audienceType == CONVERSATION_SENDERS_IMPORTANT) {
777                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_ALLOW);
778             } else {
779                 return this;
780             }
781 
782             mZenPolicy.mConversationSenders = audienceType;
783             return this;
784         }
785 
786         /**
787          * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE}
788          * that match audienceType to play sounds and visually appear or to intercept
789          * them when DND is active.
790          * @param audienceType message senders that are allowed to bypass DND
791          */
allowMessages(@eopleType int audienceType)792         public @NonNull Builder allowMessages(@PeopleType int audienceType) {
793             if (audienceType == STATE_UNSET) {
794                 return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES);
795             }
796 
797             if (audienceType == PEOPLE_TYPE_NONE) {
798                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW);
799             } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
800                     || audienceType == PEOPLE_TYPE_STARRED) {
801                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW);
802             } else {
803                 return this;
804             }
805 
806             mZenPolicy.mPriorityMessages = audienceType;
807             return this;
808         }
809 
810         /**
811          * Whether to allow notifications with category {@link Notification#CATEGORY_CALL}
812          * that match audienceType to play sounds and visually appear or to intercept
813          * them when DND is active.
814          * @param audienceType callers that are allowed to bypass DND
815          */
allowCalls(@eopleType int audienceType)816         public @NonNull  Builder allowCalls(@PeopleType int audienceType) {
817             if (audienceType == STATE_UNSET) {
818                 return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS);
819             }
820 
821             if (audienceType == PEOPLE_TYPE_NONE) {
822                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW);
823             } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
824                     || audienceType == PEOPLE_TYPE_STARRED) {
825                 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW);
826             } else {
827                 return this;
828             }
829 
830             mZenPolicy.mPriorityCalls = audienceType;
831             return this;
832         }
833 
834         /**
835          * Whether to allow repeat callers (notifications with category
836          * {@link Notification#CATEGORY_CALL} that have recently called
837          * to play sounds and visually appear.
838          */
allowRepeatCallers(boolean allow)839         public @NonNull Builder allowRepeatCallers(boolean allow) {
840             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS,
841                     allow ? STATE_ALLOW : STATE_DISALLOW);
842             return this;
843         }
844 
845         /**
846          * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM}
847          * to play sounds and visually appear or to intercept them when DND is active.
848          * Disallowing alarms will mute the alarm stream when DND is active.
849          */
allowAlarms(boolean allow)850         public @NonNull Builder allowAlarms(boolean allow) {
851             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS,
852                     allow ? STATE_ALLOW : STATE_DISALLOW);
853             return this;
854         }
855 
856         /**
857          * Whether to allow media notifications to play sounds and visually
858          * appear or to intercept them when DND is active.
859          * Disallowing media will mute the media stream when DND is active.
860          */
allowMedia(boolean allow)861         public @NonNull Builder allowMedia(boolean allow) {
862             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA,
863                     allow ? STATE_ALLOW : STATE_DISALLOW);
864             return this;
865         }
866 
867         /**
868          * Whether to allow system sounds to play when DND is active.
869          * Disallowing system sounds will mute the system stream when DND is active.
870          */
allowSystem(boolean allow)871         public @NonNull Builder allowSystem(boolean allow) {
872             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM,
873                     allow ? STATE_ALLOW : STATE_DISALLOW);
874             return this;
875         }
876 
877         /**
878          * Whether to allow {@link PriorityCategory} sounds to play when DND is active.
879          * @hide
880          */
allowCategory(@riorityCategory int category, boolean allow)881         public @NonNull Builder allowCategory(@PriorityCategory int category, boolean allow) {
882             switch (category) {
883                 case PRIORITY_CATEGORY_ALARMS:
884                     allowAlarms(allow);
885                     break;
886                 case PRIORITY_CATEGORY_MEDIA:
887                     allowMedia(allow);
888                     break;
889                 case PRIORITY_CATEGORY_SYSTEM:
890                     allowSystem(allow);
891                     break;
892                 case PRIORITY_CATEGORY_REMINDERS:
893                     allowReminders(allow);
894                     break;
895                 case PRIORITY_CATEGORY_EVENTS:
896                     allowEvents(allow);
897                     break;
898                 case PRIORITY_CATEGORY_REPEAT_CALLERS:
899                     allowRepeatCallers(allow);
900                     break;
901             }
902             return this;
903         }
904 
905         /**
906          * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
907          * by DND are shown.
908          */
showFullScreenIntent(boolean show)909         public @NonNull Builder showFullScreenIntent(boolean show) {
910             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT,
911                     show ? STATE_ALLOW : STATE_DISALLOW);
912             return this;
913         }
914 
915         /**
916          * Whether {@link NotificationChannel#shouldShowLights() notification lights} from
917          * notifications intercepted by DND are blocked.
918          */
showLights(boolean show)919         public @NonNull Builder showLights(boolean show) {
920             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS,
921                     show ? STATE_ALLOW : STATE_DISALLOW);
922             return this;
923         }
924 
925         /**
926          * Whether notifications intercepted by DND are prevented from peeking.
927          */
showPeeking(boolean show)928         public @NonNull Builder showPeeking(boolean show) {
929             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK,
930                     show ? STATE_ALLOW : STATE_DISALLOW);
931             return this;
932         }
933 
934         /**
935          * Whether notifications intercepted by DND are prevented from appearing in the status bar
936          * on devices that support status bars.
937          */
showStatusBarIcons(boolean show)938         public @NonNull Builder showStatusBarIcons(boolean show) {
939             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR,
940                     show ? STATE_ALLOW : STATE_DISALLOW);
941             return this;
942         }
943 
944         /**
945          * Whether {@link NotificationChannel#canShowBadge() badges} from
946          * notifications intercepted by DND are allowed on devices that support badging.
947          */
showBadges(boolean show)948         public @NonNull Builder showBadges(boolean show) {
949             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE,
950                     show ? STATE_ALLOW : STATE_DISALLOW);
951             return this;
952         }
953 
954         /**
955          * Whether notification intercepted by DND are prevented from appearing on ambient displays
956          * on devices that support ambient display.
957          */
showInAmbientDisplay(boolean show)958         public @NonNull Builder showInAmbientDisplay(boolean show) {
959             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT,
960                     show ? STATE_ALLOW : STATE_DISALLOW);
961             return this;
962         }
963 
964         /**
965          * Whether notification intercepted by DND are prevented from appearing in notification
966          * list views like the notification shade or lockscreen on devices that support those
967          * views.
968          */
showInNotificationList(boolean show)969         public @NonNull Builder showInNotificationList(boolean show) {
970             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST,
971                     show ? STATE_ALLOW : STATE_DISALLOW);
972             return this;
973         }
974 
975         /**
976          * Whether notifications intercepted by DND are prevented from appearing for
977          * {@link VisualEffect}
978          * @hide
979          */
showVisualEffect(@isualEffect int effect, boolean show)980         public @NonNull Builder showVisualEffect(@VisualEffect int effect, boolean show) {
981             switch (effect) {
982                 case VISUAL_EFFECT_FULL_SCREEN_INTENT:
983                     showFullScreenIntent(show);
984                     break;
985                 case VISUAL_EFFECT_LIGHTS:
986                     showLights(show);
987                     break;
988                 case VISUAL_EFFECT_PEEK:
989                     showPeeking(show);
990                     break;
991                 case VISUAL_EFFECT_STATUS_BAR:
992                     showStatusBarIcons(show);
993                     break;
994                 case VISUAL_EFFECT_BADGE:
995                     showBadges(show);
996                     break;
997                 case VISUAL_EFFECT_AMBIENT:
998                     showInAmbientDisplay(show);
999                     break;
1000                 case VISUAL_EFFECT_NOTIFICATION_LIST:
1001                     showInNotificationList(show);
1002                     break;
1003             }
1004             return this;
1005         }
1006 
1007         /**
1008          * Set whether priority channels are permitted to break through DND.
1009          */
1010         @SuppressLint("BuilderSetStyle")
1011         @FlaggedApi(Flags.FLAG_MODES_API)
allowPriorityChannels(boolean allow)1012         public @NonNull Builder allowPriorityChannels(boolean allow) {
1013             mZenPolicy.mAllowChannels = allow ? CHANNEL_POLICY_PRIORITY : CHANNEL_POLICY_NONE;
1014             return this;
1015         }
1016 
1017         /** @hide */
allowChannels(@hannelType int channelType)1018         public @NonNull Builder allowChannels(@ChannelType int channelType) {
1019             mZenPolicy.mAllowChannels = channelType;
1020             return this;
1021         }
1022     }
1023 
1024     @Override
describeContents()1025     public int describeContents() {
1026         return 0;
1027     }
1028 
1029     @Override
writeToParcel(Parcel dest, int flags)1030     public void writeToParcel(Parcel dest, int flags) {
1031         dest.writeList(mPriorityCategories);
1032         dest.writeList(mVisualEffects);
1033         dest.writeInt(mPriorityMessages);
1034         dest.writeInt(mPriorityCalls);
1035         dest.writeInt(mConversationSenders);
1036         if (Flags.modesApi()) {
1037             dest.writeInt(mAllowChannels);
1038         }
1039     }
1040 
1041     public static final @NonNull Creator<ZenPolicy> CREATOR =
1042             new Creator<ZenPolicy>() {
1043                 @Override
1044                 public ZenPolicy createFromParcel(Parcel source) {
1045                     ZenPolicy policy;
1046                     if (Flags.modesApi()) {
1047                         policy = new ZenPolicy(
1048                                 trimList(source.readArrayList(Integer.class.getClassLoader(),
1049                                         Integer.class), NUM_PRIORITY_CATEGORIES),
1050                                 trimList(source.readArrayList(Integer.class.getClassLoader(),
1051                                         Integer.class), NUM_VISUAL_EFFECTS),
1052                                 source.readInt(), source.readInt(), source.readInt(),
1053                                 source.readInt()
1054                         );
1055                     } else {
1056                         policy = new ZenPolicy();
1057                         policy.mPriorityCategories =
1058                                 trimList(source.readArrayList(Integer.class.getClassLoader(),
1059                                         Integer.class), NUM_PRIORITY_CATEGORIES);
1060                         policy.mVisualEffects =
1061                                 trimList(source.readArrayList(Integer.class.getClassLoader(),
1062                                         Integer.class), NUM_VISUAL_EFFECTS);
1063                         policy.mPriorityMessages = source.readInt();
1064                         policy.mPriorityCalls = source.readInt();
1065                         policy.mConversationSenders = source.readInt();
1066                     }
1067                     return policy;
1068                 }
1069 
1070                 @Override
1071                 public ZenPolicy[] newArray(int size) {
1072                     return new ZenPolicy[size];
1073                 }
1074             };
1075 
1076     @Override
toString()1077     public String toString() {
1078         StringBuilder sb = new StringBuilder(ZenPolicy.class.getSimpleName())
1079                 .append('{')
1080                 .append("priorityCategories=[").append(priorityCategoriesToString())
1081                 .append("], visualEffects=[").append(visualEffectsToString())
1082                 .append("], priorityCallsSenders=").append(peopleTypeToString(mPriorityCalls))
1083                 .append(", priorityMessagesSenders=").append(peopleTypeToString(mPriorityMessages))
1084                 .append(", priorityConversationSenders=").append(
1085                         conversationTypeToString(mConversationSenders));
1086         if (Flags.modesApi()) {
1087             sb.append(", allowChannels=").append(channelTypeToString(mAllowChannels));
1088         }
1089         return sb.append('}').toString();
1090     }
1091 
1092     /** @hide */
fieldsToString(@odifiableField int bitmask)1093     public static String fieldsToString(@ModifiableField int bitmask) {
1094         ArrayList<String> modified = new ArrayList<>();
1095         if ((bitmask & FIELD_MESSAGES) != 0) {
1096             modified.add("FIELD_MESSAGES");
1097         }
1098         if ((bitmask & FIELD_CALLS) != 0) {
1099             modified.add("FIELD_CALLS");
1100         }
1101         if ((bitmask & FIELD_CONVERSATIONS) != 0) {
1102             modified.add("FIELD_CONVERSATIONS");
1103         }
1104         if ((bitmask & FIELD_ALLOW_CHANNELS) != 0) {
1105             modified.add("FIELD_ALLOW_CHANNELS");
1106         }
1107         if ((bitmask & FIELD_PRIORITY_CATEGORY_REMINDERS) != 0) {
1108             modified.add("FIELD_PRIORITY_CATEGORY_REMINDERS");
1109         }
1110         if ((bitmask & FIELD_PRIORITY_CATEGORY_EVENTS) != 0) {
1111             modified.add("FIELD_PRIORITY_CATEGORY_EVENTS");
1112         }
1113         if ((bitmask & FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS) != 0) {
1114             modified.add("FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS");
1115         }
1116         if ((bitmask & FIELD_PRIORITY_CATEGORY_ALARMS) != 0) {
1117             modified.add("FIELD_PRIORITY_CATEGORY_ALARMS");
1118         }
1119         if ((bitmask & FIELD_PRIORITY_CATEGORY_MEDIA) != 0) {
1120             modified.add("FIELD_PRIORITY_CATEGORY_MEDIA");
1121         }
1122         if ((bitmask & FIELD_PRIORITY_CATEGORY_SYSTEM) != 0) {
1123             modified.add("FIELD_PRIORITY_CATEGORY_SYSTEM");
1124         }
1125         if ((bitmask & FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT) != 0) {
1126             modified.add("FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT");
1127         }
1128         if ((bitmask & FIELD_VISUAL_EFFECT_LIGHTS) != 0) {
1129             modified.add("FIELD_VISUAL_EFFECT_LIGHTS");
1130         }
1131         if ((bitmask & FIELD_VISUAL_EFFECT_PEEK) != 0) {
1132             modified.add("FIELD_VISUAL_EFFECT_PEEK");
1133         }
1134         if ((bitmask & FIELD_VISUAL_EFFECT_STATUS_BAR) != 0) {
1135             modified.add("FIELD_VISUAL_EFFECT_STATUS_BAR");
1136         }
1137         if ((bitmask & FIELD_VISUAL_EFFECT_BADGE) != 0) {
1138             modified.add("FIELD_VISUAL_EFFECT_BADGE");
1139         }
1140         if ((bitmask & FIELD_VISUAL_EFFECT_AMBIENT) != 0) {
1141             modified.add("FIELD_VISUAL_EFFECT_AMBIENT");
1142         }
1143         if ((bitmask & FIELD_VISUAL_EFFECT_NOTIFICATION_LIST) != 0) {
1144             modified.add("FIELD_VISUAL_EFFECT_NOTIFICATION_LIST");
1145         }
1146         return "{" + String.join(",", modified) + "}";
1147     }
1148 
1149     // Returns a list containing the first maxLength elements of the input list if the list is
1150     // longer than that size. For the lists in ZenPolicy, this should not happen unless the input
1151     // is corrupt.
trimList(ArrayList<Integer> list, int maxLength)1152     private static ArrayList<Integer> trimList(ArrayList<Integer> list, int maxLength) {
1153         if (list == null || list.size() <= maxLength) {
1154             return list;
1155         }
1156         return new ArrayList<>(list.subList(0, maxLength));
1157     }
1158 
priorityCategoriesToString()1159     private String priorityCategoriesToString() {
1160         StringBuilder builder = new StringBuilder();
1161         for (int i = 0; i < mPriorityCategories.size(); i++) {
1162             if (mPriorityCategories.get(i) != STATE_UNSET) {
1163                 builder.append(indexToCategory(i))
1164                         .append("=")
1165                         .append(stateToString(mPriorityCategories.get(i)))
1166                         .append(" ");
1167             }
1168 
1169         }
1170         return builder.toString();
1171     }
1172 
visualEffectsToString()1173     private String visualEffectsToString() {
1174         StringBuilder builder = new StringBuilder();
1175         for (int i = 0; i < mVisualEffects.size(); i++) {
1176             if (mVisualEffects.get(i) != STATE_UNSET) {
1177                 builder.append(indexToVisualEffect(i))
1178                         .append("=")
1179                         .append(stateToString(mVisualEffects.get(i)))
1180                         .append(" ");
1181             }
1182 
1183         }
1184         return builder.toString();
1185     }
1186 
indexToVisualEffect(@isualEffect int visualEffectIndex)1187     private String indexToVisualEffect(@VisualEffect int visualEffectIndex) {
1188         switch (visualEffectIndex) {
1189             case VISUAL_EFFECT_FULL_SCREEN_INTENT:
1190                 return "fullScreenIntent";
1191             case VISUAL_EFFECT_LIGHTS:
1192                 return "lights";
1193             case VISUAL_EFFECT_PEEK:
1194                 return "peek";
1195             case VISUAL_EFFECT_STATUS_BAR:
1196                 return "statusBar";
1197             case VISUAL_EFFECT_BADGE:
1198                 return "badge";
1199             case VISUAL_EFFECT_AMBIENT:
1200                 return "ambient";
1201             case VISUAL_EFFECT_NOTIFICATION_LIST:
1202                 return "notificationList";
1203         }
1204         return null;
1205     }
1206 
indexToCategory(@riorityCategory int categoryIndex)1207     private String indexToCategory(@PriorityCategory int categoryIndex) {
1208         switch (categoryIndex) {
1209             case PRIORITY_CATEGORY_REMINDERS:
1210                 return "reminders";
1211             case PRIORITY_CATEGORY_EVENTS:
1212                 return "events";
1213             case PRIORITY_CATEGORY_MESSAGES:
1214                 return "messages";
1215             case PRIORITY_CATEGORY_CALLS:
1216                 return "calls";
1217             case PRIORITY_CATEGORY_REPEAT_CALLERS:
1218                 return "repeatCallers";
1219             case PRIORITY_CATEGORY_ALARMS:
1220                 return "alarms";
1221             case PRIORITY_CATEGORY_MEDIA:
1222                 return "media";
1223             case PRIORITY_CATEGORY_SYSTEM:
1224                 return "system";
1225             case PRIORITY_CATEGORY_CONVERSATIONS:
1226                 return "convs";
1227         }
1228         return null;
1229     }
1230 
stateToString(@tate int state)1231     private String stateToString(@State int state) {
1232         switch (state) {
1233             case STATE_UNSET:
1234                 return "unset";
1235             case STATE_DISALLOW:
1236                 return "disallow";
1237             case STATE_ALLOW:
1238                 return "allow";
1239         }
1240         return "invalidState{" + state + "}";
1241     }
1242 
peopleTypeToString(@eopleType int peopleType)1243     private String peopleTypeToString(@PeopleType int peopleType) {
1244         switch (peopleType) {
1245             case PEOPLE_TYPE_ANYONE:
1246                 return "anyone";
1247             case PEOPLE_TYPE_CONTACTS:
1248                 return "contacts";
1249             case PEOPLE_TYPE_NONE:
1250                 return "none";
1251             case PEOPLE_TYPE_STARRED:
1252                 return "starred_contacts";
1253             case STATE_UNSET:
1254                 return "unset";
1255         }
1256         return "invalidPeopleType{" + peopleType + "}";
1257     }
1258 
1259     /**
1260      * @hide
1261      */
conversationTypeToString(@onversationSenders int conversationType)1262     public static String conversationTypeToString(@ConversationSenders int conversationType) {
1263         switch (conversationType) {
1264             case CONVERSATION_SENDERS_ANYONE:
1265                 return "anyone";
1266             case CONVERSATION_SENDERS_IMPORTANT:
1267                 return "important";
1268             case CONVERSATION_SENDERS_NONE:
1269                 return "none";
1270             case CONVERSATION_SENDERS_UNSET:
1271                 return "unset";
1272         }
1273         return "invalidConversationType{" + conversationType + "}";
1274     }
1275 
1276     /**
1277      * @hide
1278      */
1279     @FlaggedApi(Flags.FLAG_MODES_API)
channelTypeToString(@hannelType int channelType)1280     public static String channelTypeToString(@ChannelType int channelType) {
1281         switch (channelType) {
1282             case CHANNEL_POLICY_UNSET:
1283                 return "unset";
1284             case CHANNEL_POLICY_PRIORITY:
1285                 return "priority";
1286             case CHANNEL_POLICY_NONE:
1287                 return "none";
1288         }
1289         return "invalidChannelType{" + channelType + "}";
1290     }
1291 
1292     @Override
equals(@ullable Object o)1293     public boolean equals(@Nullable Object o) {
1294         if (!(o instanceof ZenPolicy)) return false;
1295         if (o == this) return true;
1296         final ZenPolicy other = (ZenPolicy) o;
1297 
1298         boolean eq = Objects.equals(other.mPriorityCategories, mPriorityCategories)
1299                 && Objects.equals(other.mVisualEffects, mVisualEffects)
1300                 && other.mPriorityCalls == mPriorityCalls
1301                 && other.mPriorityMessages == mPriorityMessages
1302                 && other.mConversationSenders == mConversationSenders;
1303         if (Flags.modesApi()) {
1304             return eq && other.mAllowChannels == mAllowChannels;
1305         }
1306         return eq;
1307     }
1308 
1309     @Override
hashCode()1310     public int hashCode() {
1311         if (Flags.modesApi()) {
1312             return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls,
1313                     mPriorityMessages, mConversationSenders, mAllowChannels);
1314         }
1315         return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
1316                 mConversationSenders);
1317     }
1318 
getZenPolicyPriorityCategoryState(@riorityCategory int category)1319     private @State int getZenPolicyPriorityCategoryState(@PriorityCategory int
1320             category) {
1321         switch (category) {
1322             case PRIORITY_CATEGORY_REMINDERS:
1323                 return getPriorityCategoryReminders();
1324             case PRIORITY_CATEGORY_EVENTS:
1325                 return getPriorityCategoryEvents();
1326             case PRIORITY_CATEGORY_MESSAGES:
1327                 return getPriorityCategoryMessages();
1328             case PRIORITY_CATEGORY_CALLS:
1329                 return getPriorityCategoryCalls();
1330             case PRIORITY_CATEGORY_REPEAT_CALLERS:
1331                 return getPriorityCategoryRepeatCallers();
1332             case PRIORITY_CATEGORY_ALARMS:
1333                 return getPriorityCategoryAlarms();
1334             case PRIORITY_CATEGORY_MEDIA:
1335                 return getPriorityCategoryMedia();
1336             case PRIORITY_CATEGORY_SYSTEM:
1337                 return getPriorityCategorySystem();
1338             case PRIORITY_CATEGORY_CONVERSATIONS:
1339                 return getPriorityCategoryConversations();
1340         }
1341         return -1;
1342     }
1343 
getZenPolicyVisualEffectState(@isualEffect int effect)1344     private @State int getZenPolicyVisualEffectState(@VisualEffect int effect) {
1345         switch (effect) {
1346             case VISUAL_EFFECT_FULL_SCREEN_INTENT:
1347                 return getVisualEffectFullScreenIntent();
1348             case VISUAL_EFFECT_LIGHTS:
1349                 return getVisualEffectLights();
1350             case VISUAL_EFFECT_PEEK:
1351                 return getVisualEffectPeek();
1352             case VISUAL_EFFECT_STATUS_BAR:
1353                 return getVisualEffectStatusBar();
1354             case VISUAL_EFFECT_BADGE:
1355                 return getVisualEffectBadge();
1356             case VISUAL_EFFECT_AMBIENT:
1357                 return getVisualEffectAmbient();
1358             case VISUAL_EFFECT_NOTIFICATION_LIST:
1359                 return getVisualEffectNotificationList();
1360         }
1361         return -1;
1362     }
1363 
1364     /** @hide */
stateToBoolean(@tate int state, boolean defaultVal)1365     public static boolean stateToBoolean(@State int state, boolean defaultVal) {
1366         switch (state) {
1367             case STATE_ALLOW:
1368                 return true;
1369             case STATE_DISALLOW:
1370                 return false;
1371             default:
1372                 return defaultVal;
1373         }
1374     }
1375 
1376     /** @hide */
isCategoryAllowed(@riorityCategory int category, boolean defaultVal)1377     public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) {
1378         return stateToBoolean(getZenPolicyPriorityCategoryState(category), defaultVal);
1379     }
1380 
1381     /** @hide */
isVisualEffectAllowed(@isualEffect int effect, boolean defaultVal)1382     public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) {
1383         return stateToBoolean(getZenPolicyVisualEffectState(effect), defaultVal);
1384     }
1385 
1386     /**
1387      * Applies another policy on top of this policy. For each field, the resulting policy will have
1388      * most restrictive setting that is set of the two policies (if only one has a field set, the
1389      * result will inherit that policy's setting).
1390      *
1391      * @hide
1392      */
apply(ZenPolicy policyToApply)1393     public void apply(ZenPolicy policyToApply) {
1394         if (policyToApply == null) {
1395             return;
1396         }
1397 
1398         // apply priority categories
1399         for (int category = 0; category < mPriorityCategories.size(); category++) {
1400             if (mPriorityCategories.get(category) == STATE_DISALLOW) {
1401                 // if a priority category is already disallowed by the policy, cannot allow
1402                 continue;
1403             }
1404 
1405             @State int newState = policyToApply.mPriorityCategories.get(category);
1406             if (newState != STATE_UNSET) {
1407                 mPriorityCategories.set(category, newState);
1408 
1409                 if (category == PRIORITY_CATEGORY_MESSAGES
1410                         && mPriorityMessages < policyToApply.mPriorityMessages) {
1411                     mPriorityMessages = policyToApply.mPriorityMessages;
1412                 } else if (category == PRIORITY_CATEGORY_CALLS
1413                         && mPriorityCalls < policyToApply.mPriorityCalls) {
1414                     mPriorityCalls = policyToApply.mPriorityCalls;
1415                 } else if (category == PRIORITY_CATEGORY_CONVERSATIONS
1416                         && mConversationSenders < policyToApply.mConversationSenders) {
1417                     mConversationSenders = policyToApply.mConversationSenders;
1418                 }
1419             }
1420         }
1421 
1422         // apply visual effects
1423         for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) {
1424             if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) {
1425                 // if a visual effect is already disallowed by the policy, cannot allow
1426                 continue;
1427             }
1428 
1429             if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) {
1430                 mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect));
1431             }
1432         }
1433 
1434         // apply allowed channels
1435         if (Flags.modesApi()) {
1436             // if no channels are allowed, can't newly allow them
1437             if (mAllowChannels != CHANNEL_POLICY_NONE
1438                     && policyToApply.mAllowChannels != CHANNEL_POLICY_UNSET) {
1439                 mAllowChannels = policyToApply.mAllowChannels;
1440             }
1441         }
1442     }
1443 
1444     /**
1445      * Overwrites any policy values in this ZenPolicy with set values from newPolicy and
1446      * returns a copy of the resulting ZenPolicy.
1447      * Unlike apply(), values set in newPolicy will always be kept over pre-existing
1448      * fields. Any values in newPolicy that are not set keep their currently set values.
1449      *
1450      * @hide
1451      */
1452     @TestApi
1453     @FlaggedApi(Flags.FLAG_MODES_API)
overwrittenWith(@ullable ZenPolicy newPolicy)1454     public @NonNull ZenPolicy overwrittenWith(@Nullable ZenPolicy newPolicy) {
1455         ZenPolicy result = this.copy();
1456 
1457         if (newPolicy == null) {
1458             return result;
1459         }
1460 
1461         // set priority categories
1462         for (int category = 0; category < mPriorityCategories.size(); category++) {
1463             @State int newState = newPolicy.mPriorityCategories.get(category);
1464             if (newState != STATE_UNSET) {
1465                 result.mPriorityCategories.set(category, newState);
1466 
1467                 if (category == PRIORITY_CATEGORY_MESSAGES) {
1468                     result.mPriorityMessages = newPolicy.mPriorityMessages;
1469                 } else if (category == PRIORITY_CATEGORY_CALLS) {
1470                     result.mPriorityCalls = newPolicy.mPriorityCalls;
1471                 } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
1472                     result.mConversationSenders = newPolicy.mConversationSenders;
1473                 }
1474             }
1475         }
1476 
1477         // set visual effects
1478         for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) {
1479             if (newPolicy.mVisualEffects.get(visualEffect) != STATE_UNSET) {
1480                 result.mVisualEffects.set(visualEffect, newPolicy.mVisualEffects.get(visualEffect));
1481             }
1482         }
1483 
1484         // set allowed channels
1485         if (newPolicy.mAllowChannels != CHANNEL_POLICY_UNSET) {
1486             result.mAllowChannels = newPolicy.mAllowChannels;
1487         }
1488 
1489         return result;
1490     }
1491 
1492     /**
1493      * @hide
1494      */
dumpDebug(ProtoOutputStream proto, long fieldId)1495     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
1496         final long token = proto.start(fieldId);
1497 
1498         proto.write(ZenPolicyProto.REMINDERS, getPriorityCategoryReminders());
1499         proto.write(ZenPolicyProto.EVENTS, getPriorityCategoryEvents());
1500         proto.write(ZenPolicyProto.MESSAGES, getPriorityCategoryMessages());
1501         proto.write(ZenPolicyProto.CALLS, getPriorityCategoryCalls());
1502         proto.write(ZenPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
1503         proto.write(ZenPolicyProto.ALARMS, getPriorityCategoryAlarms());
1504         proto.write(ZenPolicyProto.MEDIA, getPriorityCategoryMedia());
1505         proto.write(ZenPolicyProto.SYSTEM, getPriorityCategorySystem());
1506 
1507         proto.write(ZenPolicyProto.FULL_SCREEN_INTENT, getVisualEffectFullScreenIntent());
1508         proto.write(ZenPolicyProto.LIGHTS, getVisualEffectLights());
1509         proto.write(ZenPolicyProto.PEEK, getVisualEffectPeek());
1510         proto.write(ZenPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
1511         proto.write(ZenPolicyProto.BADGE, getVisualEffectBadge());
1512         proto.write(ZenPolicyProto.AMBIENT, getVisualEffectAmbient());
1513         proto.write(ZenPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
1514 
1515         proto.write(ZenPolicyProto.PRIORITY_MESSAGES, getPriorityMessageSenders());
1516         proto.write(ZenPolicyProto.PRIORITY_CALLS, getPriorityCallSenders());
1517         proto.end(token);
1518     }
1519 
1520     /**
1521      * Converts a policy to a statsd proto.
1522      * @hide
1523      */
toProto()1524     public byte[] toProto() {
1525         // TODO: b/308672510 - log user-customized ZenPolicy fields to DNDPolicyProto.
1526         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
1527         ProtoOutputStream proto = new ProtoOutputStream(bytes);
1528 
1529         proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls());
1530         proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
1531         proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages());
1532         proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations());
1533         proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders());
1534         proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents());
1535         proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms());
1536         proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia());
1537         proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem());
1538 
1539         proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent());
1540         proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights());
1541         proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek());
1542         proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
1543         proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge());
1544         proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient());
1545         proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
1546 
1547         proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders());
1548         proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
1549         proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
1550 
1551         if (Flags.modesApi()) {
1552             proto.write(DNDPolicyProto.ALLOW_CHANNELS, getPriorityChannelsAllowed());
1553         }
1554 
1555         proto.flush();
1556         return bytes.toByteArray();
1557     }
1558 
1559     /**
1560      * Makes deep copy of this ZenPolicy.
1561      * @hide
1562      */
copy()1563     public @NonNull ZenPolicy copy() {
1564         final Parcel parcel = Parcel.obtain();
1565         try {
1566             writeToParcel(parcel, 0);
1567             parcel.setDataPosition(0);
1568             return CREATOR.createFromParcel(parcel);
1569         } finally {
1570             parcel.recycle();
1571         }
1572     }
1573 }
1574