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.Nullable;
19 import android.annotation.SystemApi;
20 import android.app.NotificationManager.Importance;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.media.AudioAttributes;
25 import android.net.Uri;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.provider.Settings;
29 import android.service.notification.NotificationListenerService;
30 import android.text.TextUtils;
31 import android.util.proto.ProtoOutputStream;
32 
33 import com.android.internal.util.Preconditions;
34 
35 import org.json.JSONException;
36 import org.json.JSONObject;
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlSerializer;
39 
40 import java.io.IOException;
41 import java.util.Arrays;
42 
43 /**
44  * A representation of settings that apply to a collection of similarly themed notifications.
45  */
46 public final class NotificationChannel implements Parcelable {
47 
48     /**
49      * The id of the default channel for an app. This id is reserved by the system. All
50      * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or
51      * earlier without a notification channel specified are posted to this channel.
52      */
53     public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
54 
55     /**
56      * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
57      * limit.
58      */
59     private static final int MAX_TEXT_LENGTH = 1000;
60 
61     private static final String TAG_CHANNEL = "channel";
62     private static final String ATT_NAME = "name";
63     private static final String ATT_DESC = "desc";
64     private static final String ATT_ID = "id";
65     private static final String ATT_DELETED = "deleted";
66     private static final String ATT_PRIORITY = "priority";
67     private static final String ATT_VISIBILITY = "visibility";
68     private static final String ATT_IMPORTANCE = "importance";
69     private static final String ATT_LIGHTS = "lights";
70     private static final String ATT_LIGHT_COLOR = "light_color";
71     private static final String ATT_VIBRATION = "vibration";
72     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
73     private static final String ATT_SOUND = "sound";
74     private static final String ATT_USAGE = "usage";
75     private static final String ATT_FLAGS = "flags";
76     private static final String ATT_CONTENT_TYPE = "content_type";
77     private static final String ATT_SHOW_BADGE = "show_badge";
78     private static final String ATT_USER_LOCKED = "locked";
79     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
80     private static final String ATT_GROUP = "group";
81     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
82     private static final String DELIMITER = ",";
83 
84     /**
85      * @hide
86      */
87     public static final int USER_LOCKED_PRIORITY = 0x00000001;
88     /**
89      * @hide
90      */
91     public static final int USER_LOCKED_VISIBILITY = 0x00000002;
92     /**
93      * @hide
94      */
95     public static final int USER_LOCKED_IMPORTANCE = 0x00000004;
96     /**
97      * @hide
98      */
99     public static final int USER_LOCKED_LIGHTS = 0x00000008;
100     /**
101      * @hide
102      */
103     public static final int USER_LOCKED_VIBRATION = 0x00000010;
104     /**
105      * @hide
106      */
107     public static final int USER_LOCKED_SOUND = 0x00000020;
108 
109     /**
110      * @hide
111      */
112     public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
113 
114     /**
115      * @hide
116      */
117     public static final int[] LOCKABLE_FIELDS = new int[] {
118             USER_LOCKED_PRIORITY,
119             USER_LOCKED_VISIBILITY,
120             USER_LOCKED_IMPORTANCE,
121             USER_LOCKED_LIGHTS,
122             USER_LOCKED_VIBRATION,
123             USER_LOCKED_SOUND,
124             USER_LOCKED_SHOW_BADGE,
125     };
126 
127     private static final int DEFAULT_LIGHT_COLOR = 0;
128     private static final int DEFAULT_VISIBILITY =
129             NotificationManager.VISIBILITY_NO_OVERRIDE;
130     private static final int DEFAULT_IMPORTANCE =
131             NotificationManager.IMPORTANCE_UNSPECIFIED;
132     private static final boolean DEFAULT_DELETED = false;
133     private static final boolean DEFAULT_SHOW_BADGE = true;
134 
135     private final String mId;
136     private String mName;
137     private String mDesc;
138     private int mImportance = DEFAULT_IMPORTANCE;
139     private boolean mBypassDnd;
140     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
141     private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
142     private boolean mLights;
143     private int mLightColor = DEFAULT_LIGHT_COLOR;
144     private long[] mVibration;
145     // Bitwise representation of fields that have been changed by the user, preventing the app from
146     // making changes to these fields.
147     private int mUserLockedFields;
148     private boolean mFgServiceShown;
149     private boolean mVibrationEnabled;
150     private boolean mShowBadge = DEFAULT_SHOW_BADGE;
151     private boolean mDeleted = DEFAULT_DELETED;
152     private String mGroup;
153     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
154     // If this is a blockable system notification channel.
155     private boolean mBlockableSystem = false;
156 
157     /**
158      * Creates a notification channel.
159      *
160      * @param id The id of the channel. Must be unique per package. The value may be truncated if
161      *           it is too long.
162      * @param name The user visible name of the channel. You can rename this channel when the system
163      *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
164      *             broadcast. The recommended maximum length is 40 characters; the value may be
165      *             truncated if it is too long.
166      * @param importance The importance of the channel. This controls how interruptive notifications
167      *                   posted to this channel are.
168      */
NotificationChannel(String id, CharSequence name, @Importance int importance)169     public NotificationChannel(String id, CharSequence name, @Importance int importance) {
170         this.mId = getTrimmedString(id);
171         this.mName = name != null ? getTrimmedString(name.toString()) : null;
172         this.mImportance = importance;
173     }
174 
175     /**
176      * @hide
177      */
NotificationChannel(Parcel in)178     protected NotificationChannel(Parcel in) {
179         if (in.readByte() != 0) {
180             mId = in.readString();
181         } else {
182             mId = null;
183         }
184         if (in.readByte() != 0) {
185             mName = in.readString();
186         } else {
187             mName = null;
188         }
189         if (in.readByte() != 0) {
190             mDesc = in.readString();
191         } else {
192             mDesc = null;
193         }
194         mImportance = in.readInt();
195         mBypassDnd = in.readByte() != 0;
196         mLockscreenVisibility = in.readInt();
197         if (in.readByte() != 0) {
198             mSound = Uri.CREATOR.createFromParcel(in);
199         } else {
200             mSound = null;
201         }
202         mLights = in.readByte() != 0;
203         mVibration = in.createLongArray();
204         mUserLockedFields = in.readInt();
205         mFgServiceShown = in.readByte() != 0;
206         mVibrationEnabled = in.readByte() != 0;
207         mShowBadge = in.readByte() != 0;
208         mDeleted = in.readByte() != 0;
209         if (in.readByte() != 0) {
210             mGroup = in.readString();
211         } else {
212             mGroup = null;
213         }
214         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
215         mLightColor = in.readInt();
216         mBlockableSystem = in.readBoolean();
217     }
218 
219     @Override
writeToParcel(Parcel dest, int flags)220     public void writeToParcel(Parcel dest, int flags) {
221         if (mId != null) {
222             dest.writeByte((byte) 1);
223             dest.writeString(mId);
224         } else {
225             dest.writeByte((byte) 0);
226         }
227         if (mName != null) {
228             dest.writeByte((byte) 1);
229             dest.writeString(mName);
230         } else {
231             dest.writeByte((byte) 0);
232         }
233         if (mDesc != null) {
234             dest.writeByte((byte) 1);
235             dest.writeString(mDesc);
236         } else {
237             dest.writeByte((byte) 0);
238         }
239         dest.writeInt(mImportance);
240         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
241         dest.writeInt(mLockscreenVisibility);
242         if (mSound != null) {
243             dest.writeByte((byte) 1);
244             mSound.writeToParcel(dest, 0);
245         } else {
246             dest.writeByte((byte) 0);
247         }
248         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
249         dest.writeLongArray(mVibration);
250         dest.writeInt(mUserLockedFields);
251         dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0);
252         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
253         dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
254         dest.writeByte(mDeleted ? (byte) 1 : (byte) 0);
255         if (mGroup != null) {
256             dest.writeByte((byte) 1);
257             dest.writeString(mGroup);
258         } else {
259             dest.writeByte((byte) 0);
260         }
261         if (mAudioAttributes != null) {
262             dest.writeInt(1);
263             mAudioAttributes.writeToParcel(dest, 0);
264         } else {
265             dest.writeInt(0);
266         }
267         dest.writeInt(mLightColor);
268         dest.writeBoolean(mBlockableSystem);
269     }
270 
271     /**
272      * @hide
273      */
lockFields(int field)274     public void lockFields(int field) {
275         mUserLockedFields |= field;
276     }
277 
278     /**
279      * @hide
280      */
unlockFields(int field)281     public void unlockFields(int field) {
282         mUserLockedFields &= ~field;
283     }
284 
285     /**
286      * @hide
287      */
setFgServiceShown(boolean shown)288     public void setFgServiceShown(boolean shown) {
289         mFgServiceShown = shown;
290     }
291 
292     /**
293      * @hide
294      */
setDeleted(boolean deleted)295     public void setDeleted(boolean deleted) {
296         mDeleted = deleted;
297     }
298 
299     /**
300      * @hide
301      */
setBlockableSystem(boolean blockableSystem)302     public void setBlockableSystem(boolean blockableSystem) {
303         mBlockableSystem = blockableSystem;
304     }
305     // Modifiable by apps post channel creation
306 
307     /**
308      * Sets the user visible name of this channel.
309      *
310      * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too
311      * long.
312      */
setName(CharSequence name)313     public void setName(CharSequence name) {
314         mName = name != null ? getTrimmedString(name.toString()) : null;
315     }
316 
317     /**
318      * Sets the user visible description of this channel.
319      *
320      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
321      * long.
322      */
setDescription(String description)323     public void setDescription(String description) {
324         mDesc = getTrimmedString(description);
325     }
326 
getTrimmedString(String input)327     private String getTrimmedString(String input) {
328         if (input != null && input.length() > MAX_TEXT_LENGTH) {
329             return input.substring(0, MAX_TEXT_LENGTH);
330         }
331         return input;
332     }
333 
334     // Modifiable by apps on channel creation.
335 
336     /**
337      * Sets what group this channel belongs to.
338      *
339      * Group information is only used for presentation, not for behavior.
340      *
341      * Only modifiable before the channel is submitted to
342      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
343      * channel is not currently part of a group.
344      *
345      * @param groupId the id of a group created by
346      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
347      */
setGroup(String groupId)348     public void setGroup(String groupId) {
349         this.mGroup = groupId;
350     }
351 
352     /**
353      * Sets whether notifications posted to this channel can appear as application icon badges
354      * in a Launcher.
355      *
356      * Only modifiable before the channel is submitted to
357      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
358      *
359      * @param showBadge true if badges should be allowed to be shown.
360      */
setShowBadge(boolean showBadge)361     public void setShowBadge(boolean showBadge) {
362         this.mShowBadge = showBadge;
363     }
364 
365     /**
366      * Sets the sound that should be played for notifications posted to this channel and its
367      * audio attributes. Notification channels with an {@link #getImportance() importance} of at
368      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
369      *
370      * Only modifiable before the channel is submitted to
371      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
372      */
setSound(Uri sound, AudioAttributes audioAttributes)373     public void setSound(Uri sound, AudioAttributes audioAttributes) {
374         this.mSound = sound;
375         this.mAudioAttributes = audioAttributes;
376     }
377 
378     /**
379      * Sets whether notifications posted to this channel should display notification lights,
380      * on devices that support that feature.
381      *
382      * Only modifiable before the channel is submitted to
383      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
384      */
enableLights(boolean lights)385     public void enableLights(boolean lights) {
386         this.mLights = lights;
387     }
388 
389     /**
390      * Sets the notification light color for notifications posted to this channel, if lights are
391      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
392      *
393      * Only modifiable before the channel is submitted to
394      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
395      */
setLightColor(int argb)396     public void setLightColor(int argb) {
397         this.mLightColor = argb;
398     }
399 
400     /**
401      * Sets whether notification posted to this channel should vibrate. The vibration pattern can
402      * be set with {@link #setVibrationPattern(long[])}.
403      *
404      * Only modifiable before the channel is submitted to
405      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
406      */
enableVibration(boolean vibration)407     public void enableVibration(boolean vibration) {
408         this.mVibrationEnabled = vibration;
409     }
410 
411     /**
412      * Sets the vibration pattern for notifications posted to this channel. If the provided
413      * pattern is valid (non-null, non-empty), will {@link #enableVibration(boolean)} enable
414      * vibration} as well. Otherwise, vibration will be disabled.
415      *
416      * Only modifiable before the channel is submitted to
417      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
418      */
setVibrationPattern(long[] vibrationPattern)419     public void setVibrationPattern(long[] vibrationPattern) {
420         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
421         this.mVibration = vibrationPattern;
422     }
423 
424     /**
425      * Sets the level of interruption of this notification channel.
426      *
427      * Only modifiable before the channel is submitted to
428      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
429      *
430      * @param importance the amount the user should be interrupted by
431      *            notifications from this channel.
432      */
setImportance(@mportance int importance)433     public void setImportance(@Importance int importance) {
434         this.mImportance = importance;
435     }
436 
437     // Modifiable by a notification ranker.
438 
439     /**
440      * Sets whether or not notifications posted to this channel can interrupt the user in
441      * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
442      *
443      * Only modifiable by the system and notification ranker.
444      */
setBypassDnd(boolean bypassDnd)445     public void setBypassDnd(boolean bypassDnd) {
446         this.mBypassDnd = bypassDnd;
447     }
448 
449     /**
450      * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,
451      * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
452      *
453      * Only modifiable by the system and notification ranker.
454      */
setLockscreenVisibility(int lockscreenVisibility)455     public void setLockscreenVisibility(int lockscreenVisibility) {
456         this.mLockscreenVisibility = lockscreenVisibility;
457     }
458 
459     /**
460      * Returns the id of this channel.
461      */
getId()462     public String getId() {
463         return mId;
464     }
465 
466     /**
467      * Returns the user visible name of this channel.
468      */
getName()469     public CharSequence getName() {
470         return mName;
471     }
472 
473     /**
474      * Returns the user visible description of this channel.
475      */
getDescription()476     public String getDescription() {
477         return mDesc;
478     }
479 
480     /**
481      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
482      * notifications posted to this channel. Note: This value might be >
483      * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
484      * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
485      * See {@link NotificationChannelGroup#isBlocked()} and
486      * {@link NotificationManager#areNotificationsEnabled()}.
487      */
getImportance()488     public int getImportance() {
489         return mImportance;
490     }
491 
492     /**
493      * Whether or not notifications posted to this channel can bypass the Do Not Disturb
494      * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
495      */
canBypassDnd()496     public boolean canBypassDnd() {
497         return mBypassDnd;
498     }
499 
500     /**
501      * Returns the notification sound for this channel.
502      */
getSound()503     public Uri getSound() {
504         return mSound;
505     }
506 
507     /**
508      * Returns the audio attributes for sound played by notifications posted to this channel.
509      */
getAudioAttributes()510     public AudioAttributes getAudioAttributes() {
511         return mAudioAttributes;
512     }
513 
514     /**
515      * Returns whether notifications posted to this channel trigger notification lights.
516      */
shouldShowLights()517     public boolean shouldShowLights() {
518         return mLights;
519     }
520 
521     /**
522      * Returns the notification light color for notifications posted to this channel. Irrelevant
523      * unless {@link #shouldShowLights()}.
524      */
getLightColor()525     public int getLightColor() {
526         return mLightColor;
527     }
528 
529     /**
530      * Returns whether notifications posted to this channel always vibrate.
531      */
shouldVibrate()532     public boolean shouldVibrate() {
533         return mVibrationEnabled;
534     }
535 
536     /**
537      * Returns the vibration pattern for notifications posted to this channel. Will be ignored if
538      * vibration is not enabled ({@link #shouldVibrate()}.
539      */
getVibrationPattern()540     public long[] getVibrationPattern() {
541         return mVibration;
542     }
543 
544     /**
545      * Returns whether or not notifications posted to this channel are shown on the lockscreen in
546      * full or redacted form.
547      */
getLockscreenVisibility()548     public int getLockscreenVisibility() {
549         return mLockscreenVisibility;
550     }
551 
552     /**
553      * Returns whether notifications posted to this channel can appear as badges in a Launcher
554      * application.
555      *
556      * Note that badging may be disabled for other reasons.
557      */
canShowBadge()558     public boolean canShowBadge() {
559         return mShowBadge;
560     }
561 
562     /**
563      * Returns what group this channel belongs to.
564      *
565      * This is used only for visually grouping channels in the UI.
566      */
getGroup()567     public String getGroup() {
568         return mGroup;
569     }
570 
571     /**
572      * @hide
573      */
574     @SystemApi
isDeleted()575     public boolean isDeleted() {
576         return mDeleted;
577     }
578 
579     /**
580      * @hide
581      */
582     @SystemApi
getUserLockedFields()583     public int getUserLockedFields() {
584         return mUserLockedFields;
585     }
586 
587     /**
588      * @hide
589      */
isFgServiceShown()590     public boolean isFgServiceShown() {
591         return mFgServiceShown;
592     }
593 
594     /**
595      * @hide
596      */
isBlockableSystem()597     public boolean isBlockableSystem() {
598         return mBlockableSystem;
599     }
600 
601     /**
602      * @hide
603      */
populateFromXmlForRestore(XmlPullParser parser, Context context)604     public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
605         populateFromXml(parser, true, context);
606     }
607 
608     /**
609      * @hide
610      */
611     @SystemApi
populateFromXml(XmlPullParser parser)612     public void populateFromXml(XmlPullParser parser) {
613         populateFromXml(parser, false, null);
614     }
615 
616     /**
617      * If {@param forRestore} is true, {@param Context} MUST be non-null.
618      */
populateFromXml(XmlPullParser parser, boolean forRestore, @Nullable Context context)619     private void populateFromXml(XmlPullParser parser, boolean forRestore,
620             @Nullable Context context) {
621         Preconditions.checkArgument(!forRestore || context != null,
622                 "forRestore is true but got null context");
623 
624         // Name, id, and importance are set in the constructor.
625         setDescription(parser.getAttributeValue(null, ATT_DESC));
626         setBypassDnd(Notification.PRIORITY_DEFAULT
627                 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
628         setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
629 
630         Uri sound = safeUri(parser, ATT_SOUND);
631         setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
632 
633         enableLights(safeBool(parser, ATT_LIGHTS, false));
634         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
635         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
636         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
637         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
638         setDeleted(safeBool(parser, ATT_DELETED, false));
639         setGroup(parser.getAttributeValue(null, ATT_GROUP));
640         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
641         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
642         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
643     }
644 
645     @Nullable
restoreSoundUri(Context context, @Nullable Uri uri)646     private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
647         if (uri == null) {
648             return null;
649         }
650         ContentResolver contentResolver = context.getContentResolver();
651         // There are backups out there with uncanonical uris (because we fixed this after
652         // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
653         // verify the uri against device storage and we'll possibly end up with a broken uri.
654         // We then canonicalize the uri to uncanonicalize it back, which means we properly check
655         // the uri and in the case of not having the resource we end up with the default - better
656         // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
657         // according to the docs because canonicalize method has to handle canonical uris as well.
658         Uri canonicalizedUri = contentResolver.canonicalize(uri);
659         if (canonicalizedUri == null) {
660             // We got a null because the uri in the backup does not exist here, so we return default
661             return Settings.System.DEFAULT_NOTIFICATION_URI;
662         }
663         return contentResolver.uncanonicalize(canonicalizedUri);
664     }
665 
666     /**
667      * @hide
668      */
669     @SystemApi
writeXml(XmlSerializer out)670     public void writeXml(XmlSerializer out) throws IOException {
671         writeXml(out, false, null);
672     }
673 
674     /**
675      * @hide
676      */
writeXmlForBackup(XmlSerializer out, Context context)677     public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
678         writeXml(out, true, context);
679     }
680 
getSoundForBackup(Context context)681     private Uri getSoundForBackup(Context context) {
682         Uri sound = getSound();
683         if (sound == null) {
684             return null;
685         }
686         Uri canonicalSound = context.getContentResolver().canonicalize(sound);
687         if (canonicalSound == null) {
688             // The content provider does not support canonical uris so we backup the default
689             return Settings.System.DEFAULT_NOTIFICATION_URI;
690         }
691         return canonicalSound;
692     }
693 
694     /**
695      * If {@param forBackup} is true, {@param Context} MUST be non-null.
696      */
writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)697     private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)
698             throws IOException {
699         Preconditions.checkArgument(!forBackup || context != null,
700                 "forBackup is true but got null context");
701         out.startTag(null, TAG_CHANNEL);
702         out.attribute(null, ATT_ID, getId());
703         if (getName() != null) {
704             out.attribute(null, ATT_NAME, getName().toString());
705         }
706         if (getDescription() != null) {
707             out.attribute(null, ATT_DESC, getDescription());
708         }
709         if (getImportance() != DEFAULT_IMPORTANCE) {
710             out.attribute(
711                     null, ATT_IMPORTANCE, Integer.toString(getImportance()));
712         }
713         if (canBypassDnd()) {
714             out.attribute(
715                     null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX));
716         }
717         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
718             out.attribute(null, ATT_VISIBILITY,
719                     Integer.toString(getLockscreenVisibility()));
720         }
721         Uri sound = forBackup ? getSoundForBackup(context) : getSound();
722         if (sound != null) {
723             out.attribute(null, ATT_SOUND, sound.toString());
724         }
725         if (getAudioAttributes() != null) {
726             out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
727             out.attribute(null, ATT_CONTENT_TYPE,
728                     Integer.toString(getAudioAttributes().getContentType()));
729             out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
730         }
731         if (shouldShowLights()) {
732             out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights()));
733         }
734         if (getLightColor() != DEFAULT_LIGHT_COLOR) {
735             out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
736         }
737         if (shouldVibrate()) {
738             out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
739         }
740         if (getVibrationPattern() != null) {
741             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
742         }
743         if (getUserLockedFields() != 0) {
744             out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
745         }
746         if (isFgServiceShown()) {
747             out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown()));
748         }
749         if (canShowBadge()) {
750             out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
751         }
752         if (isDeleted()) {
753             out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted()));
754         }
755         if (getGroup() != null) {
756             out.attribute(null, ATT_GROUP, getGroup());
757         }
758         if (isBlockableSystem()) {
759             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
760         }
761 
762         out.endTag(null, TAG_CHANNEL);
763     }
764 
765     /**
766      * @hide
767      */
768     @SystemApi
toJson()769     public JSONObject toJson() throws JSONException {
770         JSONObject record = new JSONObject();
771         record.put(ATT_ID, getId());
772         record.put(ATT_NAME, getName());
773         record.put(ATT_DESC, getDescription());
774         if (getImportance() != DEFAULT_IMPORTANCE) {
775             record.put(ATT_IMPORTANCE,
776                     NotificationListenerService.Ranking.importanceToString(getImportance()));
777         }
778         if (canBypassDnd()) {
779             record.put(ATT_PRIORITY, Notification.PRIORITY_MAX);
780         }
781         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
782             record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
783         }
784         if (getSound() != null) {
785             record.put(ATT_SOUND, getSound().toString());
786         }
787         if (getAudioAttributes() != null) {
788             record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
789             record.put(ATT_CONTENT_TYPE,
790                     Integer.toString(getAudioAttributes().getContentType()));
791             record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
792         }
793         record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
794         record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
795         record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
796         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
797         record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown()));
798         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
799         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
800         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
801         record.put(ATT_GROUP, getGroup());
802         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
803         return record;
804     }
805 
safeAudioAttributes(XmlPullParser parser)806     private static AudioAttributes safeAudioAttributes(XmlPullParser parser) {
807         int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION);
808         int contentType = safeInt(parser, ATT_CONTENT_TYPE,
809                 AudioAttributes.CONTENT_TYPE_SONIFICATION);
810         int flags = safeInt(parser, ATT_FLAGS, 0);
811         return new AudioAttributes.Builder()
812                 .setUsage(usage)
813                 .setContentType(contentType)
814                 .setFlags(flags)
815                 .build();
816     }
817 
safeUri(XmlPullParser parser, String att)818     private static Uri safeUri(XmlPullParser parser, String att) {
819         final String val = parser.getAttributeValue(null, att);
820         return val == null ? null : Uri.parse(val);
821     }
822 
safeInt(XmlPullParser parser, String att, int defValue)823     private static int safeInt(XmlPullParser parser, String att, int defValue) {
824         final String val = parser.getAttributeValue(null, att);
825         return tryParseInt(val, defValue);
826     }
827 
tryParseInt(String value, int defValue)828     private static int tryParseInt(String value, int defValue) {
829         if (TextUtils.isEmpty(value)) return defValue;
830         try {
831             return Integer.parseInt(value);
832         } catch (NumberFormatException e) {
833             return defValue;
834         }
835     }
836 
safeBool(XmlPullParser parser, String att, boolean defValue)837     private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
838         final String value = parser.getAttributeValue(null, att);
839         if (TextUtils.isEmpty(value)) return defValue;
840         return Boolean.parseBoolean(value);
841     }
842 
safeLongArray(XmlPullParser parser, String att, long[] defValue)843     private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) {
844         final String attributeValue = parser.getAttributeValue(null, att);
845         if (TextUtils.isEmpty(attributeValue)) return defValue;
846         String[] values = attributeValue.split(DELIMITER);
847         long[] longValues = new long[values.length];
848         for (int i = 0; i < values.length; i++) {
849             try {
850                 longValues[i] = Long.parseLong(values[i]);
851             } catch (NumberFormatException e) {
852                 longValues[i] = 0;
853             }
854         }
855         return longValues;
856     }
857 
longArrayToString(long[] values)858     private static String longArrayToString(long[] values) {
859         StringBuffer sb = new StringBuffer();
860         if (values != null && values.length > 0) {
861             for (int i = 0; i < values.length - 1; i++) {
862                 sb.append(values[i]).append(DELIMITER);
863             }
864             sb.append(values[values.length - 1]);
865         }
866         return sb.toString();
867     }
868 
869     public static final Creator<NotificationChannel> CREATOR = new Creator<NotificationChannel>() {
870         @Override
871         public NotificationChannel createFromParcel(Parcel in) {
872             return new NotificationChannel(in);
873         }
874 
875         @Override
876         public NotificationChannel[] newArray(int size) {
877             return new NotificationChannel[size];
878         }
879     };
880 
881     @Override
describeContents()882     public int describeContents() {
883         return 0;
884     }
885 
886     @Override
equals(Object o)887     public boolean equals(Object o) {
888         if (this == o) return true;
889         if (o == null || getClass() != o.getClass()) return false;
890 
891         NotificationChannel that = (NotificationChannel) o;
892 
893         if (getImportance() != that.getImportance()) return false;
894         if (mBypassDnd != that.mBypassDnd) return false;
895         if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
896         if (mLights != that.mLights) return false;
897         if (getLightColor() != that.getLightColor()) return false;
898         if (getUserLockedFields() != that.getUserLockedFields()) return false;
899         if (mVibrationEnabled != that.mVibrationEnabled) return false;
900         if (mShowBadge != that.mShowBadge) return false;
901         if (isDeleted() != that.isDeleted()) return false;
902         if (isBlockableSystem() != that.isBlockableSystem()) return false;
903         if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
904         if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
905             return false;
906         }
907         if (getDescription() != null ? !getDescription().equals(that.getDescription())
908                 : that.getDescription() != null) {
909             return false;
910         }
911         if (getSound() != null ? !getSound().equals(that.getSound()) : that.getSound() != null) {
912             return false;
913         }
914         if (!Arrays.equals(mVibration, that.mVibration)) return false;
915         if (getGroup() != null ? !getGroup().equals(that.getGroup()) : that.getGroup() != null) {
916             return false;
917         }
918         return getAudioAttributes() != null ? getAudioAttributes().equals(that.getAudioAttributes())
919                 : that.getAudioAttributes() == null;
920 
921     }
922 
923     @Override
hashCode()924     public int hashCode() {
925         int result = getId() != null ? getId().hashCode() : 0;
926         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
927         result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
928         result = 31 * result + getImportance();
929         result = 31 * result + (mBypassDnd ? 1 : 0);
930         result = 31 * result + getLockscreenVisibility();
931         result = 31 * result + (getSound() != null ? getSound().hashCode() : 0);
932         result = 31 * result + (mLights ? 1 : 0);
933         result = 31 * result + getLightColor();
934         result = 31 * result + Arrays.hashCode(mVibration);
935         result = 31 * result + getUserLockedFields();
936         result = 31 * result + (mVibrationEnabled ? 1 : 0);
937         result = 31 * result + (mShowBadge ? 1 : 0);
938         result = 31 * result + (isDeleted() ? 1 : 0);
939         result = 31 * result + (getGroup() != null ? getGroup().hashCode() : 0);
940         result = 31 * result + (getAudioAttributes() != null ? getAudioAttributes().hashCode() : 0);
941         result = 31 * result + (isBlockableSystem() ? 1 : 0);
942         return result;
943     }
944 
945     @Override
toString()946     public String toString() {
947         return "NotificationChannel{"
948                 + "mId='" + mId + '\''
949                 + ", mName=" + mName
950                 + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
951                 + ", mImportance=" + mImportance
952                 + ", mBypassDnd=" + mBypassDnd
953                 + ", mLockscreenVisibility=" + mLockscreenVisibility
954                 + ", mSound=" + mSound
955                 + ", mLights=" + mLights
956                 + ", mLightColor=" + mLightColor
957                 + ", mVibration=" + Arrays.toString(mVibration)
958                 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
959                 + ", mFgServiceShown=" + mFgServiceShown
960                 + ", mVibrationEnabled=" + mVibrationEnabled
961                 + ", mShowBadge=" + mShowBadge
962                 + ", mDeleted=" + mDeleted
963                 + ", mGroup='" + mGroup + '\''
964                 + ", mAudioAttributes=" + mAudioAttributes
965                 + ", mBlockableSystem=" + mBlockableSystem
966                 + '}';
967     }
968 
969     /** @hide */
writeToProto(ProtoOutputStream proto, long fieldId)970     public void writeToProto(ProtoOutputStream proto, long fieldId) {
971         final long token = proto.start(fieldId);
972 
973         proto.write(NotificationChannelProto.ID, mId);
974         proto.write(NotificationChannelProto.NAME, mName);
975         proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
976         proto.write(NotificationChannelProto.IMPORTANCE, mImportance);
977         proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd);
978         proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility);
979         if (mSound != null) {
980             proto.write(NotificationChannelProto.SOUND, mSound.toString());
981         }
982         proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
983         proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
984         if (mVibration != null) {
985             for (long v : mVibration) {
986                 proto.write(NotificationChannelProto.VIBRATION, v);
987             }
988         }
989         proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields);
990         proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown);
991         proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled);
992         proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge);
993         proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
994         proto.write(NotificationChannelProto.GROUP, mGroup);
995         if (mAudioAttributes != null) {
996             mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
997         }
998         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
999 
1000         proto.end(token);
1001     }
1002 }
1003