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