Call.java revision 876dbfb4767da4a2ba5459d5b78fb6eb55e4516f
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 17package android.telecom; 18 19import android.annotation.SystemApi; 20import android.net.Uri; 21import android.os.Bundle; 22import android.os.Handler; 23 24import java.lang.String; 25import java.util.ArrayList; 26import java.util.Collections; 27import java.util.List; 28import java.util.Map; 29import java.util.Objects; 30import java.util.concurrent.CopyOnWriteArrayList; 31 32/** 33 * Represents an ongoing phone call that the in-call app should present to the user. 34 */ 35public final class Call { 36 /** 37 * The state of a {@code Call} when newly created. 38 */ 39 public static final int STATE_NEW = 0; 40 41 /** 42 * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected. 43 */ 44 public static final int STATE_DIALING = 1; 45 46 /** 47 * The state of an incoming {@code Call} when ringing locally, but not yet connected. 48 */ 49 public static final int STATE_RINGING = 2; 50 51 /** 52 * The state of a {@code Call} when in a holding state. 53 */ 54 public static final int STATE_HOLDING = 3; 55 56 /** 57 * The state of a {@code Call} when actively supporting conversation. 58 */ 59 public static final int STATE_ACTIVE = 4; 60 61 /** 62 * The state of a {@code Call} when no further voice or other communication is being 63 * transmitted, the remote side has been or will inevitably be informed that the {@code Call} 64 * is no longer active, and the local data transport has or inevitably will release resources 65 * associated with this {@code Call}. 66 */ 67 public static final int STATE_DISCONNECTED = 7; 68 69 /** 70 * The state of an outgoing {@code Call} when waiting on user to select a 71 * {@link PhoneAccount} through which to place the call. 72 */ 73 public static final int STATE_SELECT_PHONE_ACCOUNT = 8; 74 75 /** 76 * @hide 77 * @deprecated use STATE_SELECT_PHONE_ACCOUNT. 78 */ 79 @Deprecated 80 @SystemApi 81 public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT; 82 83 /** 84 * The initial state of an outgoing {@code Call}. 85 * Common transitions are to {@link #STATE_DIALING} state for a successful call or 86 * {@link #STATE_DISCONNECTED} if it failed. 87 */ 88 public static final int STATE_CONNECTING = 9; 89 90 /** 91 * The state of a {@code Call} when the user has initiated a disconnection of the call, but the 92 * call has not yet been disconnected by the underlying {@code ConnectionService}. The next 93 * state of the call is (potentially) {@link #STATE_DISCONNECTED}. 94 */ 95 public static final int STATE_DISCONNECTING = 10; 96 97 /** 98 * The state of an external call which is in the process of being pulled from a remote device to 99 * the local device. 100 * <p> 101 * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property 102 * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call. 103 * <p> 104 * An {@link InCallService} will only see this state if it has the 105 * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its 106 * manifest. 107 */ 108 public static final int STATE_PULLING_CALL = 11; 109 110 /** 111 * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call 112 * extras. Used to pass the phone accounts to display on the front end to the user in order to 113 * select phone accounts to (for example) place a call. 114 */ 115 public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; 116 117 public static class Details { 118 119 /** Call can currently be put on hold or unheld. */ 120 public static final int CAPABILITY_HOLD = 0x00000001; 121 122 /** Call supports the hold feature. */ 123 public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002; 124 125 /** 126 * Calls within a conference can be merged. A {@link ConnectionService} has the option to 127 * add a {@link Conference} call before the child {@link Connection}s are merged. This is how 128 * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this 129 * capability allows a merge button to be shown while the conference call is in the foreground 130 * of the in-call UI. 131 * <p> 132 * This is only intended for use by a {@link Conference}. 133 */ 134 public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004; 135 136 /** 137 * Calls within a conference can be swapped between foreground and background. 138 * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information. 139 * <p> 140 * This is only intended for use by a {@link Conference}. 141 */ 142 public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008; 143 144 /** 145 * @hide 146 */ 147 public static final int CAPABILITY_UNUSED_1 = 0x00000010; 148 149 /** Call supports responding via text option. */ 150 public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020; 151 152 /** Call can be muted. */ 153 public static final int CAPABILITY_MUTE = 0x00000040; 154 155 /** 156 * Call supports conference call management. This capability only applies to {@link Conference} 157 * calls which can have {@link Connection}s as children. 158 */ 159 public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080; 160 161 /** 162 * Local device supports receiving video. 163 */ 164 public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100; 165 166 /** 167 * Local device supports transmitting video. 168 */ 169 public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200; 170 171 /** 172 * Local device supports bidirectional video calling. 173 */ 174 public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 175 CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX; 176 177 /** 178 * Remote device supports receiving video. 179 */ 180 public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400; 181 182 /** 183 * Remote device supports transmitting video. 184 */ 185 public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800; 186 187 /** 188 * Remote device supports bidirectional video calling. 189 */ 190 public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 191 CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX; 192 193 /** 194 * Call is able to be separated from its parent {@code Conference}, if any. 195 */ 196 public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000; 197 198 /** 199 * Call is able to be individually disconnected when in a {@code Conference}. 200 */ 201 public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000; 202 203 /** 204 * Speed up audio setup for MT call. 205 * @hide 206 */ 207 public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000; 208 209 /** 210 * Call can be upgraded to a video call. 211 * @hide 212 */ 213 public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000; 214 215 /** 216 * For video calls, indicates whether the outgoing video for the call can be paused using 217 * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 218 */ 219 public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000; 220 221 /** 222 * Call sends responses through connection. 223 * @hide 224 */ 225 public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000; 226 227 /** 228 * When set, prevents a video {@code Call} from being downgraded to an audio-only call. 229 * <p> 230 * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or 231 * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be 232 * downgraded from a video call back to a VideoState of 233 * {@link VideoProfile#STATE_AUDIO_ONLY}. 234 * <p> 235 * Intuitively, a call which can be downgraded to audio should also have local and remote 236 * video 237 * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and 238 * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}). 239 */ 240 public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000; 241 242 /** 243 * When set for an external call, indicates that this {@code Call} can be pulled from a 244 * remote device to the current device. 245 * <p> 246 * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set. 247 * <p> 248 * An {@link InCallService} will only see calls with this capability if it has the 249 * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} 250 * in its manifest. 251 * <p> 252 * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and 253 * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}. 254 */ 255 public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000; 256 257 //****************************************************************************************** 258 // Next CAPABILITY value: 0x01000000 259 //****************************************************************************************** 260 261 /** 262 * Whether the call is currently a conference. 263 */ 264 public static final int PROPERTY_CONFERENCE = 0x00000001; 265 266 /** 267 * Whether the call is a generic conference, where we do not know the precise state of 268 * participants in the conference (eg. on CDMA). 269 */ 270 public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002; 271 272 /** 273 * Whether the call is made while the device is in emergency callback mode. 274 */ 275 public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004; 276 277 /** 278 * Connection is using WIFI. 279 */ 280 public static final int PROPERTY_WIFI = 0x00000008; 281 282 /** 283 * Call is using high definition audio. 284 */ 285 public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010; 286 287 /** 288 * Whether the call is associated with the work profile. 289 */ 290 public static final int PROPERTY_WORK_CALL = 0x00000020; 291 292 /** 293 * When set, indicates that this {@code Call} does not actually exist locally for the 294 * {@link ConnectionService}. 295 * <p> 296 * Consider, for example, a scenario where a user has two phones with the same phone number. 297 * When a user places a call on one device, the telephony stack can represent that call on 298 * the other device by adding it to the {@link ConnectionService} with the 299 * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set. 300 * <p> 301 * An {@link InCallService} will only see calls with this property if it has the 302 * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} 303 * in its manifest. 304 * <p> 305 * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}. 306 */ 307 public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040; 308 309 //****************************************************************************************** 310 // Next PROPERTY value: 0x00000100 311 //****************************************************************************************** 312 313 private final String mTelecomCallId; 314 private final Uri mHandle; 315 private final int mHandlePresentation; 316 private final String mCallerDisplayName; 317 private final int mCallerDisplayNamePresentation; 318 private final PhoneAccountHandle mAccountHandle; 319 private final int mCallCapabilities; 320 private final int mCallProperties; 321 private final DisconnectCause mDisconnectCause; 322 private final long mConnectTimeMillis; 323 private final GatewayInfo mGatewayInfo; 324 private final int mVideoState; 325 private final StatusHints mStatusHints; 326 private final Bundle mExtras; 327 private final Bundle mIntentExtras; 328 329 /** 330 * Whether the supplied capabilities supports the specified capability. 331 * 332 * @param capabilities A bit field of capabilities. 333 * @param capability The capability to check capabilities for. 334 * @return Whether the specified capability is supported. 335 */ 336 public static boolean can(int capabilities, int capability) { 337 return (capabilities & capability) == capability; 338 } 339 340 /** 341 * Whether the capabilities of this {@code Details} supports the specified capability. 342 * 343 * @param capability The capability to check capabilities for. 344 * @return Whether the specified capability is supported. 345 */ 346 public boolean can(int capability) { 347 return can(mCallCapabilities, capability); 348 } 349 350 /** 351 * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string. 352 * 353 * @param capabilities A capability bit field. 354 * @return A human readable string representation. 355 */ 356 public static String capabilitiesToString(int capabilities) { 357 StringBuilder builder = new StringBuilder(); 358 builder.append("[Capabilities:"); 359 if (can(capabilities, CAPABILITY_HOLD)) { 360 builder.append(" CAPABILITY_HOLD"); 361 } 362 if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) { 363 builder.append(" CAPABILITY_SUPPORT_HOLD"); 364 } 365 if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) { 366 builder.append(" CAPABILITY_MERGE_CONFERENCE"); 367 } 368 if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) { 369 builder.append(" CAPABILITY_SWAP_CONFERENCE"); 370 } 371 if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) { 372 builder.append(" CAPABILITY_RESPOND_VIA_TEXT"); 373 } 374 if (can(capabilities, CAPABILITY_MUTE)) { 375 builder.append(" CAPABILITY_MUTE"); 376 } 377 if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) { 378 builder.append(" CAPABILITY_MANAGE_CONFERENCE"); 379 } 380 if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) { 381 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX"); 382 } 383 if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) { 384 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX"); 385 } 386 if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) { 387 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL"); 388 } 389 if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) { 390 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX"); 391 } 392 if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) { 393 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX"); 394 } 395 if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) { 396 builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO"); 397 } 398 if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { 399 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL"); 400 } 401 if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) { 402 builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO"); 403 } 404 if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) { 405 builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO"); 406 } 407 if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) { 408 builder.append(" CAPABILITY_CAN_PAUSE_VIDEO"); 409 } 410 if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) { 411 builder.append(" CAPABILITY_CAN_PULL_CALL"); 412 } 413 builder.append("]"); 414 return builder.toString(); 415 } 416 417 /** 418 * Whether the supplied properties includes the specified property. 419 * 420 * @param properties A bit field of properties. 421 * @param property The property to check properties for. 422 * @return Whether the specified property is supported. 423 */ 424 public static boolean hasProperty(int properties, int property) { 425 return (properties & property) == property; 426 } 427 428 /** 429 * Whether the properties of this {@code Details} includes the specified property. 430 * 431 * @param property The property to check properties for. 432 * @return Whether the specified property is supported. 433 */ 434 public boolean hasProperty(int property) { 435 return hasProperty(mCallProperties, property); 436 } 437 438 /** 439 * Render a set of property bits ({@code PROPERTY_*}) as a human readable string. 440 * 441 * @param properties A property bit field. 442 * @return A human readable string representation. 443 */ 444 public static String propertiesToString(int properties) { 445 StringBuilder builder = new StringBuilder(); 446 builder.append("[Properties:"); 447 if (hasProperty(properties, PROPERTY_CONFERENCE)) { 448 builder.append(" PROPERTY_CONFERENCE"); 449 } 450 if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) { 451 builder.append(" PROPERTY_GENERIC_CONFERENCE"); 452 } 453 if (hasProperty(properties, PROPERTY_WIFI)) { 454 builder.append(" PROPERTY_WIFI"); 455 } 456 if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) { 457 builder.append(" PROPERTY_HIGH_DEF_AUDIO"); 458 } 459 if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) { 460 builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE"); 461 } 462 if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) { 463 builder.append(" PROPERTY_IS_EXTERNAL_CALL"); 464 } 465 builder.append("]"); 466 return builder.toString(); 467 } 468 469 /** {@hide} */ 470 public String getTelecomCallId() { 471 return mTelecomCallId; 472 } 473 474 /** 475 * @return The handle (e.g., phone number) to which the {@code Call} is currently 476 * connected. 477 */ 478 public Uri getHandle() { 479 return mHandle; 480 } 481 482 /** 483 * @return The presentation requirements for the handle. See 484 * {@link TelecomManager} for valid values. 485 */ 486 public int getHandlePresentation() { 487 return mHandlePresentation; 488 } 489 490 /** 491 * @return The display name for the caller. 492 */ 493 public String getCallerDisplayName() { 494 return mCallerDisplayName; 495 } 496 497 /** 498 * @return The presentation requirements for the caller display name. See 499 * {@link TelecomManager} for valid values. 500 */ 501 public int getCallerDisplayNamePresentation() { 502 return mCallerDisplayNamePresentation; 503 } 504 505 /** 506 * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being 507 * routed. 508 */ 509 public PhoneAccountHandle getAccountHandle() { 510 return mAccountHandle; 511 } 512 513 /** 514 * @return A bitmask of the capabilities of the {@code Call}, as defined by the various 515 * {@code CAPABILITY_*} constants in this class. 516 */ 517 public int getCallCapabilities() { 518 return mCallCapabilities; 519 } 520 521 /** 522 * @return A bitmask of the properties of the {@code Call}, as defined by the various 523 * {@code PROPERTY_*} constants in this class. 524 */ 525 public int getCallProperties() { 526 return mCallProperties; 527 } 528 529 /** 530 * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed 531 * by {@link android.telecom.DisconnectCause}. 532 */ 533 public DisconnectCause getDisconnectCause() { 534 return mDisconnectCause; 535 } 536 537 /** 538 * @return The time the {@code Call} has been connected. This information is updated 539 * periodically, but user interfaces should not rely on this to display any "call time 540 * clock". 541 */ 542 public final long getConnectTimeMillis() { 543 return mConnectTimeMillis; 544 } 545 546 /** 547 * @return Information about any calling gateway the {@code Call} may be using. 548 */ 549 public GatewayInfo getGatewayInfo() { 550 return mGatewayInfo; 551 } 552 553 /** 554 * @return The video state of the {@code Call}. 555 */ 556 public int getVideoState() { 557 return mVideoState; 558 } 559 560 /** 561 * @return The current {@link android.telecom.StatusHints}, or {@code null} if none 562 * have been set. 563 */ 564 public StatusHints getStatusHints() { 565 return mStatusHints; 566 } 567 568 /** 569 * @return The extras associated with this call. 570 */ 571 public Bundle getExtras() { 572 return mExtras; 573 } 574 575 /** 576 * @return The extras used with the original intent to place this call. 577 */ 578 public Bundle getIntentExtras() { 579 return mIntentExtras; 580 } 581 582 @Override 583 public boolean equals(Object o) { 584 if (o instanceof Details) { 585 Details d = (Details) o; 586 return 587 Objects.equals(mHandle, d.mHandle) && 588 Objects.equals(mHandlePresentation, d.mHandlePresentation) && 589 Objects.equals(mCallerDisplayName, d.mCallerDisplayName) && 590 Objects.equals(mCallerDisplayNamePresentation, 591 d.mCallerDisplayNamePresentation) && 592 Objects.equals(mAccountHandle, d.mAccountHandle) && 593 Objects.equals(mCallCapabilities, d.mCallCapabilities) && 594 Objects.equals(mCallProperties, d.mCallProperties) && 595 Objects.equals(mDisconnectCause, d.mDisconnectCause) && 596 Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && 597 Objects.equals(mGatewayInfo, d.mGatewayInfo) && 598 Objects.equals(mVideoState, d.mVideoState) && 599 Objects.equals(mStatusHints, d.mStatusHints) && 600 areBundlesEqual(mExtras, d.mExtras) && 601 areBundlesEqual(mIntentExtras, d.mIntentExtras); 602 } 603 return false; 604 } 605 606 @Override 607 public int hashCode() { 608 return 609 Objects.hashCode(mHandle) + 610 Objects.hashCode(mHandlePresentation) + 611 Objects.hashCode(mCallerDisplayName) + 612 Objects.hashCode(mCallerDisplayNamePresentation) + 613 Objects.hashCode(mAccountHandle) + 614 Objects.hashCode(mCallCapabilities) + 615 Objects.hashCode(mCallProperties) + 616 Objects.hashCode(mDisconnectCause) + 617 Objects.hashCode(mConnectTimeMillis) + 618 Objects.hashCode(mGatewayInfo) + 619 Objects.hashCode(mVideoState) + 620 Objects.hashCode(mStatusHints) + 621 Objects.hashCode(mExtras) + 622 Objects.hashCode(mIntentExtras); 623 } 624 625 /** {@hide} */ 626 public Details( 627 String telecomCallId, 628 Uri handle, 629 int handlePresentation, 630 String callerDisplayName, 631 int callerDisplayNamePresentation, 632 PhoneAccountHandle accountHandle, 633 int capabilities, 634 int properties, 635 DisconnectCause disconnectCause, 636 long connectTimeMillis, 637 GatewayInfo gatewayInfo, 638 int videoState, 639 StatusHints statusHints, 640 Bundle extras, 641 Bundle intentExtras) { 642 mTelecomCallId = telecomCallId; 643 mHandle = handle; 644 mHandlePresentation = handlePresentation; 645 mCallerDisplayName = callerDisplayName; 646 mCallerDisplayNamePresentation = callerDisplayNamePresentation; 647 mAccountHandle = accountHandle; 648 mCallCapabilities = capabilities; 649 mCallProperties = properties; 650 mDisconnectCause = disconnectCause; 651 mConnectTimeMillis = connectTimeMillis; 652 mGatewayInfo = gatewayInfo; 653 mVideoState = videoState; 654 mStatusHints = statusHints; 655 mExtras = extras; 656 mIntentExtras = intentExtras; 657 } 658 659 /** {@hide} */ 660 public static Details createFromParcelableCall(ParcelableCall parcelableCall) { 661 return new Details( 662 parcelableCall.getId(), 663 parcelableCall.getHandle(), 664 parcelableCall.getHandlePresentation(), 665 parcelableCall.getCallerDisplayName(), 666 parcelableCall.getCallerDisplayNamePresentation(), 667 parcelableCall.getAccountHandle(), 668 parcelableCall.getCapabilities(), 669 parcelableCall.getProperties(), 670 parcelableCall.getDisconnectCause(), 671 parcelableCall.getConnectTimeMillis(), 672 parcelableCall.getGatewayInfo(), 673 parcelableCall.getVideoState(), 674 parcelableCall.getStatusHints(), 675 parcelableCall.getExtras(), 676 parcelableCall.getIntentExtras()); 677 } 678 679 @Override 680 public String toString() { 681 StringBuilder sb = new StringBuilder(); 682 sb.append("[pa: "); 683 sb.append(mAccountHandle); 684 sb.append(", hdl: "); 685 sb.append(Log.pii(mHandle)); 686 sb.append(", caps: "); 687 sb.append(capabilitiesToString(mCallCapabilities)); 688 sb.append(", props: "); 689 sb.append(mCallProperties); 690 sb.append("]"); 691 return sb.toString(); 692 } 693 } 694 695 public static abstract class Callback { 696 /** 697 * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. 698 * 699 * @param call The {@code Call} invoking this method. 700 * @param state The new state of the {@code Call}. 701 */ 702 public void onStateChanged(Call call, int state) {} 703 704 /** 705 * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}. 706 * 707 * @param call The {@code Call} invoking this method. 708 * @param parent The new parent of the {@code Call}. 709 */ 710 public void onParentChanged(Call call, Call parent) {} 711 712 /** 713 * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}. 714 * 715 * @param call The {@code Call} invoking this method. 716 * @param children The new children of the {@code Call}. 717 */ 718 public void onChildrenChanged(Call call, List<Call> children) {} 719 720 /** 721 * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}. 722 * 723 * @param call The {@code Call} invoking this method. 724 * @param details A {@code Details} object describing the {@code Call}. 725 */ 726 public void onDetailsChanged(Call call, Details details) {} 727 728 /** 729 * Invoked when the text messages that can be used as responses to the incoming 730 * {@code Call} are loaded from the relevant database. 731 * See {@link #getCannedTextResponses()}. 732 * 733 * @param call The {@code Call} invoking this method. 734 * @param cannedTextResponses The text messages useable as responses. 735 */ 736 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {} 737 738 /** 739 * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause 740 * character. This causes the post-dial signals to stop pending user confirmation. An 741 * implementation should present this choice to the user and invoke 742 * {@link #postDialContinue(boolean)} when the user makes the choice. 743 * 744 * @param call The {@code Call} invoking this method. 745 * @param remainingPostDialSequence The post-dial characters that remain to be sent. 746 */ 747 public void onPostDialWait(Call call, String remainingPostDialSequence) {} 748 749 /** 750 * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed. 751 * 752 * @param call The {@code Call} invoking this method. 753 * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}. 754 */ 755 public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {} 756 757 /** 758 * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning 759 * up their UI for the {@code Call} in response to state transitions. Specifically, 760 * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of 761 * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather, 762 * clients should wait for this method to be invoked. 763 * 764 * @param call The {@code Call} being destroyed. 765 */ 766 public void onCallDestroyed(Call call) {} 767 768 /** 769 * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be 770 * conferenced. 771 * 772 * @param call The {@code Call} being updated. 773 * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be 774 * conferenced. 775 */ 776 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {} 777 778 /** 779 * Invoked when a call receives an event from its associated {@link Connection}. 780 * <p> 781 * See {@link Connection#sendConnectionEvent(String, Bundle)}. 782 * 783 * @param call The {@code Call} receiving the event. 784 * @param event The event. 785 * @param extras Extras associated with the connection event. 786 */ 787 public void onConnectionEvent(Call call, String event, Bundle extras) {} 788 } 789 790 /** 791 * @deprecated Use {@code Call.Callback} instead. 792 * @hide 793 */ 794 @Deprecated 795 @SystemApi 796 public static abstract class Listener extends Callback { } 797 798 private final Phone mPhone; 799 private final String mTelecomCallId; 800 private final InCallAdapter mInCallAdapter; 801 private final List<String> mChildrenIds = new ArrayList<>(); 802 private final List<Call> mChildren = new ArrayList<>(); 803 private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren); 804 private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>(); 805 private final List<Call> mConferenceableCalls = new ArrayList<>(); 806 private final List<Call> mUnmodifiableConferenceableCalls = 807 Collections.unmodifiableList(mConferenceableCalls); 808 809 private boolean mChildrenCached; 810 private String mParentId = null; 811 private int mState; 812 private List<String> mCannedTextResponses = null; 813 private String mRemainingPostDialSequence; 814 private VideoCallImpl mVideoCallImpl; 815 private Details mDetails; 816 817 /** 818 * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any. 819 * 820 * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence 821 * remaining or this {@code Call} is not in a post-dial state. 822 */ 823 public String getRemainingPostDialSequence() { 824 return mRemainingPostDialSequence; 825 } 826 827 /** 828 * Instructs this {@link #STATE_RINGING} {@code Call} to answer. 829 * @param videoState The video state in which to answer the call. 830 */ 831 public void answer(int videoState) { 832 mInCallAdapter.answerCall(mTelecomCallId, videoState); 833 } 834 835 /** 836 * Instructs this {@link #STATE_RINGING} {@code Call} to reject. 837 * 838 * @param rejectWithMessage Whether to reject with a text message. 839 * @param textMessage An optional text message with which to respond. 840 */ 841 public void reject(boolean rejectWithMessage, String textMessage) { 842 mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage); 843 } 844 845 /** 846 * Instructs this {@code Call} to disconnect. 847 */ 848 public void disconnect() { 849 mInCallAdapter.disconnectCall(mTelecomCallId); 850 } 851 852 /** 853 * Instructs this {@code Call} to go on hold. 854 */ 855 public void hold() { 856 mInCallAdapter.holdCall(mTelecomCallId); 857 } 858 859 /** 860 * Instructs this {@link #STATE_HOLDING} call to release from hold. 861 */ 862 public void unhold() { 863 mInCallAdapter.unholdCall(mTelecomCallId); 864 } 865 866 /** 867 * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone. 868 * 869 * Any other currently playing DTMF tone in the specified call is immediately stopped. 870 * 871 * @param digit A character representing the DTMF digit for which to play the tone. This 872 * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. 873 */ 874 public void playDtmfTone(char digit) { 875 mInCallAdapter.playDtmfTone(mTelecomCallId, digit); 876 } 877 878 /** 879 * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone 880 * currently playing. 881 * 882 * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is 883 * currently playing, this method will do nothing. 884 */ 885 public void stopDtmfTone() { 886 mInCallAdapter.stopDtmfTone(mTelecomCallId); 887 } 888 889 /** 890 * Instructs this {@code Call} to continue playing a post-dial DTMF string. 891 * 892 * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, 893 * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. 894 * 895 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this 896 * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. 897 * 898 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this 899 * {@code Call} will pause playing the tones and notify callbacks via 900 * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app 901 * should display to the user an indication of this state and an affordance to continue 902 * the postdial sequence. When the user decides to continue the postdial sequence, the in-call 903 * app should invoke the {@link #postDialContinue(boolean)} method. 904 * 905 * @param proceed Whether or not to continue with the post-dial sequence. 906 */ 907 public void postDialContinue(boolean proceed) { 908 mInCallAdapter.postDialContinue(mTelecomCallId, proceed); 909 } 910 911 /** 912 * Notifies this {@code Call} that an account has been selected and to proceed with placing 913 * an outgoing call. Optionally sets this account as the default account. 914 */ 915 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) { 916 mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault); 917 918 } 919 920 /** 921 * Instructs this {@code Call} to enter a conference. 922 * 923 * @param callToConferenceWith The other call with which to conference. 924 */ 925 public void conference(Call callToConferenceWith) { 926 if (callToConferenceWith != null) { 927 mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId); 928 } 929 } 930 931 /** 932 * Instructs this {@code Call} to split from any conference call with which it may be 933 * connected. 934 */ 935 public void splitFromConference() { 936 mInCallAdapter.splitFromConference(mTelecomCallId); 937 } 938 939 /** 940 * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}. 941 */ 942 public void mergeConference() { 943 mInCallAdapter.mergeConference(mTelecomCallId); 944 } 945 946 /** 947 * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}. 948 */ 949 public void swapConference() { 950 mInCallAdapter.swapConference(mTelecomCallId); 951 } 952 953 /** 954 * Initiates a request to the {@link ConnectionService} to pull an external call to the local 955 * device. 956 * <p> 957 * Calls to this method are ignored if the call does not have the 958 * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set. 959 * <p> 960 * An {@link InCallService} will only see calls which support this method if it has the 961 * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} 962 * in its manifest. 963 */ 964 public void pullExternalCall() { 965 // If this isn't an external call, ignore the request. 966 if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) { 967 return; 968 } 969 970 mInCallAdapter.pullExternalCall(mTelecomCallId); 971 } 972 973 /** 974 * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in 975 * the {@link ConnectionService}. 976 * <p> 977 * Events are exposed to {@link ConnectionService} implementations via 978 * {@link android.telecom.Connection#onCallEvent(String, Bundle)}. 979 * <p> 980 * No assumptions should be made as to how a {@link ConnectionService} will handle these events. 981 * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts. 982 * 983 * @param event The connection event. 984 * @param extras Bundle containing extra information associated with the event. 985 */ 986 public void sendCallEvent(String event, Bundle extras) { 987 mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras); 988 } 989 990 /** 991 * Obtains the parent of this {@code Call} in a conference, if any. 992 * 993 * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a 994 * child of any conference {@code Call}s. 995 */ 996 public Call getParent() { 997 if (mParentId != null) { 998 return mPhone.internalGetCallByTelecomId(mParentId); 999 } 1000 return null; 1001 } 1002 1003 /** 1004 * Obtains the children of this conference {@code Call}, if any. 1005 * 1006 * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty 1007 * {@code List} otherwise. 1008 */ 1009 public List<Call> getChildren() { 1010 if (!mChildrenCached) { 1011 mChildrenCached = true; 1012 mChildren.clear(); 1013 1014 for(String id : mChildrenIds) { 1015 Call call = mPhone.internalGetCallByTelecomId(id); 1016 if (call == null) { 1017 // At least one child was still not found, so do not save true for "cached" 1018 mChildrenCached = false; 1019 } else { 1020 mChildren.add(call); 1021 } 1022 } 1023 } 1024 1025 return mUnmodifiableChildren; 1026 } 1027 1028 /** 1029 * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference. 1030 * 1031 * @return The list of conferenceable {@code Call}s. 1032 */ 1033 public List<Call> getConferenceableCalls() { 1034 return mUnmodifiableConferenceableCalls; 1035 } 1036 1037 /** 1038 * Obtains the state of this {@code Call}. 1039 * 1040 * @return A state value, chosen from the {@code STATE_*} constants. 1041 */ 1042 public int getState() { 1043 return mState; 1044 } 1045 1046 /** 1047 * Obtains a list of canned, pre-configured message responses to present to the user as 1048 * ways of rejecting this {@code Call} using via a text message. 1049 * 1050 * @see #reject(boolean, String) 1051 * 1052 * @return A list of canned text message responses. 1053 */ 1054 public List<String> getCannedTextResponses() { 1055 return mCannedTextResponses; 1056 } 1057 1058 /** 1059 * Obtains an object that can be used to display video from this {@code Call}. 1060 * 1061 * @return An {@code Call.VideoCall}. 1062 */ 1063 public InCallService.VideoCall getVideoCall() { 1064 return mVideoCallImpl; 1065 } 1066 1067 /** 1068 * Obtains an object containing call details. 1069 * 1070 * @return A {@link Details} object. Depending on the state of the {@code Call}, the 1071 * result may be {@code null}. 1072 */ 1073 public Details getDetails() { 1074 return mDetails; 1075 } 1076 1077 /** 1078 * Registers a callback to this {@code Call}. 1079 * 1080 * @param callback A {@code Callback}. 1081 */ 1082 public void registerCallback(Callback callback) { 1083 registerCallback(callback, new Handler()); 1084 } 1085 1086 /** 1087 * Registers a callback to this {@code Call}. 1088 * 1089 * @param callback A {@code Callback}. 1090 * @param handler A handler which command and status changes will be delivered to. 1091 */ 1092 public void registerCallback(Callback callback, Handler handler) { 1093 unregisterCallback(callback); 1094 // Don't allow new callback registration if the call is already being destroyed. 1095 if (callback != null && handler != null && mState != STATE_DISCONNECTED) { 1096 mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler)); 1097 } 1098 } 1099 1100 /** 1101 * Unregisters a callback from this {@code Call}. 1102 * 1103 * @param callback A {@code Callback}. 1104 */ 1105 public void unregisterCallback(Callback callback) { 1106 // Don't allow callback deregistration if the call is already being destroyed. 1107 if (callback != null && mState != STATE_DISCONNECTED) { 1108 for (CallbackRecord<Callback> record : mCallbackRecords) { 1109 if (record.getCallback() == callback) { 1110 mCallbackRecords.remove(record); 1111 break; 1112 } 1113 } 1114 } 1115 } 1116 1117 @Override 1118 public String toString() { 1119 return new StringBuilder(). 1120 append("Call [id: "). 1121 append(mTelecomCallId). 1122 append(", state: "). 1123 append(stateToString(mState)). 1124 append(", details: "). 1125 append(mDetails). 1126 append("]").toString(); 1127 } 1128 1129 /** 1130 * @param state An integer value of a {@code STATE_*} constant. 1131 * @return A string representation of the value. 1132 */ 1133 private static String stateToString(int state) { 1134 switch (state) { 1135 case STATE_NEW: 1136 return "NEW"; 1137 case STATE_RINGING: 1138 return "RINGING"; 1139 case STATE_DIALING: 1140 return "DIALING"; 1141 case STATE_ACTIVE: 1142 return "ACTIVE"; 1143 case STATE_HOLDING: 1144 return "HOLDING"; 1145 case STATE_DISCONNECTED: 1146 return "DISCONNECTED"; 1147 case STATE_CONNECTING: 1148 return "CONNECTING"; 1149 case STATE_DISCONNECTING: 1150 return "DISCONNECTING"; 1151 case STATE_SELECT_PHONE_ACCOUNT: 1152 return "SELECT_PHONE_ACCOUNT"; 1153 default: 1154 Log.w(Call.class, "Unknown state %d", state); 1155 return "UNKNOWN"; 1156 } 1157 } 1158 1159 /** 1160 * Adds a listener to this {@code Call}. 1161 * 1162 * @param listener A {@code Listener}. 1163 * @deprecated Use {@link #registerCallback} instead. 1164 * @hide 1165 */ 1166 @Deprecated 1167 @SystemApi 1168 public void addListener(Listener listener) { 1169 registerCallback(listener); 1170 } 1171 1172 /** 1173 * Removes a listener from this {@code Call}. 1174 * 1175 * @param listener A {@code Listener}. 1176 * @deprecated Use {@link #unregisterCallback} instead. 1177 * @hide 1178 */ 1179 @Deprecated 1180 @SystemApi 1181 public void removeListener(Listener listener) { 1182 unregisterCallback(listener); 1183 } 1184 1185 /** {@hide} */ 1186 Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) { 1187 mPhone = phone; 1188 mTelecomCallId = telecomCallId; 1189 mInCallAdapter = inCallAdapter; 1190 mState = STATE_NEW; 1191 } 1192 1193 /** {@hide} */ 1194 Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) { 1195 mPhone = phone; 1196 mTelecomCallId = telecomCallId; 1197 mInCallAdapter = inCallAdapter; 1198 mState = state; 1199 } 1200 1201 /** {@hide} */ 1202 final String internalGetCallId() { 1203 return mTelecomCallId; 1204 } 1205 1206 /** {@hide} */ 1207 final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) { 1208 // First, we update the internal state as far as possible before firing any updates. 1209 Details details = Details.createFromParcelableCall(parcelableCall); 1210 boolean detailsChanged = !Objects.equals(mDetails, details); 1211 if (detailsChanged) { 1212 mDetails = details; 1213 } 1214 1215 boolean cannedTextResponsesChanged = false; 1216 if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null 1217 && !parcelableCall.getCannedSmsResponses().isEmpty()) { 1218 mCannedTextResponses = 1219 Collections.unmodifiableList(parcelableCall.getCannedSmsResponses()); 1220 cannedTextResponsesChanged = true; 1221 } 1222 1223 VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(); 1224 boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() && 1225 !Objects.equals(mVideoCallImpl, newVideoCallImpl); 1226 if (videoCallChanged) { 1227 mVideoCallImpl = newVideoCallImpl; 1228 } 1229 if (mVideoCallImpl != null) { 1230 mVideoCallImpl.setVideoState(getDetails().getVideoState()); 1231 } 1232 1233 int state = parcelableCall.getState(); 1234 boolean stateChanged = mState != state; 1235 if (stateChanged) { 1236 mState = state; 1237 } 1238 1239 String parentId = parcelableCall.getParentCallId(); 1240 boolean parentChanged = !Objects.equals(mParentId, parentId); 1241 if (parentChanged) { 1242 mParentId = parentId; 1243 } 1244 1245 List<String> childCallIds = parcelableCall.getChildCallIds(); 1246 boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds); 1247 if (childrenChanged) { 1248 mChildrenIds.clear(); 1249 mChildrenIds.addAll(parcelableCall.getChildCallIds()); 1250 mChildrenCached = false; 1251 } 1252 1253 List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds(); 1254 List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size()); 1255 for (String otherId : conferenceableCallIds) { 1256 if (callIdMap.containsKey(otherId)) { 1257 conferenceableCalls.add(callIdMap.get(otherId)); 1258 } 1259 } 1260 1261 if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) { 1262 mConferenceableCalls.clear(); 1263 mConferenceableCalls.addAll(conferenceableCalls); 1264 fireConferenceableCallsChanged(); 1265 } 1266 1267 // Now we fire updates, ensuring that any client who listens to any of these notifications 1268 // gets the most up-to-date state. 1269 1270 if (stateChanged) { 1271 fireStateChanged(mState); 1272 } 1273 if (detailsChanged) { 1274 fireDetailsChanged(mDetails); 1275 } 1276 if (cannedTextResponsesChanged) { 1277 fireCannedTextResponsesLoaded(mCannedTextResponses); 1278 } 1279 if (videoCallChanged) { 1280 fireVideoCallChanged(mVideoCallImpl); 1281 } 1282 if (parentChanged) { 1283 fireParentChanged(getParent()); 1284 } 1285 if (childrenChanged) { 1286 fireChildrenChanged(getChildren()); 1287 } 1288 1289 // If we have transitioned to DISCONNECTED, that means we need to notify clients and 1290 // remove ourselves from the Phone. Note that we do this after completing all state updates 1291 // so a client can cleanly transition all their UI to the state appropriate for a 1292 // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. 1293 if (mState == STATE_DISCONNECTED) { 1294 fireCallDestroyed(); 1295 } 1296 } 1297 1298 /** {@hide} */ 1299 final void internalSetPostDialWait(String remaining) { 1300 mRemainingPostDialSequence = remaining; 1301 firePostDialWait(mRemainingPostDialSequence); 1302 } 1303 1304 /** {@hide} */ 1305 final void internalSetDisconnected() { 1306 if (mState != Call.STATE_DISCONNECTED) { 1307 mState = Call.STATE_DISCONNECTED; 1308 fireStateChanged(mState); 1309 fireCallDestroyed(); 1310 } 1311 } 1312 1313 /** {@hide} */ 1314 final void internalOnConnectionEvent(String event, Bundle extras) { 1315 fireOnConnectionEvent(event, extras); 1316 } 1317 1318 private void fireStateChanged(final int newState) { 1319 for (CallbackRecord<Callback> record : mCallbackRecords) { 1320 final Call call = this; 1321 final Callback callback = record.getCallback(); 1322 record.getHandler().post(new Runnable() { 1323 @Override 1324 public void run() { 1325 callback.onStateChanged(call, newState); 1326 } 1327 }); 1328 } 1329 } 1330 1331 private void fireParentChanged(final Call newParent) { 1332 for (CallbackRecord<Callback> record : mCallbackRecords) { 1333 final Call call = this; 1334 final Callback callback = record.getCallback(); 1335 record.getHandler().post(new Runnable() { 1336 @Override 1337 public void run() { 1338 callback.onParentChanged(call, newParent); 1339 } 1340 }); 1341 } 1342 } 1343 1344 private void fireChildrenChanged(final List<Call> children) { 1345 for (CallbackRecord<Callback> record : mCallbackRecords) { 1346 final Call call = this; 1347 final Callback callback = record.getCallback(); 1348 record.getHandler().post(new Runnable() { 1349 @Override 1350 public void run() { 1351 callback.onChildrenChanged(call, children); 1352 } 1353 }); 1354 } 1355 } 1356 1357 private void fireDetailsChanged(final Details details) { 1358 for (CallbackRecord<Callback> record : mCallbackRecords) { 1359 final Call call = this; 1360 final Callback callback = record.getCallback(); 1361 record.getHandler().post(new Runnable() { 1362 @Override 1363 public void run() { 1364 callback.onDetailsChanged(call, details); 1365 } 1366 }); 1367 } 1368 } 1369 1370 private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) { 1371 for (CallbackRecord<Callback> record : mCallbackRecords) { 1372 final Call call = this; 1373 final Callback callback = record.getCallback(); 1374 record.getHandler().post(new Runnable() { 1375 @Override 1376 public void run() { 1377 callback.onCannedTextResponsesLoaded(call, cannedTextResponses); 1378 } 1379 }); 1380 } 1381 } 1382 1383 private void fireVideoCallChanged(final InCallService.VideoCall videoCall) { 1384 for (CallbackRecord<Callback> record : mCallbackRecords) { 1385 final Call call = this; 1386 final Callback callback = record.getCallback(); 1387 record.getHandler().post(new Runnable() { 1388 @Override 1389 public void run() { 1390 callback.onVideoCallChanged(call, videoCall); 1391 } 1392 }); 1393 } 1394 } 1395 1396 private void firePostDialWait(final String remainingPostDialSequence) { 1397 for (CallbackRecord<Callback> record : mCallbackRecords) { 1398 final Call call = this; 1399 final Callback callback = record.getCallback(); 1400 record.getHandler().post(new Runnable() { 1401 @Override 1402 public void run() { 1403 callback.onPostDialWait(call, remainingPostDialSequence); 1404 } 1405 }); 1406 } 1407 } 1408 1409 private void fireCallDestroyed() { 1410 /** 1411 * To preserve the ordering of the Call's onCallDestroyed callback and Phone's 1412 * onCallRemoved callback, we remove this call from the Phone's record 1413 * only once all of the registered onCallDestroyed callbacks are executed. 1414 * All the callbacks get removed from our records as a part of this operation 1415 * since onCallDestroyed is the final callback. 1416 */ 1417 final Call call = this; 1418 if (mCallbackRecords.isEmpty()) { 1419 // No callbacks registered, remove the call from Phone's record. 1420 mPhone.internalRemoveCall(call); 1421 } 1422 for (final CallbackRecord<Callback> record : mCallbackRecords) { 1423 final Callback callback = record.getCallback(); 1424 record.getHandler().post(new Runnable() { 1425 @Override 1426 public void run() { 1427 boolean isFinalRemoval = false; 1428 RuntimeException toThrow = null; 1429 try { 1430 callback.onCallDestroyed(call); 1431 } catch (RuntimeException e) { 1432 toThrow = e; 1433 } 1434 synchronized(Call.this) { 1435 mCallbackRecords.remove(record); 1436 if (mCallbackRecords.isEmpty()) { 1437 isFinalRemoval = true; 1438 } 1439 } 1440 if (isFinalRemoval) { 1441 mPhone.internalRemoveCall(call); 1442 } 1443 if (toThrow != null) { 1444 throw toThrow; 1445 } 1446 } 1447 }); 1448 } 1449 } 1450 1451 private void fireConferenceableCallsChanged() { 1452 for (CallbackRecord<Callback> record : mCallbackRecords) { 1453 final Call call = this; 1454 final Callback callback = record.getCallback(); 1455 record.getHandler().post(new Runnable() { 1456 @Override 1457 public void run() { 1458 callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls); 1459 } 1460 }); 1461 } 1462 } 1463 1464 /** 1465 * Notifies listeners of an incoming connection event. 1466 * <p> 1467 * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}. 1468 * 1469 * @param event 1470 * @param extras 1471 */ 1472 private void fireOnConnectionEvent(final String event, final Bundle extras) { 1473 for (CallbackRecord<Callback> record : mCallbackRecords) { 1474 final Call call = this; 1475 final Callback callback = record.getCallback(); 1476 record.getHandler().post(new Runnable() { 1477 @Override 1478 public void run() { 1479 callback.onConnectionEvent(call, event, extras); 1480 } 1481 }); 1482 } 1483 } 1484 1485 /** 1486 * Determines if two bundles are equal. 1487 * 1488 * @param bundle The original bundle. 1489 * @param newBundle The bundle to compare with. 1490 * @retrun {@code true} if the bundles are equal, {@code false} otherwise. 1491 */ 1492 private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) { 1493 if (bundle == null || newBundle == null) { 1494 return bundle == newBundle; 1495 } 1496 1497 if (bundle.size() != newBundle.size()) { 1498 return false; 1499 } 1500 1501 for(String key : bundle.keySet()) { 1502 if (key != null) { 1503 final Object value = bundle.get(key); 1504 final Object newValue = newBundle.get(key); 1505 if (!Objects.equals(value, newValue)) { 1506 return false; 1507 } 1508 } 1509 } 1510 return true; 1511 } 1512} 1513