1 /*
2  * Copyright (C) 2016 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 package android.app;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.annotation.TestApi;
22 import android.app.NotificationManager.Importance;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ShortcutInfo;
28 import android.media.AudioAttributes;
29 import android.net.Uri;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.provider.Settings;
33 import android.service.notification.NotificationListenerService;
34 import android.text.TextUtils;
35 import android.util.Slog;
36 import android.util.TypedXmlPullParser;
37 import android.util.TypedXmlSerializer;
38 import android.util.proto.ProtoOutputStream;
39 
40 import com.android.internal.util.Preconditions;
41 import com.android.internal.util.XmlUtils;
42 
43 import org.json.JSONException;
44 import org.json.JSONObject;
45 import org.xmlpull.v1.XmlPullParser;
46 import org.xmlpull.v1.XmlSerializer;
47 
48 import java.io.IOException;
49 import java.io.PrintWriter;
50 import java.util.Arrays;
51 import java.util.Objects;
52 
53 /**
54  * A representation of settings that apply to a collection of similarly themed notifications.
55  */
56 public final class NotificationChannel implements Parcelable {
57 
58     /**
59      * The id of the default channel for an app. This id is reserved by the system. All
60      * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or
61      * earlier without a notification channel specified are posted to this channel.
62      */
63     public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
64 
65     /**
66      * The formatter used by the system to create an id for notification
67      * channels when it automatically creates conversation channels on behalf of an app. The format
68      * string takes two arguments, in this order: the
69      * {@link #getId()} of the original notification channel, and the
70      * {@link ShortcutInfo#getId() id} of the conversation.
71      * @hide
72      */
73     public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
74 
75     /**
76      * TODO: STOPSHIP  remove
77      * Conversation id to use for apps that aren't providing them yet.
78      * @hide
79      */
80     public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id";
81 
82     /**
83      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
84      * that have to do with editing sound, like a tone picker
85      * ({@link #setSound(Uri, AudioAttributes)}).
86      */
87     public static final String EDIT_SOUND = "sound";
88     /**
89      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
90      * that have to do with editing vibration ({@link #enableVibration(boolean)},
91      * {@link #setVibrationPattern(long[])}).
92      */
93     public static final String EDIT_VIBRATION = "vibration";
94     /**
95      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
96      * that have to do with editing importance ({@link #setImportance(int)}) and/or conversation
97      * priority.
98      */
99     public static final String EDIT_IMPORTANCE = "importance";
100     /**
101      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
102      * that have to do with editing behavior on devices that are locked or have a turned off
103      * display ({@link #setLockscreenVisibility(int)}, {@link #enableLights(boolean)},
104      * {@link #setLightColor(int)}).
105      */
106     public static final String EDIT_LOCKED_DEVICE = "locked";
107     /**
108      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
109      * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) .
110      */
111     public static final String EDIT_ZEN = "zen";
112     /**
113      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
114      * that have to do with editing conversation settings (demoting or restoring a channel to
115      * be a Conversation, changing bubble behavior, or setting the priority of a conversation).
116      */
117     public static final String EDIT_CONVERSATION = "conversation";
118     /**
119      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
120      * that have to do with editing launcher behavior (showing badges)}.
121      */
122     public static final String EDIT_LAUNCHER = "launcher";
123 
124     /**
125      * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
126      * limit.
127      */
128     private static final int MAX_TEXT_LENGTH = 1000;
129 
130     private static final String TAG_CHANNEL = "channel";
131     private static final String ATT_NAME = "name";
132     private static final String ATT_DESC = "desc";
133     private static final String ATT_ID = "id";
134     private static final String ATT_DELETED = "deleted";
135     private static final String ATT_PRIORITY = "priority";
136     private static final String ATT_VISIBILITY = "visibility";
137     private static final String ATT_IMPORTANCE = "importance";
138     private static final String ATT_LIGHTS = "lights";
139     private static final String ATT_LIGHT_COLOR = "light_color";
140     private static final String ATT_VIBRATION = "vibration";
141     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
142     private static final String ATT_SOUND = "sound";
143     private static final String ATT_USAGE = "usage";
144     private static final String ATT_FLAGS = "flags";
145     private static final String ATT_CONTENT_TYPE = "content_type";
146     private static final String ATT_SHOW_BADGE = "show_badge";
147     private static final String ATT_USER_LOCKED = "locked";
148     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
149     private static final String ATT_GROUP = "group";
150     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
151     private static final String ATT_ALLOW_BUBBLE = "allow_bubbles";
152     private static final String ATT_ORIG_IMP = "orig_imp";
153     private static final String ATT_PARENT_CHANNEL = "parent";
154     private static final String ATT_CONVERSATION_ID = "conv_id";
155     private static final String ATT_IMP_CONVERSATION = "imp_conv";
156     private static final String ATT_DEMOTE = "dem";
157     private static final String ATT_DELETED_TIME_MS = "del_time";
158     private static final String DELIMITER = ",";
159 
160     /**
161      * @hide
162      */
163     public static final int USER_LOCKED_PRIORITY = 0x00000001;
164     /**
165      * @hide
166      */
167     public static final int USER_LOCKED_VISIBILITY = 0x00000002;
168     /**
169      * @hide
170      */
171     public static final int USER_LOCKED_IMPORTANCE = 0x00000004;
172     /**
173      * @hide
174      */
175     public static final int USER_LOCKED_LIGHTS = 0x00000008;
176     /**
177      * @hide
178      */
179     public static final int USER_LOCKED_VIBRATION = 0x00000010;
180     /**
181      * @hide
182      */
183     @SystemApi
184     public static final int USER_LOCKED_SOUND = 0x00000020;
185 
186     /**
187      * @hide
188      */
189     public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
190 
191     /**
192      * @hide
193      */
194     public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;
195 
196     /**
197      * @hide
198      */
199     public static final int[] LOCKABLE_FIELDS = new int[] {
200             USER_LOCKED_PRIORITY,
201             USER_LOCKED_VISIBILITY,
202             USER_LOCKED_IMPORTANCE,
203             USER_LOCKED_LIGHTS,
204             USER_LOCKED_VIBRATION,
205             USER_LOCKED_SOUND,
206             USER_LOCKED_SHOW_BADGE,
207             USER_LOCKED_ALLOW_BUBBLE
208     };
209 
210     /**
211      * @hide
212      */
213     public static final int DEFAULT_ALLOW_BUBBLE = -1;
214     /**
215      * @hide
216      */
217     public static final int ALLOW_BUBBLE_ON = 1;
218     /**
219      * @hide
220      */
221     public static final int ALLOW_BUBBLE_OFF = 0;
222 
223     private static final int DEFAULT_LIGHT_COLOR = 0;
224     private static final int DEFAULT_VISIBILITY =
225             NotificationManager.VISIBILITY_NO_OVERRIDE;
226     private static final int DEFAULT_IMPORTANCE =
227             NotificationManager.IMPORTANCE_UNSPECIFIED;
228     private static final boolean DEFAULT_DELETED = false;
229     private static final boolean DEFAULT_SHOW_BADGE = true;
230     private static final long DEFAULT_DELETION_TIME_MS = -1;
231 
232     @UnsupportedAppUsage
233     private String mId;
234     private String mName;
235     private String mDesc;
236     private int mImportance = DEFAULT_IMPORTANCE;
237     private int mOriginalImportance = DEFAULT_IMPORTANCE;
238     private boolean mBypassDnd;
239     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
240     private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
241     private boolean mLights;
242     private int mLightColor = DEFAULT_LIGHT_COLOR;
243     private long[] mVibration;
244     // Bitwise representation of fields that have been changed by the user, preventing the app from
245     // making changes to these fields.
246     private int mUserLockedFields;
247     private boolean mFgServiceShown;
248     private boolean mVibrationEnabled;
249     private boolean mShowBadge = DEFAULT_SHOW_BADGE;
250     private boolean mDeleted = DEFAULT_DELETED;
251     private String mGroup;
252     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
253     // If this is a blockable system notification channel.
254     private boolean mBlockableSystem = false;
255     private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
256     private boolean mImportanceLockedByOEM;
257     private boolean mImportanceLockedDefaultApp;
258     private String mParentId = null;
259     private String mConversationId = null;
260     private boolean mDemoted = false;
261     private boolean mImportantConvo = false;
262     private long mDeletedTime = DEFAULT_DELETION_TIME_MS;
263 
264     /**
265      * Creates a notification channel.
266      *
267      * @param id The id of the channel. Must be unique per package. The value may be truncated if
268      *           it is too long.
269      * @param name The user visible name of the channel. You can rename this channel when the system
270      *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
271      *             broadcast. The recommended maximum length is 40 characters; the value may be
272      *             truncated if it is too long.
273      * @param importance The importance of the channel. This controls how interruptive notifications
274      *                   posted to this channel are.
275      */
NotificationChannel(String id, CharSequence name, @Importance int importance)276     public NotificationChannel(String id, CharSequence name, @Importance int importance) {
277         this.mId = getTrimmedString(id);
278         this.mName = name != null ? getTrimmedString(name.toString()) : null;
279         this.mImportance = importance;
280     }
281 
282     /**
283      * @hide
284      */
NotificationChannel(Parcel in)285     protected NotificationChannel(Parcel in) {
286         if (in.readByte() != 0) {
287             mId = in.readString();
288         } else {
289             mId = null;
290         }
291         if (in.readByte() != 0) {
292             mName = in.readString();
293         } else {
294             mName = null;
295         }
296         if (in.readByte() != 0) {
297             mDesc = in.readString();
298         } else {
299             mDesc = null;
300         }
301         mImportance = in.readInt();
302         mBypassDnd = in.readByte() != 0;
303         mLockscreenVisibility = in.readInt();
304         if (in.readByte() != 0) {
305             mSound = Uri.CREATOR.createFromParcel(in);
306         } else {
307             mSound = null;
308         }
309         mLights = in.readByte() != 0;
310         mVibration = in.createLongArray();
311         mUserLockedFields = in.readInt();
312         mFgServiceShown = in.readByte() != 0;
313         mVibrationEnabled = in.readByte() != 0;
314         mShowBadge = in.readByte() != 0;
315         mDeleted = in.readByte() != 0;
316         if (in.readByte() != 0) {
317             mGroup = in.readString();
318         } else {
319             mGroup = null;
320         }
321         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
322         mLightColor = in.readInt();
323         mBlockableSystem = in.readBoolean();
324         mAllowBubbles = in.readInt();
325         mImportanceLockedByOEM = in.readBoolean();
326         mOriginalImportance = in.readInt();
327         mParentId = in.readString();
328         mConversationId = in.readString();
329         mDemoted = in.readBoolean();
330         mImportantConvo = in.readBoolean();
331         mDeletedTime = in.readLong();
332     }
333 
334     @Override
writeToParcel(Parcel dest, int flags)335     public void writeToParcel(Parcel dest, int flags) {
336         if (mId != null) {
337             dest.writeByte((byte) 1);
338             dest.writeString(mId);
339         } else {
340             dest.writeByte((byte) 0);
341         }
342         if (mName != null) {
343             dest.writeByte((byte) 1);
344             dest.writeString(mName);
345         } else {
346             dest.writeByte((byte) 0);
347         }
348         if (mDesc != null) {
349             dest.writeByte((byte) 1);
350             dest.writeString(mDesc);
351         } else {
352             dest.writeByte((byte) 0);
353         }
354         dest.writeInt(mImportance);
355         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
356         dest.writeInt(mLockscreenVisibility);
357         if (mSound != null) {
358             dest.writeByte((byte) 1);
359             mSound.writeToParcel(dest, 0);
360         } else {
361             dest.writeByte((byte) 0);
362         }
363         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
364         dest.writeLongArray(mVibration);
365         dest.writeInt(mUserLockedFields);
366         dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0);
367         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
368         dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
369         dest.writeByte(mDeleted ? (byte) 1 : (byte) 0);
370         if (mGroup != null) {
371             dest.writeByte((byte) 1);
372             dest.writeString(mGroup);
373         } else {
374             dest.writeByte((byte) 0);
375         }
376         if (mAudioAttributes != null) {
377             dest.writeInt(1);
378             mAudioAttributes.writeToParcel(dest, 0);
379         } else {
380             dest.writeInt(0);
381         }
382         dest.writeInt(mLightColor);
383         dest.writeBoolean(mBlockableSystem);
384         dest.writeInt(mAllowBubbles);
385         dest.writeBoolean(mImportanceLockedByOEM);
386         dest.writeInt(mOriginalImportance);
387         dest.writeString(mParentId);
388         dest.writeString(mConversationId);
389         dest.writeBoolean(mDemoted);
390         dest.writeBoolean(mImportantConvo);
391         dest.writeLong(mDeletedTime);
392     }
393 
394     /**
395      * @hide
396      */
397     @TestApi
lockFields(int field)398     public void lockFields(int field) {
399         mUserLockedFields |= field;
400     }
401 
402     /**
403      * @hide
404      */
unlockFields(int field)405     public void unlockFields(int field) {
406         mUserLockedFields &= ~field;
407     }
408 
409     /**
410      * @hide
411      */
412     @TestApi
setFgServiceShown(boolean shown)413     public void setFgServiceShown(boolean shown) {
414         mFgServiceShown = shown;
415     }
416 
417     /**
418      * @hide
419      */
420     @TestApi
setDeleted(boolean deleted)421     public void setDeleted(boolean deleted) {
422         mDeleted = deleted;
423     }
424 
425     /**
426      * @hide
427      */
428     @TestApi
setDeletedTimeMs(long time)429     public void setDeletedTimeMs(long time) {
430         mDeletedTime = time;
431     }
432 
433     /**
434      * @hide
435      */
436     @TestApi
setImportantConversation(boolean importantConvo)437     public void setImportantConversation(boolean importantConvo) {
438         mImportantConvo = importantConvo;
439     }
440 
441     /**
442      * Allows users to block notifications sent through this channel, if this channel belongs to
443      * a package that is signed with the system signature.
444      *
445      * If the channel does not belong to a package that is signed with the system signature, this
446      * method does nothing, since such channels are blockable by default and cannot be set to be
447      * unblockable.
448      * @param blockable if {@code true}, allows users to block notifications on this channel.
449      * @hide
450      */
451     @SystemApi
setBlockable(boolean blockable)452     public void setBlockable(boolean blockable) {
453         mBlockableSystem = blockable;
454     }
455     // Modifiable by apps post channel creation
456 
457     /**
458      * Sets the user visible name of this channel.
459      *
460      * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too
461      * long.
462      */
setName(CharSequence name)463     public void setName(CharSequence name) {
464         mName = name != null ? getTrimmedString(name.toString()) : null;
465     }
466 
467     /**
468      * Sets the user visible description of this channel.
469      *
470      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
471      * long.
472      */
setDescription(String description)473     public void setDescription(String description) {
474         mDesc = getTrimmedString(description);
475     }
476 
getTrimmedString(String input)477     private String getTrimmedString(String input) {
478         if (input != null && input.length() > MAX_TEXT_LENGTH) {
479             return input.substring(0, MAX_TEXT_LENGTH);
480         }
481         return input;
482     }
483 
484     /**
485      * @hide
486      */
setId(String id)487     public void setId(String id) {
488         mId = id;
489     }
490 
491     // Modifiable by apps on channel creation.
492 
493     /**
494      * Sets what group this channel belongs to.
495      *
496      * Group information is only used for presentation, not for behavior.
497      *
498      * Only modifiable before the channel is submitted to
499      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
500      * channel is not currently part of a group.
501      *
502      * @param groupId the id of a group created by
503      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
504      */
setGroup(String groupId)505     public void setGroup(String groupId) {
506         this.mGroup = groupId;
507     }
508 
509     /**
510      * Sets whether notifications posted to this channel can appear as application icon badges
511      * in a Launcher.
512      *
513      * Only modifiable before the channel is submitted to
514      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
515      *
516      * @param showBadge true if badges should be allowed to be shown.
517      */
setShowBadge(boolean showBadge)518     public void setShowBadge(boolean showBadge) {
519         this.mShowBadge = showBadge;
520     }
521 
522     /**
523      * Sets the sound that should be played for notifications posted to this channel and its
524      * audio attributes. Notification channels with an {@link #getImportance() importance} of at
525      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
526      *
527      * Only modifiable before the channel is submitted to
528      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
529      */
setSound(Uri sound, AudioAttributes audioAttributes)530     public void setSound(Uri sound, AudioAttributes audioAttributes) {
531         this.mSound = sound;
532         this.mAudioAttributes = audioAttributes;
533     }
534 
535     /**
536      * Sets whether notifications posted to this channel should display notification lights,
537      * on devices that support that feature.
538      *
539      * Only modifiable before the channel is submitted to
540      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
541      */
enableLights(boolean lights)542     public void enableLights(boolean lights) {
543         this.mLights = lights;
544     }
545 
546     /**
547      * Sets the notification light color for notifications posted to this channel, if lights are
548      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
549      *
550      * Only modifiable before the channel is submitted to
551      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
552      */
setLightColor(int argb)553     public void setLightColor(int argb) {
554         this.mLightColor = argb;
555     }
556 
557     /**
558      * Sets whether notification posted to this channel should vibrate. The vibration pattern can
559      * be set with {@link #setVibrationPattern(long[])}.
560      *
561      * Only modifiable before the channel is submitted to
562      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
563      */
enableVibration(boolean vibration)564     public void enableVibration(boolean vibration) {
565         this.mVibrationEnabled = vibration;
566     }
567 
568     /**
569      * Sets the vibration pattern for notifications posted to this channel. If the provided
570      * pattern is valid (non-null, non-empty), will {@link #enableVibration(boolean)} enable
571      * vibration} as well. Otherwise, vibration will be disabled.
572      *
573      * Only modifiable before the channel is submitted to
574      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
575      */
setVibrationPattern(long[] vibrationPattern)576     public void setVibrationPattern(long[] vibrationPattern) {
577         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
578         this.mVibration = vibrationPattern;
579     }
580 
581     /**
582      * Sets the level of interruption of this notification channel.
583      *
584      * Only modifiable before the channel is submitted to
585      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
586      *
587      * @param importance the amount the user should be interrupted by
588      *            notifications from this channel.
589      */
setImportance(@mportance int importance)590     public void setImportance(@Importance int importance) {
591         this.mImportance = importance;
592     }
593 
594     // Modifiable by a notification ranker.
595 
596     /**
597      * Sets whether or not notifications posted to this channel can interrupt the user in
598      * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
599      *
600      * Only modifiable by the system and notification ranker.
601      */
setBypassDnd(boolean bypassDnd)602     public void setBypassDnd(boolean bypassDnd) {
603         this.mBypassDnd = bypassDnd;
604     }
605 
606     /**
607      * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,
608      * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
609      *
610      * Only modifiable by the system and notification ranker.
611      */
setLockscreenVisibility(int lockscreenVisibility)612     public void setLockscreenVisibility(int lockscreenVisibility) {
613         this.mLockscreenVisibility = lockscreenVisibility;
614     }
615 
616     /**
617      * As of Android 11 this value is no longer respected.
618      * @see #canBubble()
619      * @see Notification#getBubbleMetadata()
620      */
setAllowBubbles(boolean allowBubbles)621     public void setAllowBubbles(boolean allowBubbles) {
622         mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF;
623     }
624 
625     /**
626      * @hide
627      */
setAllowBubbles(int allowed)628     public void setAllowBubbles(int allowed) {
629         mAllowBubbles = allowed;
630     }
631 
632     /**
633      * Sets this channel as being converastion-centric. Different settings and functionality may be
634      * exposed for conversation-centric channels.
635      *
636      * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
637      *                        this type would be posted to in absence of a specific conversation id.
638      *                        For example, if this channel represents 'Messages from Person A', the
639      *                        parent channel would be 'Messages.'
640      * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
641      *                       channel's conversation.
642      */
setConversationId(@onNull String parentChannelId, @NonNull String conversationId)643     public void setConversationId(@NonNull String parentChannelId,
644             @NonNull String conversationId) {
645         mParentId = parentChannelId;
646         mConversationId = conversationId;
647     }
648 
649     /**
650      * Returns the id of this channel.
651      */
getId()652     public String getId() {
653         return mId;
654     }
655 
656     /**
657      * Returns the user visible name of this channel.
658      */
getName()659     public CharSequence getName() {
660         return mName;
661     }
662 
663     /**
664      * Returns the user visible description of this channel.
665      */
getDescription()666     public String getDescription() {
667         return mDesc;
668     }
669 
670     /**
671      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
672      * notifications posted to this channel. Note: This value might be >
673      * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
674      * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
675      * See {@link NotificationChannelGroup#isBlocked()} and
676      * {@link NotificationManager#areNotificationsEnabled()}.
677      */
getImportance()678     public int getImportance() {
679         return mImportance;
680     }
681 
682     /**
683      * Whether or not notifications posted to this channel can bypass the Do Not Disturb
684      * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
685      */
canBypassDnd()686     public boolean canBypassDnd() {
687         return mBypassDnd;
688     }
689 
690     /**
691      * Whether or not this channel represents a conversation.
692      */
isConversation()693     public boolean isConversation() {
694         return !TextUtils.isEmpty(getConversationId());
695     }
696 
697 
698     /**
699      * Whether or not notifications in this conversation are considered important.
700      *
701      * <p>Important conversations may get special visual treatment, and might be able to bypass DND.
702      *
703      * <p>This is only valid for channels that represent conversations, that is,
704      * where {@link #isConversation()} is true.
705      */
isImportantConversation()706     public boolean isImportantConversation() {
707         return mImportantConvo;
708     }
709 
710     /**
711      * Returns the notification sound for this channel.
712      */
getSound()713     public Uri getSound() {
714         return mSound;
715     }
716 
717     /**
718      * Returns the audio attributes for sound played by notifications posted to this channel.
719      */
getAudioAttributes()720     public AudioAttributes getAudioAttributes() {
721         return mAudioAttributes;
722     }
723 
724     /**
725      * Returns whether notifications posted to this channel trigger notification lights.
726      */
shouldShowLights()727     public boolean shouldShowLights() {
728         return mLights;
729     }
730 
731     /**
732      * Returns the notification light color for notifications posted to this channel. Irrelevant
733      * unless {@link #shouldShowLights()}.
734      */
getLightColor()735     public int getLightColor() {
736         return mLightColor;
737     }
738 
739     /**
740      * Returns whether notifications posted to this channel always vibrate.
741      */
shouldVibrate()742     public boolean shouldVibrate() {
743         return mVibrationEnabled;
744     }
745 
746     /**
747      * Returns the vibration pattern for notifications posted to this channel. Will be ignored if
748      * vibration is not enabled ({@link #shouldVibrate()}.
749      */
getVibrationPattern()750     public long[] getVibrationPattern() {
751         return mVibration;
752     }
753 
754     /**
755      * Returns whether or not notifications posted to this channel are shown on the lockscreen in
756      * full or redacted form.
757      */
getLockscreenVisibility()758     public int getLockscreenVisibility() {
759         return mLockscreenVisibility;
760     }
761 
762     /**
763      * Returns whether notifications posted to this channel can appear as badges in a Launcher
764      * application.
765      *
766      * Note that badging may be disabled for other reasons.
767      */
canShowBadge()768     public boolean canShowBadge() {
769         return mShowBadge;
770     }
771 
772     /**
773      * Returns what group this channel belongs to.
774      *
775      * This is used only for visually grouping channels in the UI.
776      */
getGroup()777     public String getGroup() {
778         return mGroup;
779     }
780 
781     /**
782      * Returns whether notifications posted to this channel are allowed to display outside of the
783      * notification shade, in a floating window on top of other apps.
784      *
785      * @see Notification#getBubbleMetadata()
786      */
canBubble()787     public boolean canBubble() {
788         return mAllowBubbles == ALLOW_BUBBLE_ON;
789     }
790 
791     /**
792      * @hide
793      */
getAllowBubbles()794     public int getAllowBubbles() {
795         return mAllowBubbles;
796     }
797 
798     /**
799      * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's
800      * a conversation related channel. See {@link #setConversationId(String, String)}.
801      */
getParentChannelId()802     public @Nullable String getParentChannelId() {
803         return mParentId;
804     }
805 
806     /**
807      * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's
808      * associated with a conversation. See {@link #setConversationId(String, String)}.
809      */
getConversationId()810     public @Nullable String getConversationId() {
811         return mConversationId;
812     }
813 
814     /**
815      * @hide
816      */
817     @SystemApi
isDeleted()818     public boolean isDeleted() {
819         return mDeleted;
820     }
821 
822     /**
823      * @hide
824      */
getDeletedTimeMs()825     public long getDeletedTimeMs() {
826         return mDeletedTime;
827     }
828 
829     /**
830      * @hide
831      */
832     @SystemApi
getUserLockedFields()833     public int getUserLockedFields() {
834         return mUserLockedFields;
835     }
836 
837     /**
838      * @hide
839      */
isFgServiceShown()840     public boolean isFgServiceShown() {
841         return mFgServiceShown;
842     }
843 
844     /**
845      * @hide
846      */
847     @TestApi
isBlockable()848     public boolean isBlockable() {
849         return mBlockableSystem;
850     }
851 
852     /**
853      * @hide
854      */
855     @TestApi
setImportanceLockedByOEM(boolean locked)856     public void setImportanceLockedByOEM(boolean locked) {
857         mImportanceLockedByOEM = locked;
858     }
859 
860     /**
861      * @hide
862      */
863     @TestApi
setImportanceLockedByCriticalDeviceFunction(boolean locked)864     public void setImportanceLockedByCriticalDeviceFunction(boolean locked) {
865         mImportanceLockedDefaultApp = locked;
866     }
867 
868     /**
869      * @hide
870      */
871     @TestApi
isImportanceLockedByOEM()872     public boolean isImportanceLockedByOEM() {
873         return mImportanceLockedByOEM;
874     }
875 
876     /**
877      * @hide
878      */
879     @TestApi
isImportanceLockedByCriticalDeviceFunction()880     public boolean isImportanceLockedByCriticalDeviceFunction() {
881         return mImportanceLockedDefaultApp;
882     }
883 
884     /**
885      * @hide
886      */
887     @TestApi
getOriginalImportance()888     public int getOriginalImportance() {
889         return mOriginalImportance;
890     }
891 
892     /**
893      * @hide
894      */
895     @TestApi
setOriginalImportance(int importance)896     public void setOriginalImportance(int importance) {
897         mOriginalImportance = importance;
898     }
899 
900     /**
901      * @hide
902      */
903     @TestApi
setDemoted(boolean demoted)904     public void setDemoted(boolean demoted) {
905         mDemoted = demoted;
906     }
907 
908     /**
909      * Returns whether the user has decided that this channel does not represent a conversation. The
910      * value will always be false for channels that never claimed to be conversations - that is,
911      * for channels where {@link #getConversationId()} and {@link #getParentChannelId()} are empty.
912      */
isDemoted()913     public boolean isDemoted() {
914         return mDemoted;
915     }
916 
917     /**
918      * Returns whether the user has chosen the importance of this channel, either to affirm the
919      * initial selection from the app, or changed it to be higher or lower.
920      * @see #getImportance()
921      */
hasUserSetImportance()922     public boolean hasUserSetImportance() {
923         return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
924     }
925 
926     /**
927      * Returns whether the user has chosen the sound of this channel.
928      * @see #getSound()
929      */
hasUserSetSound()930     public boolean hasUserSetSound() {
931         return (mUserLockedFields & USER_LOCKED_SOUND) != 0;
932     }
933 
934     /**
935      * @hide
936      */
populateFromXmlForRestore(XmlPullParser parser, Context context)937     public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
938         populateFromXml(XmlUtils.makeTyped(parser), true, context);
939     }
940 
941     /**
942      * @hide
943      */
944     @SystemApi
populateFromXml(XmlPullParser parser)945     public void populateFromXml(XmlPullParser parser) {
946         populateFromXml(XmlUtils.makeTyped(parser), false, null);
947     }
948 
949     /**
950      * If {@param forRestore} is true, {@param Context} MUST be non-null.
951      */
populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context)952     private void populateFromXml(TypedXmlPullParser parser, boolean forRestore,
953             @Nullable Context context) {
954         Preconditions.checkArgument(!forRestore || context != null,
955                 "forRestore is true but got null context");
956 
957         // Name, id, and importance are set in the constructor.
958         setDescription(parser.getAttributeValue(null, ATT_DESC));
959         setBypassDnd(Notification.PRIORITY_DEFAULT
960                 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
961         setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
962 
963         Uri sound = safeUri(parser, ATT_SOUND);
964         setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
965 
966         enableLights(safeBool(parser, ATT_LIGHTS, false));
967         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
968         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
969         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
970         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
971         setDeleted(safeBool(parser, ATT_DELETED, false));
972         setDeletedTimeMs(XmlUtils.readLongAttribute(
973                 parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS));
974         setGroup(parser.getAttributeValue(null, ATT_GROUP));
975         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
976         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
977         setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
978         setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
979         setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
980         setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
981                 parser.getAttributeValue(null, ATT_CONVERSATION_ID));
982         setDemoted(safeBool(parser, ATT_DEMOTE, false));
983         setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
984     }
985 
986     @Nullable
restoreSoundUri(Context context, @Nullable Uri uri)987     private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
988         if (uri == null || Uri.EMPTY.equals(uri)) {
989             return null;
990         }
991         ContentResolver contentResolver = context.getContentResolver();
992         // There are backups out there with uncanonical uris (because we fixed this after
993         // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
994         // verify the uri against device storage and we'll possibly end up with a broken uri.
995         // We then canonicalize the uri to uncanonicalize it back, which means we properly check
996         // the uri and in the case of not having the resource we end up with the default - better
997         // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
998         // according to the docs because canonicalize method has to handle canonical uris as well.
999         Uri canonicalizedUri = contentResolver.canonicalize(uri);
1000         if (canonicalizedUri == null) {
1001             // We got a null because the uri in the backup does not exist here, so we return default
1002             return Settings.System.DEFAULT_NOTIFICATION_URI;
1003         }
1004         return contentResolver.uncanonicalize(canonicalizedUri);
1005     }
1006 
1007     /**
1008      * @hide
1009      */
1010     @SystemApi
writeXml(XmlSerializer out)1011     public void writeXml(XmlSerializer out) throws IOException {
1012         writeXml(XmlUtils.makeTyped(out), false, null);
1013     }
1014 
1015     /**
1016      * @hide
1017      */
writeXmlForBackup(XmlSerializer out, Context context)1018     public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
1019         writeXml(XmlUtils.makeTyped(out), true, context);
1020     }
1021 
getSoundForBackup(Context context)1022     private Uri getSoundForBackup(Context context) {
1023         Uri sound = getSound();
1024         if (sound == null || Uri.EMPTY.equals(sound)) {
1025             return null;
1026         }
1027         Uri canonicalSound = context.getContentResolver().canonicalize(sound);
1028         if (canonicalSound == null) {
1029             // The content provider does not support canonical uris so we backup the default
1030             return Settings.System.DEFAULT_NOTIFICATION_URI;
1031         }
1032         return canonicalSound;
1033     }
1034 
1035     /**
1036      * If {@param forBackup} is true, {@param Context} MUST be non-null.
1037      */
writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)1038     private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)
1039             throws IOException {
1040         Preconditions.checkArgument(!forBackup || context != null,
1041                 "forBackup is true but got null context");
1042         out.startTag(null, TAG_CHANNEL);
1043         out.attribute(null, ATT_ID, getId());
1044         if (getName() != null) {
1045             out.attribute(null, ATT_NAME, getName().toString());
1046         }
1047         if (getDescription() != null) {
1048             out.attribute(null, ATT_DESC, getDescription());
1049         }
1050         if (getImportance() != DEFAULT_IMPORTANCE) {
1051             out.attributeInt(null, ATT_IMPORTANCE, getImportance());
1052         }
1053         if (canBypassDnd()) {
1054             out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX);
1055         }
1056         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
1057             out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility());
1058         }
1059         Uri sound = forBackup ? getSoundForBackup(context) : getSound();
1060         if (sound != null) {
1061             out.attribute(null, ATT_SOUND, sound.toString());
1062         }
1063         if (getAudioAttributes() != null) {
1064             out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage());
1065             out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType());
1066             out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags());
1067         }
1068         if (shouldShowLights()) {
1069             out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights());
1070         }
1071         if (getLightColor() != DEFAULT_LIGHT_COLOR) {
1072             out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor());
1073         }
1074         if (shouldVibrate()) {
1075             out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate());
1076         }
1077         if (getVibrationPattern() != null) {
1078             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
1079         }
1080         if (getUserLockedFields() != 0) {
1081             out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
1082         }
1083         if (isFgServiceShown()) {
1084             out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isFgServiceShown());
1085         }
1086         if (canShowBadge()) {
1087             out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge());
1088         }
1089         if (isDeleted()) {
1090             out.attributeBoolean(null, ATT_DELETED, isDeleted());
1091         }
1092         if (getDeletedTimeMs() >= 0) {
1093             out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs());
1094         }
1095         if (getGroup() != null) {
1096             out.attribute(null, ATT_GROUP, getGroup());
1097         }
1098         if (isBlockable()) {
1099             out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable());
1100         }
1101         if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) {
1102             out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles());
1103         }
1104         if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
1105             out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance());
1106         }
1107         if (getParentChannelId() != null) {
1108             out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
1109         }
1110         if (getConversationId() != null) {
1111             out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
1112         }
1113         if (isDemoted()) {
1114             out.attributeBoolean(null, ATT_DEMOTE, isDemoted());
1115         }
1116         if (isImportantConversation()) {
1117             out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation());
1118         }
1119 
1120         // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
1121         // truth and so aren't written to this xml file
1122 
1123         out.endTag(null, TAG_CHANNEL);
1124     }
1125 
1126     /**
1127      * @hide
1128      */
1129     @SystemApi
toJson()1130     public JSONObject toJson() throws JSONException {
1131         JSONObject record = new JSONObject();
1132         record.put(ATT_ID, getId());
1133         record.put(ATT_NAME, getName());
1134         record.put(ATT_DESC, getDescription());
1135         if (getImportance() != DEFAULT_IMPORTANCE) {
1136             record.put(ATT_IMPORTANCE,
1137                     NotificationListenerService.Ranking.importanceToString(getImportance()));
1138         }
1139         if (canBypassDnd()) {
1140             record.put(ATT_PRIORITY, Notification.PRIORITY_MAX);
1141         }
1142         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
1143             record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
1144         }
1145         if (getSound() != null) {
1146             record.put(ATT_SOUND, getSound().toString());
1147         }
1148         if (getAudioAttributes() != null) {
1149             record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
1150             record.put(ATT_CONTENT_TYPE,
1151                     Integer.toString(getAudioAttributes().getContentType()));
1152             record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
1153         }
1154         record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
1155         record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
1156         record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
1157         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
1158         record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown()));
1159         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
1160         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
1161         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
1162         record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
1163         record.put(ATT_GROUP, getGroup());
1164         record.put(ATT_BLOCKABLE_SYSTEM, isBlockable());
1165         record.put(ATT_ALLOW_BUBBLE, getAllowBubbles());
1166         // TODO: original importance
1167         return record;
1168     }
1169 
safeAudioAttributes(TypedXmlPullParser parser)1170     private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) {
1171         int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION);
1172         int contentType = safeInt(parser, ATT_CONTENT_TYPE,
1173                 AudioAttributes.CONTENT_TYPE_SONIFICATION);
1174         int flags = safeInt(parser, ATT_FLAGS, 0);
1175         return new AudioAttributes.Builder()
1176                 .setUsage(usage)
1177                 .setContentType(contentType)
1178                 .setFlags(flags)
1179                 .build();
1180     }
1181 
safeUri(TypedXmlPullParser parser, String att)1182     private static Uri safeUri(TypedXmlPullParser parser, String att) {
1183         final String val = parser.getAttributeValue(null, att);
1184         return val == null ? null : Uri.parse(val);
1185     }
1186 
safeInt(TypedXmlPullParser parser, String att, int defValue)1187     private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
1188         return parser.getAttributeInt(null, att, defValue);
1189     }
1190 
safeBool(TypedXmlPullParser parser, String att, boolean defValue)1191     private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) {
1192         return parser.getAttributeBoolean(null, att, defValue);
1193     }
1194 
safeLongArray(TypedXmlPullParser parser, String att, long[] defValue)1195     private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) {
1196         final String attributeValue = parser.getAttributeValue(null, att);
1197         if (TextUtils.isEmpty(attributeValue)) return defValue;
1198         String[] values = attributeValue.split(DELIMITER);
1199         long[] longValues = new long[values.length];
1200         for (int i = 0; i < values.length; i++) {
1201             try {
1202                 longValues[i] = Long.parseLong(values[i]);
1203             } catch (NumberFormatException e) {
1204                 longValues[i] = 0;
1205             }
1206         }
1207         return longValues;
1208     }
1209 
longArrayToString(long[] values)1210     private static String longArrayToString(long[] values) {
1211         StringBuilder sb = new StringBuilder();
1212         if (values != null && values.length > 0) {
1213             for (int i = 0; i < values.length - 1; i++) {
1214                 sb.append(values[i]).append(DELIMITER);
1215             }
1216             sb.append(values[values.length - 1]);
1217         }
1218         return sb.toString();
1219     }
1220 
1221     public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR =
1222             new Creator<NotificationChannel>() {
1223         @Override
1224         public NotificationChannel createFromParcel(Parcel in) {
1225             return new NotificationChannel(in);
1226         }
1227 
1228         @Override
1229         public NotificationChannel[] newArray(int size) {
1230             return new NotificationChannel[size];
1231         }
1232     };
1233 
1234     @Override
describeContents()1235     public int describeContents() {
1236         return 0;
1237     }
1238 
1239     @Override
equals(@ullable Object o)1240     public boolean equals(@Nullable Object o) {
1241         if (this == o) return true;
1242         if (o == null || getClass() != o.getClass()) return false;
1243         NotificationChannel that = (NotificationChannel) o;
1244         return getImportance() == that.getImportance()
1245                 && mBypassDnd == that.mBypassDnd
1246                 && getLockscreenVisibility() == that.getLockscreenVisibility()
1247                 && mLights == that.mLights
1248                 && getLightColor() == that.getLightColor()
1249                 && getUserLockedFields() == that.getUserLockedFields()
1250                 && isFgServiceShown() == that.isFgServiceShown()
1251                 && mVibrationEnabled == that.mVibrationEnabled
1252                 && mShowBadge == that.mShowBadge
1253                 && isDeleted() == that.isDeleted()
1254                 && getDeletedTimeMs() == that.getDeletedTimeMs()
1255                 && isBlockable() == that.isBlockable()
1256                 && mAllowBubbles == that.mAllowBubbles
1257                 && Objects.equals(getId(), that.getId())
1258                 && Objects.equals(getName(), that.getName())
1259                 && Objects.equals(mDesc, that.mDesc)
1260                 && Objects.equals(getSound(), that.getSound())
1261                 && Arrays.equals(mVibration, that.mVibration)
1262                 && Objects.equals(getGroup(), that.getGroup())
1263                 && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
1264                 && mImportanceLockedByOEM == that.mImportanceLockedByOEM
1265                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
1266                 && mOriginalImportance == that.mOriginalImportance
1267                 && Objects.equals(getParentChannelId(), that.getParentChannelId())
1268                 && Objects.equals(getConversationId(), that.getConversationId())
1269                 && isDemoted() == that.isDemoted()
1270                 && isImportantConversation() == that.isImportantConversation();
1271     }
1272 
1273     @Override
hashCode()1274     public int hashCode() {
1275         int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd,
1276                 getLockscreenVisibility(), getSound(), mLights, getLightColor(),
1277                 getUserLockedFields(),
1278                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
1279                 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
1280                 mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
1281                 mParentId, mConversationId, mDemoted, mImportantConvo);
1282         result = 31 * result + Arrays.hashCode(mVibration);
1283         return result;
1284     }
1285 
1286     /** @hide */
dump(PrintWriter pw, String prefix, boolean redacted)1287     public void dump(PrintWriter pw, String prefix, boolean redacted) {
1288         String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName;
1289         String output = "NotificationChannel{"
1290                 + "mId='" + mId + '\''
1291                 + ", mName=" + redactedName
1292                 + getFieldsString()
1293                 + '}';
1294         pw.println(prefix + output);
1295     }
1296 
1297     @Override
toString()1298     public String toString() {
1299         return "NotificationChannel{"
1300                 + "mId='" + mId + '\''
1301                 + ", mName=" + mName
1302                 + getFieldsString()
1303                 + '}';
1304     }
1305 
getFieldsString()1306     private String getFieldsString() {
1307         return  ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
1308                 + ", mImportance=" + mImportance
1309                 + ", mBypassDnd=" + mBypassDnd
1310                 + ", mLockscreenVisibility=" + mLockscreenVisibility
1311                 + ", mSound=" + mSound
1312                 + ", mLights=" + mLights
1313                 + ", mLightColor=" + mLightColor
1314                 + ", mVibration=" + Arrays.toString(mVibration)
1315                 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
1316                 + ", mFgServiceShown=" + mFgServiceShown
1317                 + ", mVibrationEnabled=" + mVibrationEnabled
1318                 + ", mShowBadge=" + mShowBadge
1319                 + ", mDeleted=" + mDeleted
1320                 + ", mDeletedTimeMs=" + mDeletedTime
1321                 + ", mGroup='" + mGroup + '\''
1322                 + ", mAudioAttributes=" + mAudioAttributes
1323                 + ", mBlockableSystem=" + mBlockableSystem
1324                 + ", mAllowBubbles=" + mAllowBubbles
1325                 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
1326                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
1327                 + ", mOriginalImp=" + mOriginalImportance
1328                 + ", mParent=" + mParentId
1329                 + ", mConversationId=" + mConversationId
1330                 + ", mDemoted=" + mDemoted
1331                 + ", mImportantConvo=" + mImportantConvo;
1332     }
1333 
1334     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)1335     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
1336         final long token = proto.start(fieldId);
1337 
1338         proto.write(NotificationChannelProto.ID, mId);
1339         proto.write(NotificationChannelProto.NAME, mName);
1340         proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
1341         proto.write(NotificationChannelProto.IMPORTANCE, mImportance);
1342         proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd);
1343         proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility);
1344         if (mSound != null) {
1345             proto.write(NotificationChannelProto.SOUND, mSound.toString());
1346         }
1347         proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
1348         proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
1349         if (mVibration != null) {
1350             for (long v : mVibration) {
1351                 proto.write(NotificationChannelProto.VIBRATION, v);
1352             }
1353         }
1354         proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields);
1355         proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown);
1356         proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled);
1357         proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge);
1358         proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
1359         proto.write(NotificationChannelProto.GROUP, mGroup);
1360         if (mAudioAttributes != null) {
1361             mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
1362         }
1363         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
1364         proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);
1365 
1366         proto.end(token);
1367     }
1368 }
1369