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