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 static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; 19 import static android.service.notification.Adjustment.TYPE_NEWS; 20 import static android.service.notification.Adjustment.TYPE_PROMOTION; 21 import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; 22 import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT; 23 24 import android.annotation.FlaggedApi; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SystemApi; 28 import android.annotation.TestApi; 29 import android.app.NotificationManager.Importance; 30 import android.compat.annotation.UnsupportedAppUsage; 31 import android.content.ContentResolver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.ShortcutInfo; 35 import android.media.AudioAttributes; 36 import android.media.RingtoneManager; 37 import android.net.Uri; 38 import android.os.Parcel; 39 import android.os.Parcelable; 40 import android.os.VibrationEffect; 41 import android.os.vibrator.persistence.VibrationXmlParser; 42 import android.os.vibrator.persistence.VibrationXmlSerializer; 43 import android.provider.Settings; 44 import android.service.notification.Adjustment; 45 import android.service.notification.NotificationListenerService; 46 import android.text.TextUtils; 47 import android.util.Log; 48 import android.util.Slog; 49 import android.util.proto.ProtoOutputStream; 50 51 import com.android.internal.util.Preconditions; 52 import com.android.internal.util.XmlUtils; 53 import com.android.modules.utils.TypedXmlPullParser; 54 import com.android.modules.utils.TypedXmlSerializer; 55 56 import org.json.JSONException; 57 import org.json.JSONObject; 58 import org.xmlpull.v1.XmlPullParser; 59 import org.xmlpull.v1.XmlSerializer; 60 61 import java.io.FileNotFoundException; 62 import java.io.IOException; 63 import java.io.PrintWriter; 64 import java.io.StringReader; 65 import java.io.StringWriter; 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.List; 69 import java.util.Objects; 70 71 /** 72 * A representation of settings that apply to a collection of similarly themed notifications. 73 */ 74 public final class NotificationChannel implements Parcelable { 75 private static final String TAG = "NotificationChannel"; 76 77 /** 78 * The id of the default channel for an app. This id is reserved by the system. All 79 * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or 80 * earlier without a notification channel specified are posted to this channel. 81 */ 82 public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; 83 84 /** 85 * A reserved id for a system channel reserved for promotional notifications. 86 * @hide 87 */ 88 @TestApi 89 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 90 public static final String PROMOTIONS_ID = "android.app.promotions"; 91 /** 92 * A reserved id for a system channel reserved for non-conversation social media notifications. 93 * @hide 94 */ 95 @TestApi 96 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 97 public static final String SOCIAL_MEDIA_ID = "android.app.social"; 98 /** 99 * A reserved id for a system channel reserved for news notifications. 100 * @hide 101 */ 102 @TestApi 103 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 104 public static final String NEWS_ID = "android.app.news"; 105 /** 106 * A reserved id for a system channel reserved for content recommendation notifications. 107 * @hide 108 */ 109 @TestApi 110 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 111 public static final String RECS_ID = "android.app.recs"; 112 113 /** @hide */ 114 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 115 public static final ArrayList<String> SYSTEM_RESERVED_IDS = new ArrayList<>( 116 List.of(NEWS_ID, SOCIAL_MEDIA_ID, PROMOTIONS_ID, RECS_ID)); 117 118 /** 119 * The formatter used by the system to create an id for notification 120 * channels when it automatically creates conversation channels on behalf of an app. The format 121 * string takes two arguments, in this order: the 122 * {@link #getId()} of the original notification channel, and the 123 * {@link ShortcutInfo#getId() id} of the conversation. 124 * @hide 125 */ 126 public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s"; 127 128 /** 129 * TODO: STOPSHIP remove 130 * Conversation id to use for apps that aren't providing them yet. 131 * @hide 132 */ 133 public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id"; 134 135 /** 136 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 137 * that have to do with editing sound, like a tone picker 138 * ({@link #setSound(Uri, AudioAttributes)}). 139 */ 140 public static final String EDIT_SOUND = "sound"; 141 /** 142 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 143 * that have to do with editing vibration ({@link #enableVibration(boolean)}, 144 * {@link #setVibrationPattern(long[])}). 145 */ 146 public static final String EDIT_VIBRATION = "vibration"; 147 /** 148 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 149 * that have to do with editing importance ({@link #setImportance(int)}) and/or conversation 150 * priority. 151 */ 152 public static final String EDIT_IMPORTANCE = "importance"; 153 /** 154 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 155 * that have to do with editing behavior on devices that are locked or have a turned off 156 * display ({@link #setLockscreenVisibility(int)}, {@link #enableLights(boolean)}, 157 * {@link #setLightColor(int)}). 158 */ 159 public static final String EDIT_LOCKED_DEVICE = "locked"; 160 /** 161 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 162 * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) . 163 */ 164 public static final String EDIT_ZEN = "zen"; 165 /** 166 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 167 * that have to do with editing conversation settings (demoting or restoring a channel to 168 * be a Conversation, changing bubble behavior, or setting the priority of a conversation). 169 */ 170 public static final String EDIT_CONVERSATION = "conversation"; 171 /** 172 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 173 * that have to do with editing launcher behavior (showing badges)}. 174 */ 175 public static final String EDIT_LAUNCHER = "launcher"; 176 177 /** 178 * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this 179 * limit. 180 * @hide 181 */ 182 public static final int MAX_TEXT_LENGTH = 1000; 183 /** 184 * @hide 185 */ 186 public static final int MAX_VIBRATION_LENGTH = 500; 187 /** 188 * @hide 189 */ 190 public static final int MAX_SERIALIZED_VIBRATION_LENGTH = 32_768; 191 192 private static final String TAG_CHANNEL = "channel"; 193 private static final String ATT_NAME = "name"; 194 private static final String ATT_DESC = "desc"; 195 private static final String ATT_ID = "id"; 196 private static final String ATT_DELETED = "deleted"; 197 private static final String ATT_PRIORITY = "priority"; 198 private static final String ATT_VISIBILITY = "visibility"; 199 private static final String ATT_IMPORTANCE = "importance"; 200 private static final String ATT_LIGHTS = "lights"; 201 private static final String ATT_LIGHT_COLOR = "light_color"; 202 private static final String ATT_VIBRATION = "vibration"; 203 private static final String ATT_VIBRATION_EFFECT = "vibration_effect"; 204 private static final String ATT_VIBRATION_ENABLED = "vibration_enabled"; 205 private static final String ATT_SOUND = "sound"; 206 private static final String ATT_USAGE = "usage"; 207 private static final String ATT_FLAGS = "flags"; 208 private static final String ATT_CONTENT_TYPE = "content_type"; 209 private static final String ATT_SHOW_BADGE = "show_badge"; 210 private static final String ATT_USER_LOCKED = "locked"; 211 /** 212 * This attribute represents both foreground services and user initiated jobs in U+. 213 * It was not renamed in U on purpose, in order to avoid creating an unnecessary migration path. 214 */ 215 private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; 216 private static final String ATT_GROUP = "group"; 217 private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; 218 private static final String ATT_ALLOW_BUBBLE = "allow_bubbles"; 219 private static final String ATT_ORIG_IMP = "orig_imp"; 220 private static final String ATT_PARENT_CHANNEL = "parent"; 221 private static final String ATT_CONVERSATION_ID = "conv_id"; 222 private static final String ATT_IMP_CONVERSATION = "imp_conv"; 223 private static final String ATT_DEMOTE = "dem"; 224 private static final String ATT_DELETED_TIME_MS = "del_time"; 225 private static final String DELIMITER = ","; 226 227 /** 228 * @hide 229 */ 230 public static final int USER_LOCKED_PRIORITY = 0x00000001; 231 /** 232 * @hide 233 */ 234 public static final int USER_LOCKED_VISIBILITY = 0x00000002; 235 /** 236 * @hide 237 */ 238 public static final int USER_LOCKED_IMPORTANCE = 0x00000004; 239 /** 240 * @hide 241 */ 242 public static final int USER_LOCKED_LIGHTS = 0x00000008; 243 /** 244 * @hide 245 */ 246 public static final int USER_LOCKED_VIBRATION = 0x00000010; 247 /** 248 * @hide 249 */ 250 @SystemApi 251 public static final int USER_LOCKED_SOUND = 0x00000020; 252 253 /** 254 * @hide 255 */ 256 public static final int USER_LOCKED_SHOW_BADGE = 0x00000080; 257 258 /** 259 * @hide 260 */ 261 public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100; 262 263 /** 264 * @hide 265 */ 266 public static final int[] LOCKABLE_FIELDS = new int[] { 267 USER_LOCKED_PRIORITY, 268 USER_LOCKED_VISIBILITY, 269 USER_LOCKED_IMPORTANCE, 270 USER_LOCKED_LIGHTS, 271 USER_LOCKED_VIBRATION, 272 USER_LOCKED_SOUND, 273 USER_LOCKED_SHOW_BADGE, 274 USER_LOCKED_ALLOW_BUBBLE 275 }; 276 277 /** 278 * @hide 279 */ 280 public static final int DEFAULT_ALLOW_BUBBLE = -1; 281 /** 282 * @hide 283 */ 284 public static final int ALLOW_BUBBLE_ON = 1; 285 /** 286 * @hide 287 */ 288 public static final int ALLOW_BUBBLE_OFF = 0; 289 290 private static final int DEFAULT_LIGHT_COLOR = 0; 291 private static final int DEFAULT_VISIBILITY = 292 NotificationManager.VISIBILITY_NO_OVERRIDE; 293 private static final int DEFAULT_IMPORTANCE = 294 NotificationManager.IMPORTANCE_UNSPECIFIED; 295 private static final boolean DEFAULT_DELETED = false; 296 private static final boolean DEFAULT_SHOW_BADGE = true; 297 private static final long DEFAULT_DELETION_TIME_MS = -1; 298 299 @UnsupportedAppUsage 300 private String mId; 301 private String mName; 302 private String mDesc; 303 private int mImportance = DEFAULT_IMPORTANCE; 304 private int mOriginalImportance = DEFAULT_IMPORTANCE; 305 private boolean mBypassDnd; 306 private int mLockscreenVisibility = DEFAULT_VISIBILITY; 307 private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; 308 private boolean mSoundRestored = false; 309 private boolean mLights; 310 private int mLightColor = DEFAULT_LIGHT_COLOR; 311 private long[] mVibrationPattern; 312 private VibrationEffect mVibrationEffect; 313 // Bitwise representation of fields that have been changed by the user, preventing the app from 314 // making changes to these fields. 315 private int mUserLockedFields; 316 private boolean mUserVisibleTaskShown; 317 private boolean mVibrationEnabled; 318 private boolean mShowBadge = DEFAULT_SHOW_BADGE; 319 private boolean mDeleted = DEFAULT_DELETED; 320 private String mGroup; 321 private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; 322 // If this is a blockable system notification channel. 323 private boolean mBlockableSystem = false; 324 private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; 325 private boolean mImportanceLockedDefaultApp; 326 private String mParentId = null; 327 private String mConversationId = null; 328 private boolean mDemoted = false; 329 private boolean mImportantConvo = false; 330 private long mDeletedTime = DEFAULT_DELETION_TIME_MS; 331 /** Do not (de)serialize this value: it only affects logic in system_server and that logic 332 * is reset on each boot {@link NotificationAttentionHelper#buzzBeepBlinkLocked}. 333 */ 334 private long mLastNotificationUpdateTimeMs = 0; 335 336 /** 337 * Creates a notification channel. 338 * 339 * @param id The id of the channel. Must be unique per package. The value may be truncated if 340 * it is too long. 341 * @param name The user visible name of the channel. You can rename this channel when the system 342 * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} 343 * broadcast. The recommended maximum length is 40 characters; the value may be 344 * truncated if it is too long. 345 * @param importance The importance of the channel. This controls how interruptive notifications 346 * posted to this channel are. 347 */ NotificationChannel(String id, CharSequence name, @Importance int importance)348 public NotificationChannel(String id, CharSequence name, @Importance int importance) { 349 this.mId = getTrimmedString(id); 350 this.mName = name != null ? getTrimmedString(name.toString()) : null; 351 this.mImportance = importance; 352 } 353 354 /** 355 * @hide 356 */ NotificationChannel(Parcel in)357 protected NotificationChannel(Parcel in) { 358 if (in.readByte() != 0) { 359 mId = getTrimmedString(in.readString()); 360 } else { 361 mId = null; 362 } 363 if (in.readByte() != 0) { 364 mName = getTrimmedString(in.readString()); 365 } else { 366 mName = null; 367 } 368 if (in.readByte() != 0) { 369 mDesc = getTrimmedString(in.readString()); 370 } else { 371 mDesc = null; 372 } 373 mImportance = in.readInt(); 374 mBypassDnd = in.readByte() != 0; 375 mLockscreenVisibility = in.readInt(); 376 if (in.readByte() != 0) { 377 mSound = Uri.CREATOR.createFromParcel(in); 378 mSound = Uri.parse(getTrimmedString(mSound.toString())); 379 } else { 380 mSound = null; 381 } 382 mLights = in.readByte() != 0; 383 mUserLockedFields = in.readInt(); 384 mUserVisibleTaskShown = in.readByte() != 0; 385 mVibrationEnabled = in.readByte() != 0; 386 mShowBadge = in.readByte() != 0; 387 mDeleted = in.readByte() != 0; 388 if (in.readByte() != 0) { 389 mGroup = getTrimmedString(in.readString()); 390 } else { 391 mGroup = null; 392 } 393 mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; 394 mLightColor = in.readInt(); 395 mBlockableSystem = in.readBoolean(); 396 mAllowBubbles = in.readInt(); 397 mOriginalImportance = in.readInt(); 398 mParentId = getTrimmedString(in.readString()); 399 mConversationId = getTrimmedString(in.readString()); 400 mDemoted = in.readBoolean(); 401 mImportantConvo = in.readBoolean(); 402 mDeletedTime = in.readLong(); 403 mImportanceLockedDefaultApp = in.readBoolean(); 404 405 // Add new fields above this line and not after vibration effect! When 406 // notif_channel_estimate_effect_size is true, we use parcel size to detect whether the 407 // vibration effect might be too large to handle, so this must remain at the end lest any 408 // following fields cause the data to get incorrectly dropped. 409 mVibrationPattern = in.createLongArray(); 410 if (mVibrationPattern != null && mVibrationPattern.length > MAX_VIBRATION_LENGTH) { 411 mVibrationPattern = Arrays.copyOf(mVibrationPattern, MAX_VIBRATION_LENGTH); 412 } 413 boolean largeEffect = false; 414 if (Flags.notifChannelEstimateEffectSize()) { 415 // Note that we must check the length of remaining data in the parcel before reading in 416 // the data. 417 largeEffect = (in.dataAvail() > MAX_SERIALIZED_VIBRATION_LENGTH); 418 } 419 if (Flags.notificationChannelVibrationEffectApi()) { 420 mVibrationEffect = 421 in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null; 422 if (Flags.notifChannelCropVibrationEffects() && mVibrationEffect != null) { 423 if (Flags.notifChannelEstimateEffectSize()) { 424 // Try trimming the effect if the remaining parcel size is large. If trimming is 425 // not applicable for the effect, rather than serializing to XML (expensive) to 426 // check the exact serialized length, we just reject the effect. 427 if (largeEffect) { 428 mVibrationEffect = mVibrationEffect.cropToLengthOrNull( 429 MAX_VIBRATION_LENGTH); 430 } 431 } else { 432 mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect); 433 } 434 } 435 } 436 } 437 438 @Override writeToParcel(Parcel dest, int flags)439 public void writeToParcel(Parcel dest, int flags) { 440 if (mId != null) { 441 dest.writeByte((byte) 1); 442 dest.writeString(mId); 443 } else { 444 dest.writeByte((byte) 0); 445 } 446 if (mName != null) { 447 dest.writeByte((byte) 1); 448 dest.writeString(mName); 449 } else { 450 dest.writeByte((byte) 0); 451 } 452 if (mDesc != null) { 453 dest.writeByte((byte) 1); 454 dest.writeString(mDesc); 455 } else { 456 dest.writeByte((byte) 0); 457 } 458 dest.writeInt(mImportance); 459 dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0); 460 dest.writeInt(mLockscreenVisibility); 461 if (mSound != null) { 462 dest.writeByte((byte) 1); 463 mSound.writeToParcel(dest, 0); 464 } else { 465 dest.writeByte((byte) 0); 466 } 467 dest.writeByte(mLights ? (byte) 1 : (byte) 0); 468 dest.writeInt(mUserLockedFields); 469 dest.writeByte(mUserVisibleTaskShown ? (byte) 1 : (byte) 0); 470 dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); 471 dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); 472 dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); 473 if (mGroup != null) { 474 dest.writeByte((byte) 1); 475 dest.writeString(mGroup); 476 } else { 477 dest.writeByte((byte) 0); 478 } 479 if (mAudioAttributes != null) { 480 dest.writeInt(1); 481 mAudioAttributes.writeToParcel(dest, 0); 482 } else { 483 dest.writeInt(0); 484 } 485 dest.writeInt(mLightColor); 486 dest.writeBoolean(mBlockableSystem); 487 dest.writeInt(mAllowBubbles); 488 dest.writeInt(mOriginalImportance); 489 dest.writeString(mParentId); 490 dest.writeString(mConversationId); 491 dest.writeBoolean(mDemoted); 492 dest.writeBoolean(mImportantConvo); 493 dest.writeLong(mDeletedTime); 494 dest.writeBoolean(mImportanceLockedDefaultApp); 495 496 // Add new fields above this line; vibration effect must remain the last entry. 497 dest.writeLongArray(mVibrationPattern); 498 if (Flags.notificationChannelVibrationEffectApi()) { 499 if (mVibrationEffect != null) { 500 dest.writeInt(1); 501 mVibrationEffect.writeToParcel(dest, /* flags= */ 0); 502 } else { 503 dest.writeInt(0); 504 } 505 } 506 } 507 508 /** @hide */ 509 @TestApi 510 @NonNull copy()511 public NotificationChannel copy() { 512 NotificationChannel copy = new NotificationChannel(mId, mName, mImportance); 513 copy.setDescription(mDesc); 514 copy.setBypassDnd(mBypassDnd); 515 copy.setLockscreenVisibility(mLockscreenVisibility); 516 copy.setSound(mSound, mAudioAttributes); 517 copy.setLightColor(mLightColor); 518 copy.enableLights(mLights); 519 copy.setVibrationPattern(mVibrationPattern); 520 if (Flags.notificationChannelVibrationEffectApi()) { 521 copy.setVibrationEffect(mVibrationEffect); 522 } 523 copy.lockFields(mUserLockedFields); 524 copy.setUserVisibleTaskShown(mUserVisibleTaskShown); 525 copy.enableVibration(mVibrationEnabled); 526 copy.setShowBadge(mShowBadge); 527 copy.setDeleted(mDeleted); 528 copy.setGroup(mGroup); 529 copy.setBlockable(mBlockableSystem); 530 copy.setAllowBubbles(mAllowBubbles); 531 copy.setOriginalImportance(mOriginalImportance); 532 copy.setConversationId(mParentId, mConversationId); 533 copy.setDemoted(mDemoted); 534 copy.setImportantConversation(mImportantConvo); 535 copy.setDeletedTimeMs(mDeletedTime); 536 copy.setImportanceLockedByCriticalDeviceFunction(mImportanceLockedDefaultApp); 537 copy.setLastNotificationUpdateTimeMs(mLastNotificationUpdateTimeMs); 538 539 return copy; 540 } 541 542 /** 543 * @hide 544 */ 545 @TestApi lockFields(int field)546 public void lockFields(int field) { 547 mUserLockedFields |= field; 548 } 549 550 /** 551 * @hide 552 */ unlockFields(int field)553 public void unlockFields(int field) { 554 mUserLockedFields &= ~field; 555 } 556 557 /** 558 * @hide 559 */ 560 @TestApi setUserVisibleTaskShown(boolean shown)561 public void setUserVisibleTaskShown(boolean shown) { 562 mUserVisibleTaskShown = shown; 563 } 564 565 /** 566 * @hide 567 */ 568 @TestApi setDeleted(boolean deleted)569 public void setDeleted(boolean deleted) { 570 mDeleted = deleted; 571 } 572 573 /** 574 * @hide 575 */ 576 @TestApi setDeletedTimeMs(long time)577 public void setDeletedTimeMs(long time) { 578 mDeletedTime = time; 579 } 580 581 /** @hide */ 582 @TestApi 583 @SystemApi 584 @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT) setImportantConversation(boolean importantConvo)585 public void setImportantConversation(boolean importantConvo) { 586 mImportantConvo = importantConvo; 587 } 588 589 /** 590 * Allows users to block notifications sent through this channel, if this channel belongs to 591 * a package that otherwise would have notifications "fixed" as enabled. 592 * 593 * If the channel does not belong to a package that has a fixed notification permission, this 594 * method does nothing, since such channels are blockable by default and cannot be set to be 595 * unblockable. 596 * @param blockable if {@code true}, allows users to block notifications on this channel. 597 */ setBlockable(boolean blockable)598 public void setBlockable(boolean blockable) { 599 mBlockableSystem = blockable; 600 } 601 // Modifiable by apps post channel creation 602 603 /** 604 * Sets the user visible name of this channel. 605 * 606 * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too 607 * long. 608 */ setName(CharSequence name)609 public void setName(CharSequence name) { 610 mName = name != null ? getTrimmedString(name.toString()) : null; 611 } 612 613 /** 614 * Sets the user visible description of this channel. 615 * 616 * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too 617 * long. 618 */ setDescription(String description)619 public void setDescription(String description) { 620 mDesc = getTrimmedString(description); 621 } 622 getTrimmedString(String input)623 private String getTrimmedString(String input) { 624 if (input != null && input.length() > MAX_TEXT_LENGTH) { 625 return input.substring(0, MAX_TEXT_LENGTH); 626 } 627 return input; 628 } 629 630 // Returns trimmed vibration effect or null if not trimmable and the serialized string is too 631 // long. Note that this method involves serializing the full VibrationEffect, which may be 632 // expensive. getTrimmedVibrationEffect(VibrationEffect effect)633 private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) { 634 if (effect == null) { 635 return null; 636 } 637 // trim if possible; check serialized length; reject if it is still too long 638 VibrationEffect result = effect; 639 VibrationEffect trimmed = effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH); 640 if (trimmed != null) { 641 result = trimmed; 642 } 643 if (vibrationToString(result).length() > MAX_SERIALIZED_VIBRATION_LENGTH) { 644 return null; 645 } 646 return result; 647 } 648 649 /** 650 * @hide 651 */ setId(String id)652 public void setId(String id) { 653 mId = id; 654 } 655 656 // Modifiable by apps on channel creation. 657 658 /** 659 * Sets what group this channel belongs to. 660 * 661 * Group information is only used for presentation, not for behavior. 662 * 663 * Only modifiable before the channel is submitted to 664 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the 665 * channel is not currently part of a group. 666 * 667 * @param groupId the id of a group created by 668 * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}. 669 */ setGroup(String groupId)670 public void setGroup(String groupId) { 671 this.mGroup = groupId; 672 } 673 674 /** 675 * Sets whether notifications posted to this channel can appear as application icon badges 676 * in a Launcher. 677 * 678 * Only modifiable before the channel is submitted to 679 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 680 * 681 * @param showBadge true if badges should be allowed to be shown. 682 */ setShowBadge(boolean showBadge)683 public void setShowBadge(boolean showBadge) { 684 this.mShowBadge = showBadge; 685 } 686 687 /** 688 * Sets the sound that should be played for notifications posted to this channel and its 689 * audio attributes. Notification channels with an {@link #getImportance() importance} of at 690 * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound. 691 * 692 * Note: An app-specific sound can be provided in the Uri parameter, but because channels are 693 * persistent for the duration of the app install, and are backed up and restored, the Uri 694 * should be stable. For this reason it is not recommended to use a 695 * {@link ContentResolver#SCHEME_ANDROID_RESOURCE} uri, as resource ids can change on app 696 * upgrade. 697 * 698 * Only modifiable before the channel is submitted to 699 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 700 */ setSound(Uri sound, AudioAttributes audioAttributes)701 public void setSound(Uri sound, AudioAttributes audioAttributes) { 702 this.mSound = sound; 703 this.mAudioAttributes = audioAttributes; 704 } 705 706 /** 707 * Sets whether notifications posted to this channel should display notification lights, 708 * on devices that support that feature. 709 * 710 * Only modifiable before the channel is submitted to 711 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 712 */ enableLights(boolean lights)713 public void enableLights(boolean lights) { 714 this.mLights = lights; 715 } 716 717 /** 718 * Sets the notification light color for notifications posted to this channel, if lights are 719 * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature. 720 * 721 * Only modifiable before the channel is submitted to 722 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 723 */ setLightColor(int argb)724 public void setLightColor(int argb) { 725 this.mLightColor = argb; 726 } 727 728 /** 729 * Sets whether notification posted to this channel should vibrate. The vibration pattern can 730 * be set with {@link #setVibrationPattern(long[])}. 731 * 732 * Only modifiable before the channel is submitted to 733 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 734 */ enableVibration(boolean vibration)735 public void enableVibration(boolean vibration) { 736 this.mVibrationEnabled = vibration; 737 } 738 739 /** 740 * Sets the vibration pattern for notifications posted to this channel. If the provided 741 * pattern is valid (non-null, non-empty with at least 1 non-zero value), will enable vibration 742 * on this channel (equivalent to calling {@link #enableVibration(boolean)} with {@code true}). 743 * Otherwise, vibration will be disabled unless {@link #enableVibration(boolean)} is 744 * used with {@code true}, in which case the default vibration will be used. 745 * 746 * Only modifiable before the channel is submitted to 747 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 748 */ setVibrationPattern(long[] vibrationPattern)749 public void setVibrationPattern(long[] vibrationPattern) { 750 this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0; 751 this.mVibrationPattern = vibrationPattern; 752 if (Flags.notifChannelCropVibrationEffects()) { 753 if (vibrationPattern != null && vibrationPattern.length > MAX_VIBRATION_LENGTH) { 754 this.mVibrationPattern = Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH); 755 } 756 } 757 if (Flags.notificationChannelVibrationEffectApi()) { 758 try { 759 this.mVibrationEffect = 760 VibrationEffect.createWaveform(vibrationPattern, /* repeat= */ -1); 761 } catch (IllegalArgumentException | NullPointerException e) { 762 this.mVibrationEffect = null; 763 } 764 } 765 } 766 767 /** 768 * Sets a {@link VibrationEffect} for notifications posted to this channel. If the 769 * provided effect is non-null, will enable vibration on this channel (equivalent 770 * to calling {@link #enableVibration(boolean)} with {@code true}). Otherwise 771 * vibration will be disabled unless {@link #enableVibration(boolean)} is used with 772 * {@code true}, in which case the default vibration will be used. 773 * 774 * <p>The effect passed here will be returned from {@link #getVibrationEffect()}. 775 * If the provided {@link VibrationEffect} is an equivalent to a wave-form 776 * vibration pattern, the equivalent wave-form pattern will be returned from 777 * {@link #getVibrationPattern()}. 778 * 779 * <p>Note that some {@link VibrationEffect}s may not be playable on some devices. 780 * In cases where such an effect is passed here, vibration will still be enabled 781 * for the channel, but the default vibration will be used. Nonetheless, the 782 * provided effect will be stored and be returned from {@link #getVibrationEffect} 783 * calls, and could be used by the same channel on a different device, for example, 784 * in cases the user backs up and restores to a device that does have the ability 785 * to play the effect, where that effect will be used instead of the default. To 786 * avoid such issues that could make the vibration behavior of your notification 787 * channel differ among different devices, it's recommended that you avoid 788 * vibration effect primitives, as the support for them differs widely among 789 * devices (read {@link VibrationEffect.Composition} for more on vibration 790 * primitives). 791 * 792 * <p>Only modifiable before the channel is submitted to 793 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 794 * 795 * @see #getVibrationEffect() 796 * @see android.os.Vibrator#areEffectsSupported(int...) 797 * @see android.os.Vibrator#arePrimitivesSupported(int...) 798 */ 799 @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) setVibrationEffect(@ullable VibrationEffect effect)800 public void setVibrationEffect(@Nullable VibrationEffect effect) { 801 this.mVibrationEnabled = effect != null; 802 this.mVibrationEffect = effect; 803 if (Flags.notifChannelCropVibrationEffects() && effect != null) { 804 long[] pattern = effect.computeCreateWaveformOffOnTimingsOrNull(); 805 if (pattern != null) { 806 // If this effect has an equivalent pattern, AND the pattern needs to be truncated 807 // due to being too long, we delegate to setVibrationPattern to re-generate the 808 // effect as well. Otherwise, we use the effect (already set above) and converted 809 // pattern directly. 810 if (pattern.length > MAX_VIBRATION_LENGTH) { 811 setVibrationPattern(pattern); 812 } else { 813 this.mVibrationPattern = pattern; 814 } 815 } else { 816 // If not convertible to a pattern directly, try trimming the vibration effect if 817 // possible and storing that version instead. 818 this.mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect); 819 this.mVibrationPattern = null; 820 } 821 } else { 822 this.mVibrationPattern = 823 mVibrationEffect == null 824 ? null : mVibrationEffect.computeCreateWaveformOffOnTimingsOrNull(); 825 } 826 } 827 828 /** 829 * Sets the level of interruption of this notification channel. 830 * 831 * Only modifiable before the channel is submitted to 832 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 833 * 834 * @param importance the amount the user should be interrupted by 835 * notifications from this channel. 836 */ setImportance(@mportance int importance)837 public void setImportance(@Importance int importance) { 838 this.mImportance = importance; 839 } 840 841 // Modifiable by a notification ranker. 842 843 /** 844 * Sets whether or not notifications posted to this channel can interrupt the user in 845 * {@link android.app.NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode. 846 * 847 * <p>Apps with Do Not Disturb policy access (see 848 * {@link NotificationManager#isNotificationPolicyAccessGranted()}) can set up their own 849 * channels this way, but only if the channel hasn't been updated by the user since its 850 * creation. 851 * 852 * <p>Otherwise, this value is only modifiable by the system and the notification ranker. 853 */ setBypassDnd(boolean bypassDnd)854 public void setBypassDnd(boolean bypassDnd) { 855 this.mBypassDnd = bypassDnd; 856 } 857 858 /** 859 * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so, 860 * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}. 861 * 862 * Only modifiable by the system and notification ranker. 863 */ setLockscreenVisibility(int lockscreenVisibility)864 public void setLockscreenVisibility(int lockscreenVisibility) { 865 this.mLockscreenVisibility = lockscreenVisibility; 866 } 867 868 /** 869 * As of Android 11 this value is no longer respected. 870 * @see #canBubble() 871 * @see Notification#getBubbleMetadata() 872 */ setAllowBubbles(boolean allowBubbles)873 public void setAllowBubbles(boolean allowBubbles) { 874 mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF; 875 } 876 877 /** 878 * @hide 879 */ setAllowBubbles(int allowed)880 public void setAllowBubbles(int allowed) { 881 mAllowBubbles = allowed; 882 } 883 884 /** 885 * Sets this channel as being converastion-centric. Different settings and functionality may be 886 * exposed for conversation-centric channels. 887 * 888 * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of 889 * this type would be posted to in absence of a specific conversation id. 890 * For example, if this channel represents 'Messages from Person A', the 891 * parent channel would be 'Messages.' 892 * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this 893 * channel's conversation. 894 */ setConversationId(@onNull String parentChannelId, @NonNull String conversationId)895 public void setConversationId(@NonNull String parentChannelId, 896 @NonNull String conversationId) { 897 mParentId = parentChannelId; 898 mConversationId = conversationId; 899 } 900 901 /** 902 * Returns the id of this channel. 903 */ getId()904 public String getId() { 905 return mId; 906 } 907 908 /** 909 * Returns the user visible name of this channel. 910 */ getName()911 public CharSequence getName() { 912 return mName; 913 } 914 915 /** 916 * Returns the user visible description of this channel. 917 */ getDescription()918 public String getDescription() { 919 return mDesc; 920 } 921 922 /** 923 * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for 924 * notifications posted to this channel. Note: This value might be > 925 * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will 926 * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked. 927 * See {@link NotificationChannelGroup#isBlocked()} and 928 * {@link NotificationManager#areNotificationsEnabled()}. 929 */ getImportance()930 public int getImportance() { 931 return mImportance; 932 } 933 934 /** 935 * Whether or not notifications posted to this channel can bypass the Do Not Disturb 936 * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode when the active policy allows 937 * priority channels to bypass notification filtering. 938 */ canBypassDnd()939 public boolean canBypassDnd() { 940 return mBypassDnd; 941 } 942 943 /** 944 * Whether or not this channel represents a conversation. 945 */ isConversation()946 public boolean isConversation() { 947 return !TextUtils.isEmpty(getConversationId()); 948 } 949 950 951 /** 952 * Whether or not notifications in this conversation are considered important. 953 * 954 * <p>Important conversations may get special visual treatment, and might be able to bypass DND. 955 * 956 * <p>This is only valid for channels that represent conversations, that is, 957 * where {@link #isConversation()} is true. 958 */ isImportantConversation()959 public boolean isImportantConversation() { 960 return mImportantConvo; 961 } 962 963 /** 964 * Returns the notification sound for this channel. 965 */ getSound()966 public Uri getSound() { 967 return mSound; 968 } 969 970 /** 971 * Returns the audio attributes for sound played by notifications posted to this channel. 972 */ getAudioAttributes()973 public AudioAttributes getAudioAttributes() { 974 return mAudioAttributes; 975 } 976 977 /** 978 * Returns whether notifications posted to this channel trigger notification lights. 979 */ shouldShowLights()980 public boolean shouldShowLights() { 981 return mLights; 982 } 983 984 /** 985 * Returns the notification light color for notifications posted to this channel. Irrelevant 986 * unless {@link #shouldShowLights()}. 987 */ getLightColor()988 public int getLightColor() { 989 return mLightColor; 990 } 991 992 /** 993 * Returns whether notifications posted to this channel always vibrate. 994 */ shouldVibrate()995 public boolean shouldVibrate() { 996 return mVibrationEnabled; 997 } 998 999 /** 1000 * Returns the vibration pattern for notifications posted to this channel. Will be ignored if 1001 * vibration is not enabled ({@link #shouldVibrate()}). 1002 */ getVibrationPattern()1003 public long[] getVibrationPattern() { 1004 return mVibrationPattern; 1005 } 1006 1007 /** 1008 * Returns the {@link VibrationEffect} for notifications posted to this channel. 1009 * The returned effect is derived from either the effect provided in the 1010 * {@link #setVibrationEffect(VibrationEffect)} method, or the equivalent vibration effect 1011 * of the pattern set via the {@link #setVibrationPattern(long[])} method, based on setter 1012 * method that was called last. 1013 * 1014 * The returned effect will be ignored in one of the following cases: 1015 * <ul> 1016 * <li> vibration is not enabled for the channel (i.e. {@link #shouldVibrate()} 1017 * returns {@code false}). 1018 * <li> the effect is not supported/playable by the device. In this case, if 1019 * vibration is enabled for the channel, the default channel vibration will 1020 * be used instead. 1021 * </ul> 1022 * 1023 * @return the {@link VibrationEffect} set via {@link 1024 * #setVibrationEffect(VibrationEffect)}, or the equivalent of the 1025 * vibration set via {@link #setVibrationPattern(long[])}. 1026 * 1027 * @see VibrationEffect#createWaveform(long[], int) 1028 */ 1029 @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) 1030 @Nullable getVibrationEffect()1031 public VibrationEffect getVibrationEffect() { 1032 return mVibrationEffect; 1033 } 1034 1035 /** 1036 * Returns whether or not notifications posted to this channel are shown on the lockscreen in 1037 * full or redacted form. 1038 */ getLockscreenVisibility()1039 public int getLockscreenVisibility() { 1040 return mLockscreenVisibility; 1041 } 1042 1043 /** 1044 * Returns whether notifications posted to this channel can appear as badges in a Launcher 1045 * application. 1046 * 1047 * Note that badging may be disabled for other reasons. 1048 */ canShowBadge()1049 public boolean canShowBadge() { 1050 return mShowBadge; 1051 } 1052 1053 /** 1054 * Returns what group this channel belongs to. 1055 * 1056 * This is used only for visually grouping channels in the UI. 1057 */ getGroup()1058 public String getGroup() { 1059 return mGroup; 1060 } 1061 1062 /** 1063 * Returns whether notifications posted to this channel are allowed to display outside of the 1064 * notification shade, in a floating window on top of other apps. 1065 * 1066 * @see Notification#getBubbleMetadata() 1067 */ canBubble()1068 public boolean canBubble() { 1069 return mAllowBubbles == ALLOW_BUBBLE_ON; 1070 } 1071 1072 /** 1073 * @hide 1074 */ getAllowBubbles()1075 public int getAllowBubbles() { 1076 return mAllowBubbles; 1077 } 1078 1079 /** 1080 * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's 1081 * a conversation related channel. See {@link #setConversationId(String, String)}. 1082 */ getParentChannelId()1083 public @Nullable String getParentChannelId() { 1084 return mParentId; 1085 } 1086 1087 /** 1088 * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's 1089 * associated with a conversation. See {@link #setConversationId(String, String)}. 1090 */ getConversationId()1091 public @Nullable String getConversationId() { 1092 return mConversationId; 1093 } 1094 1095 /** 1096 * @hide 1097 */ 1098 @SystemApi isDeleted()1099 public boolean isDeleted() { 1100 return mDeleted; 1101 } 1102 1103 /** 1104 * @hide 1105 */ getDeletedTimeMs()1106 public long getDeletedTimeMs() { 1107 return mDeletedTime; 1108 } 1109 1110 /** 1111 * @hide 1112 */ 1113 @SystemApi getUserLockedFields()1114 public int getUserLockedFields() { 1115 return mUserLockedFields; 1116 } 1117 1118 /** 1119 * @hide 1120 */ isUserVisibleTaskShown()1121 public boolean isUserVisibleTaskShown() { 1122 return mUserVisibleTaskShown; 1123 } 1124 1125 /** 1126 * Returns whether this channel is always blockable, even if the app is 'fixed' as 1127 * non-blockable. 1128 */ isBlockable()1129 public boolean isBlockable() { 1130 return mBlockableSystem; 1131 } 1132 1133 /** 1134 * @hide 1135 */ 1136 @TestApi setImportanceLockedByCriticalDeviceFunction(boolean locked)1137 public void setImportanceLockedByCriticalDeviceFunction(boolean locked) { 1138 mImportanceLockedDefaultApp = locked; 1139 } 1140 1141 /** 1142 * @hide 1143 */ 1144 @TestApi isImportanceLockedByCriticalDeviceFunction()1145 public boolean isImportanceLockedByCriticalDeviceFunction() { 1146 return mImportanceLockedDefaultApp; 1147 } 1148 1149 /** 1150 * @hide 1151 */ 1152 @TestApi getOriginalImportance()1153 public int getOriginalImportance() { 1154 return mOriginalImportance; 1155 } 1156 1157 /** 1158 * @hide 1159 */ 1160 @TestApi setOriginalImportance(int importance)1161 public void setOriginalImportance(int importance) { 1162 mOriginalImportance = importance; 1163 } 1164 1165 /** 1166 * @hide 1167 */ 1168 @TestApi setDemoted(boolean demoted)1169 public void setDemoted(boolean demoted) { 1170 mDemoted = demoted; 1171 } 1172 1173 /** 1174 * Returns whether the user has decided that this channel does not represent a conversation. The 1175 * value will always be false for channels that never claimed to be conversations - that is, 1176 * for channels where {@link #getConversationId()} and {@link #getParentChannelId()} are empty. 1177 */ isDemoted()1178 public boolean isDemoted() { 1179 return mDemoted; 1180 } 1181 1182 /** 1183 * Returns whether the user has chosen the importance of this channel, either to affirm the 1184 * initial selection from the app, or changed it to be higher or lower. 1185 * @see #getImportance() 1186 */ hasUserSetImportance()1187 public boolean hasUserSetImportance() { 1188 return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0; 1189 } 1190 1191 /** 1192 * Returns whether the user has chosen the sound of this channel. 1193 * @see #getSound() 1194 */ hasUserSetSound()1195 public boolean hasUserSetSound() { 1196 return (mUserLockedFields & USER_LOCKED_SOUND) != 0; 1197 } 1198 1199 /** 1200 * Returns the time of the notification post or last update for this channel. 1201 * @return time of post / last update 1202 * @hide 1203 */ getLastNotificationUpdateTimeMs()1204 public long getLastNotificationUpdateTimeMs() { 1205 return mLastNotificationUpdateTimeMs; 1206 } 1207 1208 /** 1209 * Sets the time of the notification post or last update for this channel. 1210 * @hide 1211 */ setLastNotificationUpdateTimeMs(long updateTimeMs)1212 public void setLastNotificationUpdateTimeMs(long updateTimeMs) { 1213 mLastNotificationUpdateTimeMs = updateTimeMs; 1214 } 1215 1216 /** 1217 * @hide 1218 */ populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, Context context)1219 public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, 1220 Context context) { 1221 populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context); 1222 } 1223 1224 /** 1225 * @hide 1226 */ 1227 @SystemApi populateFromXml(XmlPullParser parser)1228 public void populateFromXml(XmlPullParser parser) { 1229 populateFromXml(XmlUtils.makeTyped(parser), false, true, null); 1230 } 1231 1232 /** 1233 * If {@param forRestore} is true, {@param Context} MUST be non-null. 1234 */ populateFromXml(TypedXmlPullParser parser, boolean forRestore, boolean pkgInstalled, @Nullable Context context)1235 private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, 1236 boolean pkgInstalled, @Nullable Context context) { 1237 Preconditions.checkArgument(!forRestore || context != null, 1238 "forRestore is true but got null context"); 1239 1240 // Name, id, and importance are set in the constructor. 1241 setDescription(parser.getAttributeValue(null, ATT_DESC)); 1242 setBypassDnd(Notification.PRIORITY_DEFAULT 1243 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); 1244 setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); 1245 1246 Uri sound = safeUri(parser, ATT_SOUND); 1247 1248 final AudioAttributes audioAttributes = safeAudioAttributes(parser); 1249 final int usage = audioAttributes.getUsage(); 1250 setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled, usage) : sound, 1251 audioAttributes); 1252 1253 enableLights(safeBool(parser, ATT_LIGHTS, false)); 1254 setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); 1255 // Set the pattern before the effect, so that we can properly handle cases where the pattern 1256 // is null, but the effect is not null (i.e. for non-waveform VibrationEffects - the ones 1257 // which cannot be represented as a vibration pattern). 1258 setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); 1259 if (Flags.notificationChannelVibrationEffectApi()) { 1260 VibrationEffect vibrationEffect = safeVibrationEffect(parser, ATT_VIBRATION_EFFECT); 1261 if (vibrationEffect != null) { 1262 // Restore the effect only if it is not null. This allows to avoid undoing a 1263 // `setVibrationPattern` call above, if that was done with a non-null pattern 1264 // (e.g. back up from a version that did not support `setVibrationEffect`), or 1265 // when notif_channel_crop_vibration_effects is true, if there is an equivalent 1266 // vibration pattern available. 1267 setVibrationEffect(vibrationEffect); 1268 } 1269 } 1270 enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); 1271 setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); 1272 setDeleted(safeBool(parser, ATT_DELETED, false)); 1273 setDeletedTimeMs(XmlUtils.readLongAttribute( 1274 parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS)); 1275 setGroup(parser.getAttributeValue(null, ATT_GROUP)); 1276 lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); 1277 setUserVisibleTaskShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); 1278 setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); 1279 setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); 1280 setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE)); 1281 setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL), 1282 parser.getAttributeValue(null, ATT_CONVERSATION_ID)); 1283 setDemoted(safeBool(parser, ATT_DEMOTE, false)); 1284 setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); 1285 } 1286 1287 /** 1288 * Returns whether the sound for this channel was successfully restored 1289 * from backup. 1290 * @return false if the sound was not restored successfully. true otherwise (default value) 1291 * @hide 1292 */ isSoundRestored()1293 public boolean isSoundRestored() { 1294 return mSoundRestored; 1295 } 1296 1297 @Nullable getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri)1298 private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { 1299 if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) { 1300 return uri; 1301 } 1302 1303 if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { 1304 try { 1305 contentResolver.getResourceId(uri); 1306 return uri; 1307 } catch (FileNotFoundException e) { 1308 return null; 1309 } 1310 } 1311 1312 if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { 1313 return uri; 1314 } 1315 return contentResolver.canonicalize(uri); 1316 } 1317 1318 @Nullable getUncanonicalizedSoundUri( ContentResolver contentResolver, @NonNull Uri uri, int usage)1319 private Uri getUncanonicalizedSoundUri( 1320 ContentResolver contentResolver, @NonNull Uri uri, int usage) { 1321 if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri) 1322 || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme()) 1323 || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { 1324 return uri; 1325 } 1326 int ringtoneType = 0; 1327 1328 // Consistent with UI(SoundPreferenceController.handlePreferenceTreeClick). 1329 if (AudioAttributes.USAGE_ALARM == usage) { 1330 ringtoneType = RingtoneManager.TYPE_ALARM; 1331 } else if (AudioAttributes.USAGE_NOTIFICATION_RINGTONE == usage) { 1332 ringtoneType = RingtoneManager.TYPE_RINGTONE; 1333 } else { 1334 ringtoneType = RingtoneManager.TYPE_NOTIFICATION; 1335 } 1336 try { 1337 return RingtoneManager.getRingtoneUriForRestore( 1338 contentResolver, uri.toString(), ringtoneType); 1339 } catch (Exception e) { 1340 Log.e(TAG, "Failed to uncanonicalized sound uri for " + uri + " " + e); 1341 return Settings.System.DEFAULT_NOTIFICATION_URI; 1342 } 1343 } 1344 1345 /** 1346 * Restore/validate sound Uri from backup 1347 * @param context The Context 1348 * @param uri The sound Uri to restore 1349 * @param pkgInstalled If the parent package is installed 1350 * @return restored and validated Uri 1351 * @hide 1352 */ 1353 @Nullable restoreSoundUri( Context context, @Nullable Uri uri, boolean pkgInstalled, int usage)1354 public Uri restoreSoundUri( 1355 Context context, @Nullable Uri uri, boolean pkgInstalled, int usage) { 1356 if (uri == null || Uri.EMPTY.equals(uri)) { 1357 return null; 1358 } 1359 ContentResolver contentResolver = context.getContentResolver(); 1360 // There are backups out there with uncanonical uris (because we fixed this after 1361 // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't 1362 // verify the uri against device storage and we'll possibly end up with a broken uri. 1363 // We then canonicalize the uri to uncanonicalize it back, which means we properly check 1364 // the uri and in the case of not having the resource we end up with the default - better 1365 // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine 1366 // according to the docs because canonicalize method has to handle canonical uris as well. 1367 Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri); 1368 if (canonicalizedUri == null) { 1369 // Uri failed to restore with package installed 1370 if (!mSoundRestored && pkgInstalled) { 1371 mSoundRestored = true; 1372 // We got a null because the uri in the backup does not exist here, so we return 1373 // default 1374 return Settings.System.DEFAULT_NOTIFICATION_URI; 1375 } else { 1376 // Flag as unrestored and try again later (on package install) 1377 mSoundRestored = false; 1378 return uri; 1379 } 1380 } 1381 mSoundRestored = true; 1382 return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri, usage); 1383 } 1384 1385 /** 1386 * @hide 1387 */ 1388 @SystemApi writeXml(XmlSerializer out)1389 public void writeXml(XmlSerializer out) throws IOException { 1390 writeXml(XmlUtils.makeTyped(out), false, null); 1391 } 1392 1393 /** 1394 * @hide 1395 */ writeXmlForBackup(XmlSerializer out, Context context)1396 public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { 1397 writeXml(XmlUtils.makeTyped(out), true, context); 1398 } 1399 getSoundForBackup(Context context)1400 private Uri getSoundForBackup(Context context) { 1401 Uri sound = getSound(); 1402 if (sound == null || Uri.EMPTY.equals(sound)) { 1403 return null; 1404 } 1405 try { 1406 Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound); 1407 if (canonicalSound == null) { 1408 // The content provider does not support canonical uris so we backup the default 1409 return Settings.System.DEFAULT_NOTIFICATION_URI; 1410 } 1411 return canonicalSound; 1412 } catch (Exception e) { 1413 Slog.e(TAG, "Cannot find file for sound " + sound + " using default"); 1414 return Settings.System.DEFAULT_NOTIFICATION_URI; 1415 } 1416 } 1417 1418 /** 1419 * If {@param forBackup} is true, {@param Context} MUST be non-null. 1420 */ writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)1421 private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context) 1422 throws IOException { 1423 Preconditions.checkArgument(!forBackup || context != null, 1424 "forBackup is true but got null context"); 1425 out.startTag(null, TAG_CHANNEL); 1426 out.attribute(null, ATT_ID, getId()); 1427 if (getName() != null) { 1428 out.attribute(null, ATT_NAME, getName().toString()); 1429 } 1430 if (getDescription() != null) { 1431 out.attribute(null, ATT_DESC, getDescription()); 1432 } 1433 if (getImportance() != DEFAULT_IMPORTANCE) { 1434 out.attributeInt(null, ATT_IMPORTANCE, getImportance()); 1435 } 1436 if (canBypassDnd()) { 1437 out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX); 1438 } 1439 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 1440 out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility()); 1441 } 1442 Uri sound = forBackup ? getSoundForBackup(context) : getSound(); 1443 if (sound != null) { 1444 out.attribute(null, ATT_SOUND, sound.toString()); 1445 } 1446 if (getAudioAttributes() != null) { 1447 out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage()); 1448 out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType()); 1449 out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags()); 1450 } 1451 if (shouldShowLights()) { 1452 out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights()); 1453 } 1454 if (getLightColor() != DEFAULT_LIGHT_COLOR) { 1455 out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor()); 1456 } 1457 if (shouldVibrate()) { 1458 out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate()); 1459 } 1460 if (getVibrationPattern() != null) { 1461 out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1462 } 1463 if (getVibrationEffect() != null) { 1464 if (!Flags.notifChannelCropVibrationEffects() || getVibrationPattern() == null) { 1465 // When notif_channel_crop_vibration_effects is on, only serialize the vibration 1466 // effect if we do not already have an equivalent vibration pattern. 1467 out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect())); 1468 } 1469 } 1470 if (getUserLockedFields() != 0) { 1471 out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields()); 1472 } 1473 if (isUserVisibleTaskShown()) { 1474 out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isUserVisibleTaskShown()); 1475 } 1476 if (canShowBadge()) { 1477 out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge()); 1478 } 1479 if (isDeleted()) { 1480 out.attributeBoolean(null, ATT_DELETED, isDeleted()); 1481 } 1482 if (getDeletedTimeMs() >= 0) { 1483 out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs()); 1484 } 1485 if (getGroup() != null) { 1486 out.attribute(null, ATT_GROUP, getGroup()); 1487 } 1488 if (isBlockable()) { 1489 out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable()); 1490 } 1491 if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { 1492 out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles()); 1493 } 1494 if (getOriginalImportance() != DEFAULT_IMPORTANCE) { 1495 out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance()); 1496 } 1497 if (getParentChannelId() != null) { 1498 out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId()); 1499 } 1500 if (getConversationId() != null) { 1501 out.attribute(null, ATT_CONVERSATION_ID, getConversationId()); 1502 } 1503 if (isDemoted()) { 1504 out.attributeBoolean(null, ATT_DEMOTE, isDemoted()); 1505 } 1506 if (isImportantConversation()) { 1507 out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); 1508 } 1509 1510 // mImportanceLockedDefaultApp has a different source of truth and so isn't written to 1511 // this xml file 1512 1513 out.endTag(null, TAG_CHANNEL); 1514 } 1515 1516 /** 1517 * @hide 1518 */ 1519 @SystemApi toJson()1520 public JSONObject toJson() throws JSONException { 1521 JSONObject record = new JSONObject(); 1522 record.put(ATT_ID, getId()); 1523 record.put(ATT_NAME, getName()); 1524 record.put(ATT_DESC, getDescription()); 1525 if (getImportance() != DEFAULT_IMPORTANCE) { 1526 record.put(ATT_IMPORTANCE, 1527 NotificationListenerService.Ranking.importanceToString(getImportance())); 1528 } 1529 if (canBypassDnd()) { 1530 record.put(ATT_PRIORITY, Notification.PRIORITY_MAX); 1531 } 1532 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 1533 record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility())); 1534 } 1535 if (getSound() != null) { 1536 record.put(ATT_SOUND, getSound().toString()); 1537 } 1538 if (getAudioAttributes() != null) { 1539 record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); 1540 record.put(ATT_CONTENT_TYPE, 1541 Integer.toString(getAudioAttributes().getContentType())); 1542 record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); 1543 } 1544 record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); 1545 record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); 1546 record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 1547 record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 1548 record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isUserVisibleTaskShown())); 1549 record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1550 if (getVibrationEffect() != null) { 1551 record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect())); 1552 } 1553 record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 1554 record.put(ATT_DELETED, Boolean.toString(isDeleted())); 1555 record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs())); 1556 record.put(ATT_GROUP, getGroup()); 1557 record.put(ATT_BLOCKABLE_SYSTEM, isBlockable()); 1558 record.put(ATT_ALLOW_BUBBLE, getAllowBubbles()); 1559 // TODO: original importance 1560 return record; 1561 } 1562 safeAudioAttributes(TypedXmlPullParser parser)1563 private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) { 1564 int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION); 1565 int contentType = safeInt(parser, ATT_CONTENT_TYPE, 1566 AudioAttributes.CONTENT_TYPE_SONIFICATION); 1567 int flags = safeInt(parser, ATT_FLAGS, 0); 1568 return new AudioAttributes.Builder() 1569 .setUsage(usage) 1570 .setContentType(contentType) 1571 .setFlags(flags) 1572 .build(); 1573 } 1574 safeUri(TypedXmlPullParser parser, String att)1575 private static Uri safeUri(TypedXmlPullParser parser, String att) { 1576 final String val = parser.getAttributeValue(null, att); 1577 return val == null ? null : Uri.parse(val); 1578 } 1579 vibrationToString(VibrationEffect effect)1580 private static String vibrationToString(VibrationEffect effect) { 1581 StringWriter writer = new StringWriter(); 1582 try { 1583 VibrationXmlSerializer.serialize( 1584 effect, writer, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS); 1585 } catch (IOException e) { 1586 Log.e(TAG, "Unable to serialize vibration: " + effect, e); 1587 } 1588 return writer.toString(); 1589 } 1590 safeVibrationEffect(TypedXmlPullParser parser, String att)1591 private static VibrationEffect safeVibrationEffect(TypedXmlPullParser parser, String att) { 1592 final String val = parser.getAttributeValue(null, att); 1593 if (val != null) { 1594 try { 1595 return VibrationXmlParser.parseVibrationEffect( 1596 new StringReader(val), VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS); 1597 } catch (IOException e) { 1598 Log.e(TAG, "Unable to read serialized vibration effect", e); 1599 } 1600 } 1601 return null; 1602 } 1603 safeInt(TypedXmlPullParser parser, String att, int defValue)1604 private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { 1605 return parser.getAttributeInt(null, att, defValue); 1606 } 1607 safeBool(TypedXmlPullParser parser, String att, boolean defValue)1608 private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) { 1609 return parser.getAttributeBoolean(null, att, defValue); 1610 } 1611 safeLongArray(TypedXmlPullParser parser, String att, long[] defValue)1612 private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) { 1613 final String attributeValue = parser.getAttributeValue(null, att); 1614 if (TextUtils.isEmpty(attributeValue)) return defValue; 1615 String[] values = attributeValue.split(DELIMITER); 1616 long[] longValues = new long[values.length]; 1617 for (int i = 0; i < values.length; i++) { 1618 try { 1619 longValues[i] = Long.parseLong(values[i]); 1620 } catch (NumberFormatException e) { 1621 longValues[i] = 0; 1622 } 1623 } 1624 return longValues; 1625 } 1626 longArrayToString(long[] values)1627 private static String longArrayToString(long[] values) { 1628 StringBuilder sb = new StringBuilder(); 1629 if (values != null && values.length > 0) { 1630 for (int i = 0; i < values.length - 1; i++) { 1631 sb.append(values[i]).append(DELIMITER); 1632 } 1633 sb.append(values[values.length - 1]); 1634 } 1635 return sb.toString(); 1636 } 1637 1638 /** 1639 * Get the reserved bundle channel ID for an Adjustment type 1640 * @param the Adjustment type 1641 * @return the channel ID, or null if type is invalid 1642 * @hide 1643 */ getChannelIdForBundleType(@djustment.Types int type)1644 public static @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) { 1645 switch (type) { 1646 case TYPE_CONTENT_RECOMMENDATION: 1647 return RECS_ID; 1648 case TYPE_NEWS: 1649 return NEWS_ID; 1650 case TYPE_PROMOTION: 1651 return PROMOTIONS_ID; 1652 case TYPE_SOCIAL_MEDIA: 1653 return SOCIAL_MEDIA_ID; 1654 } 1655 return null; 1656 } 1657 1658 public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR = 1659 new Creator<NotificationChannel>() { 1660 @Override 1661 public NotificationChannel createFromParcel(Parcel in) { 1662 return new NotificationChannel(in); 1663 } 1664 1665 @Override 1666 public NotificationChannel[] newArray(int size) { 1667 return new NotificationChannel[size]; 1668 } 1669 }; 1670 1671 @Override describeContents()1672 public int describeContents() { 1673 return 0; 1674 } 1675 1676 @Override equals(@ullable Object o)1677 public boolean equals(@Nullable Object o) { 1678 if (this == o) return true; 1679 if (o == null || getClass() != o.getClass()) return false; 1680 NotificationChannel that = (NotificationChannel) o; 1681 return getImportance() == that.getImportance() 1682 && mBypassDnd == that.mBypassDnd 1683 && getLockscreenVisibility() == that.getLockscreenVisibility() 1684 && mLights == that.mLights 1685 && getLightColor() == that.getLightColor() 1686 && getUserLockedFields() == that.getUserLockedFields() 1687 && isUserVisibleTaskShown() == that.isUserVisibleTaskShown() 1688 && mVibrationEnabled == that.mVibrationEnabled 1689 && mShowBadge == that.mShowBadge 1690 && isDeleted() == that.isDeleted() 1691 && getDeletedTimeMs() == that.getDeletedTimeMs() 1692 && isBlockable() == that.isBlockable() 1693 && mAllowBubbles == that.mAllowBubbles 1694 && Objects.equals(getId(), that.getId()) 1695 && Objects.equals(getName(), that.getName()) 1696 && Objects.equals(mDesc, that.mDesc) 1697 && Objects.equals(getSound(), that.getSound()) 1698 && Arrays.equals(mVibrationPattern, that.mVibrationPattern) 1699 && Objects.equals(getVibrationEffect(), that.getVibrationEffect()) 1700 && Objects.equals(getGroup(), that.getGroup()) 1701 && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) 1702 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp 1703 && mOriginalImportance == that.mOriginalImportance 1704 && Objects.equals(getParentChannelId(), that.getParentChannelId()) 1705 && Objects.equals(getConversationId(), that.getConversationId()) 1706 && isDemoted() == that.isDemoted() 1707 && isImportantConversation() == that.isImportantConversation(); 1708 } 1709 1710 @Override hashCode()1711 public int hashCode() { 1712 int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd, 1713 getLockscreenVisibility(), getSound(), mLights, getLightColor(), 1714 getUserLockedFields(), isUserVisibleTaskShown(), 1715 mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), 1716 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, 1717 mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(), 1718 mParentId, mConversationId, mDemoted, mImportantConvo); 1719 result = 31 * result + Arrays.hashCode(mVibrationPattern); 1720 return result; 1721 } 1722 1723 /** @hide */ dump(PrintWriter pw, String prefix, boolean redacted)1724 public void dump(PrintWriter pw, String prefix, boolean redacted) { 1725 String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName; 1726 String output = "NotificationChannel{" 1727 + "mId='" + mId + '\'' 1728 + ", mName=" + redactedName 1729 + getFieldsString() 1730 + '}'; 1731 pw.println(prefix + output); 1732 } 1733 1734 @Override toString()1735 public String toString() { 1736 return "NotificationChannel{" 1737 + "mId='" + mId + '\'' 1738 + ", mName=" + mName 1739 + getFieldsString() 1740 + '}'; 1741 } 1742 getFieldsString()1743 private String getFieldsString() { 1744 return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "") 1745 + ", mImportance=" + mImportance 1746 + ", mBypassDnd=" + mBypassDnd 1747 + ", mLockscreenVisibility=" + mLockscreenVisibility 1748 + ", mSound=" + mSound 1749 + ", mLights=" + mLights 1750 + ", mLightColor=" + mLightColor 1751 + ", mVibrationPattern=" + Arrays.toString(mVibrationPattern) 1752 + ", mVibrationEffect=" 1753 + (mVibrationEffect == null ? "null" : mVibrationEffect.toString()) 1754 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) 1755 + ", mUserVisibleTaskShown=" + mUserVisibleTaskShown 1756 + ", mVibrationEnabled=" + mVibrationEnabled 1757 + ", mShowBadge=" + mShowBadge 1758 + ", mDeleted=" + mDeleted 1759 + ", mDeletedTimeMs=" + mDeletedTime 1760 + ", mGroup='" + mGroup + '\'' 1761 + ", mAudioAttributes=" + mAudioAttributes 1762 + ", mBlockableSystem=" + mBlockableSystem 1763 + ", mAllowBubbles=" + mAllowBubbles 1764 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp 1765 + ", mOriginalImp=" + mOriginalImportance 1766 + ", mParent=" + mParentId 1767 + ", mConversationId=" + mConversationId 1768 + ", mDemoted=" + mDemoted 1769 + ", mImportantConvo=" + mImportantConvo 1770 + ", mLastNotificationUpdateTimeMs=" + mLastNotificationUpdateTimeMs; 1771 } 1772 1773 /** @hide */ dumpDebug(ProtoOutputStream proto, long fieldId)1774 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 1775 final long token = proto.start(fieldId); 1776 1777 proto.write(NotificationChannelProto.ID, mId); 1778 proto.write(NotificationChannelProto.NAME, mName); 1779 proto.write(NotificationChannelProto.DESCRIPTION, mDesc); 1780 proto.write(NotificationChannelProto.IMPORTANCE, mImportance); 1781 proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd); 1782 proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility); 1783 if (mSound != null) { 1784 proto.write(NotificationChannelProto.SOUND, mSound.toString()); 1785 } 1786 proto.write(NotificationChannelProto.USE_LIGHTS, mLights); 1787 proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor); 1788 if (mVibrationPattern != null) { 1789 for (long v : mVibrationPattern) { 1790 proto.write(NotificationChannelProto.VIBRATION, v); 1791 } 1792 } 1793 proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); 1794 proto.write(NotificationChannelProto.USER_VISIBLE_TASK_SHOWN, mUserVisibleTaskShown); 1795 proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); 1796 proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); 1797 proto.write(NotificationChannelProto.IS_DELETED, mDeleted); 1798 proto.write(NotificationChannelProto.GROUP, mGroup); 1799 if (mAudioAttributes != null) { 1800 mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES); 1801 } 1802 proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); 1803 proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles); 1804 1805 proto.end(token); 1806 } 1807 } 1808