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