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