Conference.java revision abfcfdc0444c48dd161e425c8417dab87de1cb69
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.telecom; 18 19import android.annotation.SystemApi; 20import android.telecom.Connection.VideoProvider; 21 22import java.util.ArrayList; 23import java.util.Collections; 24import java.util.List; 25import java.util.Locale; 26import java.util.Set; 27import java.util.concurrent.CopyOnWriteArrayList; 28import java.util.concurrent.CopyOnWriteArraySet; 29 30/** 31 * Represents a conference call which can contain any number of {@link Connection} objects. 32 */ 33public abstract class Conference extends Conferenceable { 34 35 /** 36 * Used to indicate that the conference connection time is not specified. If not specified, 37 * Telecom will set the connect time. 38 */ 39 public static final long CONNECT_TIME_NOT_SPECIFIED = 0; 40 41 /** @hide */ 42 public abstract static class Listener { 43 public void onStateChanged(Conference conference, int oldState, int newState) {} 44 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {} 45 public void onConnectionAdded(Conference conference, Connection connection) {} 46 public void onConnectionRemoved(Conference conference, Connection connection) {} 47 public void onConferenceableConnectionsChanged( 48 Conference conference, List<Connection> conferenceableConnections) {} 49 public void onDestroyed(Conference conference) {} 50 public void onConnectionCapabilitiesChanged( 51 Conference conference, int connectionCapabilities) {} 52 public void onVideoStateChanged(Conference c, int videoState) { } 53 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {} 54 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {} 55 } 56 57 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); 58 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>(); 59 private final List<Connection> mUnmodifiableChildConnections = 60 Collections.unmodifiableList(mChildConnections); 61 private final List<Connection> mConferenceableConnections = new ArrayList<>(); 62 private final List<Connection> mUnmodifiableConferenceableConnections = 63 Collections.unmodifiableList(mConferenceableConnections); 64 65 private PhoneAccountHandle mPhoneAccount; 66 private CallAudioState mCallAudioState; 67 private int mState = Connection.STATE_NEW; 68 private DisconnectCause mDisconnectCause; 69 private int mConnectionCapabilities; 70 private String mDisconnectMessage; 71 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED; 72 private StatusHints mStatusHints; 73 74 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { 75 @Override 76 public void onDestroyed(Connection c) { 77 if (mConferenceableConnections.remove(c)) { 78 fireOnConferenceableConnectionsChanged(); 79 } 80 } 81 }; 82 83 /** 84 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle} 85 * 86 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference. 87 */ 88 public Conference(PhoneAccountHandle phoneAccount) { 89 mPhoneAccount = phoneAccount; 90 } 91 92 /** 93 * Returns the {@link PhoneAccountHandle} the conference call is being placed through. 94 * 95 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference. 96 */ 97 public final PhoneAccountHandle getPhoneAccountHandle() { 98 return mPhoneAccount; 99 } 100 101 /** 102 * Returns the list of connections currently associated with the conference call. 103 * 104 * @return A list of {@code Connection} objects which represent the children of the conference. 105 */ 106 public final List<Connection> getConnections() { 107 return mUnmodifiableChildConnections; 108 } 109 110 /** 111 * Gets the state of the conference call. See {@link Connection} for valid values. 112 * 113 * @return A constant representing the state the conference call is currently in. 114 */ 115 public final int getState() { 116 return mState; 117 } 118 119 /** 120 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class 121 * {@link Connection} for valid values. 122 * 123 * @return A bitmask of the capabilities of the conference call. 124 */ 125 public final int getConnectionCapabilities() { 126 return mConnectionCapabilities; 127 } 128 129 /** 130 * Whether the given capabilities support the specified capability. 131 * 132 * @param capabilities A capability bit field. 133 * @param capability The capability to check capabilities for. 134 * @return Whether the specified capability is supported. 135 * @hide 136 */ 137 public static boolean can(int capabilities, int capability) { 138 return (capabilities & capability) != 0; 139 } 140 141 /** 142 * Whether the capabilities of this {@code Connection} supports the specified capability. 143 * 144 * @param capability The capability to check capabilities for. 145 * @return Whether the specified capability is supported. 146 * @hide 147 */ 148 public boolean can(int capability) { 149 return can(mConnectionCapabilities, capability); 150 } 151 152 /** 153 * Removes the specified capability from the set of capabilities of this {@code Conference}. 154 * 155 * @param capability The capability to remove from the set. 156 * @hide 157 */ 158 public void removeCapability(int capability) { 159 mConnectionCapabilities &= ~capability; 160 } 161 162 /** 163 * Adds the specified capability to the set of capabilities of this {@code Conference}. 164 * 165 * @param capability The capability to add to the set. 166 * @hide 167 */ 168 public void addCapability(int capability) { 169 mConnectionCapabilities |= capability; 170 } 171 172 /** 173 * @return The audio state of the conference, describing how its audio is currently 174 * being routed by the system. This is {@code null} if this Conference 175 * does not directly know about its audio state. 176 * @deprecated Use {@link #getCallAudioState()} instead. 177 * @hide 178 */ 179 @Deprecated 180 @SystemApi 181 public final AudioState getAudioState() { 182 return new AudioState(mCallAudioState); 183 } 184 185 /** 186 * @return The audio state of the conference, describing how its audio is currently 187 * being routed by the system. This is {@code null} if this Conference 188 * does not directly know about its audio state. 189 */ 190 public final CallAudioState getCallAudioState() { 191 return mCallAudioState; 192 } 193 194 /** 195 * Returns VideoProvider of the primary call. This can be null. 196 */ 197 public VideoProvider getVideoProvider() { 198 return null; 199 } 200 201 /** 202 * Returns video state of the primary call. 203 */ 204 public int getVideoState() { 205 return VideoProfile.VideoState.AUDIO_ONLY; 206 } 207 208 /** 209 * Invoked when the Conference and all it's {@link Connection}s should be disconnected. 210 */ 211 public void onDisconnect() {} 212 213 /** 214 * Invoked when the specified {@link Connection} should be separated from the conference call. 215 * 216 * @param connection The connection to separate. 217 */ 218 public void onSeparate(Connection connection) {} 219 220 /** 221 * Invoked when the specified {@link Connection} should merged with the conference call. 222 * 223 * @param connection The {@code Connection} to merge. 224 */ 225 public void onMerge(Connection connection) {} 226 227 /** 228 * Invoked when the conference should be put on hold. 229 */ 230 public void onHold() {} 231 232 /** 233 * Invoked when the conference should be moved from hold to active. 234 */ 235 public void onUnhold() {} 236 237 /** 238 * Invoked when the child calls should be merged. Only invoked if the conference contains the 239 * capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}. 240 */ 241 public void onMerge() {} 242 243 /** 244 * Invoked when the child calls should be swapped. Only invoked if the conference contains the 245 * capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}. 246 */ 247 public void onSwap() {} 248 249 /** 250 * Notifies this conference of a request to play a DTMF tone. 251 * 252 * @param c A DTMF character. 253 */ 254 public void onPlayDtmfTone(char c) {} 255 256 /** 257 * Notifies this conference of a request to stop any currently playing DTMF tones. 258 */ 259 public void onStopDtmfTone() {} 260 261 /** 262 * Notifies this conference that the {@link #getAudioState()} property has a new value. 263 * 264 * @param state The new call audio state. 265 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead. 266 * @hide 267 */ 268 @SystemApi 269 @Deprecated 270 public void onAudioStateChanged(AudioState state) {} 271 272 /** 273 * Notifies this conference that the {@link #getCallAudioState()} property has a new value. 274 * 275 * @param state The new call audio state. 276 */ 277 public void onCallAudioStateChanged(CallAudioState state) {} 278 279 /** 280 * Notifies this conference that a connection has been added to it. 281 * 282 * @param connection The newly added connection. 283 */ 284 public void onConnectionAdded(Connection connection) {} 285 286 /** 287 * Sets state to be on hold. 288 */ 289 public final void setOnHold() { 290 setState(Connection.STATE_HOLDING); 291 } 292 293 /** 294 * Sets state to be active. 295 */ 296 public final void setActive() { 297 setState(Connection.STATE_ACTIVE); 298 } 299 300 /** 301 * Sets state to disconnected. 302 * 303 * @param disconnectCause The reason for the disconnection, as described by 304 * {@link android.telecom.DisconnectCause}. 305 */ 306 public final void setDisconnected(DisconnectCause disconnectCause) { 307 mDisconnectCause = disconnectCause;; 308 setState(Connection.STATE_DISCONNECTED); 309 for (Listener l : mListeners) { 310 l.onDisconnected(this, mDisconnectCause); 311 } 312 } 313 314 /** 315 * @return The {@link DisconnectCause} for this connection. 316 */ 317 public final DisconnectCause getDisconnectCause() { 318 return mDisconnectCause; 319 } 320 321 /** 322 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class 323 * {@link Connection} for valid values. 324 * 325 * @param connectionCapabilities A bitmask of the {@code PhoneCapabilities} of the conference call. 326 */ 327 public final void setConnectionCapabilities(int connectionCapabilities) { 328 if (connectionCapabilities != mConnectionCapabilities) { 329 mConnectionCapabilities = connectionCapabilities; 330 331 for (Listener l : mListeners) { 332 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities); 333 } 334 } 335 } 336 337 /** 338 * Adds the specified connection as a child of this conference. 339 * 340 * @param connection The connection to add. 341 * @return True if the connection was successfully added. 342 */ 343 public final boolean addConnection(Connection connection) { 344 Log.d(this, "Connection=%s, connection=", connection); 345 if (connection != null && !mChildConnections.contains(connection)) { 346 if (connection.setConference(this)) { 347 mChildConnections.add(connection); 348 onConnectionAdded(connection); 349 for (Listener l : mListeners) { 350 l.onConnectionAdded(this, connection); 351 } 352 return true; 353 } 354 } 355 return false; 356 } 357 358 /** 359 * Removes the specified connection as a child of this conference. 360 * 361 * @param connection The connection to remove. 362 */ 363 public final void removeConnection(Connection connection) { 364 Log.d(this, "removing %s from %s", connection, mChildConnections); 365 if (connection != null && mChildConnections.remove(connection)) { 366 connection.resetConference(); 367 for (Listener l : mListeners) { 368 l.onConnectionRemoved(this, connection); 369 } 370 } 371 } 372 373 /** 374 * Sets the connections with which this connection can be conferenced. 375 * 376 * @param conferenceableConnections The set of connections this connection can conference with. 377 */ 378 public final void setConferenceableConnections(List<Connection> conferenceableConnections) { 379 clearConferenceableList(); 380 for (Connection c : conferenceableConnections) { 381 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a 382 // small amount of items here. 383 if (!mConferenceableConnections.contains(c)) { 384 c.addConnectionListener(mConnectionDeathListener); 385 mConferenceableConnections.add(c); 386 } 387 } 388 fireOnConferenceableConnectionsChanged(); 389 } 390 391 /** 392 * Set the video state for the conference. 393 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY}, 394 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 395 * {@link VideoProfile#STATE_TX_ENABLED}, 396 * {@link VideoProfile#STATE_RX_ENABLED}. 397 * 398 * @param videoState The new video state. 399 */ 400 public final void setVideoState(Connection c, int videoState) { 401 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s", 402 this, c, videoState); 403 for (Listener l : mListeners) { 404 l.onVideoStateChanged(this, videoState); 405 } 406 } 407 408 /** 409 * Sets the video connection provider. 410 * 411 * @param videoProvider The video provider. 412 */ 413 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) { 414 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s", 415 this, c, videoProvider); 416 for (Listener l : mListeners) { 417 l.onVideoProviderChanged(this, videoProvider); 418 } 419 } 420 421 private final void fireOnConferenceableConnectionsChanged() { 422 for (Listener l : mListeners) { 423 l.onConferenceableConnectionsChanged(this, getConferenceableConnections()); 424 } 425 } 426 427 /** 428 * Returns the connections with which this connection can be conferenced. 429 */ 430 public final List<Connection> getConferenceableConnections() { 431 return mUnmodifiableConferenceableConnections; 432 } 433 434 /** 435 * Tears down the conference object and any of its current connections. 436 */ 437 public final void destroy() { 438 Log.d(this, "destroying conference : %s", this); 439 // Tear down the children. 440 for (Connection connection : mChildConnections) { 441 Log.d(this, "removing connection %s", connection); 442 removeConnection(connection); 443 } 444 445 // If not yet disconnected, set the conference call as disconnected first. 446 if (mState != Connection.STATE_DISCONNECTED) { 447 Log.d(this, "setting to disconnected"); 448 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 449 } 450 451 // ...and notify. 452 for (Listener l : mListeners) { 453 l.onDestroyed(this); 454 } 455 } 456 457 /** 458 * Add a listener to be notified of a state change. 459 * 460 * @param listener The new listener. 461 * @return This conference. 462 * @hide 463 */ 464 public final Conference addListener(Listener listener) { 465 mListeners.add(listener); 466 return this; 467 } 468 469 /** 470 * Removes the specified listener. 471 * 472 * @param listener The listener to remove. 473 * @return This conference. 474 * @hide 475 */ 476 public final Conference removeListener(Listener listener) { 477 mListeners.remove(listener); 478 return this; 479 } 480 481 /** 482 * Retrieves the primary connection associated with the conference. The primary connection is 483 * the connection from which the conference will retrieve its current state. 484 * 485 * @return The primary connection. 486 * @hide 487 */ 488 @SystemApi 489 public Connection getPrimaryConnection() { 490 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { 491 return null; 492 } 493 return mUnmodifiableChildConnections.get(0); 494 } 495 496 /** 497 * @hide 498 * @deprecated Use {@link #setConnectionTime}. 499 */ 500 @Deprecated 501 @SystemApi 502 public final void setConnectTimeMillis(long connectTimeMillis) { 503 setConnectionTime(connectTimeMillis); 504 } 505 506 /** 507 * Sets the connection start time of the {@code Conference}. 508 * 509 * @param connectionTimeMillis The connection time, in milliseconds. 510 */ 511 public final void setConnectionTime(long connectionTimeMillis) { 512 mConnectTimeMillis = connectionTimeMillis; 513 } 514 515 /** 516 * @hide 517 * @deprecated Use {@link #getConnectionTime}. 518 */ 519 @Deprecated 520 @SystemApi 521 public final long getConnectTimeMillis() { 522 return getConnectionTime(); 523 } 524 525 /** 526 * Retrieves the connection start time of the {@code Conference}, if specified. A value of 527 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time 528 * of the conference. 529 * 530 * @return The time at which the {@code Conference} was connected. 531 */ 532 public final long getConnectionTime() { 533 return mConnectTimeMillis; 534 } 535 536 /** 537 * Inform this Conference that the state of its audio output has been changed externally. 538 * 539 * @param state The new audio state. 540 * @hide 541 */ 542 final void setCallAudioState(CallAudioState state) { 543 Log.d(this, "setCallAudioState %s", state); 544 mCallAudioState = state; 545 onAudioStateChanged(getAudioState()); 546 onCallAudioStateChanged(state); 547 } 548 549 private void setState(int newState) { 550 if (newState != Connection.STATE_ACTIVE && 551 newState != Connection.STATE_HOLDING && 552 newState != Connection.STATE_DISCONNECTED) { 553 Log.w(this, "Unsupported state transition for Conference call.", 554 Connection.stateToString(newState)); 555 return; 556 } 557 558 if (mState != newState) { 559 int oldState = mState; 560 mState = newState; 561 for (Listener l : mListeners) { 562 l.onStateChanged(this, oldState, newState); 563 } 564 } 565 } 566 567 private final void clearConferenceableList() { 568 for (Connection c : mConferenceableConnections) { 569 c.removeConnectionListener(mConnectionDeathListener); 570 } 571 mConferenceableConnections.clear(); 572 } 573 574 @Override 575 public String toString() { 576 return String.format(Locale.US, 577 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]", 578 Connection.stateToString(mState), 579 Call.Details.capabilitiesToString(mConnectionCapabilities), 580 getVideoState(), 581 getVideoProvider(), 582 super.toString()); 583 } 584 585 /** 586 * Sets the label and icon status to display in the InCall UI. 587 * 588 * @param statusHints The status label and icon to set. 589 */ 590 public final void setStatusHints(StatusHints statusHints) { 591 mStatusHints = statusHints; 592 for (Listener l : mListeners) { 593 l.onStatusHintsChanged(this, statusHints); 594 } 595 } 596 597 /** 598 * @return The status hints for this conference. 599 */ 600 public final StatusHints getStatusHints() { 601 return mStatusHints; 602 } 603} 604