• 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 static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
19 import static android.service.notification.Adjustment.TYPE_NEWS;
20 import static android.service.notification.Adjustment.TYPE_PROMOTION;
21 import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
22 import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.SystemApi;
28 import android.annotation.TestApi;
29 import android.app.NotificationManager.Importance;
30 import android.compat.annotation.UnsupportedAppUsage;
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.pm.ShortcutInfo;
35 import android.media.AudioAttributes;
36 import android.media.RingtoneManager;
37 import android.net.Uri;
38 import android.os.Parcel;
39 import android.os.Parcelable;
40 import android.os.VibrationEffect;
41 import android.os.vibrator.persistence.VibrationXmlParser;
42 import android.os.vibrator.persistence.VibrationXmlSerializer;
43 import android.provider.Settings;
44 import android.service.notification.Adjustment;
45 import android.service.notification.NotificationListenerService;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.util.Slog;
49 import android.util.proto.ProtoOutputStream;
50 
51 import com.android.internal.util.Preconditions;
52 import com.android.internal.util.XmlUtils;
53 import com.android.modules.utils.TypedXmlPullParser;
54 import com.android.modules.utils.TypedXmlSerializer;
55 
56 import org.json.JSONException;
57 import org.json.JSONObject;
58 import org.xmlpull.v1.XmlPullParser;
59 import org.xmlpull.v1.XmlSerializer;
60 
61 import java.io.FileNotFoundException;
62 import java.io.IOException;
63 import java.io.PrintWriter;
64 import java.io.StringReader;
65 import java.io.StringWriter;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.List;
69 import java.util.Objects;
70 
71 /**
72  * A representation of settings that apply to a collection of similarly themed notifications.
73  */
74 public final class NotificationChannel implements Parcelable {
75     private static final String TAG = "NotificationChannel";
76 
77     /**
78      * The id of the default channel for an app. This id is reserved by the system. All
79      * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or
80      * earlier without a notification channel specified are posted to this channel.
81      */
82     public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
83 
84     /**
85      * A reserved id for a system channel reserved for promotional notifications.
86      *  @hide
87      */
88     @TestApi
89     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
90     public static final String PROMOTIONS_ID = "android.app.promotions";
91     /**
92      * A reserved id for a system channel reserved for non-conversation social media notifications.
93      *  @hide
94      */
95     @TestApi
96     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
97     public static final String SOCIAL_MEDIA_ID = "android.app.social";
98     /**
99      * A reserved id for a system channel reserved for news notifications.
100      *  @hide
101      */
102     @TestApi
103     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
104     public static final String NEWS_ID = "android.app.news";
105     /**
106      * A reserved id for a system channel reserved for content recommendation notifications.
107      *  @hide
108      */
109     @TestApi
110     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
111     public static final String RECS_ID = "android.app.recs";
112 
113     /** @hide */
114     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
115     public static final ArrayList<String> SYSTEM_RESERVED_IDS = new ArrayList<>(
116             List.of(NEWS_ID, SOCIAL_MEDIA_ID, PROMOTIONS_ID, RECS_ID));
117 
118     /**
119      * The formatter used by the system to create an id for notification
120      * channels when it automatically creates conversation channels on behalf of an app. The format
121      * string takes two arguments, in this order: the
122      * {@link #getId()} of the original notification channel, and the
123      * {@link ShortcutInfo#getId() id} of the conversation.
124      * @hide
125      */
126     public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
127 
128     /**
129      * TODO: STOPSHIP  remove
130      * Conversation id to use for apps that aren't providing them yet.
131      * @hide
132      */
133     public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id";
134 
135     /**
136      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
137      * that have to do with editing sound, like a tone picker
138      * ({@link #setSound(Uri, AudioAttributes)}).
139      */
140     public static final String EDIT_SOUND = "sound";
141     /**
142      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
143      * that have to do with editing vibration ({@link #enableVibration(boolean)},
144      * {@link #setVibrationPattern(long[])}).
145      */
146     public static final String EDIT_VIBRATION = "vibration";
147     /**
148      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
149      * that have to do with editing importance ({@link #setImportance(int)}) and/or conversation
150      * priority.
151      */
152     public static final String EDIT_IMPORTANCE = "importance";
153     /**
154      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
155      * that have to do with editing behavior on devices that are locked or have a turned off
156      * display ({@link #setLockscreenVisibility(int)}, {@link #enableLights(boolean)},
157      * {@link #setLightColor(int)}).
158      */
159     public static final String EDIT_LOCKED_DEVICE = "locked";
160     /**
161      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
162      * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) .
163      */
164     public static final String EDIT_ZEN = "zen";
165     /**
166      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
167      * that have to do with editing conversation settings (demoting or restoring a channel to
168      * be a Conversation, changing bubble behavior, or setting the priority of a conversation).
169      */
170     public static final String EDIT_CONVERSATION = "conversation";
171     /**
172      * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
173      * that have to do with editing launcher behavior (showing badges)}.
174      */
175     public static final String EDIT_LAUNCHER = "launcher";
176 
177     /**
178      * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
179      * limit.
180      * @hide
181      */
182     public static final int MAX_TEXT_LENGTH = 1000;
183     /**
184      * @hide
185      */
186     public static final int MAX_VIBRATION_LENGTH = 500;
187     /**
188      * @hide
189      */
190     public static final int MAX_SERIALIZED_VIBRATION_LENGTH = 32_768;
191 
192     private static final String TAG_CHANNEL = "channel";
193     private static final String ATT_NAME = "name";
194     private static final String ATT_DESC = "desc";
195     private static final String ATT_ID = "id";
196     private static final String ATT_DELETED = "deleted";
197     private static final String ATT_PRIORITY = "priority";
198     private static final String ATT_VISIBILITY = "visibility";
199     private static final String ATT_IMPORTANCE = "importance";
200     private static final String ATT_LIGHTS = "lights";
201     private static final String ATT_LIGHT_COLOR = "light_color";
202     private static final String ATT_VIBRATION = "vibration";
203     private static final String ATT_VIBRATION_EFFECT = "vibration_effect";
204     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
205     private static final String ATT_SOUND = "sound";
206     private static final String ATT_USAGE = "usage";
207     private static final String ATT_FLAGS = "flags";
208     private static final String ATT_CONTENT_TYPE = "content_type";
209     private static final String ATT_SHOW_BADGE = "show_badge";
210     private static final String ATT_USER_LOCKED = "locked";
211     /**
212      * This attribute represents both foreground services and user initiated jobs in U+.
213      * It was not renamed in U on purpose, in order to avoid creating an unnecessary migration path.
214      */
215     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
216     private static final String ATT_GROUP = "group";
217     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
218     private static final String ATT_ALLOW_BUBBLE = "allow_bubbles";
219     private static final String ATT_ORIG_IMP = "orig_imp";
220     private static final String ATT_PARENT_CHANNEL = "parent";
221     private static final String ATT_CONVERSATION_ID = "conv_id";
222     private static final String ATT_IMP_CONVERSATION = "imp_conv";
223     private static final String ATT_DEMOTE = "dem";
224     private static final String ATT_DELETED_TIME_MS = "del_time";
225     private static final String DELIMITER = ",";
226 
227     /**
228      * @hide
229      */
230     public static final int USER_LOCKED_PRIORITY = 0x00000001;
231     /**
232      * @hide
233      */
234     public static final int USER_LOCKED_VISIBILITY = 0x00000002;
235     /**
236      * @hide
237      */
238     public static final int USER_LOCKED_IMPORTANCE = 0x00000004;
239     /**
240      * @hide
241      */
242     public static final int USER_LOCKED_LIGHTS = 0x00000008;
243     /**
244      * @hide
245      */
246     public static final int USER_LOCKED_VIBRATION = 0x00000010;
247     /**
248      * @hide
249      */
250     @SystemApi
251     public static final int USER_LOCKED_SOUND = 0x00000020;
252 
253     /**
254      * @hide
255      */
256     public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
257 
258     /**
259      * @hide
260      */
261     public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;
262 
263     /**
264      * @hide
265      */
266     public static final int[] LOCKABLE_FIELDS = new int[] {
267             USER_LOCKED_PRIORITY,
268             USER_LOCKED_VISIBILITY,
269             USER_LOCKED_IMPORTANCE,
270             USER_LOCKED_LIGHTS,
271             USER_LOCKED_VIBRATION,
272             USER_LOCKED_SOUND,
273             USER_LOCKED_SHOW_BADGE,
274             USER_LOCKED_ALLOW_BUBBLE
275     };
276 
277     /**
278      * @hide
279      */
280     public static final int DEFAULT_ALLOW_BUBBLE = -1;
281     /**
282      * @hide
283      */
284     public static final int ALLOW_BUBBLE_ON = 1;
285     /**
286      * @hide
287      */
288     public static final int ALLOW_BUBBLE_OFF = 0;
289 
290     private static final int DEFAULT_LIGHT_COLOR = 0;
291     private static final int DEFAULT_VISIBILITY =
292             NotificationManager.VISIBILITY_NO_OVERRIDE;
293     private static final int DEFAULT_IMPORTANCE =
294             NotificationManager.IMPORTANCE_UNSPECIFIED;
295     private static final boolean DEFAULT_DELETED = false;
296     private static final boolean DEFAULT_SHOW_BADGE = true;
297     private static final long DEFAULT_DELETION_TIME_MS = -1;
298 
299     @UnsupportedAppUsage
300     private String mId;
301     private String mName;
302     private String mDesc;
303     private int mImportance = DEFAULT_IMPORTANCE;
304     private int mOriginalImportance = DEFAULT_IMPORTANCE;
305     private boolean mBypassDnd;
306     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
307     private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
308     private boolean mSoundRestored = false;
309     private boolean mLights;
310     private int mLightColor = DEFAULT_LIGHT_COLOR;
311     private long[] mVibrationPattern;
312     private VibrationEffect mVibrationEffect;
313     // Bitwise representation of fields that have been changed by the user, preventing the app from
314     // making changes to these fields.
315     private int mUserLockedFields;
316     private boolean mUserVisibleTaskShown;
317     private boolean mVibrationEnabled;
318     private boolean mShowBadge = DEFAULT_SHOW_BADGE;
319     private boolean mDeleted = DEFAULT_DELETED;
320     private String mGroup;
321     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
322     // If this is a blockable system notification channel.
323     private boolean mBlockableSystem = false;
324     private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
325     private boolean mImportanceLockedDefaultApp;
326     private String mParentId = null;
327     private String mConversationId = null;
328     private boolean mDemoted = false;
329     private boolean mImportantConvo = false;
330     private long mDeletedTime = DEFAULT_DELETION_TIME_MS;
331     /** Do not (de)serialize this value: it only affects logic in system_server and that logic
332      * is reset on each boot {@link NotificationAttentionHelper#buzzBeepBlinkLocked}.
333      */
334     private long mLastNotificationUpdateTimeMs = 0;
335 
336     /**
337      * Creates a notification channel.
338      *
339      * @param id The id of the channel. Must be unique per package. The value may be truncated if
340      *           it is too long.
341      * @param name The user visible name of the channel. You can rename this channel when the system
342      *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
343      *             broadcast. The recommended maximum length is 40 characters; the value may be
344      *             truncated if it is too long.
345      * @param importance The importance of the channel. This controls how interruptive notifications
346      *                   posted to this channel are.
347      */
NotificationChannel(String id, CharSequence name, @Importance int importance)348     public NotificationChannel(String id, CharSequence name, @Importance int importance) {
349         this.mId = getTrimmedString(id);
350         this.mName = name != null ? getTrimmedString(name.toString()) : null;
351         this.mImportance = importance;
352     }
353 
354     /**
355      * @hide
356      */
NotificationChannel(Parcel in)357     protected NotificationChannel(Parcel in) {
358         if (in.readByte() != 0) {
359             mId = getTrimmedString(in.readString());
360         } else {
361             mId = null;
362         }
363         if (in.readByte() != 0) {
364             mName = getTrimmedString(in.readString());
365         } else {
366             mName = null;
367         }
368         if (in.readByte() != 0) {
369             mDesc = getTrimmedString(in.readString());
370         } else {
371             mDesc = null;
372         }
373         mImportance = in.readInt();
374         mBypassDnd = in.readByte() != 0;
375         mLockscreenVisibility = in.readInt();
376         if (in.readByte() != 0) {
377             mSound = Uri.CREATOR.createFromParcel(in);
378             mSound = Uri.parse(getTrimmedString(mSound.toString()));
379         } else {
380             mSound = null;
381         }
382         mLights = in.readByte() != 0;
383         mUserLockedFields = in.readInt();
384         mUserVisibleTaskShown = in.readByte() != 0;
385         mVibrationEnabled = in.readByte() != 0;
386         mShowBadge = in.readByte() != 0;
387         mDeleted = in.readByte() != 0;
388         if (in.readByte() != 0) {
389             mGroup = getTrimmedString(in.readString());
390         } else {
391             mGroup = null;
392         }
393         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
394         mLightColor = in.readInt();
395         mBlockableSystem = in.readBoolean();
396         mAllowBubbles = in.readInt();
397         mOriginalImportance = in.readInt();
398         mParentId = getTrimmedString(in.readString());
399         mConversationId = getTrimmedString(in.readString());
400         mDemoted = in.readBoolean();
401         mImportantConvo = in.readBoolean();
402         mDeletedTime = in.readLong();
403         mImportanceLockedDefaultApp = in.readBoolean();
404 
405         // Add new fields above this line and not after vibration effect! When
406         // notif_channel_estimate_effect_size is true, we use parcel size to detect whether the
407         // vibration effect might be too large to handle, so this must remain at the end lest any
408         // following fields cause the data to get incorrectly dropped.
409         mVibrationPattern = in.createLongArray();
410         if (mVibrationPattern != null && mVibrationPattern.length > MAX_VIBRATION_LENGTH) {
411             mVibrationPattern = Arrays.copyOf(mVibrationPattern, MAX_VIBRATION_LENGTH);
412         }
413         boolean largeEffect = false;
414         if (Flags.notifChannelEstimateEffectSize()) {
415             // Note that we must check the length of remaining data in the parcel before reading in
416             // the data.
417             largeEffect = (in.dataAvail() > MAX_SERIALIZED_VIBRATION_LENGTH);
418         }
419         if (Flags.notificationChannelVibrationEffectApi()) {
420             mVibrationEffect =
421                     in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
422             if (Flags.notifChannelCropVibrationEffects() && mVibrationEffect != null) {
423                 if (Flags.notifChannelEstimateEffectSize()) {
424                     // Try trimming the effect if the remaining parcel size is large. If trimming is
425                     // not applicable for the effect, rather than serializing to XML (expensive) to
426                     // check the exact serialized length, we just reject the effect.
427                     if (largeEffect) {
428                         mVibrationEffect = mVibrationEffect.cropToLengthOrNull(
429                                 MAX_VIBRATION_LENGTH);
430                     }
431                 } else {
432                     mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
433                 }
434             }
435         }
436     }
437 
438     @Override
writeToParcel(Parcel dest, int flags)439     public void writeToParcel(Parcel dest, int flags) {
440         if (mId != null) {
441             dest.writeByte((byte) 1);
442             dest.writeString(mId);
443         } else {
444             dest.writeByte((byte) 0);
445         }
446         if (mName != null) {
447             dest.writeByte((byte) 1);
448             dest.writeString(mName);
449         } else {
450             dest.writeByte((byte) 0);
451         }
452         if (mDesc != null) {
453             dest.writeByte((byte) 1);
454             dest.writeString(mDesc);
455         } else {
456             dest.writeByte((byte) 0);
457         }
458         dest.writeInt(mImportance);
459         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
460         dest.writeInt(mLockscreenVisibility);
461         if (mSound != null) {
462             dest.writeByte((byte) 1);
463             mSound.writeToParcel(dest, 0);
464         } else {
465             dest.writeByte((byte) 0);
466         }
467         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
468         dest.writeInt(mUserLockedFields);
469         dest.writeByte(mUserVisibleTaskShown ? (byte) 1 : (byte) 0);
470         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
471         dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
472         dest.writeByte(mDeleted ? (byte) 1 : (byte) 0);
473         if (mGroup != null) {
474             dest.writeByte((byte) 1);
475             dest.writeString(mGroup);
476         } else {
477             dest.writeByte((byte) 0);
478         }
479         if (mAudioAttributes != null) {
480             dest.writeInt(1);
481             mAudioAttributes.writeToParcel(dest, 0);
482         } else {
483             dest.writeInt(0);
484         }
485         dest.writeInt(mLightColor);
486         dest.writeBoolean(mBlockableSystem);
487         dest.writeInt(mAllowBubbles);
488         dest.writeInt(mOriginalImportance);
489         dest.writeString(mParentId);
490         dest.writeString(mConversationId);
491         dest.writeBoolean(mDemoted);
492         dest.writeBoolean(mImportantConvo);
493         dest.writeLong(mDeletedTime);
494         dest.writeBoolean(mImportanceLockedDefaultApp);
495 
496         // Add new fields above this line; vibration effect must remain the last entry.
497         dest.writeLongArray(mVibrationPattern);
498         if (Flags.notificationChannelVibrationEffectApi()) {
499             if (mVibrationEffect != null) {
500                 dest.writeInt(1);
501                 mVibrationEffect.writeToParcel(dest, /* flags= */ 0);
502             } else {
503                 dest.writeInt(0);
504             }
505         }
506     }
507 
508     /** @hide */
509     @TestApi
510     @NonNull
copy()511     public NotificationChannel copy() {
512         NotificationChannel copy = new NotificationChannel(mId, mName, mImportance);
513         copy.setDescription(mDesc);
514         copy.setBypassDnd(mBypassDnd);
515         copy.setLockscreenVisibility(mLockscreenVisibility);
516         copy.setSound(mSound, mAudioAttributes);
517         copy.setLightColor(mLightColor);
518         copy.enableLights(mLights);
519         copy.setVibrationPattern(mVibrationPattern);
520         if (Flags.notificationChannelVibrationEffectApi()) {
521             copy.setVibrationEffect(mVibrationEffect);
522         }
523         copy.lockFields(mUserLockedFields);
524         copy.setUserVisibleTaskShown(mUserVisibleTaskShown);
525         copy.enableVibration(mVibrationEnabled);
526         copy.setShowBadge(mShowBadge);
527         copy.setDeleted(mDeleted);
528         copy.setGroup(mGroup);
529         copy.setBlockable(mBlockableSystem);
530         copy.setAllowBubbles(mAllowBubbles);
531         copy.setOriginalImportance(mOriginalImportance);
532         copy.setConversationId(mParentId, mConversationId);
533         copy.setDemoted(mDemoted);
534         copy.setImportantConversation(mImportantConvo);
535         copy.setDeletedTimeMs(mDeletedTime);
536         copy.setImportanceLockedByCriticalDeviceFunction(mImportanceLockedDefaultApp);
537         copy.setLastNotificationUpdateTimeMs(mLastNotificationUpdateTimeMs);
538 
539         return copy;
540     }
541 
542     /**
543      * @hide
544      */
545     @TestApi
lockFields(int field)546     public void lockFields(int field) {
547         mUserLockedFields |= field;
548     }
549 
550     /**
551      * @hide
552      */
unlockFields(int field)553     public void unlockFields(int field) {
554         mUserLockedFields &= ~field;
555     }
556 
557     /**
558      * @hide
559      */
560     @TestApi
setUserVisibleTaskShown(boolean shown)561     public void setUserVisibleTaskShown(boolean shown) {
562         mUserVisibleTaskShown = shown;
563     }
564 
565     /**
566      * @hide
567      */
568     @TestApi
setDeleted(boolean deleted)569     public void setDeleted(boolean deleted) {
570         mDeleted = deleted;
571     }
572 
573     /**
574      * @hide
575      */
576     @TestApi
setDeletedTimeMs(long time)577     public void setDeletedTimeMs(long time) {
578         mDeletedTime = time;
579     }
580 
581     /** @hide */
582     @TestApi
583     @SystemApi
584     @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
setImportantConversation(boolean importantConvo)585     public void setImportantConversation(boolean importantConvo) {
586         mImportantConvo = importantConvo;
587     }
588 
589     /**
590      * Allows users to block notifications sent through this channel, if this channel belongs to
591      * a package that otherwise would have notifications "fixed" as enabled.
592      *
593      * If the channel does not belong to a package that has a fixed notification permission, this
594      * method does nothing, since such channels are blockable by default and cannot be set to be
595      * unblockable.
596      * @param blockable if {@code true}, allows users to block notifications on this channel.
597      */
setBlockable(boolean blockable)598     public void setBlockable(boolean blockable) {
599         mBlockableSystem = blockable;
600     }
601     // Modifiable by apps post channel creation
602 
603     /**
604      * Sets the user visible name of this channel.
605      *
606      * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too
607      * long.
608      */
setName(CharSequence name)609     public void setName(CharSequence name) {
610         mName = name != null ? getTrimmedString(name.toString()) : null;
611     }
612 
613     /**
614      * Sets the user visible description of this channel.
615      *
616      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
617      * long.
618      */
setDescription(String description)619     public void setDescription(String description) {
620         mDesc = getTrimmedString(description);
621     }
622 
getTrimmedString(String input)623     private String getTrimmedString(String input) {
624         if (input != null && input.length() > MAX_TEXT_LENGTH) {
625             return input.substring(0, MAX_TEXT_LENGTH);
626         }
627         return input;
628     }
629 
630     // Returns trimmed vibration effect or null if not trimmable and the serialized string is too
631     // long. Note that this method involves serializing the full VibrationEffect, which may be
632     // expensive.
getTrimmedVibrationEffect(VibrationEffect effect)633     private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) {
634         if (effect == null) {
635             return null;
636         }
637         // trim if possible; check serialized length; reject if it is still too long
638         VibrationEffect result = effect;
639         VibrationEffect trimmed = effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH);
640         if (trimmed != null) {
641             result = trimmed;
642         }
643         if (vibrationToString(result).length() > MAX_SERIALIZED_VIBRATION_LENGTH) {
644             return null;
645         }
646         return result;
647     }
648 
649     /**
650      * @hide
651      */
setId(String id)652     public void setId(String id) {
653         mId = id;
654     }
655 
656     // Modifiable by apps on channel creation.
657 
658     /**
659      * Sets what group this channel belongs to.
660      *
661      * Group information is only used for presentation, not for behavior.
662      *
663      * Only modifiable before the channel is submitted to
664      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
665      * channel is not currently part of a group.
666      *
667      * @param groupId the id of a group created by
668      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
669      */
setGroup(String groupId)670     public void setGroup(String groupId) {
671         this.mGroup = groupId;
672     }
673 
674     /**
675      * Sets whether notifications posted to this channel can appear as application icon badges
676      * in a Launcher.
677      *
678      * Only modifiable before the channel is submitted to
679      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
680      *
681      * @param showBadge true if badges should be allowed to be shown.
682      */
setShowBadge(boolean showBadge)683     public void setShowBadge(boolean showBadge) {
684         this.mShowBadge = showBadge;
685     }
686 
687     /**
688      * Sets the sound that should be played for notifications posted to this channel and its
689      * audio attributes. Notification channels with an {@link #getImportance() importance} of at
690      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
691      *
692      * Note: An app-specific sound can be provided in the Uri parameter, but because channels are
693      * persistent for the duration of the app install, and are backed up and restored, the Uri
694      * should be stable. For this reason it is not recommended to use a
695      * {@link ContentResolver#SCHEME_ANDROID_RESOURCE} uri, as resource ids can change on app
696      * upgrade.
697      *
698      * Only modifiable before the channel is submitted to
699      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
700      */
setSound(Uri sound, AudioAttributes audioAttributes)701     public void setSound(Uri sound, AudioAttributes audioAttributes) {
702         this.mSound = sound;
703         this.mAudioAttributes = audioAttributes;
704     }
705 
706     /**
707      * Sets whether notifications posted to this channel should display notification lights,
708      * on devices that support that feature.
709      *
710      * Only modifiable before the channel is submitted to
711      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
712      */
enableLights(boolean lights)713     public void enableLights(boolean lights) {
714         this.mLights = lights;
715     }
716 
717     /**
718      * Sets the notification light color for notifications posted to this channel, if lights are
719      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
720      *
721      * Only modifiable before the channel is submitted to
722      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
723      */
setLightColor(int argb)724     public void setLightColor(int argb) {
725         this.mLightColor = argb;
726     }
727 
728     /**
729      * Sets whether notification posted to this channel should vibrate. The vibration pattern can
730      * be set with {@link #setVibrationPattern(long[])}.
731      *
732      * Only modifiable before the channel is submitted to
733      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
734      */
enableVibration(boolean vibration)735     public void enableVibration(boolean vibration) {
736         this.mVibrationEnabled = vibration;
737     }
738 
739     /**
740      * Sets the vibration pattern for notifications posted to this channel. If the provided
741      * pattern is valid (non-null, non-empty with at least 1 non-zero value), will enable vibration
742      * on this channel (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
743      * Otherwise, vibration will be disabled unless {@link #enableVibration(boolean)} is
744      * used with {@code true}, in which case the default vibration will be used.
745      *
746      * Only modifiable before the channel is submitted to
747      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
748      */
setVibrationPattern(long[] vibrationPattern)749     public void setVibrationPattern(long[] vibrationPattern) {
750         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
751         this.mVibrationPattern = vibrationPattern;
752         if (Flags.notifChannelCropVibrationEffects()) {
753             if (vibrationPattern != null && vibrationPattern.length > MAX_VIBRATION_LENGTH) {
754                 this.mVibrationPattern = Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH);
755             }
756         }
757         if (Flags.notificationChannelVibrationEffectApi()) {
758             try {
759                 this.mVibrationEffect =
760                         VibrationEffect.createWaveform(vibrationPattern, /* repeat= */ -1);
761             } catch (IllegalArgumentException | NullPointerException e) {
762                 this.mVibrationEffect = null;
763             }
764         }
765     }
766 
767     /**
768      * Sets a {@link VibrationEffect} for notifications posted to this channel. If the
769      * provided effect is non-null, will enable vibration on this channel (equivalent
770      * to calling {@link #enableVibration(boolean)} with {@code true}). Otherwise
771      * vibration will be disabled unless {@link #enableVibration(boolean)} is used with
772      * {@code true}, in which case the default vibration will be used.
773      *
774      * <p>The effect passed here will be returned from {@link #getVibrationEffect()}.
775      * If the provided {@link VibrationEffect} is an equivalent to a wave-form
776      * vibration pattern, the equivalent wave-form pattern will be returned from
777      * {@link #getVibrationPattern()}.
778      *
779      * <p>Note that some {@link VibrationEffect}s may not be playable on some devices.
780      * In cases where such an effect is passed here, vibration will still be enabled
781      * for the channel, but the default vibration will be used. Nonetheless, the
782      * provided effect will be stored and be returned from {@link #getVibrationEffect}
783      * calls, and could be used by the same channel on a different device, for example,
784      * in cases the user backs up and restores to a device that does have the ability
785      * to play the effect, where that effect will be used instead of the default. To
786      * avoid such issues that could make the vibration behavior of your notification
787      * channel differ among different devices, it's recommended that you avoid
788      * vibration effect primitives, as the support for them differs widely among
789      * devices (read {@link VibrationEffect.Composition} for more on vibration
790      * primitives).
791      *
792      * <p>Only modifiable before the channel is submitted to
793      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
794      *
795      * @see #getVibrationEffect()
796      * @see android.os.Vibrator#areEffectsSupported(int...)
797      * @see android.os.Vibrator#arePrimitivesSupported(int...)
798      */
799     @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
setVibrationEffect(@ullable VibrationEffect effect)800     public void setVibrationEffect(@Nullable VibrationEffect effect) {
801         this.mVibrationEnabled = effect != null;
802         this.mVibrationEffect = effect;
803         if (Flags.notifChannelCropVibrationEffects() && effect != null) {
804             long[] pattern = effect.computeCreateWaveformOffOnTimingsOrNull();
805             if (pattern != null) {
806                 // If this effect has an equivalent pattern, AND the pattern needs to be truncated
807                 // due to being too long, we delegate to setVibrationPattern to re-generate the
808                 // effect as well. Otherwise, we use the effect (already set above) and converted
809                 // pattern directly.
810                 if (pattern.length > MAX_VIBRATION_LENGTH) {
811                     setVibrationPattern(pattern);
812                 } else {
813                     this.mVibrationPattern = pattern;
814                 }
815             } else {
816                 // If not convertible to a pattern directly, try trimming the vibration effect if
817                 // possible and storing that version instead.
818                 this.mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
819                 this.mVibrationPattern = null;
820             }
821         } else {
822             this.mVibrationPattern =
823                     mVibrationEffect == null
824                             ? null : mVibrationEffect.computeCreateWaveformOffOnTimingsOrNull();
825         }
826     }
827 
828     /**
829      * Sets the level of interruption of this notification channel.
830      *
831      * Only modifiable before the channel is submitted to
832      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
833      *
834      * @param importance the amount the user should be interrupted by
835      *            notifications from this channel.
836      */
setImportance(@mportance int importance)837     public void setImportance(@Importance int importance) {
838         this.mImportance = importance;
839     }
840 
841     // Modifiable by a notification ranker.
842 
843     /**
844      * Sets whether or not notifications posted to this channel can interrupt the user in
845      * {@link android.app.NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
846      *
847      * <p>Apps with Do Not Disturb policy access (see
848      * {@link NotificationManager#isNotificationPolicyAccessGranted()}) can set up their own
849      * channels this way, but only if the channel hasn't been updated by the user since its
850      * creation.
851      *
852      * <p>Otherwise, this value is only modifiable by the system and the notification ranker.
853      */
setBypassDnd(boolean bypassDnd)854     public void setBypassDnd(boolean bypassDnd) {
855         this.mBypassDnd = bypassDnd;
856     }
857 
858     /**
859      * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,
860      * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
861      *
862      * Only modifiable by the system and notification ranker.
863      */
setLockscreenVisibility(int lockscreenVisibility)864     public void setLockscreenVisibility(int lockscreenVisibility) {
865         this.mLockscreenVisibility = lockscreenVisibility;
866     }
867 
868     /**
869      * As of Android 11 this value is no longer respected.
870      * @see #canBubble()
871      * @see Notification#getBubbleMetadata()
872      */
setAllowBubbles(boolean allowBubbles)873     public void setAllowBubbles(boolean allowBubbles) {
874         mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF;
875     }
876 
877     /**
878      * @hide
879      */
setAllowBubbles(int allowed)880     public void setAllowBubbles(int allowed) {
881         mAllowBubbles = allowed;
882     }
883 
884     /**
885      * Sets this channel as being converastion-centric. Different settings and functionality may be
886      * exposed for conversation-centric channels.
887      *
888      * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
889      *                        this type would be posted to in absence of a specific conversation id.
890      *                        For example, if this channel represents 'Messages from Person A', the
891      *                        parent channel would be 'Messages.'
892      * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
893      *                       channel's conversation.
894      */
setConversationId(@onNull String parentChannelId, @NonNull String conversationId)895     public void setConversationId(@NonNull String parentChannelId,
896             @NonNull String conversationId) {
897         mParentId = parentChannelId;
898         mConversationId = conversationId;
899     }
900 
901     /**
902      * Returns the id of this channel.
903      */
getId()904     public String getId() {
905         return mId;
906     }
907 
908     /**
909      * Returns the user visible name of this channel.
910      */
getName()911     public CharSequence getName() {
912         return mName;
913     }
914 
915     /**
916      * Returns the user visible description of this channel.
917      */
getDescription()918     public String getDescription() {
919         return mDesc;
920     }
921 
922     /**
923      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
924      * notifications posted to this channel. Note: This value might be >
925      * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
926      * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
927      * See {@link NotificationChannelGroup#isBlocked()} and
928      * {@link NotificationManager#areNotificationsEnabled()}.
929      */
getImportance()930     public int getImportance() {
931         return mImportance;
932     }
933 
934     /**
935      * Whether or not notifications posted to this channel can bypass the Do Not Disturb
936      * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode when the active policy allows
937      * priority channels to bypass notification filtering.
938      */
canBypassDnd()939     public boolean canBypassDnd() {
940         return mBypassDnd;
941     }
942 
943     /**
944      * Whether or not this channel represents a conversation.
945      */
isConversation()946     public boolean isConversation() {
947         return !TextUtils.isEmpty(getConversationId());
948     }
949 
950 
951     /**
952      * Whether or not notifications in this conversation are considered important.
953      *
954      * <p>Important conversations may get special visual treatment, and might be able to bypass DND.
955      *
956      * <p>This is only valid for channels that represent conversations, that is,
957      * where {@link #isConversation()} is true.
958      */
isImportantConversation()959     public boolean isImportantConversation() {
960         return mImportantConvo;
961     }
962 
963     /**
964      * Returns the notification sound for this channel.
965      */
getSound()966     public Uri getSound() {
967         return mSound;
968     }
969 
970     /**
971      * Returns the audio attributes for sound played by notifications posted to this channel.
972      */
getAudioAttributes()973     public AudioAttributes getAudioAttributes() {
974         return mAudioAttributes;
975     }
976 
977     /**
978      * Returns whether notifications posted to this channel trigger notification lights.
979      */
shouldShowLights()980     public boolean shouldShowLights() {
981         return mLights;
982     }
983 
984     /**
985      * Returns the notification light color for notifications posted to this channel. Irrelevant
986      * unless {@link #shouldShowLights()}.
987      */
getLightColor()988     public int getLightColor() {
989         return mLightColor;
990     }
991 
992     /**
993      * Returns whether notifications posted to this channel always vibrate.
994      */
shouldVibrate()995     public boolean shouldVibrate() {
996         return mVibrationEnabled;
997     }
998 
999     /**
1000      * Returns the vibration pattern for notifications posted to this channel. Will be ignored if
1001      * vibration is not enabled ({@link #shouldVibrate()}).
1002      */
getVibrationPattern()1003     public long[] getVibrationPattern() {
1004         return mVibrationPattern;
1005     }
1006 
1007     /**
1008      * Returns the {@link VibrationEffect} for notifications posted to this channel.
1009      * The returned effect is derived from either the effect provided in the
1010      * {@link #setVibrationEffect(VibrationEffect)} method, or the equivalent vibration effect
1011      * of the pattern set via the {@link #setVibrationPattern(long[])} method, based on setter
1012      * method that was called last.
1013      *
1014      * The returned effect will be ignored in one of the following cases:
1015      * <ul>
1016      *   <li> vibration is not enabled for the channel (i.e. {@link #shouldVibrate()}
1017      *        returns {@code false}).
1018      *   <li> the effect is not supported/playable by the device. In this case, if
1019      *        vibration is enabled for the channel, the default channel vibration will
1020      *        be used instead.
1021      * </ul>
1022      *
1023      * @return the {@link VibrationEffect} set via {@link
1024      *         #setVibrationEffect(VibrationEffect)}, or the equivalent of the
1025      *         vibration set via {@link #setVibrationPattern(long[])}.
1026      *
1027      *  @see VibrationEffect#createWaveform(long[], int)
1028      */
1029     @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
1030     @Nullable
getVibrationEffect()1031     public VibrationEffect getVibrationEffect() {
1032         return mVibrationEffect;
1033     }
1034 
1035     /**
1036      * Returns whether or not notifications posted to this channel are shown on the lockscreen in
1037      * full or redacted form.
1038      */
getLockscreenVisibility()1039     public int getLockscreenVisibility() {
1040         return mLockscreenVisibility;
1041     }
1042 
1043     /**
1044      * Returns whether notifications posted to this channel can appear as badges in a Launcher
1045      * application.
1046      *
1047      * Note that badging may be disabled for other reasons.
1048      */
canShowBadge()1049     public boolean canShowBadge() {
1050         return mShowBadge;
1051     }
1052 
1053     /**
1054      * Returns what group this channel belongs to.
1055      *
1056      * This is used only for visually grouping channels in the UI.
1057      */
getGroup()1058     public String getGroup() {
1059         return mGroup;
1060     }
1061 
1062     /**
1063      * Returns whether notifications posted to this channel are allowed to display outside of the
1064      * notification shade, in a floating window on top of other apps.
1065      *
1066      * @see Notification#getBubbleMetadata()
1067      */
canBubble()1068     public boolean canBubble() {
1069         return mAllowBubbles == ALLOW_BUBBLE_ON;
1070     }
1071 
1072     /**
1073      * @hide
1074      */
getAllowBubbles()1075     public int getAllowBubbles() {
1076         return mAllowBubbles;
1077     }
1078 
1079     /**
1080      * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's
1081      * a conversation related channel. See {@link #setConversationId(String, String)}.
1082      */
getParentChannelId()1083     public @Nullable String getParentChannelId() {
1084         return mParentId;
1085     }
1086 
1087     /**
1088      * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's
1089      * associated with a conversation. See {@link #setConversationId(String, String)}.
1090      */
getConversationId()1091     public @Nullable String getConversationId() {
1092         return mConversationId;
1093     }
1094 
1095     /**
1096      * @hide
1097      */
1098     @SystemApi
isDeleted()1099     public boolean isDeleted() {
1100         return mDeleted;
1101     }
1102 
1103     /**
1104      * @hide
1105      */
getDeletedTimeMs()1106     public long getDeletedTimeMs() {
1107         return mDeletedTime;
1108     }
1109 
1110     /**
1111      * @hide
1112      */
1113     @SystemApi
getUserLockedFields()1114     public int getUserLockedFields() {
1115         return mUserLockedFields;
1116     }
1117 
1118     /**
1119      * @hide
1120      */
isUserVisibleTaskShown()1121     public boolean isUserVisibleTaskShown() {
1122         return mUserVisibleTaskShown;
1123     }
1124 
1125     /**
1126      * Returns whether this channel is always blockable, even if the app is 'fixed' as
1127      * non-blockable.
1128      */
isBlockable()1129     public boolean isBlockable() {
1130         return mBlockableSystem;
1131     }
1132 
1133     /**
1134      * @hide
1135      */
1136     @TestApi
setImportanceLockedByCriticalDeviceFunction(boolean locked)1137     public void setImportanceLockedByCriticalDeviceFunction(boolean locked) {
1138         mImportanceLockedDefaultApp = locked;
1139     }
1140 
1141     /**
1142      * @hide
1143      */
1144     @TestApi
isImportanceLockedByCriticalDeviceFunction()1145     public boolean isImportanceLockedByCriticalDeviceFunction() {
1146         return mImportanceLockedDefaultApp;
1147     }
1148 
1149     /**
1150      * @hide
1151      */
1152     @TestApi
getOriginalImportance()1153     public int getOriginalImportance() {
1154         return mOriginalImportance;
1155     }
1156 
1157     /**
1158      * @hide
1159      */
1160     @TestApi
setOriginalImportance(int importance)1161     public void setOriginalImportance(int importance) {
1162         mOriginalImportance = importance;
1163     }
1164 
1165     /**
1166      * @hide
1167      */
1168     @TestApi
setDemoted(boolean demoted)1169     public void setDemoted(boolean demoted) {
1170         mDemoted = demoted;
1171     }
1172 
1173     /**
1174      * Returns whether the user has decided that this channel does not represent a conversation. The
1175      * value will always be false for channels that never claimed to be conversations - that is,
1176      * for channels where {@link #getConversationId()} and {@link #getParentChannelId()} are empty.
1177      */
isDemoted()1178     public boolean isDemoted() {
1179         return mDemoted;
1180     }
1181 
1182     /**
1183      * Returns whether the user has chosen the importance of this channel, either to affirm the
1184      * initial selection from the app, or changed it to be higher or lower.
1185      * @see #getImportance()
1186      */
hasUserSetImportance()1187     public boolean hasUserSetImportance() {
1188         return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
1189     }
1190 
1191     /**
1192      * Returns whether the user has chosen the sound of this channel.
1193      * @see #getSound()
1194      */
hasUserSetSound()1195     public boolean hasUserSetSound() {
1196         return (mUserLockedFields & USER_LOCKED_SOUND) != 0;
1197     }
1198 
1199     /**
1200      * Returns the time of the notification post or last update for this channel.
1201      * @return time of post / last update
1202      * @hide
1203      */
getLastNotificationUpdateTimeMs()1204     public long getLastNotificationUpdateTimeMs() {
1205         return mLastNotificationUpdateTimeMs;
1206     }
1207 
1208     /**
1209      * Sets the time of the notification post or last update for this channel.
1210      * @hide
1211      */
setLastNotificationUpdateTimeMs(long updateTimeMs)1212     public void setLastNotificationUpdateTimeMs(long updateTimeMs) {
1213         mLastNotificationUpdateTimeMs = updateTimeMs;
1214     }
1215 
1216     /**
1217      * @hide
1218      */
populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, Context context)1219     public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled,
1220             Context context) {
1221         populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context);
1222     }
1223 
1224     /**
1225      * @hide
1226      */
1227     @SystemApi
populateFromXml(XmlPullParser parser)1228     public void populateFromXml(XmlPullParser parser) {
1229         populateFromXml(XmlUtils.makeTyped(parser), false, true, null);
1230     }
1231 
1232     /**
1233      * If {@param forRestore} is true, {@param Context} MUST be non-null.
1234      */
populateFromXml(TypedXmlPullParser parser, boolean forRestore, boolean pkgInstalled, @Nullable Context context)1235     private void populateFromXml(TypedXmlPullParser parser, boolean forRestore,
1236             boolean pkgInstalled, @Nullable Context context) {
1237         Preconditions.checkArgument(!forRestore || context != null,
1238                 "forRestore is true but got null context");
1239 
1240         // Name, id, and importance are set in the constructor.
1241         setDescription(parser.getAttributeValue(null, ATT_DESC));
1242         setBypassDnd(Notification.PRIORITY_DEFAULT
1243                 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
1244         setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
1245 
1246         Uri sound = safeUri(parser, ATT_SOUND);
1247 
1248         final AudioAttributes audioAttributes = safeAudioAttributes(parser);
1249         final int usage = audioAttributes.getUsage();
1250         setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled, usage) : sound,
1251                 audioAttributes);
1252 
1253         enableLights(safeBool(parser, ATT_LIGHTS, false));
1254         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
1255         // Set the pattern before the effect, so that we can properly handle cases where the pattern
1256         // is null, but the effect is not null (i.e. for non-waveform VibrationEffects - the ones
1257         // which cannot be represented as a vibration pattern).
1258         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
1259         if (Flags.notificationChannelVibrationEffectApi()) {
1260             VibrationEffect vibrationEffect = safeVibrationEffect(parser, ATT_VIBRATION_EFFECT);
1261             if (vibrationEffect != null) {
1262                 // Restore the effect only if it is not null. This allows to avoid undoing a
1263                 // `setVibrationPattern` call above, if that was done with a non-null pattern
1264                 // (e.g. back up from a version that did not support `setVibrationEffect`), or
1265                 // when notif_channel_crop_vibration_effects is true, if there is an equivalent
1266                 // vibration pattern available.
1267                 setVibrationEffect(vibrationEffect);
1268             }
1269         }
1270         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
1271         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
1272         setDeleted(safeBool(parser, ATT_DELETED, false));
1273         setDeletedTimeMs(XmlUtils.readLongAttribute(
1274                 parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS));
1275         setGroup(parser.getAttributeValue(null, ATT_GROUP));
1276         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
1277         setUserVisibleTaskShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
1278         setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
1279         setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
1280         setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
1281         setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
1282                 parser.getAttributeValue(null, ATT_CONVERSATION_ID));
1283         setDemoted(safeBool(parser, ATT_DEMOTE, false));
1284         setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
1285     }
1286 
1287     /**
1288      * Returns whether the sound for this channel was successfully restored
1289      *  from backup.
1290      * @return false if the sound was not restored successfully. true otherwise (default value)
1291      * @hide
1292      */
isSoundRestored()1293     public boolean isSoundRestored() {
1294         return mSoundRestored;
1295     }
1296 
1297     @Nullable
getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri)1298     private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) {
1299         if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) {
1300             return uri;
1301         }
1302 
1303         if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
1304             try {
1305                 contentResolver.getResourceId(uri);
1306                 return uri;
1307             } catch (FileNotFoundException e) {
1308                 return null;
1309             }
1310         }
1311 
1312         if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
1313             return uri;
1314         }
1315         return contentResolver.canonicalize(uri);
1316     }
1317 
1318     @Nullable
getUncanonicalizedSoundUri( ContentResolver contentResolver, @NonNull Uri uri, int usage)1319     private Uri getUncanonicalizedSoundUri(
1320             ContentResolver contentResolver, @NonNull Uri uri, int usage) {
1321         if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)
1322                 || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())
1323                 || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
1324             return uri;
1325         }
1326         int ringtoneType = 0;
1327 
1328         // Consistent with UI(SoundPreferenceController.handlePreferenceTreeClick).
1329         if (AudioAttributes.USAGE_ALARM == usage) {
1330             ringtoneType = RingtoneManager.TYPE_ALARM;
1331         } else if (AudioAttributes.USAGE_NOTIFICATION_RINGTONE == usage) {
1332             ringtoneType = RingtoneManager.TYPE_RINGTONE;
1333         } else {
1334             ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
1335         }
1336         try {
1337             return RingtoneManager.getRingtoneUriForRestore(
1338                     contentResolver, uri.toString(), ringtoneType);
1339         } catch (Exception e) {
1340             Log.e(TAG, "Failed to uncanonicalized sound uri for " + uri + " " + e);
1341             return Settings.System.DEFAULT_NOTIFICATION_URI;
1342         }
1343     }
1344 
1345     /**
1346      * Restore/validate sound Uri from backup
1347      * @param context The Context
1348      * @param uri The sound Uri to restore
1349      * @param pkgInstalled If the parent package is installed
1350      * @return restored and validated Uri
1351      * @hide
1352      */
1353     @Nullable
restoreSoundUri( Context context, @Nullable Uri uri, boolean pkgInstalled, int usage)1354     public Uri restoreSoundUri(
1355             Context context, @Nullable Uri uri, boolean pkgInstalled, int usage) {
1356         if (uri == null || Uri.EMPTY.equals(uri)) {
1357             return null;
1358         }
1359         ContentResolver contentResolver = context.getContentResolver();
1360         // There are backups out there with uncanonical uris (because we fixed this after
1361         // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
1362         // verify the uri against device storage and we'll possibly end up with a broken uri.
1363         // We then canonicalize the uri to uncanonicalize it back, which means we properly check
1364         // the uri and in the case of not having the resource we end up with the default - better
1365         // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
1366         // according to the docs because canonicalize method has to handle canonical uris as well.
1367         Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri);
1368         if (canonicalizedUri == null) {
1369             // Uri failed to restore with package installed
1370             if (!mSoundRestored && pkgInstalled) {
1371                 mSoundRestored = true;
1372                 // We got a null because the uri in the backup does not exist here, so we return
1373                 // default
1374                 return Settings.System.DEFAULT_NOTIFICATION_URI;
1375             } else {
1376                 // Flag as unrestored and try again later (on package install)
1377                 mSoundRestored = false;
1378                 return uri;
1379             }
1380         }
1381         mSoundRestored = true;
1382         return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri, usage);
1383     }
1384 
1385     /**
1386      * @hide
1387      */
1388     @SystemApi
writeXml(XmlSerializer out)1389     public void writeXml(XmlSerializer out) throws IOException {
1390         writeXml(XmlUtils.makeTyped(out), false, null);
1391     }
1392 
1393     /**
1394      * @hide
1395      */
writeXmlForBackup(XmlSerializer out, Context context)1396     public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
1397         writeXml(XmlUtils.makeTyped(out), true, context);
1398     }
1399 
getSoundForBackup(Context context)1400     private Uri getSoundForBackup(Context context) {
1401         Uri sound = getSound();
1402         if (sound == null || Uri.EMPTY.equals(sound)) {
1403             return null;
1404         }
1405         try {
1406             Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound);
1407             if (canonicalSound == null) {
1408                 // The content provider does not support canonical uris so we backup the default
1409                 return Settings.System.DEFAULT_NOTIFICATION_URI;
1410             }
1411             return canonicalSound;
1412         } catch (Exception e) {
1413             Slog.e(TAG, "Cannot find file for sound " + sound + " using default");
1414             return Settings.System.DEFAULT_NOTIFICATION_URI;
1415         }
1416     }
1417 
1418     /**
1419      * If {@param forBackup} is true, {@param Context} MUST be non-null.
1420      */
writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)1421     private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)
1422             throws IOException {
1423         Preconditions.checkArgument(!forBackup || context != null,
1424                 "forBackup is true but got null context");
1425         out.startTag(null, TAG_CHANNEL);
1426         out.attribute(null, ATT_ID, getId());
1427         if (getName() != null) {
1428             out.attribute(null, ATT_NAME, getName().toString());
1429         }
1430         if (getDescription() != null) {
1431             out.attribute(null, ATT_DESC, getDescription());
1432         }
1433         if (getImportance() != DEFAULT_IMPORTANCE) {
1434             out.attributeInt(null, ATT_IMPORTANCE, getImportance());
1435         }
1436         if (canBypassDnd()) {
1437             out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX);
1438         }
1439         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
1440             out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility());
1441         }
1442         Uri sound = forBackup ? getSoundForBackup(context) : getSound();
1443         if (sound != null) {
1444             out.attribute(null, ATT_SOUND, sound.toString());
1445         }
1446         if (getAudioAttributes() != null) {
1447             out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage());
1448             out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType());
1449             out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags());
1450         }
1451         if (shouldShowLights()) {
1452             out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights());
1453         }
1454         if (getLightColor() != DEFAULT_LIGHT_COLOR) {
1455             out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor());
1456         }
1457         if (shouldVibrate()) {
1458             out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate());
1459         }
1460         if (getVibrationPattern() != null) {
1461             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
1462         }
1463         if (getVibrationEffect() != null) {
1464             if (!Flags.notifChannelCropVibrationEffects() || getVibrationPattern() == null) {
1465                 // When notif_channel_crop_vibration_effects is on, only serialize the vibration
1466                 // effect if we do not already have an equivalent vibration pattern.
1467                 out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
1468             }
1469         }
1470         if (getUserLockedFields() != 0) {
1471             out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
1472         }
1473         if (isUserVisibleTaskShown()) {
1474             out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isUserVisibleTaskShown());
1475         }
1476         if (canShowBadge()) {
1477             out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge());
1478         }
1479         if (isDeleted()) {
1480             out.attributeBoolean(null, ATT_DELETED, isDeleted());
1481         }
1482         if (getDeletedTimeMs() >= 0) {
1483             out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs());
1484         }
1485         if (getGroup() != null) {
1486             out.attribute(null, ATT_GROUP, getGroup());
1487         }
1488         if (isBlockable()) {
1489             out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable());
1490         }
1491         if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) {
1492             out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles());
1493         }
1494         if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
1495             out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance());
1496         }
1497         if (getParentChannelId() != null) {
1498             out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
1499         }
1500         if (getConversationId() != null) {
1501             out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
1502         }
1503         if (isDemoted()) {
1504             out.attributeBoolean(null, ATT_DEMOTE, isDemoted());
1505         }
1506         if (isImportantConversation()) {
1507             out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation());
1508         }
1509 
1510         // mImportanceLockedDefaultApp has a different source of truth and so isn't written to
1511         // this xml file
1512 
1513         out.endTag(null, TAG_CHANNEL);
1514     }
1515 
1516     /**
1517      * @hide
1518      */
1519     @SystemApi
toJson()1520     public JSONObject toJson() throws JSONException {
1521         JSONObject record = new JSONObject();
1522         record.put(ATT_ID, getId());
1523         record.put(ATT_NAME, getName());
1524         record.put(ATT_DESC, getDescription());
1525         if (getImportance() != DEFAULT_IMPORTANCE) {
1526             record.put(ATT_IMPORTANCE,
1527                     NotificationListenerService.Ranking.importanceToString(getImportance()));
1528         }
1529         if (canBypassDnd()) {
1530             record.put(ATT_PRIORITY, Notification.PRIORITY_MAX);
1531         }
1532         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
1533             record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
1534         }
1535         if (getSound() != null) {
1536             record.put(ATT_SOUND, getSound().toString());
1537         }
1538         if (getAudioAttributes() != null) {
1539             record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
1540             record.put(ATT_CONTENT_TYPE,
1541                     Integer.toString(getAudioAttributes().getContentType()));
1542             record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
1543         }
1544         record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
1545         record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
1546         record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
1547         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
1548         record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isUserVisibleTaskShown()));
1549         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
1550         if (getVibrationEffect() != null) {
1551             record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
1552         }
1553         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
1554         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
1555         record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
1556         record.put(ATT_GROUP, getGroup());
1557         record.put(ATT_BLOCKABLE_SYSTEM, isBlockable());
1558         record.put(ATT_ALLOW_BUBBLE, getAllowBubbles());
1559         // TODO: original importance
1560         return record;
1561     }
1562 
safeAudioAttributes(TypedXmlPullParser parser)1563     private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) {
1564         int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION);
1565         int contentType = safeInt(parser, ATT_CONTENT_TYPE,
1566                 AudioAttributes.CONTENT_TYPE_SONIFICATION);
1567         int flags = safeInt(parser, ATT_FLAGS, 0);
1568         return new AudioAttributes.Builder()
1569                 .setUsage(usage)
1570                 .setContentType(contentType)
1571                 .setFlags(flags)
1572                 .build();
1573     }
1574 
safeUri(TypedXmlPullParser parser, String att)1575     private static Uri safeUri(TypedXmlPullParser parser, String att) {
1576         final String val = parser.getAttributeValue(null, att);
1577         return val == null ? null : Uri.parse(val);
1578     }
1579 
vibrationToString(VibrationEffect effect)1580     private static String vibrationToString(VibrationEffect effect) {
1581         StringWriter writer = new StringWriter();
1582         try {
1583             VibrationXmlSerializer.serialize(
1584                     effect, writer, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
1585         } catch (IOException e) {
1586             Log.e(TAG, "Unable to serialize vibration: " + effect, e);
1587         }
1588         return writer.toString();
1589     }
1590 
safeVibrationEffect(TypedXmlPullParser parser, String att)1591     private static VibrationEffect safeVibrationEffect(TypedXmlPullParser parser, String att) {
1592         final String val = parser.getAttributeValue(null, att);
1593         if (val != null) {
1594             try {
1595                 return VibrationXmlParser.parseVibrationEffect(
1596                         new StringReader(val), VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
1597             } catch (IOException e) {
1598                 Log.e(TAG, "Unable to read serialized vibration effect", e);
1599             }
1600         }
1601         return null;
1602     }
1603 
safeInt(TypedXmlPullParser parser, String att, int defValue)1604     private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
1605         return parser.getAttributeInt(null, att, defValue);
1606     }
1607 
safeBool(TypedXmlPullParser parser, String att, boolean defValue)1608     private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) {
1609         return parser.getAttributeBoolean(null, att, defValue);
1610     }
1611 
safeLongArray(TypedXmlPullParser parser, String att, long[] defValue)1612     private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) {
1613         final String attributeValue = parser.getAttributeValue(null, att);
1614         if (TextUtils.isEmpty(attributeValue)) return defValue;
1615         String[] values = attributeValue.split(DELIMITER);
1616         long[] longValues = new long[values.length];
1617         for (int i = 0; i < values.length; i++) {
1618             try {
1619                 longValues[i] = Long.parseLong(values[i]);
1620             } catch (NumberFormatException e) {
1621                 longValues[i] = 0;
1622             }
1623         }
1624         return longValues;
1625     }
1626 
longArrayToString(long[] values)1627     private static String longArrayToString(long[] values) {
1628         StringBuilder sb = new StringBuilder();
1629         if (values != null && values.length > 0) {
1630             for (int i = 0; i < values.length - 1; i++) {
1631                 sb.append(values[i]).append(DELIMITER);
1632             }
1633             sb.append(values[values.length - 1]);
1634         }
1635         return sb.toString();
1636     }
1637 
1638     /**
1639      * Get the reserved bundle channel ID for an Adjustment type
1640      * @param the Adjustment type
1641      * @return the channel ID, or null if type is invalid
1642      * @hide
1643      */
getChannelIdForBundleType(@djustment.Types int type)1644     public static @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
1645         switch (type) {
1646             case TYPE_CONTENT_RECOMMENDATION:
1647                 return RECS_ID;
1648             case TYPE_NEWS:
1649                 return NEWS_ID;
1650             case TYPE_PROMOTION:
1651                 return PROMOTIONS_ID;
1652             case TYPE_SOCIAL_MEDIA:
1653                 return SOCIAL_MEDIA_ID;
1654         }
1655         return null;
1656     }
1657 
1658     public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR =
1659             new Creator<NotificationChannel>() {
1660         @Override
1661         public NotificationChannel createFromParcel(Parcel in) {
1662             return new NotificationChannel(in);
1663         }
1664 
1665         @Override
1666         public NotificationChannel[] newArray(int size) {
1667             return new NotificationChannel[size];
1668         }
1669     };
1670 
1671     @Override
describeContents()1672     public int describeContents() {
1673         return 0;
1674     }
1675 
1676     @Override
equals(@ullable Object o)1677     public boolean equals(@Nullable Object o) {
1678         if (this == o) return true;
1679         if (o == null || getClass() != o.getClass()) return false;
1680         NotificationChannel that = (NotificationChannel) o;
1681         return getImportance() == that.getImportance()
1682                 && mBypassDnd == that.mBypassDnd
1683                 && getLockscreenVisibility() == that.getLockscreenVisibility()
1684                 && mLights == that.mLights
1685                 && getLightColor() == that.getLightColor()
1686                 && getUserLockedFields() == that.getUserLockedFields()
1687                 && isUserVisibleTaskShown() == that.isUserVisibleTaskShown()
1688                 && mVibrationEnabled == that.mVibrationEnabled
1689                 && mShowBadge == that.mShowBadge
1690                 && isDeleted() == that.isDeleted()
1691                 && getDeletedTimeMs() == that.getDeletedTimeMs()
1692                 && isBlockable() == that.isBlockable()
1693                 && mAllowBubbles == that.mAllowBubbles
1694                 && Objects.equals(getId(), that.getId())
1695                 && Objects.equals(getName(), that.getName())
1696                 && Objects.equals(mDesc, that.mDesc)
1697                 && Objects.equals(getSound(), that.getSound())
1698                 && Arrays.equals(mVibrationPattern, that.mVibrationPattern)
1699                 && Objects.equals(getVibrationEffect(), that.getVibrationEffect())
1700                 && Objects.equals(getGroup(), that.getGroup())
1701                 && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
1702                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
1703                 && mOriginalImportance == that.mOriginalImportance
1704                 && Objects.equals(getParentChannelId(), that.getParentChannelId())
1705                 && Objects.equals(getConversationId(), that.getConversationId())
1706                 && isDemoted() == that.isDemoted()
1707                 && isImportantConversation() == that.isImportantConversation();
1708     }
1709 
1710     @Override
hashCode()1711     public int hashCode() {
1712         int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd,
1713                 getLockscreenVisibility(), getSound(), mLights, getLightColor(),
1714                 getUserLockedFields(), isUserVisibleTaskShown(),
1715                 mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
1716                 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
1717                 mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(),
1718                 mParentId, mConversationId, mDemoted, mImportantConvo);
1719         result = 31 * result + Arrays.hashCode(mVibrationPattern);
1720         return result;
1721     }
1722 
1723     /** @hide */
dump(PrintWriter pw, String prefix, boolean redacted)1724     public void dump(PrintWriter pw, String prefix, boolean redacted) {
1725         String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName;
1726         String output = "NotificationChannel{"
1727                 + "mId='" + mId + '\''
1728                 + ", mName=" + redactedName
1729                 + getFieldsString()
1730                 + '}';
1731         pw.println(prefix + output);
1732     }
1733 
1734     @Override
toString()1735     public String toString() {
1736         return "NotificationChannel{"
1737                 + "mId='" + mId + '\''
1738                 + ", mName=" + mName
1739                 + getFieldsString()
1740                 + '}';
1741     }
1742 
getFieldsString()1743     private String getFieldsString() {
1744         return  ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
1745                 + ", mImportance=" + mImportance
1746                 + ", mBypassDnd=" + mBypassDnd
1747                 + ", mLockscreenVisibility=" + mLockscreenVisibility
1748                 + ", mSound=" + mSound
1749                 + ", mLights=" + mLights
1750                 + ", mLightColor=" + mLightColor
1751                 + ", mVibrationPattern=" + Arrays.toString(mVibrationPattern)
1752                 + ", mVibrationEffect="
1753                         + (mVibrationEffect == null ? "null" : mVibrationEffect.toString())
1754                 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
1755                 + ", mUserVisibleTaskShown=" + mUserVisibleTaskShown
1756                 + ", mVibrationEnabled=" + mVibrationEnabled
1757                 + ", mShowBadge=" + mShowBadge
1758                 + ", mDeleted=" + mDeleted
1759                 + ", mDeletedTimeMs=" + mDeletedTime
1760                 + ", mGroup='" + mGroup + '\''
1761                 + ", mAudioAttributes=" + mAudioAttributes
1762                 + ", mBlockableSystem=" + mBlockableSystem
1763                 + ", mAllowBubbles=" + mAllowBubbles
1764                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
1765                 + ", mOriginalImp=" + mOriginalImportance
1766                 + ", mParent=" + mParentId
1767                 + ", mConversationId=" + mConversationId
1768                 + ", mDemoted=" + mDemoted
1769                 + ", mImportantConvo=" + mImportantConvo
1770                 + ", mLastNotificationUpdateTimeMs=" + mLastNotificationUpdateTimeMs;
1771     }
1772 
1773     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)1774     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
1775         final long token = proto.start(fieldId);
1776 
1777         proto.write(NotificationChannelProto.ID, mId);
1778         proto.write(NotificationChannelProto.NAME, mName);
1779         proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
1780         proto.write(NotificationChannelProto.IMPORTANCE, mImportance);
1781         proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd);
1782         proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility);
1783         if (mSound != null) {
1784             proto.write(NotificationChannelProto.SOUND, mSound.toString());
1785         }
1786         proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
1787         proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
1788         if (mVibrationPattern != null) {
1789             for (long v : mVibrationPattern) {
1790                 proto.write(NotificationChannelProto.VIBRATION, v);
1791             }
1792         }
1793         proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields);
1794         proto.write(NotificationChannelProto.USER_VISIBLE_TASK_SHOWN, mUserVisibleTaskShown);
1795         proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled);
1796         proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge);
1797         proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
1798         proto.write(NotificationChannelProto.GROUP, mGroup);
1799         if (mAudioAttributes != null) {
1800             mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
1801         }
1802         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
1803         proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);
1804 
1805         proto.end(token);
1806     }
1807 }
1808