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