1 /* 2 * Copyright (C) 2014 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 17 package android.telecom; 18 19 import static android.Manifest.permission.MODIFY_PHONE_STATE; 20 21 import android.annotation.NonNull; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.content.Intent; 25 import android.graphics.drawable.Icon; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.telephony.CarrierConfigManager; 31 import android.telephony.TelephonyManager; 32 import android.text.TextUtils; 33 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Objects; 38 39 /** 40 * Represents a distinct method to place or receive a phone call. Apps which can place calls and 41 * want those calls to be integrated into the dialer and in-call UI should build an instance of 42 * this class and register it with the system using {@link TelecomManager}. 43 * <p> 44 * {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with 45 * alternative options when placing a phone call. When building a {@link PhoneAccount}, the app 46 * should supply a valid {@link PhoneAccountHandle} that references the connection service 47 * implementation Telecom will use to interact with the app. 48 */ 49 public final class PhoneAccount implements Parcelable { 50 51 /** 52 * Integer extra which determines the order in which {@link PhoneAccount}s are sorted 53 * 54 * This is an extras key set via {@link Builder#setExtras} which determines the order in which 55 * {@link PhoneAccount}s from the same {@link ConnectionService} are sorted. The accounts 56 * are sorted in ascending order by this key, and this ordering is used to 57 * determine priority when a call can be placed via multiple accounts. 58 * 59 * When multiple {@link PhoneAccount}s are supplied with the same sort order key, no ordering is 60 * guaranteed between those {@link PhoneAccount}s. Additionally, no ordering is guaranteed 61 * between {@link PhoneAccount}s that do not supply this extra, and all such accounts 62 * will be sorted after the accounts that do supply this extra. 63 * 64 * An example of a sort order key is slot index (see {@link TelephonyManager#getSlotIndex()}), 65 * which is the one used by the cell Telephony stack. 66 * @hide 67 */ 68 @SystemApi 69 public static final String EXTRA_SORT_ORDER = 70 "android.telecom.extra.SORT_ORDER"; 71 72 /** 73 * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the 74 * maximum permitted length of a call subject specified via the 75 * {@link TelecomManager#EXTRA_CALL_SUBJECT} extra on an 76 * {@link android.content.Intent#ACTION_CALL} intent. Ultimately a {@link ConnectionService} is 77 * responsible for enforcing the maximum call subject length when sending the message, however 78 * this extra is provided so that the user interface can proactively limit the length of the 79 * call subject as the user types it. 80 */ 81 public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = 82 "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH"; 83 84 /** 85 * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the 86 * character encoding to be used when determining the length of messages. 87 * The user interface can use this when determining the number of characters the user may type 88 * in a call subject. If empty-string, the call subject message size limit will be enforced on 89 * a 1:1 basis. That is, each character will count towards the messages size limit as a single 90 * character. If a character encoding is specified, the message size limit will be based on the 91 * number of bytes in the message per the specified encoding. See 92 * {@link #EXTRA_CALL_SUBJECT_MAX_LENGTH} for more information on the call subject maximum 93 * length. 94 */ 95 public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = 96 "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING"; 97 98 /** 99 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 100 * indicates that all calls from this {@link PhoneAccount} should be treated as VoIP calls 101 * rather than cellular calls by the Telecom audio handling logic. 102 */ 103 public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = 104 "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE"; 105 106 /** 107 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 108 * indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a 109 * connection (see {@code android.telecom.Call#handoverTo()}) to this {@link PhoneAccount} from 110 * a {@link PhoneAccount} specifying {@link #EXTRA_SUPPORTS_HANDOVER_FROM}. 111 * <p> 112 * A handover request is initiated by the user from the default dialer app to indicate a desire 113 * to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another. 114 */ 115 public static final String EXTRA_SUPPORTS_HANDOVER_TO = 116 "android.telecom.extra.SUPPORTS_HANDOVER_TO"; 117 118 /** 119 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 120 * indicates whether this {@link PhoneAccount} supports using a fallback if video calling is 121 * not available. This extra is for device level support, {@link 122 * android.telephony.CarrierConfigManager#KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL} should also 123 * be checked to ensure it is not disabled by individual carrier. 124 * 125 * @hide 126 */ 127 public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = 128 "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK"; 129 130 /** 131 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 132 * indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a 133 * connection from this {@link PhoneAccount} to another {@link PhoneAccount}. 134 * (see {@code android.telecom.Call#handoverTo()}) which specifies 135 * {@link #EXTRA_SUPPORTS_HANDOVER_TO}. 136 * <p> 137 * A handover request is initiated by the user from the default dialer app to indicate a desire 138 * to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another. 139 */ 140 public static final String EXTRA_SUPPORTS_HANDOVER_FROM = 141 "android.telecom.extra.SUPPORTS_HANDOVER_FROM"; 142 143 144 /** 145 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 146 * indicates whether a Self-Managed {@link PhoneAccount} should log its calls to the call log. 147 * Self-Managed {@link PhoneAccount}s are responsible for their own notifications, so the system 148 * will not create a notification when a missed call is logged. 149 * <p> 150 * By default, Self-Managed {@link PhoneAccount}s do not log their calls to the call log. 151 * Setting this extra to {@code true} provides a means for them to log their calls. 152 * <p> 153 * Note: Only calls where the {@link Call.Details#getHandle()} {@link Uri#getScheme()} is 154 * {@link #SCHEME_SIP} or {@link #SCHEME_TEL} will be logged at the current time. 155 */ 156 public static final String EXTRA_LOG_SELF_MANAGED_CALLS = 157 "android.telecom.extra.LOG_SELF_MANAGED_CALLS"; 158 159 /** 160 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 161 * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone" 162 * when the user is recording audio on the device. 163 * <p> 164 * The call recording tone is played over the telephony audio stream so that the remote party 165 * has an audible indication that it is possible their call is being recorded using a call 166 * recording app on the device. 167 * <p> 168 * This extra only has an effect for calls placed via Telephony (e.g. 169 * {@link #CAPABILITY_SIM_SUBSCRIPTION}). 170 * <p> 171 * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is 172 * in progress. 173 * @hide 174 */ 175 @SystemApi 176 public static final String EXTRA_PLAY_CALL_RECORDING_TONE = 177 "android.telecom.extra.PLAY_CALL_RECORDING_TONE"; 178 179 /** 180 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()} which 181 * indicates whether calls for a {@link PhoneAccount} should skip call filtering. 182 * <p> 183 * If not specified, this will default to false; all calls will undergo call filtering unless 184 * specifically exempted (e.g. {@link Connection#PROPERTY_EMERGENCY_CALLBACK_MODE}.) However, 185 * this may be used to skip call filtering when it has already been performed on another device. 186 * @hide 187 */ 188 public static final String EXTRA_SKIP_CALL_FILTERING = 189 "android.telecom.extra.SKIP_CALL_FILTERING"; 190 191 /** 192 * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which 193 * indicates whether a Self-managed {@link PhoneAccount} want to expose its calls to all 194 * {@link InCallService} which declares the metadata 195 * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}. 196 */ 197 public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE = 198 "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE"; 199 200 /** 201 * Flag indicating that this {@code PhoneAccount} can act as a connection manager for 202 * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount} 203 * will be allowed to manage phone calls including using its own proprietary phone-call 204 * implementation (like VoIP calling) to make calls instead of the telephony stack. 205 * <p> 206 * When a user opts to place a call using the SIM-based telephony stack, the 207 * {@link ConnectionService} associated with this {@code PhoneAccount} will be attempted first 208 * if the user has explicitly selected it to be used as the default connection manager. 209 * <p> 210 * See {@link #getCapabilities} 211 */ 212 public static final int CAPABILITY_CONNECTION_MANAGER = 0x1; 213 214 /** 215 * Flag indicating that this {@code PhoneAccount} can make phone calls in place of 216 * traditional SIM-based telephony calls. This account will be treated as a distinct method 217 * for placing calls alongside the traditional SIM-based telephony stack. This flag is 218 * distinct from {@link #CAPABILITY_CONNECTION_MANAGER} in that it is not allowed to manage 219 * or place calls from the built-in telephony stack. 220 * <p> 221 * See {@link #getCapabilities} 222 * <p> 223 */ 224 public static final int CAPABILITY_CALL_PROVIDER = 0x2; 225 226 /** 227 * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM 228 * subscription. 229 * <p> 230 * Only the Android framework can register a {@code PhoneAccount} having this capability. 231 * <p> 232 * See {@link #getCapabilities} 233 */ 234 public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4; 235 236 /** 237 * Flag indicating that this {@code PhoneAccount} is currently able to place video calls. 238 * <p> 239 * See also {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING} which indicates whether the 240 * {@code PhoneAccount} supports placing video calls. 241 * <p> 242 * See {@link #getCapabilities} 243 */ 244 public static final int CAPABILITY_VIDEO_CALLING = 0x8; 245 246 /** 247 * Flag indicating that this {@code PhoneAccount} is capable of placing emergency calls. 248 * By default all PSTN {@code PhoneAccount}s are capable of placing emergency calls. 249 * <p> 250 * See {@link #getCapabilities} 251 */ 252 public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 0x10; 253 254 /** 255 * Flag indicating that this {@code PhoneAccount} is capable of being used by all users. This 256 * should only be used by system apps (and will be ignored for all other apps trying to use it). 257 * <p> 258 * See {@link #getCapabilities} 259 * @hide 260 */ 261 @SystemApi 262 public static final int CAPABILITY_MULTI_USER = 0x20; 263 264 /** 265 * Flag indicating that this {@code PhoneAccount} supports a subject for Calls. This means a 266 * caller is able to specify a short subject line for an outgoing call. A capable receiving 267 * device displays the call subject on the incoming call screen. 268 * <p> 269 * See {@link #getCapabilities} 270 */ 271 public static final int CAPABILITY_CALL_SUBJECT = 0x40; 272 273 /** 274 * Flag indicating that this {@code PhoneAccount} should only be used for emergency calls. 275 * <p> 276 * See {@link #getCapabilities} 277 * @hide 278 */ 279 @SystemApi 280 public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80; 281 282 /** 283 * Flag indicating that for this {@code PhoneAccount}, the ability to make a video call to a 284 * number relies on presence. Should only be set if the {@code PhoneAccount} also has 285 * {@link #CAPABILITY_VIDEO_CALLING}. 286 * <p> 287 * Note: As of Android 12, using the 288 * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit on the 289 * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} column to indicate whether 290 * a contact's phone number supports video calling has been deprecated and should only be used 291 * on devices where {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set. On newer 292 * devices, applications must use {@link android.telephony.ims.RcsUceAdapter} instead to 293 * determine whether or not a contact's phone number supports carrier video calling. 294 * <p> 295 * See {@link #getCapabilities} 296 */ 297 public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 0x100; 298 299 /** 300 * Flag indicating that for this {@link PhoneAccount}, emergency video calling is allowed. 301 * <p> 302 * When set, Telecom will allow emergency video calls to be placed. When not set, Telecom will 303 * convert all outgoing video calls to emergency numbers to audio-only. 304 * @hide 305 */ 306 @SystemApi 307 public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200; 308 309 /** 310 * Flag indicating that this {@link PhoneAccount} supports video calling. 311 * This is not an indication that the {@link PhoneAccount} is currently able to make a video 312 * call, but rather that it has the ability to make video calls (but not necessarily at this 313 * time). 314 * <p> 315 * Whether a {@link PhoneAccount} can make a video call is ultimately controlled by 316 * {@link #CAPABILITY_VIDEO_CALLING}, which indicates whether the {@link PhoneAccount} is 317 * currently capable of making a video call. Consider a case where, for example, a 318 * {@link PhoneAccount} supports making video calls (e.g. 319 * {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING}), but a current lack of network connectivity 320 * prevents video calls from being made (e.g. {@link #CAPABILITY_VIDEO_CALLING}). 321 * <p> 322 * See {@link #getCapabilities} 323 */ 324 public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400; 325 326 /** 327 * Flag indicating that this {@link PhoneAccount} is responsible for managing its own 328 * {@link Connection}s. This type of {@link PhoneAccount} is ideal for use with standalone 329 * calling apps which do not wish to use the default phone app for {@link Connection} UX, 330 * but which want to leverage the call and audio routing capabilities of the Telecom framework. 331 * <p> 332 * When set, {@link Connection}s created by the self-managed {@link ConnectionService} will not 333 * be surfaced to implementations of the {@link InCallService} API. Thus it is the 334 * responsibility of a self-managed {@link ConnectionService} to provide a user interface for 335 * its {@link Connection}s. 336 * <p> 337 * Self-managed {@link Connection}s will, however, be displayed on connected Bluetooth devices. 338 */ 339 public static final int CAPABILITY_SELF_MANAGED = 0x800; 340 341 /** 342 * Flag indicating that this {@link PhoneAccount} is capable of making a call with an 343 * RTT (Real-time text) session. 344 * When set, Telecom will attempt to open an RTT session on outgoing calls that specify 345 * that they should be placed with an RTT session , and the in-call app will be displayed 346 * with text entry fields for RTT. Likewise, the in-call app can request that an RTT 347 * session be opened during a call if this bit is set. 348 */ 349 public static final int CAPABILITY_RTT = 0x1000; 350 351 /** 352 * Flag indicating that this {@link PhoneAccount} is the preferred SIM subscription for 353 * emergency calls. A {@link PhoneAccount} that sets this capability must also 354 * set the {@link #CAPABILITY_SIM_SUBSCRIPTION} and {@link #CAPABILITY_PLACE_EMERGENCY_CALLS} 355 * capabilities. There must only be one emergency preferred {@link PhoneAccount} on the device. 356 * <p> 357 * When set, Telecom will prefer this {@link PhoneAccount} over others for emergency calling, 358 * even if the emergency call was placed with a specific {@link PhoneAccount} set using the 359 * extra{@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} in 360 * {@link Intent#ACTION_CALL_EMERGENCY} or {@link TelecomManager#placeCall(Uri, Bundle)}. 361 * 362 * @hide 363 */ 364 @SystemApi 365 public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000; 366 367 /** 368 * An adhoc conference call is established by providing a list of addresses to 369 * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the 370 * {@link ConnectionService} is responsible for connecting all indicated participants 371 * to a conference simultaneously. 372 * This is in contrast to conferences formed by merging calls together (e.g. using 373 * {@link android.telecom.Call#mergeConference()}). 374 */ 375 public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000; 376 377 /** 378 * Flag indicating whether this {@link PhoneAccount} is capable of supporting the call composer 379 * functionality for enriched calls. 380 */ 381 public static final int CAPABILITY_CALL_COMPOSER = 0x8000; 382 383 /** 384 * Flag indicating that this {@link PhoneAccount} provides SIM-based voice calls, potentially as 385 * an over-the-top solution such as wi-fi calling. 386 * 387 * <p>Similar to {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING}, this capability indicates this 388 * {@link PhoneAccount} has the ability to make voice calls (but not necessarily at this time). 389 * Whether this {@link PhoneAccount} can make a voice call is ultimately controlled by {@link 390 * #CAPABILITY_VOICE_CALLING_AVAILABLE}, which indicates whether this {@link PhoneAccount} is 391 * currently capable of making a voice call. Consider a case where, for example, a {@link 392 * PhoneAccount} supports making voice calls (e.g. {@link 393 * #CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS}), but a current lack of network connectivity 394 * prevents voice calls from being made (e.g. {@link #CAPABILITY_VOICE_CALLING_AVAILABLE}). 395 * 396 * <p>In order to declare this capability, this {@link PhoneAccount} must also declare {@link 397 * #CAPABILITY_SIM_SUBSCRIPTION} or {@link #CAPABILITY_CONNECTION_MANAGER} and satisfy the 398 * associated requirements. 399 * 400 * @see #CAPABILITY_VOICE_CALLING_AVAILABLE 401 * @see #getCapabilities 402 */ 403 public static final int CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS = 0x10000; 404 405 /** 406 * Flag indicating that this {@link PhoneAccount} is <em>currently</em> able to place SIM-based 407 * voice calls, similar to {@link #CAPABILITY_VIDEO_CALLING}. 408 * 409 * <p>See also {@link #CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS}, which indicates whether 410 * the {@code PhoneAccount} supports placing SIM-based voice calls or not. 411 * 412 * <p>In order to declare this capability, this {@link PhoneAccount} must also declare {@link 413 * #CAPABILITY_SIM_SUBSCRIPTION} or {@link #CAPABILITY_CONNECTION_MANAGER} and satisfy the 414 * associated requirements. 415 * 416 * @see #CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS 417 * @see #getCapabilities 418 */ 419 public static final int CAPABILITY_VOICE_CALLING_AVAILABLE = 0x20000; 420 421 /* NEXT CAPABILITY: 0x40000 */ 422 423 /** 424 * URI scheme for telephone number URIs. 425 */ 426 public static final String SCHEME_TEL = "tel"; 427 428 /** 429 * URI scheme for voicemail URIs. 430 */ 431 public static final String SCHEME_VOICEMAIL = "voicemail"; 432 433 /** 434 * URI scheme for SIP URIs. 435 */ 436 public static final String SCHEME_SIP = "sip"; 437 438 /** 439 * Indicating no icon tint is set. 440 * @hide 441 */ 442 public static final int NO_ICON_TINT = 0; 443 444 /** 445 * Indicating no hightlight color is set. 446 */ 447 public static final int NO_HIGHLIGHT_COLOR = 0; 448 449 /** 450 * Indicating no resource ID is set. 451 */ 452 public static final int NO_RESOURCE_ID = -1; 453 454 private final PhoneAccountHandle mAccountHandle; 455 private final Uri mAddress; 456 private final Uri mSubscriptionAddress; 457 private final int mCapabilities; 458 private final int mHighlightColor; 459 private final CharSequence mLabel; 460 private final CharSequence mShortDescription; 461 private final List<String> mSupportedUriSchemes; 462 private final int mSupportedAudioRoutes; 463 private final Icon mIcon; 464 private final Bundle mExtras; 465 private boolean mIsEnabled; 466 private String mGroupId; 467 468 @Override equals(Object o)469 public boolean equals(Object o) { 470 if (this == o) return true; 471 if (o == null || getClass() != o.getClass()) return false; 472 PhoneAccount that = (PhoneAccount) o; 473 return mCapabilities == that.mCapabilities && 474 mHighlightColor == that.mHighlightColor && 475 mSupportedAudioRoutes == that.mSupportedAudioRoutes && 476 mIsEnabled == that.mIsEnabled && 477 Objects.equals(mAccountHandle, that.mAccountHandle) && 478 Objects.equals(mAddress, that.mAddress) && 479 Objects.equals(mSubscriptionAddress, that.mSubscriptionAddress) && 480 Objects.equals(mLabel, that.mLabel) && 481 Objects.equals(mShortDescription, that.mShortDescription) && 482 Objects.equals(mSupportedUriSchemes, that.mSupportedUriSchemes) && 483 areBundlesEqual(mExtras, that.mExtras) && 484 Objects.equals(mGroupId, that.mGroupId); 485 } 486 487 @Override hashCode()488 public int hashCode() { 489 return Objects.hash(mAccountHandle, mAddress, mSubscriptionAddress, mCapabilities, 490 mHighlightColor, mLabel, mShortDescription, mSupportedUriSchemes, 491 mSupportedAudioRoutes, 492 mExtras, mIsEnabled, mGroupId); 493 } 494 495 /** 496 * Helper class for creating a {@link PhoneAccount}. 497 */ 498 public static class Builder { 499 500 private PhoneAccountHandle mAccountHandle; 501 private Uri mAddress; 502 private Uri mSubscriptionAddress; 503 private int mCapabilities; 504 private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL; 505 private int mHighlightColor = NO_HIGHLIGHT_COLOR; 506 private CharSequence mLabel; 507 private CharSequence mShortDescription; 508 private List<String> mSupportedUriSchemes = new ArrayList<String>(); 509 private Icon mIcon; 510 private Bundle mExtras; 511 private boolean mIsEnabled = false; 512 private String mGroupId = ""; 513 514 /** 515 * Creates a builder with the specified {@link PhoneAccountHandle} and label. 516 */ Builder(PhoneAccountHandle accountHandle, CharSequence label)517 public Builder(PhoneAccountHandle accountHandle, CharSequence label) { 518 this.mAccountHandle = accountHandle; 519 this.mLabel = label; 520 } 521 522 /** 523 * Creates an instance of the {@link PhoneAccount.Builder} from an existing 524 * {@link PhoneAccount}. 525 * 526 * @param phoneAccount The {@link PhoneAccount} used to initialize the builder. 527 */ Builder(PhoneAccount phoneAccount)528 public Builder(PhoneAccount phoneAccount) { 529 mAccountHandle = phoneAccount.getAccountHandle(); 530 mAddress = phoneAccount.getAddress(); 531 mSubscriptionAddress = phoneAccount.getSubscriptionAddress(); 532 mCapabilities = phoneAccount.getCapabilities(); 533 mHighlightColor = phoneAccount.getHighlightColor(); 534 mLabel = phoneAccount.getLabel(); 535 mShortDescription = phoneAccount.getShortDescription(); 536 mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes()); 537 mIcon = phoneAccount.getIcon(); 538 mIsEnabled = phoneAccount.isEnabled(); 539 mExtras = phoneAccount.getExtras(); 540 mGroupId = phoneAccount.getGroupId(); 541 mSupportedAudioRoutes = phoneAccount.getSupportedAudioRoutes(); 542 } 543 544 /** 545 * Sets the label. See {@link PhoneAccount#getLabel()}. 546 * 547 * @param label The label of the phone account. 548 * @return The builder. 549 * @hide 550 */ setLabel(CharSequence label)551 public Builder setLabel(CharSequence label) { 552 this.mLabel = label; 553 return this; 554 } 555 556 /** 557 * Sets the address. See {@link PhoneAccount#getAddress}. 558 * <p> 559 * Note: The entire URI value is limited to 256 characters. This check is 560 * enforced when registering the PhoneAccount via 561 * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an 562 * {@link IllegalArgumentException} to be thrown if URI is over 256. 563 * 564 * @param value The address of the phone account. 565 * @return The builder. 566 */ setAddress(Uri value)567 public Builder setAddress(Uri value) { 568 this.mAddress = value; 569 return this; 570 } 571 572 /** 573 * Sets the subscription address. See {@link PhoneAccount#getSubscriptionAddress}. 574 * 575 * @param value The subscription address. 576 * @return The builder. 577 */ setSubscriptionAddress(Uri value)578 public Builder setSubscriptionAddress(Uri value) { 579 this.mSubscriptionAddress = value; 580 return this; 581 } 582 583 /** 584 * Sets the capabilities. See {@link PhoneAccount#getCapabilities}. 585 * 586 * @param value The capabilities to set. 587 * @return The builder. 588 */ setCapabilities(int value)589 public Builder setCapabilities(int value) { 590 this.mCapabilities = value; 591 return this; 592 } 593 594 /** 595 * Sets the icon. See {@link PhoneAccount#getIcon}. 596 * <p> 597 * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. 598 * This check is enforced when registering the PhoneAccount via 599 * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} 600 * 601 * @param icon The icon to set. 602 */ setIcon(Icon icon)603 public Builder setIcon(Icon icon) { 604 mIcon = icon; 605 return this; 606 } 607 608 /** 609 * Sets the highlight color. See {@link PhoneAccount#getHighlightColor}. 610 * 611 * @param value The highlight color. 612 * @return The builder. 613 */ setHighlightColor(int value)614 public Builder setHighlightColor(int value) { 615 this.mHighlightColor = value; 616 return this; 617 } 618 619 /** 620 * Sets the short description. See {@link PhoneAccount#getShortDescription}. 621 * 622 * @param value The short description. 623 * @return The builder. 624 */ setShortDescription(CharSequence value)625 public Builder setShortDescription(CharSequence value) { 626 this.mShortDescription = value; 627 return this; 628 } 629 630 /** 631 * Specifies an additional URI scheme supported by the {@link PhoneAccount}. 632 * 633 * <p> 634 * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will 635 * cause an {@link IllegalArgumentException} to be thrown when the account is registered. 636 * 637 * @param uriScheme The URI scheme. 638 * @return The builder. 639 */ addSupportedUriScheme(String uriScheme)640 public Builder addSupportedUriScheme(String uriScheme) { 641 if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) { 642 this.mSupportedUriSchemes.add(uriScheme); 643 } 644 return this; 645 } 646 647 /** 648 * Specifies the URI schemes supported by the {@link PhoneAccount}. 649 * 650 * <p> 651 * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is 652 * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any 653 * scheme will cause an {@link IllegalArgumentException} to be thrown when the account 654 * is registered. 655 * 656 * @param uriSchemes The URI schemes. 657 * @return The builder. 658 */ setSupportedUriSchemes(List<String> uriSchemes)659 public Builder setSupportedUriSchemes(List<String> uriSchemes) { 660 mSupportedUriSchemes.clear(); 661 662 if (uriSchemes != null && !uriSchemes.isEmpty()) { 663 for (String uriScheme : uriSchemes) { 664 addSupportedUriScheme(uriScheme); 665 } 666 } 667 return this; 668 } 669 670 /** 671 * Specifies the extras associated with the {@link PhoneAccount}. 672 * <p> 673 * {@code PhoneAccount}s only support extra values of type: {@link String}, {@link Integer}, 674 * and {@link Boolean}. Extras which are not of these types are ignored. 675 * 676 * @param extras 677 * @return 678 */ setExtras(Bundle extras)679 public Builder setExtras(Bundle extras) { 680 mExtras = extras; 681 return this; 682 } 683 684 /** 685 * Sets the enabled state of the phone account. 686 * 687 * @param isEnabled The enabled state. 688 * @return The builder. 689 * @hide 690 */ setIsEnabled(boolean isEnabled)691 public Builder setIsEnabled(boolean isEnabled) { 692 mIsEnabled = isEnabled; 693 return this; 694 } 695 696 /** 697 * Sets the group Id of the {@link PhoneAccount}. When a new {@link PhoneAccount} is 698 * registered to Telecom, it will replace another {@link PhoneAccount} that is already 699 * registered in Telecom and take on the current user defaults and enabled status. There can 700 * only be one {@link PhoneAccount} with a non-empty group number registered to Telecom at a 701 * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only 702 * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced. 703 * <p> 704 * Note: This is an API specific to the Telephony stack; the group Id will be ignored for 705 * callers not holding the correct permission. 706 * 707 * @param groupId The group Id of the {@link PhoneAccount} that will replace any other 708 * registered {@link PhoneAccount} in Telecom with the same Group Id. 709 * @return The builder 710 * @hide 711 */ 712 @SystemApi 713 @RequiresPermission(MODIFY_PHONE_STATE) setGroupId(@onNull String groupId)714 public @NonNull Builder setGroupId(@NonNull String groupId) { 715 if (groupId != null) { 716 mGroupId = groupId; 717 } else { 718 mGroupId = ""; 719 } 720 return this; 721 } 722 723 /** 724 * Sets the audio routes supported by this {@link PhoneAccount}. 725 * 726 * @param routes bit mask of available routes. 727 * @return The builder. 728 * @hide 729 */ setSupportedAudioRoutes(int routes)730 public Builder setSupportedAudioRoutes(int routes) { 731 mSupportedAudioRoutes = routes; 732 return this; 733 } 734 735 /** 736 * Creates an instance of a {@link PhoneAccount} based on the current builder settings. 737 * 738 * @return The {@link PhoneAccount}. 739 */ build()740 public PhoneAccount build() { 741 // If no supported URI schemes were defined, assume "tel" is supported. 742 if (mSupportedUriSchemes.isEmpty()) { 743 addSupportedUriScheme(SCHEME_TEL); 744 } 745 746 return new PhoneAccount( 747 mAccountHandle, 748 mAddress, 749 mSubscriptionAddress, 750 mCapabilities, 751 mIcon, 752 mHighlightColor, 753 mLabel, 754 mShortDescription, 755 mSupportedUriSchemes, 756 mExtras, 757 mSupportedAudioRoutes, 758 mIsEnabled, 759 mGroupId); 760 } 761 } 762 PhoneAccount( PhoneAccountHandle account, Uri address, Uri subscriptionAddress, int capabilities, Icon icon, int highlightColor, CharSequence label, CharSequence shortDescription, List<String> supportedUriSchemes, Bundle extras, int supportedAudioRoutes, boolean isEnabled, String groupId)763 private PhoneAccount( 764 PhoneAccountHandle account, 765 Uri address, 766 Uri subscriptionAddress, 767 int capabilities, 768 Icon icon, 769 int highlightColor, 770 CharSequence label, 771 CharSequence shortDescription, 772 List<String> supportedUriSchemes, 773 Bundle extras, 774 int supportedAudioRoutes, 775 boolean isEnabled, 776 String groupId) { 777 mAccountHandle = account; 778 mAddress = address; 779 mSubscriptionAddress = subscriptionAddress; 780 mCapabilities = capabilities; 781 mIcon = icon; 782 mHighlightColor = highlightColor; 783 mLabel = label; 784 mShortDescription = shortDescription; 785 mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes); 786 mExtras = extras; 787 mSupportedAudioRoutes = supportedAudioRoutes; 788 mIsEnabled = isEnabled; 789 mGroupId = groupId; 790 } 791 builder( PhoneAccountHandle accountHandle, CharSequence label)792 public static Builder builder( 793 PhoneAccountHandle accountHandle, 794 CharSequence label) { 795 return new Builder(accountHandle, label); 796 } 797 798 /** 799 * Returns a builder initialized with the current {@link PhoneAccount} instance. 800 * 801 * @return The builder. 802 */ toBuilder()803 public Builder toBuilder() { return new Builder(this); } 804 805 /** 806 * The unique identifier of this {@code PhoneAccount}. 807 * 808 * @return A {@code PhoneAccountHandle}. 809 */ getAccountHandle()810 public PhoneAccountHandle getAccountHandle() { 811 return mAccountHandle; 812 } 813 814 /** 815 * The address (e.g., a phone number) associated with this {@code PhoneAccount}. This 816 * represents the destination from which outgoing calls using this {@code PhoneAccount} 817 * will appear to come, if applicable, and the destination to which incoming calls using this 818 * {@code PhoneAccount} may be addressed. 819 * 820 * @return A address expressed as a {@code Uri}, for example, a phone number. 821 */ getAddress()822 public Uri getAddress() { 823 return mAddress; 824 } 825 826 /** 827 * The raw callback number used for this {@code PhoneAccount}, as distinct from 828 * {@link #getAddress()}. For the majority of {@code PhoneAccount}s this should be registered 829 * as {@code null}. It is used by the system for SIM-based {@code PhoneAccount} registration 830 * where {@link android.telephony.TelephonyManager#setLine1NumberForDisplay(String, String)} 831 * has been used to alter the callback number. 832 * <p> 833 * 834 * @return The subscription number, suitable for display to the user. 835 */ getSubscriptionAddress()836 public Uri getSubscriptionAddress() { 837 return mSubscriptionAddress; 838 } 839 840 /** 841 * The capabilities of this {@code PhoneAccount}. 842 * 843 * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities. 844 */ getCapabilities()845 public int getCapabilities() { 846 return mCapabilities; 847 } 848 849 /** 850 * Determines if this {@code PhoneAccount} has a capabilities specified by the passed in 851 * bit mask. 852 * 853 * @param capability The capabilities to check. 854 * @return {@code true} if the phone account has the capability. 855 */ hasCapabilities(int capability)856 public boolean hasCapabilities(int capability) { 857 return (mCapabilities & capability) == capability; 858 } 859 860 /** 861 * Determines if this {@code PhoneAccount} has routes specified by the passed in bit mask. 862 * 863 * @param route The routes to check. 864 * @return {@code true} if the phone account has the routes. 865 * @hide 866 */ hasAudioRoutes(int routes)867 public boolean hasAudioRoutes(int routes) { 868 return (mSupportedAudioRoutes & routes) == routes; 869 } 870 871 /** 872 * A short label describing a {@code PhoneAccount}. 873 * 874 * @return A label for this {@code PhoneAccount}. 875 */ getLabel()876 public CharSequence getLabel() { 877 return mLabel; 878 } 879 880 /** 881 * A short paragraph describing this {@code PhoneAccount}. 882 * 883 * @return A description for this {@code PhoneAccount}. 884 */ getShortDescription()885 public CharSequence getShortDescription() { 886 return mShortDescription; 887 } 888 889 /** 890 * The URI schemes supported by this {@code PhoneAccount}. 891 * 892 * @return The URI schemes. 893 */ getSupportedUriSchemes()894 public List<String> getSupportedUriSchemes() { 895 return mSupportedUriSchemes; 896 } 897 898 /** 899 * The extras associated with this {@code PhoneAccount}. 900 * <p> 901 * A {@link ConnectionService} may provide implementation specific information about the 902 * {@link PhoneAccount} via the extras. 903 * 904 * @return The extras. 905 */ getExtras()906 public Bundle getExtras() { 907 return mExtras; 908 } 909 910 /** 911 * The audio routes supported by this {@code PhoneAccount}. 912 * 913 * @hide 914 */ getSupportedAudioRoutes()915 public int getSupportedAudioRoutes() { 916 return mSupportedAudioRoutes; 917 } 918 919 /** 920 * The icon to represent this {@code PhoneAccount}. 921 * 922 * @return The icon. 923 */ getIcon()924 public Icon getIcon() { 925 return mIcon; 926 } 927 928 /** 929 * Indicates whether the user has enabled this {@code PhoneAccount} or not. This value is only 930 * populated for {@code PhoneAccount}s returned by {@link TelecomManager#getPhoneAccount}. 931 * 932 * @return {@code true} if the account is enabled by the user, {@code false} otherwise. 933 */ isEnabled()934 public boolean isEnabled() { 935 return mIsEnabled; 936 } 937 938 /** 939 * A non-empty {@link String} representing the group that A {@link PhoneAccount} is in or an 940 * empty {@link String} if the {@link PhoneAccount} is not in a group. If this 941 * {@link PhoneAccount} is in a group, this new {@link PhoneAccount} will replace a registered 942 * {@link PhoneAccount} that is in the same group. When the {@link PhoneAccount} is replaced, 943 * its user defined defaults and enabled status will also pass to this new {@link PhoneAccount}. 944 * Only {@link PhoneAccount}s that share the same {@link ConnectionService} can be replaced. 945 * 946 * @return A non-empty String Id if this {@link PhoneAccount} belongs to a group. 947 * @hide 948 */ getGroupId()949 public String getGroupId() { 950 return mGroupId; 951 } 952 953 /** 954 * Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI 955 * scheme. 956 * 957 * @param uriScheme The URI scheme to check. 958 * @return {@code true} if the {@code PhoneAccount} supports calls to/from addresses with the 959 * specified URI scheme. 960 */ supportsUriScheme(String uriScheme)961 public boolean supportsUriScheme(String uriScheme) { 962 if (mSupportedUriSchemes == null || uriScheme == null) { 963 return false; 964 } 965 966 for (String scheme : mSupportedUriSchemes) { 967 if (scheme != null && scheme.equals(uriScheme)) { 968 return true; 969 } 970 } 971 return false; 972 } 973 974 /** 975 * A highlight color to use in displaying information about this {@code PhoneAccount}. 976 * 977 * @return A hexadecimal color value. 978 */ getHighlightColor()979 public int getHighlightColor() { 980 return mHighlightColor; 981 } 982 983 /** 984 * Sets the enabled state of the phone account. 985 * @hide 986 */ setIsEnabled(boolean isEnabled)987 public void setIsEnabled(boolean isEnabled) { 988 mIsEnabled = isEnabled; 989 } 990 991 /** 992 * @return {@code true} if the {@link PhoneAccount} is self-managed, {@code false} otherwise. 993 * @hide 994 */ isSelfManaged()995 public boolean isSelfManaged() { 996 return (mCapabilities & CAPABILITY_SELF_MANAGED) == CAPABILITY_SELF_MANAGED; 997 } 998 999 // 1000 // Parcelable implementation 1001 // 1002 1003 @Override describeContents()1004 public int describeContents() { 1005 return 0; 1006 } 1007 1008 @Override writeToParcel(Parcel out, int flags)1009 public void writeToParcel(Parcel out, int flags) { 1010 if (mAccountHandle == null) { 1011 out.writeInt(0); 1012 } else { 1013 out.writeInt(1); 1014 mAccountHandle.writeToParcel(out, flags); 1015 } 1016 if (mAddress == null) { 1017 out.writeInt(0); 1018 } else { 1019 out.writeInt(1); 1020 mAddress.writeToParcel(out, flags); 1021 } 1022 if (mSubscriptionAddress == null) { 1023 out.writeInt(0); 1024 } else { 1025 out.writeInt(1); 1026 mSubscriptionAddress.writeToParcel(out, flags); 1027 } 1028 out.writeInt(mCapabilities); 1029 out.writeInt(mHighlightColor); 1030 out.writeCharSequence(mLabel); 1031 out.writeCharSequence(mShortDescription); 1032 out.writeStringList(mSupportedUriSchemes); 1033 1034 if (mIcon == null) { 1035 out.writeInt(0); 1036 } else { 1037 out.writeInt(1); 1038 mIcon.writeToParcel(out, flags); 1039 } 1040 out.writeByte((byte) (mIsEnabled ? 1 : 0)); 1041 out.writeBundle(mExtras); 1042 out.writeString(mGroupId); 1043 out.writeInt(mSupportedAudioRoutes); 1044 } 1045 1046 public static final @android.annotation.NonNull Creator<PhoneAccount> CREATOR 1047 = new Creator<PhoneAccount>() { 1048 @Override 1049 public PhoneAccount createFromParcel(Parcel in) { 1050 return new PhoneAccount(in); 1051 } 1052 1053 @Override 1054 public PhoneAccount[] newArray(int size) { 1055 return new PhoneAccount[size]; 1056 } 1057 }; 1058 PhoneAccount(Parcel in)1059 private PhoneAccount(Parcel in) { 1060 if (in.readInt() > 0) { 1061 mAccountHandle = PhoneAccountHandle.CREATOR.createFromParcel(in); 1062 } else { 1063 mAccountHandle = null; 1064 } 1065 if (in.readInt() > 0) { 1066 mAddress = Uri.CREATOR.createFromParcel(in); 1067 } else { 1068 mAddress = null; 1069 } 1070 if (in.readInt() > 0) { 1071 mSubscriptionAddress = Uri.CREATOR.createFromParcel(in); 1072 } else { 1073 mSubscriptionAddress = null; 1074 } 1075 mCapabilities = in.readInt(); 1076 mHighlightColor = in.readInt(); 1077 mLabel = in.readCharSequence(); 1078 mShortDescription = in.readCharSequence(); 1079 mSupportedUriSchemes = Collections.unmodifiableList(in.createStringArrayList()); 1080 if (in.readInt() > 0) { 1081 mIcon = Icon.CREATOR.createFromParcel(in); 1082 } else { 1083 mIcon = null; 1084 } 1085 mIsEnabled = in.readByte() == 1; 1086 mExtras = in.readBundle(); 1087 mGroupId = in.readString(); 1088 mSupportedAudioRoutes = in.readInt(); 1089 } 1090 1091 @Override toString()1092 public String toString() { 1093 StringBuilder sb = new StringBuilder().append("[[") 1094 .append(mIsEnabled ? 'X' : ' ') 1095 .append("] PhoneAccount: ") 1096 .append(mAccountHandle) 1097 .append(" Capabilities: ") 1098 .append(capabilitiesToString()) 1099 .append(" Audio Routes: ") 1100 .append(audioRoutesToString()) 1101 .append(" Schemes: "); 1102 for (String scheme : mSupportedUriSchemes) { 1103 sb.append(scheme) 1104 .append(" "); 1105 } 1106 sb.append(" Extras: "); 1107 sb.append(mExtras); 1108 sb.append(" GroupId: "); 1109 sb.append(Log.pii(mGroupId)); 1110 sb.append("]"); 1111 return sb.toString(); 1112 } 1113 1114 /** 1115 * Generates a string representation of a capabilities bitmask. 1116 * 1117 * @return String representation of the capabilities bitmask. 1118 * @hide 1119 */ capabilitiesToString()1120 public String capabilitiesToString() { 1121 StringBuilder sb = new StringBuilder(); 1122 if (hasCapabilities(CAPABILITY_SELF_MANAGED)) { 1123 sb.append("SelfManaged "); 1124 } 1125 if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) { 1126 sb.append("SuppVideo "); 1127 } 1128 if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) { 1129 sb.append("Video "); 1130 } 1131 if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) { 1132 sb.append("Presence "); 1133 } 1134 if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) { 1135 sb.append("CallProvider "); 1136 } 1137 if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) { 1138 sb.append("CallSubject "); 1139 } 1140 if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) { 1141 sb.append("ConnectionMgr "); 1142 } 1143 if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) { 1144 sb.append("EmergOnly "); 1145 } 1146 if (hasCapabilities(CAPABILITY_MULTI_USER)) { 1147 sb.append("MultiUser "); 1148 } 1149 if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) { 1150 sb.append("PlaceEmerg "); 1151 } 1152 if (hasCapabilities(CAPABILITY_EMERGENCY_PREFERRED)) { 1153 sb.append("EmerPrefer "); 1154 } 1155 if (hasCapabilities(CAPABILITY_EMERGENCY_VIDEO_CALLING)) { 1156 sb.append("EmergVideo "); 1157 } 1158 if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) { 1159 sb.append("SimSub "); 1160 } 1161 if (hasCapabilities(CAPABILITY_RTT)) { 1162 sb.append("Rtt "); 1163 } 1164 if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) { 1165 sb.append("AdhocConf "); 1166 } 1167 if (hasCapabilities(CAPABILITY_CALL_COMPOSER)) { 1168 sb.append("CallComposer "); 1169 } 1170 if (hasCapabilities(CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS)) { 1171 sb.append("SuppVoice "); 1172 } 1173 if (hasCapabilities(CAPABILITY_VOICE_CALLING_AVAILABLE)) { 1174 sb.append("Voice "); 1175 } 1176 return sb.toString(); 1177 } 1178 audioRoutesToString()1179 private String audioRoutesToString() { 1180 StringBuilder sb = new StringBuilder(); 1181 1182 if (hasAudioRoutes(CallAudioState.ROUTE_BLUETOOTH)) { 1183 sb.append("B"); 1184 } 1185 if (hasAudioRoutes(CallAudioState.ROUTE_EARPIECE)) { 1186 sb.append("E"); 1187 } 1188 if (hasAudioRoutes(CallAudioState.ROUTE_SPEAKER)) { 1189 sb.append("S"); 1190 } 1191 if (hasAudioRoutes(CallAudioState.ROUTE_WIRED_HEADSET)) { 1192 sb.append("W"); 1193 } 1194 1195 return sb.toString(); 1196 } 1197 1198 /** 1199 * Determines if two {@link Bundle}s are equal. 1200 * @param extras First {@link Bundle} to check. 1201 * @param newExtras {@link Bundle} to compare against. 1202 * @return {@code true} if the {@link Bundle}s are equal, {@code false} otherwise. 1203 */ areBundlesEqual(Bundle extras, Bundle newExtras)1204 private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 1205 if (extras == null || newExtras == null) { 1206 return extras == newExtras; 1207 } 1208 1209 if (extras.size() != newExtras.size()) { 1210 return false; 1211 } 1212 1213 for(String key : extras.keySet()) { 1214 if (key != null) { 1215 final Object value = extras.get(key); 1216 final Object newValue = newExtras.get(key); 1217 if (!Objects.equals(value, newValue)) { 1218 return false; 1219 } 1220 } 1221 } 1222 return true; 1223 } 1224 } 1225