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