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