Home | History | Annotate | Download | only in app
      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      */
    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      */
    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
    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      */
    274     public void lockFields(int field) {
    275         mUserLockedFields |= field;
    276     }
    277 
    278     /**
    279      * @hide
    280      */
    281     public void unlockFields(int field) {
    282         mUserLockedFields &= ~field;
    283     }
    284 
    285     /**
    286      * @hide
    287      */
    288     public void setFgServiceShown(boolean shown) {
    289         mFgServiceShown = shown;
    290     }
    291 
    292     /**
    293      * @hide
    294      */
    295     public void setDeleted(boolean deleted) {
    296         mDeleted = deleted;
    297     }
    298 
    299     /**
    300      * @hide
    301      */
    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      */
    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      */
    323     public void setDescription(String description) {
    324         mDesc = getTrimmedString(description);
    325     }
    326 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    455     public void setLockscreenVisibility(int lockscreenVisibility) {
    456         this.mLockscreenVisibility = lockscreenVisibility;
    457     }
    458 
    459     /**
    460      * Returns the id of this channel.
    461      */
    462     public String getId() {
    463         return mId;
    464     }
    465 
    466     /**
    467      * Returns the user visible name of this channel.
    468      */
    469     public CharSequence getName() {
    470         return mName;
    471     }
    472 
    473     /**
    474      * Returns the user visible description of this channel.
    475      */
    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      */
    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      */
    496     public boolean canBypassDnd() {
    497         return mBypassDnd;
    498     }
    499 
    500     /**
    501      * Returns the notification sound for this channel.
    502      */
    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      */
    510     public AudioAttributes getAudioAttributes() {
    511         return mAudioAttributes;
    512     }
    513 
    514     /**
    515      * Returns whether notifications posted to this channel trigger notification lights.
    516      */
    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      */
    525     public int getLightColor() {
    526         return mLightColor;
    527     }
    528 
    529     /**
    530      * Returns whether notifications posted to this channel always vibrate.
    531      */
    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      */
    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      */
    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      */
    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      */
    567     public String getGroup() {
    568         return mGroup;
    569     }
    570 
    571     /**
    572      * @hide
    573      */
    574     @SystemApi
    575     public boolean isDeleted() {
    576         return mDeleted;
    577     }
    578 
    579     /**
    580      * @hide
    581      */
    582     @SystemApi
    583     public int getUserLockedFields() {
    584         return mUserLockedFields;
    585     }
    586 
    587     /**
    588      * @hide
    589      */
    590     public boolean isFgServiceShown() {
    591         return mFgServiceShown;
    592     }
    593 
    594     /**
    595      * @hide
    596      */
    597     public boolean isBlockableSystem() {
    598         return mBlockableSystem;
    599     }
    600 
    601     /**
    602      * @hide
    603      */
    604     public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
    605         populateFromXml(parser, true, context);
    606     }
    607 
    608     /**
    609      * @hide
    610      */
    611     @SystemApi
    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      */
    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
    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
    670     public void writeXml(XmlSerializer out) throws IOException {
    671         writeXml(out, false, null);
    672     }
    673 
    674     /**
    675      * @hide
    676      */
    677     public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
    678         writeXml(out, true, context);
    679     }
    680 
    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      */
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    882     public int describeContents() {
    883         return 0;
    884     }
    885 
    886     @Override
    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
    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
    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 */
    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