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