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