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