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.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.SystemApi; 21 import android.annotation.TestApi; 22 import android.app.NotificationManager.Importance; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ShortcutInfo; 28 import android.media.AudioAttributes; 29 import android.net.Uri; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.provider.Settings; 33 import android.service.notification.NotificationListenerService; 34 import android.text.TextUtils; 35 import android.util.proto.ProtoOutputStream; 36 37 import com.android.internal.util.Preconditions; 38 39 import org.json.JSONException; 40 import org.json.JSONObject; 41 import org.xmlpull.v1.XmlPullParser; 42 import org.xmlpull.v1.XmlSerializer; 43 44 import java.io.IOException; 45 import java.io.PrintWriter; 46 import java.util.Arrays; 47 import java.util.Objects; 48 49 /** 50 * A representation of settings that apply to a collection of similarly themed notifications. 51 */ 52 public final class NotificationChannel implements Parcelable { 53 54 /** 55 * The id of the default channel for an app. This id is reserved by the system. All 56 * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or 57 * earlier without a notification channel specified are posted to this channel. 58 */ 59 public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; 60 61 /** 62 * The formatter used by the system to create an id for notification 63 * channels when it automatically creates conversation channels on behalf of an app. The format 64 * string takes two arguments, in this order: the 65 * {@link #getId()} of the original notification channel, and the 66 * {@link ShortcutInfo#getId() id} of the conversation. 67 * @hide 68 */ 69 public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s"; 70 71 /** 72 * TODO: STOPSHIP remove 73 * Conversation id to use for apps that aren't providing them yet. 74 * @hide 75 */ 76 public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id"; 77 78 /** 79 * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this 80 * limit. 81 */ 82 private static final int MAX_TEXT_LENGTH = 1000; 83 84 private static final String TAG_CHANNEL = "channel"; 85 private static final String ATT_NAME = "name"; 86 private static final String ATT_DESC = "desc"; 87 private static final String ATT_ID = "id"; 88 private static final String ATT_DELETED = "deleted"; 89 private static final String ATT_PRIORITY = "priority"; 90 private static final String ATT_VISIBILITY = "visibility"; 91 private static final String ATT_IMPORTANCE = "importance"; 92 private static final String ATT_LIGHTS = "lights"; 93 private static final String ATT_LIGHT_COLOR = "light_color"; 94 private static final String ATT_VIBRATION = "vibration"; 95 private static final String ATT_VIBRATION_ENABLED = "vibration_enabled"; 96 private static final String ATT_SOUND = "sound"; 97 private static final String ATT_USAGE = "usage"; 98 private static final String ATT_FLAGS = "flags"; 99 private static final String ATT_CONTENT_TYPE = "content_type"; 100 private static final String ATT_SHOW_BADGE = "show_badge"; 101 private static final String ATT_USER_LOCKED = "locked"; 102 private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; 103 private static final String ATT_GROUP = "group"; 104 private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; 105 private static final String ATT_ALLOW_BUBBLE = "allow_bubbles"; 106 private static final String ATT_ORIG_IMP = "orig_imp"; 107 private static final String ATT_PARENT_CHANNEL = "parent"; 108 private static final String ATT_CONVERSATION_ID = "conv_id"; 109 private static final String ATT_IMP_CONVERSATION = "imp_conv"; 110 private static final String ATT_DEMOTE = "dem"; 111 private static final String DELIMITER = ","; 112 113 /** 114 * @hide 115 */ 116 public static final int USER_LOCKED_PRIORITY = 0x00000001; 117 /** 118 * @hide 119 */ 120 public static final int USER_LOCKED_VISIBILITY = 0x00000002; 121 /** 122 * @hide 123 */ 124 public static final int USER_LOCKED_IMPORTANCE = 0x00000004; 125 /** 126 * @hide 127 */ 128 public static final int USER_LOCKED_LIGHTS = 0x00000008; 129 /** 130 * @hide 131 */ 132 public static final int USER_LOCKED_VIBRATION = 0x00000010; 133 /** 134 * @hide 135 */ 136 @SystemApi 137 @TestApi 138 public static final int USER_LOCKED_SOUND = 0x00000020; 139 140 /** 141 * @hide 142 */ 143 public static final int USER_LOCKED_SHOW_BADGE = 0x00000080; 144 145 /** 146 * @hide 147 */ 148 public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100; 149 150 /** 151 * @hide 152 */ 153 public static final int[] LOCKABLE_FIELDS = new int[] { 154 USER_LOCKED_PRIORITY, 155 USER_LOCKED_VISIBILITY, 156 USER_LOCKED_IMPORTANCE, 157 USER_LOCKED_LIGHTS, 158 USER_LOCKED_VIBRATION, 159 USER_LOCKED_SOUND, 160 USER_LOCKED_SHOW_BADGE, 161 USER_LOCKED_ALLOW_BUBBLE 162 }; 163 164 /** 165 * @hide 166 */ 167 public static final int DEFAULT_ALLOW_BUBBLE = -1; 168 /** 169 * @hide 170 */ 171 public static final int ALLOW_BUBBLE_ON = 1; 172 /** 173 * @hide 174 */ 175 public static final int ALLOW_BUBBLE_OFF = 0; 176 177 private static final int DEFAULT_LIGHT_COLOR = 0; 178 private static final int DEFAULT_VISIBILITY = 179 NotificationManager.VISIBILITY_NO_OVERRIDE; 180 private static final int DEFAULT_IMPORTANCE = 181 NotificationManager.IMPORTANCE_UNSPECIFIED; 182 private static final boolean DEFAULT_DELETED = false; 183 private static final boolean DEFAULT_SHOW_BADGE = true; 184 185 @UnsupportedAppUsage 186 private String mId; 187 private String mName; 188 private String mDesc; 189 private int mImportance = DEFAULT_IMPORTANCE; 190 private int mOriginalImportance = DEFAULT_IMPORTANCE; 191 private boolean mBypassDnd; 192 private int mLockscreenVisibility = DEFAULT_VISIBILITY; 193 private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; 194 private boolean mLights; 195 private int mLightColor = DEFAULT_LIGHT_COLOR; 196 private long[] mVibration; 197 // Bitwise representation of fields that have been changed by the user, preventing the app from 198 // making changes to these fields. 199 private int mUserLockedFields; 200 private boolean mFgServiceShown; 201 private boolean mVibrationEnabled; 202 private boolean mShowBadge = DEFAULT_SHOW_BADGE; 203 private boolean mDeleted = DEFAULT_DELETED; 204 private String mGroup; 205 private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; 206 // If this is a blockable system notification channel. 207 private boolean mBlockableSystem = false; 208 private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; 209 private boolean mImportanceLockedByOEM; 210 private boolean mImportanceLockedDefaultApp; 211 private String mParentId = null; 212 private String mConversationId = null; 213 private boolean mDemoted = false; 214 private boolean mImportantConvo = false; 215 216 /** 217 * Creates a notification channel. 218 * 219 * @param id The id of the channel. Must be unique per package. The value may be truncated if 220 * it is too long. 221 * @param name The user visible name of the channel. You can rename this channel when the system 222 * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} 223 * broadcast. The recommended maximum length is 40 characters; the value may be 224 * truncated if it is too long. 225 * @param importance The importance of the channel. This controls how interruptive notifications 226 * posted to this channel are. 227 */ NotificationChannel(String id, CharSequence name, @Importance int importance)228 public NotificationChannel(String id, CharSequence name, @Importance int importance) { 229 this.mId = getTrimmedString(id); 230 this.mName = name != null ? getTrimmedString(name.toString()) : null; 231 this.mImportance = importance; 232 } 233 234 /** 235 * @hide 236 */ NotificationChannel(Parcel in)237 protected NotificationChannel(Parcel in) { 238 if (in.readByte() != 0) { 239 mId = in.readString(); 240 } else { 241 mId = null; 242 } 243 if (in.readByte() != 0) { 244 mName = in.readString(); 245 } else { 246 mName = null; 247 } 248 if (in.readByte() != 0) { 249 mDesc = in.readString(); 250 } else { 251 mDesc = null; 252 } 253 mImportance = in.readInt(); 254 mBypassDnd = in.readByte() != 0; 255 mLockscreenVisibility = in.readInt(); 256 if (in.readByte() != 0) { 257 mSound = Uri.CREATOR.createFromParcel(in); 258 } else { 259 mSound = null; 260 } 261 mLights = in.readByte() != 0; 262 mVibration = in.createLongArray(); 263 mUserLockedFields = in.readInt(); 264 mFgServiceShown = in.readByte() != 0; 265 mVibrationEnabled = in.readByte() != 0; 266 mShowBadge = in.readByte() != 0; 267 mDeleted = in.readByte() != 0; 268 if (in.readByte() != 0) { 269 mGroup = in.readString(); 270 } else { 271 mGroup = null; 272 } 273 mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; 274 mLightColor = in.readInt(); 275 mBlockableSystem = in.readBoolean(); 276 mAllowBubbles = in.readInt(); 277 mImportanceLockedByOEM = in.readBoolean(); 278 mOriginalImportance = in.readInt(); 279 mParentId = in.readString(); 280 mConversationId = in.readString(); 281 mDemoted = in.readBoolean(); 282 mImportantConvo = in.readBoolean(); 283 } 284 285 @Override writeToParcel(Parcel dest, int flags)286 public void writeToParcel(Parcel dest, int flags) { 287 if (mId != null) { 288 dest.writeByte((byte) 1); 289 dest.writeString(mId); 290 } else { 291 dest.writeByte((byte) 0); 292 } 293 if (mName != null) { 294 dest.writeByte((byte) 1); 295 dest.writeString(mName); 296 } else { 297 dest.writeByte((byte) 0); 298 } 299 if (mDesc != null) { 300 dest.writeByte((byte) 1); 301 dest.writeString(mDesc); 302 } else { 303 dest.writeByte((byte) 0); 304 } 305 dest.writeInt(mImportance); 306 dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0); 307 dest.writeInt(mLockscreenVisibility); 308 if (mSound != null) { 309 dest.writeByte((byte) 1); 310 mSound.writeToParcel(dest, 0); 311 } else { 312 dest.writeByte((byte) 0); 313 } 314 dest.writeByte(mLights ? (byte) 1 : (byte) 0); 315 dest.writeLongArray(mVibration); 316 dest.writeInt(mUserLockedFields); 317 dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0); 318 dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); 319 dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); 320 dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); 321 if (mGroup != null) { 322 dest.writeByte((byte) 1); 323 dest.writeString(mGroup); 324 } else { 325 dest.writeByte((byte) 0); 326 } 327 if (mAudioAttributes != null) { 328 dest.writeInt(1); 329 mAudioAttributes.writeToParcel(dest, 0); 330 } else { 331 dest.writeInt(0); 332 } 333 dest.writeInt(mLightColor); 334 dest.writeBoolean(mBlockableSystem); 335 dest.writeInt(mAllowBubbles); 336 dest.writeBoolean(mImportanceLockedByOEM); 337 dest.writeInt(mOriginalImportance); 338 dest.writeString(mParentId); 339 dest.writeString(mConversationId); 340 dest.writeBoolean(mDemoted); 341 dest.writeBoolean(mImportantConvo); 342 } 343 344 /** 345 * @hide 346 */ 347 @TestApi lockFields(int field)348 public void lockFields(int field) { 349 mUserLockedFields |= field; 350 } 351 352 /** 353 * @hide 354 */ unlockFields(int field)355 public void unlockFields(int field) { 356 mUserLockedFields &= ~field; 357 } 358 359 /** 360 * @hide 361 */ 362 @TestApi setFgServiceShown(boolean shown)363 public void setFgServiceShown(boolean shown) { 364 mFgServiceShown = shown; 365 } 366 367 /** 368 * @hide 369 */ 370 @TestApi setDeleted(boolean deleted)371 public void setDeleted(boolean deleted) { 372 mDeleted = deleted; 373 } 374 375 /** 376 * @hide 377 */ 378 @TestApi setImportantConversation(boolean importantConvo)379 public void setImportantConversation(boolean importantConvo) { 380 mImportantConvo = importantConvo; 381 } 382 383 /** 384 * Allows users to block notifications sent through this channel, if this channel belongs to 385 * a package that is signed with the system signature. 386 * 387 * If the channel does not belong to a package that is signed with the system signature, this 388 * method does nothing, since such channels are blockable by default and cannot be set to be 389 * unblockable. 390 * @param blockable if {@code true}, allows users to block notifications on this channel. 391 * @hide 392 */ 393 @SystemApi 394 @TestApi setBlockable(boolean blockable)395 public void setBlockable(boolean blockable) { 396 mBlockableSystem = blockable; 397 } 398 // Modifiable by apps post channel creation 399 400 /** 401 * Sets the user visible name of this channel. 402 * 403 * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too 404 * long. 405 */ setName(CharSequence name)406 public void setName(CharSequence name) { 407 mName = name != null ? getTrimmedString(name.toString()) : null; 408 } 409 410 /** 411 * Sets the user visible description of this channel. 412 * 413 * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too 414 * long. 415 */ setDescription(String description)416 public void setDescription(String description) { 417 mDesc = getTrimmedString(description); 418 } 419 getTrimmedString(String input)420 private String getTrimmedString(String input) { 421 if (input != null && input.length() > MAX_TEXT_LENGTH) { 422 return input.substring(0, MAX_TEXT_LENGTH); 423 } 424 return input; 425 } 426 427 /** 428 * @hide 429 */ setId(String id)430 public void setId(String id) { 431 mId = id; 432 } 433 434 // Modifiable by apps on channel creation. 435 436 /** 437 * Sets what group this channel belongs to. 438 * 439 * Group information is only used for presentation, not for behavior. 440 * 441 * Only modifiable before the channel is submitted to 442 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the 443 * channel is not currently part of a group. 444 * 445 * @param groupId the id of a group created by 446 * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}. 447 */ setGroup(String groupId)448 public void setGroup(String groupId) { 449 this.mGroup = groupId; 450 } 451 452 /** 453 * Sets whether notifications posted to this channel can appear as application icon badges 454 * in a Launcher. 455 * 456 * Only modifiable before the channel is submitted to 457 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 458 * 459 * @param showBadge true if badges should be allowed to be shown. 460 */ setShowBadge(boolean showBadge)461 public void setShowBadge(boolean showBadge) { 462 this.mShowBadge = showBadge; 463 } 464 465 /** 466 * Sets the sound that should be played for notifications posted to this channel and its 467 * audio attributes. Notification channels with an {@link #getImportance() importance} of at 468 * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound. 469 * 470 * Only modifiable before the channel is submitted to 471 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 472 */ setSound(Uri sound, AudioAttributes audioAttributes)473 public void setSound(Uri sound, AudioAttributes audioAttributes) { 474 this.mSound = sound; 475 this.mAudioAttributes = audioAttributes; 476 } 477 478 /** 479 * Sets whether notifications posted to this channel should display notification lights, 480 * on devices that support that feature. 481 * 482 * Only modifiable before the channel is submitted to 483 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 484 */ enableLights(boolean lights)485 public void enableLights(boolean lights) { 486 this.mLights = lights; 487 } 488 489 /** 490 * Sets the notification light color for notifications posted to this channel, if lights are 491 * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature. 492 * 493 * Only modifiable before the channel is submitted to 494 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 495 */ setLightColor(int argb)496 public void setLightColor(int argb) { 497 this.mLightColor = argb; 498 } 499 500 /** 501 * Sets whether notification posted to this channel should vibrate. The vibration pattern can 502 * be set with {@link #setVibrationPattern(long[])}. 503 * 504 * Only modifiable before the channel is submitted to 505 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 506 */ enableVibration(boolean vibration)507 public void enableVibration(boolean vibration) { 508 this.mVibrationEnabled = vibration; 509 } 510 511 /** 512 * Sets the vibration pattern for notifications posted to this channel. If the provided 513 * pattern is valid (non-null, non-empty), will {@link #enableVibration(boolean)} enable 514 * vibration} as well. Otherwise, vibration will be disabled. 515 * 516 * Only modifiable before the channel is submitted to 517 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 518 */ setVibrationPattern(long[] vibrationPattern)519 public void setVibrationPattern(long[] vibrationPattern) { 520 this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0; 521 this.mVibration = vibrationPattern; 522 } 523 524 /** 525 * Sets the level of interruption of this notification channel. 526 * 527 * Only modifiable before the channel is submitted to 528 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 529 * 530 * @param importance the amount the user should be interrupted by 531 * notifications from this channel. 532 */ setImportance(@mportance int importance)533 public void setImportance(@Importance int importance) { 534 this.mImportance = importance; 535 } 536 537 // Modifiable by a notification ranker. 538 539 /** 540 * Sets whether or not notifications posted to this channel can interrupt the user in 541 * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode. 542 * 543 * Only modifiable by the system and notification ranker. 544 */ setBypassDnd(boolean bypassDnd)545 public void setBypassDnd(boolean bypassDnd) { 546 this.mBypassDnd = bypassDnd; 547 } 548 549 /** 550 * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so, 551 * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}. 552 * 553 * Only modifiable by the system and notification ranker. 554 */ setLockscreenVisibility(int lockscreenVisibility)555 public void setLockscreenVisibility(int lockscreenVisibility) { 556 this.mLockscreenVisibility = lockscreenVisibility; 557 } 558 559 /** 560 * As of Android 11 this value is no longer respected. 561 * @see #canBubble() 562 * @see Notification#getBubbleMetadata() 563 */ setAllowBubbles(boolean allowBubbles)564 public void setAllowBubbles(boolean allowBubbles) { 565 mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF; 566 } 567 568 /** 569 * @hide 570 */ setAllowBubbles(int allowed)571 public void setAllowBubbles(int allowed) { 572 mAllowBubbles = allowed; 573 } 574 575 /** 576 * Sets this channel as being converastion-centric. Different settings and functionality may be 577 * exposed for conversation-centric channels. 578 * 579 * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of 580 * this type would be posted to in absence of a specific conversation id. 581 * For example, if this channel represents 'Messages from Person A', the 582 * parent channel would be 'Messages.' 583 * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this 584 * channel's conversation. 585 */ setConversationId(@onNull String parentChannelId, @NonNull String conversationId)586 public void setConversationId(@NonNull String parentChannelId, 587 @NonNull String conversationId) { 588 mParentId = parentChannelId; 589 mConversationId = conversationId; 590 } 591 592 /** 593 * Returns the id of this channel. 594 */ getId()595 public String getId() { 596 return mId; 597 } 598 599 /** 600 * Returns the user visible name of this channel. 601 */ getName()602 public CharSequence getName() { 603 return mName; 604 } 605 606 /** 607 * Returns the user visible description of this channel. 608 */ getDescription()609 public String getDescription() { 610 return mDesc; 611 } 612 613 /** 614 * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for 615 * notifications posted to this channel. Note: This value might be > 616 * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will 617 * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked. 618 * See {@link NotificationChannelGroup#isBlocked()} and 619 * {@link NotificationManager#areNotificationsEnabled()}. 620 */ getImportance()621 public int getImportance() { 622 return mImportance; 623 } 624 625 /** 626 * Whether or not notifications posted to this channel can bypass the Do Not Disturb 627 * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode. 628 */ canBypassDnd()629 public boolean canBypassDnd() { 630 return mBypassDnd; 631 } 632 633 /** 634 * Whether or not notifications in this conversation are considered important. 635 * 636 * <p>Important conversations may get special visual treatment, and might be able to bypass DND. 637 * 638 * <p>This is only valid for channels that represent conversations, that is, those with a valid 639 * {@link #getConversationId() conversation id}. 640 */ isImportantConversation()641 public boolean isImportantConversation() { 642 return mImportantConvo; 643 } 644 645 /** 646 * Returns the notification sound for this channel. 647 */ getSound()648 public Uri getSound() { 649 return mSound; 650 } 651 652 /** 653 * Returns the audio attributes for sound played by notifications posted to this channel. 654 */ getAudioAttributes()655 public AudioAttributes getAudioAttributes() { 656 return mAudioAttributes; 657 } 658 659 /** 660 * Returns whether notifications posted to this channel trigger notification lights. 661 */ shouldShowLights()662 public boolean shouldShowLights() { 663 return mLights; 664 } 665 666 /** 667 * Returns the notification light color for notifications posted to this channel. Irrelevant 668 * unless {@link #shouldShowLights()}. 669 */ getLightColor()670 public int getLightColor() { 671 return mLightColor; 672 } 673 674 /** 675 * Returns whether notifications posted to this channel always vibrate. 676 */ shouldVibrate()677 public boolean shouldVibrate() { 678 return mVibrationEnabled; 679 } 680 681 /** 682 * Returns the vibration pattern for notifications posted to this channel. Will be ignored if 683 * vibration is not enabled ({@link #shouldVibrate()}. 684 */ getVibrationPattern()685 public long[] getVibrationPattern() { 686 return mVibration; 687 } 688 689 /** 690 * Returns whether or not notifications posted to this channel are shown on the lockscreen in 691 * full or redacted form. 692 */ getLockscreenVisibility()693 public int getLockscreenVisibility() { 694 return mLockscreenVisibility; 695 } 696 697 /** 698 * Returns whether notifications posted to this channel can appear as badges in a Launcher 699 * application. 700 * 701 * Note that badging may be disabled for other reasons. 702 */ canShowBadge()703 public boolean canShowBadge() { 704 return mShowBadge; 705 } 706 707 /** 708 * Returns what group this channel belongs to. 709 * 710 * This is used only for visually grouping channels in the UI. 711 */ getGroup()712 public String getGroup() { 713 return mGroup; 714 } 715 716 /** 717 * Returns whether notifications posted to this channel are allowed to display outside of the 718 * notification shade, in a floating window on top of other apps. 719 * 720 * @see Notification#getBubbleMetadata() 721 */ canBubble()722 public boolean canBubble() { 723 return mAllowBubbles == ALLOW_BUBBLE_ON; 724 } 725 726 /** 727 * @hide 728 */ getAllowBubbles()729 public int getAllowBubbles() { 730 return mAllowBubbles; 731 } 732 733 /** 734 * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's 735 * a conversation related channel. See {@link #setConversationId(String, String)}. 736 */ getParentChannelId()737 public @Nullable String getParentChannelId() { 738 return mParentId; 739 } 740 741 /** 742 * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's 743 * associated with a conversation. See {@link #setConversationId(String, String)}. 744 */ getConversationId()745 public @Nullable String getConversationId() { 746 return mConversationId; 747 } 748 749 /** 750 * @hide 751 */ 752 @SystemApi isDeleted()753 public boolean isDeleted() { 754 return mDeleted; 755 } 756 757 /** 758 * @hide 759 */ 760 @SystemApi getUserLockedFields()761 public int getUserLockedFields() { 762 return mUserLockedFields; 763 } 764 765 /** 766 * @hide 767 */ isFgServiceShown()768 public boolean isFgServiceShown() { 769 return mFgServiceShown; 770 } 771 772 /** 773 * @hide 774 */ 775 @TestApi isBlockable()776 public boolean isBlockable() { 777 return mBlockableSystem; 778 } 779 780 /** 781 * @hide 782 */ 783 @TestApi setImportanceLockedByOEM(boolean locked)784 public void setImportanceLockedByOEM(boolean locked) { 785 mImportanceLockedByOEM = locked; 786 } 787 788 /** 789 * @hide 790 */ 791 @TestApi setImportanceLockedByCriticalDeviceFunction(boolean locked)792 public void setImportanceLockedByCriticalDeviceFunction(boolean locked) { 793 mImportanceLockedDefaultApp = locked; 794 } 795 796 /** 797 * @hide 798 */ 799 @TestApi isImportanceLockedByOEM()800 public boolean isImportanceLockedByOEM() { 801 return mImportanceLockedByOEM; 802 } 803 804 /** 805 * @hide 806 */ 807 @TestApi isImportanceLockedByCriticalDeviceFunction()808 public boolean isImportanceLockedByCriticalDeviceFunction() { 809 return mImportanceLockedDefaultApp; 810 } 811 812 /** 813 * @hide 814 */ 815 @TestApi getOriginalImportance()816 public int getOriginalImportance() { 817 return mOriginalImportance; 818 } 819 820 /** 821 * @hide 822 */ 823 @TestApi setOriginalImportance(int importance)824 public void setOriginalImportance(int importance) { 825 mOriginalImportance = importance; 826 } 827 828 /** 829 * @hide 830 */ setDemoted(boolean demoted)831 public void setDemoted(boolean demoted) { 832 mDemoted = demoted; 833 } 834 835 /** 836 * @hide 837 */ isDemoted()838 public boolean isDemoted() { 839 return mDemoted; 840 } 841 842 /** 843 * Returns whether the user has chosen the importance of this channel, either to affirm the 844 * initial selection from the app, or changed it to be higher or lower. 845 * @see #getImportance() 846 */ hasUserSetImportance()847 public boolean hasUserSetImportance() { 848 return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0; 849 } 850 851 /** 852 * Returns whether the user has chosen the sound of this channel. 853 * @see #getSound() 854 */ hasUserSetSound()855 public boolean hasUserSetSound() { 856 return (mUserLockedFields & USER_LOCKED_SOUND) != 0; 857 } 858 859 /** 860 * @hide 861 */ populateFromXmlForRestore(XmlPullParser parser, Context context)862 public void populateFromXmlForRestore(XmlPullParser parser, Context context) { 863 populateFromXml(parser, true, context); 864 } 865 866 /** 867 * @hide 868 */ 869 @SystemApi populateFromXml(XmlPullParser parser)870 public void populateFromXml(XmlPullParser parser) { 871 populateFromXml(parser, false, null); 872 } 873 874 /** 875 * If {@param forRestore} is true, {@param Context} MUST be non-null. 876 */ populateFromXml(XmlPullParser parser, boolean forRestore, @Nullable Context context)877 private void populateFromXml(XmlPullParser parser, boolean forRestore, 878 @Nullable Context context) { 879 Preconditions.checkArgument(!forRestore || context != null, 880 "forRestore is true but got null context"); 881 882 // Name, id, and importance are set in the constructor. 883 setDescription(parser.getAttributeValue(null, ATT_DESC)); 884 setBypassDnd(Notification.PRIORITY_DEFAULT 885 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); 886 setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); 887 888 Uri sound = safeUri(parser, ATT_SOUND); 889 setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); 890 891 enableLights(safeBool(parser, ATT_LIGHTS, false)); 892 setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); 893 setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); 894 enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); 895 setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); 896 setDeleted(safeBool(parser, ATT_DELETED, false)); 897 setGroup(parser.getAttributeValue(null, ATT_GROUP)); 898 lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); 899 setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); 900 setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); 901 setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); 902 setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE)); 903 setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL), 904 parser.getAttributeValue(null, ATT_CONVERSATION_ID)); 905 setDemoted(safeBool(parser, ATT_DEMOTE, false)); 906 setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); 907 } 908 909 @Nullable restoreSoundUri(Context context, @Nullable Uri uri)910 private Uri restoreSoundUri(Context context, @Nullable Uri uri) { 911 if (uri == null || Uri.EMPTY.equals(uri)) { 912 return null; 913 } 914 ContentResolver contentResolver = context.getContentResolver(); 915 // There are backups out there with uncanonical uris (because we fixed this after 916 // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't 917 // verify the uri against device storage and we'll possibly end up with a broken uri. 918 // We then canonicalize the uri to uncanonicalize it back, which means we properly check 919 // the uri and in the case of not having the resource we end up with the default - better 920 // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine 921 // according to the docs because canonicalize method has to handle canonical uris as well. 922 Uri canonicalizedUri = contentResolver.canonicalize(uri); 923 if (canonicalizedUri == null) { 924 // We got a null because the uri in the backup does not exist here, so we return default 925 return Settings.System.DEFAULT_NOTIFICATION_URI; 926 } 927 return contentResolver.uncanonicalize(canonicalizedUri); 928 } 929 930 /** 931 * @hide 932 */ 933 @SystemApi writeXml(XmlSerializer out)934 public void writeXml(XmlSerializer out) throws IOException { 935 writeXml(out, false, null); 936 } 937 938 /** 939 * @hide 940 */ writeXmlForBackup(XmlSerializer out, Context context)941 public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { 942 writeXml(out, true, context); 943 } 944 getSoundForBackup(Context context)945 private Uri getSoundForBackup(Context context) { 946 Uri sound = getSound(); 947 if (sound == null || Uri.EMPTY.equals(sound)) { 948 return null; 949 } 950 Uri canonicalSound = context.getContentResolver().canonicalize(sound); 951 if (canonicalSound == null) { 952 // The content provider does not support canonical uris so we backup the default 953 return Settings.System.DEFAULT_NOTIFICATION_URI; 954 } 955 return canonicalSound; 956 } 957 958 /** 959 * If {@param forBackup} is true, {@param Context} MUST be non-null. 960 */ writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)961 private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context) 962 throws IOException { 963 Preconditions.checkArgument(!forBackup || context != null, 964 "forBackup is true but got null context"); 965 out.startTag(null, TAG_CHANNEL); 966 out.attribute(null, ATT_ID, getId()); 967 if (getName() != null) { 968 out.attribute(null, ATT_NAME, getName().toString()); 969 } 970 if (getDescription() != null) { 971 out.attribute(null, ATT_DESC, getDescription()); 972 } 973 if (getImportance() != DEFAULT_IMPORTANCE) { 974 out.attribute( 975 null, ATT_IMPORTANCE, Integer.toString(getImportance())); 976 } 977 if (canBypassDnd()) { 978 out.attribute( 979 null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX)); 980 } 981 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 982 out.attribute(null, ATT_VISIBILITY, 983 Integer.toString(getLockscreenVisibility())); 984 } 985 Uri sound = forBackup ? getSoundForBackup(context) : getSound(); 986 if (sound != null) { 987 out.attribute(null, ATT_SOUND, sound.toString()); 988 } 989 if (getAudioAttributes() != null) { 990 out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); 991 out.attribute(null, ATT_CONTENT_TYPE, 992 Integer.toString(getAudioAttributes().getContentType())); 993 out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); 994 } 995 if (shouldShowLights()) { 996 out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights())); 997 } 998 if (getLightColor() != DEFAULT_LIGHT_COLOR) { 999 out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor())); 1000 } 1001 if (shouldVibrate()) { 1002 out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 1003 } 1004 if (getVibrationPattern() != null) { 1005 out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1006 } 1007 if (getUserLockedFields() != 0) { 1008 out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 1009 } 1010 if (isFgServiceShown()) { 1011 out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); 1012 } 1013 if (canShowBadge()) { 1014 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 1015 } 1016 if (isDeleted()) { 1017 out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted())); 1018 } 1019 if (getGroup() != null) { 1020 out.attribute(null, ATT_GROUP, getGroup()); 1021 } 1022 if (isBlockable()) { 1023 out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable())); 1024 } 1025 if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { 1026 out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles())); 1027 } 1028 if (getOriginalImportance() != DEFAULT_IMPORTANCE) { 1029 out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance())); 1030 } 1031 if (getParentChannelId() != null) { 1032 out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId()); 1033 } 1034 if (getConversationId() != null) { 1035 out.attribute(null, ATT_CONVERSATION_ID, getConversationId()); 1036 } 1037 if (isDemoted()) { 1038 out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted())); 1039 } 1040 if (isImportantConversation()) { 1041 out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation())); 1042 } 1043 1044 // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of 1045 // truth and so aren't written to this xml file 1046 1047 out.endTag(null, TAG_CHANNEL); 1048 } 1049 1050 /** 1051 * @hide 1052 */ 1053 @SystemApi toJson()1054 public JSONObject toJson() throws JSONException { 1055 JSONObject record = new JSONObject(); 1056 record.put(ATT_ID, getId()); 1057 record.put(ATT_NAME, getName()); 1058 record.put(ATT_DESC, getDescription()); 1059 if (getImportance() != DEFAULT_IMPORTANCE) { 1060 record.put(ATT_IMPORTANCE, 1061 NotificationListenerService.Ranking.importanceToString(getImportance())); 1062 } 1063 if (canBypassDnd()) { 1064 record.put(ATT_PRIORITY, Notification.PRIORITY_MAX); 1065 } 1066 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 1067 record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility())); 1068 } 1069 if (getSound() != null) { 1070 record.put(ATT_SOUND, getSound().toString()); 1071 } 1072 if (getAudioAttributes() != null) { 1073 record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); 1074 record.put(ATT_CONTENT_TYPE, 1075 Integer.toString(getAudioAttributes().getContentType())); 1076 record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); 1077 } 1078 record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); 1079 record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); 1080 record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 1081 record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 1082 record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); 1083 record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1084 record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 1085 record.put(ATT_DELETED, Boolean.toString(isDeleted())); 1086 record.put(ATT_GROUP, getGroup()); 1087 record.put(ATT_BLOCKABLE_SYSTEM, isBlockable()); 1088 record.put(ATT_ALLOW_BUBBLE, getAllowBubbles()); 1089 // TODO: original importance 1090 return record; 1091 } 1092 safeAudioAttributes(XmlPullParser parser)1093 private static AudioAttributes safeAudioAttributes(XmlPullParser parser) { 1094 int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION); 1095 int contentType = safeInt(parser, ATT_CONTENT_TYPE, 1096 AudioAttributes.CONTENT_TYPE_SONIFICATION); 1097 int flags = safeInt(parser, ATT_FLAGS, 0); 1098 return new AudioAttributes.Builder() 1099 .setUsage(usage) 1100 .setContentType(contentType) 1101 .setFlags(flags) 1102 .build(); 1103 } 1104 safeUri(XmlPullParser parser, String att)1105 private static Uri safeUri(XmlPullParser parser, String att) { 1106 final String val = parser.getAttributeValue(null, att); 1107 return val == null ? null : Uri.parse(val); 1108 } 1109 safeInt(XmlPullParser parser, String att, int defValue)1110 private static int safeInt(XmlPullParser parser, String att, int defValue) { 1111 final String val = parser.getAttributeValue(null, att); 1112 return tryParseInt(val, defValue); 1113 } 1114 tryParseInt(String value, int defValue)1115 private static int tryParseInt(String value, int defValue) { 1116 if (TextUtils.isEmpty(value)) return defValue; 1117 try { 1118 return Integer.parseInt(value); 1119 } catch (NumberFormatException e) { 1120 return defValue; 1121 } 1122 } 1123 safeBool(XmlPullParser parser, String att, boolean defValue)1124 private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { 1125 final String value = parser.getAttributeValue(null, att); 1126 if (TextUtils.isEmpty(value)) return defValue; 1127 return Boolean.parseBoolean(value); 1128 } 1129 safeLongArray(XmlPullParser parser, String att, long[] defValue)1130 private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) { 1131 final String attributeValue = parser.getAttributeValue(null, att); 1132 if (TextUtils.isEmpty(attributeValue)) return defValue; 1133 String[] values = attributeValue.split(DELIMITER); 1134 long[] longValues = new long[values.length]; 1135 for (int i = 0; i < values.length; i++) { 1136 try { 1137 longValues[i] = Long.parseLong(values[i]); 1138 } catch (NumberFormatException e) { 1139 longValues[i] = 0; 1140 } 1141 } 1142 return longValues; 1143 } 1144 longArrayToString(long[] values)1145 private static String longArrayToString(long[] values) { 1146 StringBuffer sb = new StringBuffer(); 1147 if (values != null && values.length > 0) { 1148 for (int i = 0; i < values.length - 1; i++) { 1149 sb.append(values[i]).append(DELIMITER); 1150 } 1151 sb.append(values[values.length - 1]); 1152 } 1153 return sb.toString(); 1154 } 1155 1156 public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR = 1157 new Creator<NotificationChannel>() { 1158 @Override 1159 public NotificationChannel createFromParcel(Parcel in) { 1160 return new NotificationChannel(in); 1161 } 1162 1163 @Override 1164 public NotificationChannel[] newArray(int size) { 1165 return new NotificationChannel[size]; 1166 } 1167 }; 1168 1169 @Override describeContents()1170 public int describeContents() { 1171 return 0; 1172 } 1173 1174 @Override equals(Object o)1175 public boolean equals(Object o) { 1176 if (this == o) return true; 1177 if (o == null || getClass() != o.getClass()) return false; 1178 NotificationChannel that = (NotificationChannel) o; 1179 return getImportance() == that.getImportance() 1180 && mBypassDnd == that.mBypassDnd 1181 && getLockscreenVisibility() == that.getLockscreenVisibility() 1182 && mLights == that.mLights 1183 && getLightColor() == that.getLightColor() 1184 && getUserLockedFields() == that.getUserLockedFields() 1185 && isFgServiceShown() == that.isFgServiceShown() 1186 && mVibrationEnabled == that.mVibrationEnabled 1187 && mShowBadge == that.mShowBadge 1188 && isDeleted() == that.isDeleted() 1189 && isBlockable() == that.isBlockable() 1190 && mAllowBubbles == that.mAllowBubbles 1191 && Objects.equals(getId(), that.getId()) 1192 && Objects.equals(getName(), that.getName()) 1193 && Objects.equals(mDesc, that.mDesc) 1194 && Objects.equals(getSound(), that.getSound()) 1195 && Arrays.equals(mVibration, that.mVibration) 1196 && Objects.equals(getGroup(), that.getGroup()) 1197 && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) 1198 && mImportanceLockedByOEM == that.mImportanceLockedByOEM 1199 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp 1200 && mOriginalImportance == that.mOriginalImportance 1201 && Objects.equals(getParentChannelId(), that.getParentChannelId()) 1202 && Objects.equals(getConversationId(), that.getConversationId()) 1203 && isDemoted() == that.isDemoted() 1204 && isImportantConversation() == that.isImportantConversation(); 1205 } 1206 1207 @Override hashCode()1208 public int hashCode() { 1209 int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd, 1210 getLockscreenVisibility(), getSound(), mLights, getLightColor(), 1211 getUserLockedFields(), 1212 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(), 1213 getAudioAttributes(), isBlockable(), mAllowBubbles, 1214 mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance, 1215 mParentId, mConversationId, mDemoted, mImportantConvo); 1216 result = 31 * result + Arrays.hashCode(mVibration); 1217 return result; 1218 } 1219 1220 /** @hide */ dump(PrintWriter pw, String prefix, boolean redacted)1221 public void dump(PrintWriter pw, String prefix, boolean redacted) { 1222 String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName; 1223 String output = "NotificationChannel{" 1224 + "mId='" + mId + '\'' 1225 + ", mName=" + redactedName 1226 + getFieldsString() 1227 + '}'; 1228 pw.println(prefix + output); 1229 } 1230 1231 @Override toString()1232 public String toString() { 1233 return "NotificationChannel{" 1234 + "mId='" + mId + '\'' 1235 + ", mName=" + mName 1236 + getFieldsString() 1237 + '}'; 1238 } 1239 getFieldsString()1240 private String getFieldsString() { 1241 return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "") 1242 + ", mImportance=" + mImportance 1243 + ", mBypassDnd=" + mBypassDnd 1244 + ", mLockscreenVisibility=" + mLockscreenVisibility 1245 + ", mSound=" + mSound 1246 + ", mLights=" + mLights 1247 + ", mLightColor=" + mLightColor 1248 + ", mVibration=" + Arrays.toString(mVibration) 1249 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) 1250 + ", mFgServiceShown=" + mFgServiceShown 1251 + ", mVibrationEnabled=" + mVibrationEnabled 1252 + ", mShowBadge=" + mShowBadge 1253 + ", mDeleted=" + mDeleted 1254 + ", mGroup='" + mGroup + '\'' 1255 + ", mAudioAttributes=" + mAudioAttributes 1256 + ", mBlockableSystem=" + mBlockableSystem 1257 + ", mAllowBubbles=" + mAllowBubbles 1258 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM 1259 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp 1260 + ", mOriginalImp=" + mOriginalImportance 1261 + ", mParent=" + mParentId 1262 + ", mConversationId=" + mConversationId 1263 + ", mDemoted=" + mDemoted 1264 + ", mImportantConvo=" + mImportantConvo; 1265 } 1266 1267 /** @hide */ dumpDebug(ProtoOutputStream proto, long fieldId)1268 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 1269 final long token = proto.start(fieldId); 1270 1271 proto.write(NotificationChannelProto.ID, mId); 1272 proto.write(NotificationChannelProto.NAME, mName); 1273 proto.write(NotificationChannelProto.DESCRIPTION, mDesc); 1274 proto.write(NotificationChannelProto.IMPORTANCE, mImportance); 1275 proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd); 1276 proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility); 1277 if (mSound != null) { 1278 proto.write(NotificationChannelProto.SOUND, mSound.toString()); 1279 } 1280 proto.write(NotificationChannelProto.USE_LIGHTS, mLights); 1281 proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor); 1282 if (mVibration != null) { 1283 for (long v : mVibration) { 1284 proto.write(NotificationChannelProto.VIBRATION, v); 1285 } 1286 } 1287 proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); 1288 proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown); 1289 proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); 1290 proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); 1291 proto.write(NotificationChannelProto.IS_DELETED, mDeleted); 1292 proto.write(NotificationChannelProto.GROUP, mGroup); 1293 if (mAudioAttributes != null) { 1294 mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES); 1295 } 1296 proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); 1297 proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles); 1298 1299 proto.end(token); 1300 } 1301 } 1302