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 static android.Manifest.permission.MODIFY_PHONE_STATE; 20 21 import android.annotation.ElapsedRealtimeLong; 22 import android.annotation.IntRange; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.annotation.TestApi; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.SystemClock; 31 import android.telecom.Connection.VideoProvider; 32 import android.util.ArraySet; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.List; 38 import java.util.Locale; 39 import java.util.Set; 40 import java.util.concurrent.CopyOnWriteArrayList; 41 import java.util.concurrent.CopyOnWriteArraySet; 42 43 /** 44 * Represents a conference call which can contain any number of {@link Connection} objects. 45 */ 46 public abstract class Conference extends Conferenceable { 47 48 /** 49 * Used to indicate that the conference connection time is not specified. If not specified, 50 * Telecom will set the connect time. 51 */ 52 public static final long CONNECT_TIME_NOT_SPECIFIED = 0; 53 54 /** @hide */ 55 abstract static class Listener { onStateChanged(Conference conference, int oldState, int newState)56 public void onStateChanged(Conference conference, int oldState, int newState) {} onDisconnected(Conference conference, DisconnectCause disconnectCause)57 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {} onConnectionAdded(Conference conference, Connection connection)58 public void onConnectionAdded(Conference conference, Connection connection) {} onConnectionRemoved(Conference conference, Connection connection)59 public void onConnectionRemoved(Conference conference, Connection connection) {} onConferenceableConnectionsChanged( Conference conference, List<Connection> conferenceableConnections)60 public void onConferenceableConnectionsChanged( 61 Conference conference, List<Connection> conferenceableConnections) {} onDestroyed(Conference conference)62 public void onDestroyed(Conference conference) {} onConnectionCapabilitiesChanged( Conference conference, int connectionCapabilities)63 public void onConnectionCapabilitiesChanged( 64 Conference conference, int connectionCapabilities) {} onConnectionPropertiesChanged( Conference conference, int connectionProperties)65 public void onConnectionPropertiesChanged( 66 Conference conference, int connectionProperties) {} onVideoStateChanged(Conference c, int videoState)67 public void onVideoStateChanged(Conference c, int videoState) { } onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider)68 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {} onStatusHintsChanged(Conference conference, StatusHints statusHints)69 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {} onExtrasChanged(Conference c, Bundle extras)70 public void onExtrasChanged(Conference c, Bundle extras) {} onExtrasRemoved(Conference c, List<String> keys)71 public void onExtrasRemoved(Conference c, List<String> keys) {} onConferenceStateChanged(Conference c, boolean isConference)72 public void onConferenceStateChanged(Conference c, boolean isConference) {} onAddressChanged(Conference c, Uri newAddress, int presentation)73 public void onAddressChanged(Conference c, Uri newAddress, int presentation) {} onConnectionEvent(Conference c, String event, Bundle extras)74 public void onConnectionEvent(Conference c, String event, Bundle extras) {} onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation)75 public void onCallerDisplayNameChanged( 76 Conference c, String callerDisplayName, int presentation) {} onCallDirectionChanged(Conference c, int callDirection)77 public void onCallDirectionChanged(Conference c, int callDirection) {} onRingbackRequested(Conference c, boolean ringback)78 public void onRingbackRequested(Conference c, boolean ringback) {} 79 } 80 81 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); 82 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>(); 83 private final List<Connection> mUnmodifiableChildConnections = 84 Collections.unmodifiableList(mChildConnections); 85 private final List<Connection> mConferenceableConnections = new ArrayList<>(); 86 private final List<Connection> mUnmodifiableConferenceableConnections = 87 Collections.unmodifiableList(mConferenceableConnections); 88 89 private String mTelecomCallId; 90 private PhoneAccountHandle mPhoneAccount; 91 private CallAudioState mCallAudioState; 92 private int mState = Connection.STATE_NEW; 93 private DisconnectCause mDisconnectCause; 94 private int mConnectionCapabilities; 95 private int mConnectionProperties; 96 private String mDisconnectMessage; 97 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED; 98 private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED; 99 private StatusHints mStatusHints; 100 private Bundle mExtras; 101 private Set<String> mPreviousExtraKeys; 102 private final Object mExtrasLock = new Object(); 103 private Uri mAddress; 104 private int mAddressPresentation; 105 private String mCallerDisplayName; 106 private int mCallerDisplayNamePresentation; 107 private int mCallDirection; 108 private boolean mRingbackRequested = false; 109 private boolean mIsMultiparty = true; 110 111 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { 112 @Override 113 public void onDestroyed(Connection c) { 114 if (mConferenceableConnections.remove(c)) { 115 fireOnConferenceableConnectionsChanged(); 116 } 117 } 118 }; 119 120 /** 121 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle} 122 * 123 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference. 124 */ Conference(PhoneAccountHandle phoneAccount)125 public Conference(PhoneAccountHandle phoneAccount) { 126 mPhoneAccount = phoneAccount; 127 } 128 129 /** 130 * Returns the telecom internal call ID associated with this conference. 131 * <p> 132 * Note: This is ONLY used for debugging purposes so that the Telephony stack can better 133 * associate logs in Telephony with those in Telecom. 134 * The ID returned should not be used for any other purpose. 135 * 136 * @return The telecom call ID. 137 * @hide 138 */ 139 @SystemApi 140 @TestApi getTelecomCallId()141 public final @NonNull String getTelecomCallId() { 142 return mTelecomCallId; 143 } 144 145 /** 146 * Sets the telecom internal call ID associated with this conference. 147 * 148 * @param telecomCallId The telecom call ID. 149 * @hide 150 */ setTelecomCallId(String telecomCallId)151 public final void setTelecomCallId(String telecomCallId) { 152 mTelecomCallId = telecomCallId; 153 } 154 155 /** 156 * Returns the {@link PhoneAccountHandle} the conference call is being placed through. 157 * 158 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference. 159 */ getPhoneAccountHandle()160 public final PhoneAccountHandle getPhoneAccountHandle() { 161 return mPhoneAccount; 162 } 163 164 /** 165 * Returns the list of connections currently associated with the conference call. 166 * 167 * @return A list of {@code Connection} objects which represent the children of the conference. 168 */ getConnections()169 public final List<Connection> getConnections() { 170 return mUnmodifiableChildConnections; 171 } 172 173 /** 174 * Gets the state of the conference call. See {@link Connection} for valid values. 175 * 176 * @return A constant representing the state the conference call is currently in. 177 */ getState()178 public final int getState() { 179 return mState; 180 } 181 182 /** 183 * Returns whether this conference is requesting that the system play a ringback tone 184 * on its behalf. 185 * @hide 186 */ isRingbackRequested()187 public final boolean isRingbackRequested() { 188 return mRingbackRequested; 189 } 190 191 /** 192 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class 193 * {@link Connection} for valid values. 194 * 195 * @return A bitmask of the capabilities of the conference call. 196 */ getConnectionCapabilities()197 public final int getConnectionCapabilities() { 198 return mConnectionCapabilities; 199 } 200 201 /** 202 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class 203 * {@link Connection} for valid values. 204 * 205 * @return A bitmask of the properties of the conference call. 206 */ getConnectionProperties()207 public final int getConnectionProperties() { 208 return mConnectionProperties; 209 } 210 211 /** 212 * @return The audio state of the conference, describing how its audio is currently 213 * being routed by the system. This is {@code null} if this Conference 214 * does not directly know about its audio state. 215 * @deprecated Use {@link #getCallAudioState()} instead. 216 * @hide 217 */ 218 @Deprecated 219 @SystemApi getAudioState()220 public final AudioState getAudioState() { 221 return new AudioState(mCallAudioState); 222 } 223 224 /** 225 * @return The audio state of the conference, describing how its audio is currently 226 * being routed by the system. This is {@code null} if this Conference 227 * does not directly know about its audio state. 228 */ getCallAudioState()229 public final CallAudioState getCallAudioState() { 230 return mCallAudioState; 231 } 232 233 /** 234 * Returns VideoProvider of the primary call. This can be null. 235 */ getVideoProvider()236 public VideoProvider getVideoProvider() { 237 return null; 238 } 239 240 /** 241 * Returns video state of the primary call. 242 */ getVideoState()243 public int getVideoState() { 244 return VideoProfile.STATE_AUDIO_ONLY; 245 } 246 247 /** 248 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should 249 * be disconnected. 250 */ onDisconnect()251 public void onDisconnect() {} 252 253 /** 254 * Notifies the {@link Conference} when the specified {@link Connection} should be separated 255 * from the conference call. 256 * 257 * @param connection The connection to separate. 258 */ onSeparate(Connection connection)259 public void onSeparate(Connection connection) {} 260 261 /** 262 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the 263 * conference call. 264 * 265 * @param connection The {@code Connection} to merge. 266 */ onMerge(Connection connection)267 public void onMerge(Connection connection) {} 268 269 /** 270 * Notifies the {@link Conference} when it should be put on hold. 271 */ onHold()272 public void onHold() {} 273 274 /** 275 * Notifies the {@link Conference} when it should be moved from a held to active state. 276 */ onUnhold()277 public void onUnhold() {} 278 279 /** 280 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the 281 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}. 282 */ onMerge()283 public void onMerge() {} 284 285 /** 286 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the 287 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}. 288 */ onSwap()289 public void onSwap() {} 290 291 /** 292 * Notifies the {@link Conference} of a request to play a DTMF tone. 293 * 294 * @param c A DTMF character. 295 */ onPlayDtmfTone(char c)296 public void onPlayDtmfTone(char c) {} 297 298 /** 299 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones. 300 */ onStopDtmfTone()301 public void onStopDtmfTone() {} 302 303 /** 304 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value. 305 * 306 * @param state The new call audio state. 307 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead. 308 * @hide 309 */ 310 @SystemApi 311 @Deprecated onAudioStateChanged(AudioState state)312 public void onAudioStateChanged(AudioState state) {} 313 314 /** 315 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new 316 * value. 317 * 318 * @param state The new call audio state. 319 */ onCallAudioStateChanged(CallAudioState state)320 public void onCallAudioStateChanged(CallAudioState state) {} 321 322 /** 323 * Notifies the {@link Conference} that a {@link Connection} has been added to it. 324 * 325 * @param connection The newly added connection. 326 */ onConnectionAdded(Connection connection)327 public void onConnectionAdded(Connection connection) {} 328 329 /** 330 * Notifies the {@link Conference} of a request to add a new participants to the conference call 331 * @param participants that will be added to this conference call 332 * @hide 333 */ onAddConferenceParticipants(@onNull List<Uri> participants)334 public void onAddConferenceParticipants(@NonNull List<Uri> participants) {} 335 336 /** 337 * Notifies this Conference, which is in {@code STATE_RINGING}, of 338 * a request to accept. 339 * For managed {@link ConnectionService}s, this will be called when the user answers a call via 340 * the default dialer's {@link InCallService}. 341 * 342 * @param videoState The video state in which to answer the connection. 343 * @hide 344 */ onAnswer(int videoState)345 public void onAnswer(int videoState) {} 346 347 /** 348 * Notifies this Conference, which is in {@code STATE_RINGING}, of 349 * a request to accept. 350 * For managed {@link ConnectionService}s, this will be called when the user answers a call via 351 * the default dialer's {@link InCallService}. 352 * @hide 353 */ onAnswer()354 public final void onAnswer() { 355 onAnswer(VideoProfile.STATE_AUDIO_ONLY); 356 } 357 358 /** 359 * Notifies this Conference, which is in {@code STATE_RINGING}, of 360 * a request to reject. 361 * For managed {@link ConnectionService}s, this will be called when the user rejects a call via 362 * the default dialer's {@link InCallService}. 363 * @hide 364 */ onReject()365 public void onReject() {} 366 367 /** 368 * Sets state to be on hold. 369 */ setOnHold()370 public final void setOnHold() { 371 setState(Connection.STATE_HOLDING); 372 } 373 374 /** 375 * Sets state to be dialing. 376 */ setDialing()377 public final void setDialing() { 378 setState(Connection.STATE_DIALING); 379 } 380 381 /** 382 * Sets state to be ringing. 383 * @hide 384 */ setRinging()385 public final void setRinging() { 386 setState(Connection.STATE_RINGING); 387 } 388 389 /** 390 * Sets state to be active. 391 */ setActive()392 public final void setActive() { 393 setRingbackRequested(false); 394 setState(Connection.STATE_ACTIVE); 395 } 396 397 /** 398 * Sets state to disconnected. 399 * 400 * @param disconnectCause The reason for the disconnection, as described by 401 * {@link android.telecom.DisconnectCause}. 402 */ setDisconnected(DisconnectCause disconnectCause)403 public final void setDisconnected(DisconnectCause disconnectCause) { 404 mDisconnectCause = disconnectCause;; 405 setState(Connection.STATE_DISCONNECTED); 406 for (Listener l : mListeners) { 407 l.onDisconnected(this, mDisconnectCause); 408 } 409 } 410 411 /** 412 * @return The {@link DisconnectCause} for this connection. 413 */ getDisconnectCause()414 public final DisconnectCause getDisconnectCause() { 415 return mDisconnectCause; 416 } 417 418 /** 419 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class 420 * {@link Connection} for valid values. 421 * 422 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call. 423 */ setConnectionCapabilities(int connectionCapabilities)424 public final void setConnectionCapabilities(int connectionCapabilities) { 425 if (connectionCapabilities != mConnectionCapabilities) { 426 mConnectionCapabilities = connectionCapabilities; 427 428 for (Listener l : mListeners) { 429 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities); 430 } 431 } 432 } 433 434 /** 435 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class 436 * {@link Connection} for valid values. 437 * 438 * @param connectionProperties A bitmask of the {@code Properties} of the conference call. 439 */ setConnectionProperties(int connectionProperties)440 public final void setConnectionProperties(int connectionProperties) { 441 if (connectionProperties != mConnectionProperties) { 442 mConnectionProperties = connectionProperties; 443 444 for (Listener l : mListeners) { 445 l.onConnectionPropertiesChanged(this, mConnectionProperties); 446 } 447 } 448 } 449 450 /** 451 * Adds the specified connection as a child of this conference. 452 * 453 * @param connection The connection to add. 454 * @return True if the connection was successfully added. 455 */ addConnection(Connection connection)456 public final boolean addConnection(Connection connection) { 457 Log.d(this, "Connection=%s, connection=", connection); 458 if (connection != null && !mChildConnections.contains(connection)) { 459 if (connection.setConference(this)) { 460 mChildConnections.add(connection); 461 onConnectionAdded(connection); 462 for (Listener l : mListeners) { 463 l.onConnectionAdded(this, connection); 464 } 465 return true; 466 } 467 } 468 return false; 469 } 470 471 /** 472 * Removes the specified connection as a child of this conference. 473 * 474 * @param connection The connection to remove. 475 */ removeConnection(Connection connection)476 public final void removeConnection(Connection connection) { 477 Log.d(this, "removing %s from %s", connection, mChildConnections); 478 if (connection != null && mChildConnections.remove(connection)) { 479 connection.resetConference(); 480 for (Listener l : mListeners) { 481 l.onConnectionRemoved(this, connection); 482 } 483 } 484 } 485 486 /** 487 * Sets the connections with which this connection can be conferenced. 488 * 489 * @param conferenceableConnections The set of connections this connection can conference with. 490 */ setConferenceableConnections(List<Connection> conferenceableConnections)491 public final void setConferenceableConnections(List<Connection> conferenceableConnections) { 492 clearConferenceableList(); 493 for (Connection c : conferenceableConnections) { 494 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a 495 // small amount of items here. 496 if (!mConferenceableConnections.contains(c)) { 497 c.addConnectionListener(mConnectionDeathListener); 498 mConferenceableConnections.add(c); 499 } 500 } 501 fireOnConferenceableConnectionsChanged(); 502 } 503 504 /** 505 * Requests that the framework play a ringback tone. This is to be invoked by implementations 506 * that do not play a ringback tone themselves in the conference's audio stream. 507 * 508 * @param ringback Whether the ringback tone is to be played. 509 * @hide 510 */ setRingbackRequested(boolean ringback)511 public final void setRingbackRequested(boolean ringback) { 512 if (mRingbackRequested != ringback) { 513 mRingbackRequested = ringback; 514 for (Listener l : mListeners) { 515 l.onRingbackRequested(this, ringback); 516 } 517 } 518 } 519 520 /** 521 * Set the video state for the conference. 522 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY}, 523 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 524 * {@link VideoProfile#STATE_TX_ENABLED}, 525 * {@link VideoProfile#STATE_RX_ENABLED}. 526 * 527 * @param videoState The new video state. 528 */ setVideoState(Connection c, int videoState)529 public final void setVideoState(Connection c, int videoState) { 530 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s", 531 this, c, videoState); 532 for (Listener l : mListeners) { 533 l.onVideoStateChanged(this, videoState); 534 } 535 } 536 537 /** 538 * Sets the video connection provider. 539 * 540 * @param videoProvider The video provider. 541 */ setVideoProvider(Connection c, Connection.VideoProvider videoProvider)542 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) { 543 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s", 544 this, c, videoProvider); 545 for (Listener l : mListeners) { 546 l.onVideoProviderChanged(this, videoProvider); 547 } 548 } 549 fireOnConferenceableConnectionsChanged()550 private final void fireOnConferenceableConnectionsChanged() { 551 for (Listener l : mListeners) { 552 l.onConferenceableConnectionsChanged(this, getConferenceableConnections()); 553 } 554 } 555 556 /** 557 * Returns the connections with which this connection can be conferenced. 558 */ getConferenceableConnections()559 public final List<Connection> getConferenceableConnections() { 560 return mUnmodifiableConferenceableConnections; 561 } 562 563 /** 564 * Tears down the conference object and any of its current connections. 565 */ destroy()566 public final void destroy() { 567 Log.d(this, "destroying conference : %s", this); 568 // Tear down the children. 569 for (Connection connection : mChildConnections) { 570 Log.d(this, "removing connection %s", connection); 571 removeConnection(connection); 572 } 573 574 // If not yet disconnected, set the conference call as disconnected first. 575 if (mState != Connection.STATE_DISCONNECTED) { 576 Log.d(this, "setting to disconnected"); 577 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 578 } 579 580 // ...and notify. 581 for (Listener l : mListeners) { 582 l.onDestroyed(this); 583 } 584 } 585 586 /** 587 * Add a listener to be notified of a state change. 588 * 589 * @param listener The new listener. 590 * @return This conference. 591 * @hide 592 */ addListener(Listener listener)593 final Conference addListener(Listener listener) { 594 mListeners.add(listener); 595 return this; 596 } 597 598 /** 599 * Removes the specified listener. 600 * 601 * @param listener The listener to remove. 602 * @return This conference. 603 * @hide 604 */ removeListener(Listener listener)605 final Conference removeListener(Listener listener) { 606 mListeners.remove(listener); 607 return this; 608 } 609 610 /** 611 * Retrieves the primary connection associated with the conference. The primary connection is 612 * the connection from which the conference will retrieve its current state. 613 * 614 * @return The primary connection. 615 * @hide 616 */ 617 @TestApi 618 @SystemApi getPrimaryConnection()619 public Connection getPrimaryConnection() { 620 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { 621 return null; 622 } 623 return mUnmodifiableChildConnections.get(0); 624 } 625 626 /** 627 * @hide 628 * @deprecated Use {@link #setConnectionTime}. 629 */ 630 @Deprecated 631 @SystemApi setConnectTimeMillis(long connectTimeMillis)632 public final void setConnectTimeMillis(long connectTimeMillis) { 633 setConnectionTime(connectTimeMillis); 634 } 635 636 /** 637 * Sets the connection start time of the {@code Conference}. This is used in the call log to 638 * indicate the date and time when the conference took place. 639 * <p> 640 * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}. 641 * <p> 642 * When setting the connection time, you should always set the connection elapsed time via 643 * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected. 644 * 645 * @param connectionTimeMillis The connection time, in milliseconds, as returned by 646 * {@link System#currentTimeMillis()}. 647 */ setConnectionTime(@ntRangefrom = 0) long connectionTimeMillis)648 public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) { 649 mConnectTimeMillis = connectionTimeMillis; 650 } 651 652 /** 653 * Sets the start time of the {@link Conference} which is the basis for the determining the 654 * duration of the {@link Conference}. 655 * <p> 656 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time 657 * zone changes do not impact the conference duration. 658 * <p> 659 * When setting this, you should also set the connection time via 660 * {@link #setConnectionTime(long)}. 661 * 662 * @param connectionStartElapsedRealTime The connection time, as measured by 663 * {@link SystemClock#elapsedRealtime()}. 664 * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead. 665 */ 666 @Deprecated setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime)667 public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) { 668 setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime); 669 } 670 671 /** 672 * Sets the start time of the {@link Conference} which is the basis for the determining the 673 * duration of the {@link Conference}. 674 * <p> 675 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time 676 * zone changes do not impact the conference duration. 677 * <p> 678 * When setting this, you should also set the connection time via 679 * {@link #setConnectionTime(long)}. 680 * 681 * @param connectionStartElapsedRealTime The connection time, as measured by 682 * {@link SystemClock#elapsedRealtime()}. 683 */ setConnectionStartElapsedRealtimeMillis( @lapsedRealtimeLong long connectionStartElapsedRealTime)684 public final void setConnectionStartElapsedRealtimeMillis( 685 @ElapsedRealtimeLong long connectionStartElapsedRealTime) { 686 mConnectionStartElapsedRealTime = connectionStartElapsedRealTime; 687 } 688 689 /** 690 * @hide 691 * @deprecated Use {@link #getConnectionTime}. 692 */ 693 @Deprecated 694 @SystemApi getConnectTimeMillis()695 public final long getConnectTimeMillis() { 696 return getConnectionTime(); 697 } 698 699 /** 700 * Retrieves the connection start time of the {@code Conference}, if specified. A value of 701 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time 702 * of the conference. 703 * 704 * @return The time at which the {@code Conference} was connected. 705 */ getConnectionTime()706 public final @IntRange(from = 0) long getConnectionTime() { 707 return mConnectTimeMillis; 708 } 709 710 /** 711 * Retrieves the connection start time of the {@link Conference}, if specified. A value of 712 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time 713 * of the conference. 714 * <p> 715 * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not 716 * impacted by wall clock changes (user initiated, network initiated, time zone change, etc). 717 * <p> 718 * Note: This is only exposed for use by the Telephony framework which needs it to copy 719 * conference start times among conference participants. It is exposed as a system API since it 720 * has no general use other than to the Telephony framework. 721 * 722 * @return The elapsed time at which the {@link Conference} was connected. 723 */ getConnectionStartElapsedRealtimeMillis()724 public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { 725 return mConnectionStartElapsedRealTime; 726 } 727 728 /** 729 * Inform this Conference that the state of its audio output has been changed externally. 730 * 731 * @param state The new audio state. 732 * @hide 733 */ setCallAudioState(CallAudioState state)734 final void setCallAudioState(CallAudioState state) { 735 Log.d(this, "setCallAudioState %s", state); 736 mCallAudioState = state; 737 onAudioStateChanged(getAudioState()); 738 onCallAudioStateChanged(state); 739 } 740 setState(int newState)741 private void setState(int newState) { 742 if (mState != newState) { 743 int oldState = mState; 744 mState = newState; 745 for (Listener l : mListeners) { 746 l.onStateChanged(this, oldState, newState); 747 } 748 } 749 } 750 751 private static class FailureSignalingConference extends Conference { 752 private boolean mImmutable = false; FailureSignalingConference(DisconnectCause disconnectCause, PhoneAccountHandle phoneAccount)753 public FailureSignalingConference(DisconnectCause disconnectCause, 754 PhoneAccountHandle phoneAccount) { 755 super(phoneAccount); 756 setDisconnected(disconnectCause); 757 mImmutable = true; 758 } checkImmutable()759 public void checkImmutable() { 760 if (mImmutable) { 761 throw new UnsupportedOperationException("Conference is immutable"); 762 } 763 } 764 } 765 766 /** 767 * Return a {@code Conference} which represents a failed conference attempt. The returned 768 * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified, 769 * and a {@link #getState()} of {@code STATE_DISCONNECTED}. 770 * <p> 771 * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate, 772 * so users of this method need not maintain a reference to its return value to destroy it. 773 * 774 * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}). 775 * @return A {@code Conference} which indicates failure. 776 * @hide 777 */ createFailedConference( @onNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount)778 public @NonNull static Conference createFailedConference( 779 @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) { 780 return new FailureSignalingConference(disconnectCause, phoneAccount); 781 } 782 clearConferenceableList()783 private final void clearConferenceableList() { 784 for (Connection c : mConferenceableConnections) { 785 c.removeConnectionListener(mConnectionDeathListener); 786 } 787 mConferenceableConnections.clear(); 788 } 789 790 @Override toString()791 public String toString() { 792 return String.format(Locale.US, 793 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s," 794 + "isRingbackRequested: %s, ThisObject %s]", 795 Connection.stateToString(mState), 796 Call.Details.capabilitiesToString(mConnectionCapabilities), 797 getVideoState(), 798 getVideoProvider(), 799 isRingbackRequested() ? "Y" : "N", 800 super.toString()); 801 } 802 803 /** 804 * Sets the label and icon status to display in the InCall UI. 805 * 806 * @param statusHints The status label and icon to set. 807 */ setStatusHints(StatusHints statusHints)808 public final void setStatusHints(StatusHints statusHints) { 809 mStatusHints = statusHints; 810 for (Listener l : mListeners) { 811 l.onStatusHintsChanged(this, statusHints); 812 } 813 } 814 815 /** 816 * @return The status hints for this conference. 817 */ getStatusHints()818 public final StatusHints getStatusHints() { 819 return mStatusHints; 820 } 821 822 /** 823 * Replaces all the extras associated with this {@code Conference}. 824 * <p> 825 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer 826 * in the new extras, but were present the last time {@code setExtras} was called are removed. 827 * <p> 828 * Alternatively you may use the {@link #putExtras(Bundle)}, and 829 * {@link #removeExtras(String...)} methods to modify the extras. 830 * <p> 831 * No assumptions should be made as to how an In-Call UI or service will handle these extras. 832 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts. 833 * 834 * @param extras The extras associated with this {@code Conference}. 835 */ setExtras(@ullable Bundle extras)836 public final void setExtras(@Nullable Bundle extras) { 837 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a 838 // block instead of letting other threads put/remove while this method is running. 839 synchronized (mExtrasLock) { 840 // Add/replace any new or changed extras values. 841 putExtras(extras); 842 // If we have used "setExtras" in the past, compare the key set from the last invocation 843 // to the current one and remove any keys that went away. 844 if (mPreviousExtraKeys != null) { 845 List<String> toRemove = new ArrayList<String>(); 846 for (String oldKey : mPreviousExtraKeys) { 847 if (extras == null || !extras.containsKey(oldKey)) { 848 toRemove.add(oldKey); 849 } 850 } 851 852 if (!toRemove.isEmpty()) { 853 removeExtras(toRemove); 854 } 855 } 856 857 // Track the keys the last time set called setExtras. This way, the next time setExtras 858 // is called we can see if the caller has removed any extras values. 859 if (mPreviousExtraKeys == null) { 860 mPreviousExtraKeys = new ArraySet<String>(); 861 } 862 mPreviousExtraKeys.clear(); 863 if (extras != null) { 864 mPreviousExtraKeys.addAll(extras.keySet()); 865 } 866 } 867 } 868 869 /** 870 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are 871 * added. 872 * <p> 873 * No assumptions should be made as to how an In-Call UI or service will handle these extras. 874 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts. 875 * 876 * @param extras The extras to add. 877 */ putExtras(@onNull Bundle extras)878 public final void putExtras(@NonNull Bundle extras) { 879 if (extras == null) { 880 return; 881 } 882 883 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling 884 // onExtrasChanged. 885 Bundle listenersBundle; 886 synchronized (mExtrasLock) { 887 if (mExtras == null) { 888 mExtras = new Bundle(); 889 } 890 mExtras.putAll(extras); 891 listenersBundle = new Bundle(mExtras); 892 } 893 894 for (Listener l : mListeners) { 895 l.onExtrasChanged(this, new Bundle(listenersBundle)); 896 } 897 } 898 899 /** 900 * Adds a boolean extra to this {@link Conference}. 901 * 902 * @param key The extra key. 903 * @param value The value. 904 * @hide 905 */ putExtra(String key, boolean value)906 public final void putExtra(String key, boolean value) { 907 Bundle newExtras = new Bundle(); 908 newExtras.putBoolean(key, value); 909 putExtras(newExtras); 910 } 911 912 /** 913 * Adds an integer extra to this {@link Conference}. 914 * 915 * @param key The extra key. 916 * @param value The value. 917 * @hide 918 */ putExtra(String key, int value)919 public final void putExtra(String key, int value) { 920 Bundle newExtras = new Bundle(); 921 newExtras.putInt(key, value); 922 putExtras(newExtras); 923 } 924 925 /** 926 * Adds a string extra to this {@link Conference}. 927 * 928 * @param key The extra key. 929 * @param value The value. 930 * @hide 931 */ putExtra(String key, String value)932 public final void putExtra(String key, String value) { 933 Bundle newExtras = new Bundle(); 934 newExtras.putString(key, value); 935 putExtras(newExtras); 936 } 937 938 /** 939 * Removes extras from this {@link Conference}. 940 * 941 * @param keys The keys of the extras to remove. 942 */ removeExtras(List<String> keys)943 public final void removeExtras(List<String> keys) { 944 if (keys == null || keys.isEmpty()) { 945 return; 946 } 947 948 synchronized (mExtrasLock) { 949 if (mExtras != null) { 950 for (String key : keys) { 951 mExtras.remove(key); 952 } 953 } 954 } 955 956 List<String> unmodifiableKeys = Collections.unmodifiableList(keys); 957 for (Listener l : mListeners) { 958 l.onExtrasRemoved(this, unmodifiableKeys); 959 } 960 } 961 962 /** 963 * Removes extras from this {@link Conference}. 964 * 965 * @param keys The keys of the extras to remove. 966 */ removeExtras(String .... keys)967 public final void removeExtras(String ... keys) { 968 removeExtras(Arrays.asList(keys)); 969 } 970 971 /** 972 * Returns the extras associated with this conference. 973 * <p> 974 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}. 975 * <p> 976 * Telecom or an {@link InCallService} can also update the extras via 977 * {@link android.telecom.Call#putExtras(Bundle)}, and 978 * {@link Call#removeExtras(List)}. 979 * <p> 980 * The conference is notified of changes to the extras made by Telecom or an 981 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}. 982 * 983 * @return The extras associated with this connection. 984 */ getExtras()985 public final Bundle getExtras() { 986 return mExtras; 987 } 988 989 /** 990 * Notifies this {@link Conference} of a change to the extras made outside the 991 * {@link ConnectionService}. 992 * <p> 993 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via 994 * {@link android.telecom.Call#putExtras(Bundle)}, and 995 * {@link Call#removeExtras(List)}. 996 * 997 * @param extras The new extras bundle. 998 */ onExtrasChanged(Bundle extras)999 public void onExtrasChanged(Bundle extras) {} 1000 1001 /** 1002 * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or 1003 * if it should treat it as a single-party call. 1004 * This method is used as part of a workaround regarding IMS conference calls and user 1005 * expectation. In IMS, once a conference is formed, the UE is connected to an IMS conference 1006 * server. If all participants of the conference drop out of the conference except for one, the 1007 * UE is still connected to the IMS conference server. At this point, the user logically 1008 * assumes they're no longer in a conference, yet the underlying network actually is. 1009 * To help provide a better user experiece, IMS conference calls can pretend to actually be a 1010 * single-party call when the participant count drops to 1. Although the dialer/phone app 1011 * could perform this trickery, it makes sense to do this in Telephony since a fix there will 1012 * ensure that bluetooth head units, auto and wearable apps all behave consistently. 1013 * <p> 1014 * This API is intended for use by the platform Telephony stack only. 1015 * 1016 * @param isConference {@code true} if this {@link Conference} should be treated like a 1017 * conference call, {@code false} if it should be treated like a single-party call. 1018 * @hide 1019 */ 1020 @SystemApi 1021 @TestApi 1022 @RequiresPermission(MODIFY_PHONE_STATE) setConferenceState(boolean isConference)1023 public void setConferenceState(boolean isConference) { 1024 mIsMultiparty = isConference; 1025 for (Listener l : mListeners) { 1026 l.onConferenceStateChanged(this, isConference); 1027 } 1028 } 1029 1030 /** 1031 * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have 1032 * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The 1033 * direction of a {@link Conference} is only applicable to the case where 1034 * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction 1035 * will be ignored. 1036 * @param callDirection The direction of the conference. 1037 * @hide 1038 */ 1039 @RequiresPermission(MODIFY_PHONE_STATE) setCallDirection(@all.Details.CallDirection int callDirection)1040 public final void setCallDirection(@Call.Details.CallDirection int callDirection) { 1041 Log.d(this, "setDirection %d", callDirection); 1042 mCallDirection = callDirection; 1043 for (Listener l : mListeners) { 1044 l.onCallDirectionChanged(this, callDirection); 1045 } 1046 } 1047 1048 /** 1049 * Determines if the {@link Conference} is considered "multiparty" or not. By default all 1050 * conferences are considered multiparty. A multiparty conference is one where there are 1051 * multiple conference participants (other than the host) in the conference. 1052 * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to 1053 * have a conference appear as if it is a standalone call, in which case the conference will 1054 * no longer be multiparty. 1055 * @return {@code true} if conference is treated as a conference (i.e. it is multiparty), 1056 * {@code false} if it should emulate a standalone call (i.e. not multiparty). 1057 * @hide 1058 */ isMultiparty()1059 public boolean isMultiparty() { 1060 return mIsMultiparty; 1061 } 1062 1063 /** 1064 * Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)} 1065 * is called to mark a conference temporarily as NOT a conference. 1066 * <p> 1067 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is 1068 * not intended for use outside of the Telephony stack. 1069 * 1070 * @param address The new address. 1071 * @param presentation The presentation requirements for the address. 1072 * See {@link TelecomManager} for valid values. 1073 * @hide 1074 */ 1075 @SystemApi 1076 @TestApi 1077 @RequiresPermission(MODIFY_PHONE_STATE) setAddress(@onNull Uri address, @TelecomManager.Presentation int presentation)1078 public final void setAddress(@NonNull Uri address, 1079 @TelecomManager.Presentation int presentation) { 1080 Log.d(this, "setAddress %s", address); 1081 mAddress = address; 1082 mAddressPresentation = presentation; 1083 for (Listener l : mListeners) { 1084 l.onAddressChanged(this, address, presentation); 1085 } 1086 } 1087 1088 /** 1089 * Returns the "address" associated with the conference. This is applicable in two cases: 1090 * <ol> 1091 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as 1092 * temporarily "not a conference"; we need to present the correct address in the in-call 1093 * UI.</li> 1094 * <li>When the conference is not hosted on the current device, we need to know the address 1095 * information for the purpose of showing the original address to the user, as well as for 1096 * logging to the call log.</li> 1097 * </ol> 1098 * @return The address of the conference, or {@code null} if not applicable. 1099 * @hide 1100 */ getAddress()1101 public final Uri getAddress() { 1102 return mAddress; 1103 } 1104 1105 /** 1106 * Returns the address presentation associated with the conference. 1107 * <p> 1108 * This is applicable in two cases: 1109 * <ol> 1110 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as 1111 * temporarily "not a conference"; we need to present the correct address presentation in 1112 * the in-call UI.</li> 1113 * <li>When the conference is not hosted on the current device, we need to know the address 1114 * presentation information for the purpose of showing the original address to the user, as 1115 * well as for logging to the call log.</li> 1116 * </ol> 1117 * @return The address presentation of the conference. 1118 * @hide 1119 */ getAddressPresentation()1120 public final @TelecomManager.Presentation int getAddressPresentation() { 1121 return mAddressPresentation; 1122 } 1123 1124 /** 1125 * @return The caller display name (CNAP). 1126 * @hide 1127 */ getCallerDisplayName()1128 public final String getCallerDisplayName() { 1129 return mCallerDisplayName; 1130 } 1131 1132 /** 1133 * @return The presentation requirements for the handle. 1134 * See {@link TelecomManager} for valid values. 1135 * @hide 1136 */ getCallerDisplayNamePresentation()1137 public final int getCallerDisplayNamePresentation() { 1138 return mCallerDisplayNamePresentation; 1139 } 1140 1141 /** 1142 * @return The call direction of this conference. Only applicable when 1143 * {@link #setConferenceState(boolean)} is set to false. 1144 * @hide 1145 */ getCallDirection()1146 public final @Call.Details.CallDirection int getCallDirection() { 1147 return mCallDirection; 1148 } 1149 1150 /** 1151 * Sets the caller display name (CNAP) of this {@link Conference}. Used when 1152 * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a 1153 * conference. 1154 * <p> 1155 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is 1156 * not intended for use outside of the Telephony stack. 1157 * 1158 * @param callerDisplayName The new display name. 1159 * @param presentation The presentation requirements for the handle. 1160 * See {@link TelecomManager} for valid values. 1161 * @hide 1162 */ 1163 @SystemApi 1164 @TestApi setCallerDisplayName(@onNull String callerDisplayName, @TelecomManager.Presentation int presentation)1165 public final void setCallerDisplayName(@NonNull String callerDisplayName, 1166 @TelecomManager.Presentation int presentation) { 1167 Log.d(this, "setCallerDisplayName %s", callerDisplayName); 1168 mCallerDisplayName = callerDisplayName; 1169 mCallerDisplayNamePresentation = presentation; 1170 for (Listener l : mListeners) { 1171 l.onCallerDisplayNameChanged(this, callerDisplayName, presentation); 1172 } 1173 } 1174 1175 /** 1176 * Handles a change to extras received from Telecom. 1177 * 1178 * @param extras The new extras. 1179 * @hide 1180 */ handleExtrasChanged(Bundle extras)1181 final void handleExtrasChanged(Bundle extras) { 1182 Bundle b = null; 1183 synchronized (mExtrasLock) { 1184 mExtras = extras; 1185 if (mExtras != null) { 1186 b = new Bundle(mExtras); 1187 } 1188 } 1189 onExtrasChanged(b); 1190 } 1191 1192 /** 1193 * Sends an event associated with this {@link Conference} with associated event extras to the 1194 * {@link InCallService}. 1195 * <p> 1196 * Connection events are used to communicate point in time information from a 1197 * {@link ConnectionService} to an {@link InCallService} implementation. An example of a 1198 * custom connection event includes notifying the UI when a WIFI call has been handed over to 1199 * LTE, which the InCall UI might use to inform the user that billing charges may apply. The 1200 * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE} 1201 * connection event when a call to {@link Call#mergeConference()} has completed successfully. 1202 * <p> 1203 * Events are exposed to {@link InCallService} implementations via 1204 * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. 1205 * <p> 1206 * No assumptions should be made as to how an In-Call UI or service will handle these events. 1207 * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore 1208 * some events altogether. 1209 * <p> 1210 * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid 1211 * conflicts between {@link ConnectionService} implementations. Further, custom 1212 * {@link ConnectionService} implementations shall not re-purpose events in the 1213 * {@code android.*} namespace, nor shall they define new event types in this namespace. When 1214 * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly 1215 * defined. Extra keys for this bundle should be named similar to the event type (e.g. 1216 * {@code com.example.extra.MY_EXTRA}). 1217 * <p> 1218 * When defining events and the associated extras, it is important to keep their behavior 1219 * consistent when the associated {@link ConnectionService} is updated. Support for deprecated 1220 * events/extras should me maintained to ensure backwards compatibility with older 1221 * {@link InCallService} implementations which were built to support the older behavior. 1222 * <p> 1223 * Expected connection events from the Telephony stack are: 1224 * <p> 1225 * <ul> 1226 * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the 1227 * {@link Conference} could not be held.</li> 1228 * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new 1229 * call is being merged into the conference.</li> 1230 * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call 1231 * has completed being merged into the conference.</li> 1232 * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new 1233 * call has failed to merge into the conference (the dialer app can determine which call 1234 * failed to merge based on the fact that the call still exists outside of the conference 1235 * at the end of the merge process).</li> 1236 * </ul> 1237 * 1238 * @param event The conference event. 1239 * @param extras Optional bundle containing extra information associated with the event. 1240 */ sendConferenceEvent(@onNull String event, @Nullable Bundle extras)1241 public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) { 1242 for (Listener l : mListeners) { 1243 l.onConnectionEvent(this, event, extras); 1244 } 1245 } 1246 } 1247