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