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