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