Conference.java revision 5d2e4f20fee033a22fbadffb291c4e47f35b7633
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 implements 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 AudioState mAudioState; 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 */ 177 public final AudioState getAudioState() { 178 return mAudioState; 179 } 180 181 /** 182 * Returns VideoProvider of the primary call. This can be null. 183 */ 184 public VideoProvider getVideoProvider() { 185 return null; 186 } 187 188 /** 189 * Returns video state of the primary call. 190 */ 191 public int getVideoState() { 192 return VideoProfile.VideoState.AUDIO_ONLY; 193 } 194 195 /** 196 * Invoked when the Conference and all it's {@link Connection}s should be disconnected. 197 */ 198 public void onDisconnect() {} 199 200 /** 201 * Invoked when the specified {@link Connection} should be separated from the conference call. 202 * 203 * @param connection The connection to separate. 204 */ 205 public void onSeparate(Connection connection) {} 206 207 /** 208 * Invoked when the specified {@link Connection} should merged with the conference call. 209 * 210 * @param connection The {@code Connection} to merge. 211 */ 212 public void onMerge(Connection connection) {} 213 214 /** 215 * Invoked when the conference should be put on hold. 216 */ 217 public void onHold() {} 218 219 /** 220 * Invoked when the conference should be moved from hold to active. 221 */ 222 public void onUnhold() {} 223 224 /** 225 * Invoked when the child calls should be merged. Only invoked if the conference contains the 226 * capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}. 227 */ 228 public void onMerge() {} 229 230 /** 231 * Invoked when the child calls should be swapped. Only invoked if the conference contains the 232 * capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}. 233 */ 234 public void onSwap() {} 235 236 /** 237 * Notifies this conference of a request to play a DTMF tone. 238 * 239 * @param c A DTMF character. 240 */ 241 public void onPlayDtmfTone(char c) {} 242 243 /** 244 * Notifies this conference of a request to stop any currently playing DTMF tones. 245 */ 246 public void onStopDtmfTone() {} 247 248 /** 249 * Notifies this conference that the {@link #getAudioState()} property has a new value. 250 * 251 * @param state The new call audio state. 252 */ 253 public void onAudioStateChanged(AudioState state) {} 254 255 /** 256 * Notifies this conference that a connection has been added to it. 257 * 258 * @param connection The newly added connection. 259 */ 260 public void onConnectionAdded(Connection connection) {} 261 262 /** 263 * Sets state to be on hold. 264 */ 265 public final void setOnHold() { 266 setState(Connection.STATE_HOLDING); 267 } 268 269 /** 270 * Sets state to be active. 271 */ 272 public final void setActive() { 273 setState(Connection.STATE_ACTIVE); 274 } 275 276 /** 277 * Sets state to disconnected. 278 * 279 * @param disconnectCause The reason for the disconnection, as described by 280 * {@link android.telecom.DisconnectCause}. 281 */ 282 public final void setDisconnected(DisconnectCause disconnectCause) { 283 mDisconnectCause = disconnectCause;; 284 setState(Connection.STATE_DISCONNECTED); 285 for (Listener l : mListeners) { 286 l.onDisconnected(this, mDisconnectCause); 287 } 288 } 289 290 /** 291 * @return The {@link DisconnectCause} for this connection. 292 */ 293 public final DisconnectCause getDisconnectCause() { 294 return mDisconnectCause; 295 } 296 297 /** 298 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class 299 * {@link Connection} for valid values. 300 * 301 * @param connectionCapabilities A bitmask of the {@code PhoneCapabilities} of the conference call. 302 */ 303 public final void setConnectionCapabilities(int connectionCapabilities) { 304 if (connectionCapabilities != mConnectionCapabilities) { 305 mConnectionCapabilities = connectionCapabilities; 306 307 for (Listener l : mListeners) { 308 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities); 309 } 310 } 311 } 312 313 /** 314 * Adds the specified connection as a child of this conference. 315 * 316 * @param connection The connection to add. 317 * @return True if the connection was successfully added. 318 */ 319 public final boolean addConnection(Connection connection) { 320 Log.d(this, "Connection=%s, connection=", connection); 321 if (connection != null && !mChildConnections.contains(connection)) { 322 if (connection.setConference(this)) { 323 mChildConnections.add(connection); 324 onConnectionAdded(connection); 325 for (Listener l : mListeners) { 326 l.onConnectionAdded(this, connection); 327 } 328 return true; 329 } 330 } 331 return false; 332 } 333 334 /** 335 * Removes the specified connection as a child of this conference. 336 * 337 * @param connection The connection to remove. 338 */ 339 public final void removeConnection(Connection connection) { 340 Log.d(this, "removing %s from %s", connection, mChildConnections); 341 if (connection != null && mChildConnections.remove(connection)) { 342 connection.resetConference(); 343 for (Listener l : mListeners) { 344 l.onConnectionRemoved(this, connection); 345 } 346 } 347 } 348 349 /** 350 * Sets the connections with which this connection can be conferenced. 351 * 352 * @param conferenceableConnections The set of connections this connection can conference with. 353 */ 354 public final void setConferenceableConnections(List<Connection> conferenceableConnections) { 355 clearConferenceableList(); 356 for (Connection c : conferenceableConnections) { 357 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a 358 // small amount of items here. 359 if (!mConferenceableConnections.contains(c)) { 360 c.addConnectionListener(mConnectionDeathListener); 361 mConferenceableConnections.add(c); 362 } 363 } 364 fireOnConferenceableConnectionsChanged(); 365 } 366 367 /** 368 * Set the video state for the conference. 369 * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY}, 370 * {@link VideoProfile.VideoState#BIDIRECTIONAL}, 371 * {@link VideoProfile.VideoState#TX_ENABLED}, 372 * {@link VideoProfile.VideoState#RX_ENABLED}. 373 * 374 * @param videoState The new video state. 375 */ 376 public final void setVideoState(Connection c, int videoState) { 377 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s", 378 this, c, videoState); 379 for (Listener l : mListeners) { 380 l.onVideoStateChanged(this, videoState); 381 } 382 } 383 384 /** 385 * Sets the video connection provider. 386 * 387 * @param videoProvider The video provider. 388 */ 389 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) { 390 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s", 391 this, c, videoProvider); 392 for (Listener l : mListeners) { 393 l.onVideoProviderChanged(this, videoProvider); 394 } 395 } 396 397 private final void fireOnConferenceableConnectionsChanged() { 398 for (Listener l : mListeners) { 399 l.onConferenceableConnectionsChanged(this, getConferenceableConnections()); 400 } 401 } 402 403 /** 404 * Returns the connections with which this connection can be conferenced. 405 */ 406 public final List<Connection> getConferenceableConnections() { 407 return mUnmodifiableConferenceableConnections; 408 } 409 410 /** 411 * Tears down the conference object and any of its current connections. 412 */ 413 public final void destroy() { 414 Log.d(this, "destroying conference : %s", this); 415 // Tear down the children. 416 for (Connection connection : mChildConnections) { 417 Log.d(this, "removing connection %s", connection); 418 removeConnection(connection); 419 } 420 421 // If not yet disconnected, set the conference call as disconnected first. 422 if (mState != Connection.STATE_DISCONNECTED) { 423 Log.d(this, "setting to disconnected"); 424 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 425 } 426 427 // ...and notify. 428 for (Listener l : mListeners) { 429 l.onDestroyed(this); 430 } 431 } 432 433 /** 434 * Add a listener to be notified of a state change. 435 * 436 * @param listener The new listener. 437 * @return This conference. 438 * @hide 439 */ 440 public final Conference addListener(Listener listener) { 441 mListeners.add(listener); 442 return this; 443 } 444 445 /** 446 * Removes the specified listener. 447 * 448 * @param listener The listener to remove. 449 * @return This conference. 450 * @hide 451 */ 452 public final Conference removeListener(Listener listener) { 453 mListeners.remove(listener); 454 return this; 455 } 456 457 /** 458 * Retrieves the primary connection associated with the conference. The primary connection is 459 * the connection from which the conference will retrieve its current state. 460 * 461 * @return The primary connection. 462 * @hide 463 */ 464 @SystemApi 465 public final Connection getPrimaryConnection() { 466 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { 467 return null; 468 } 469 return mUnmodifiableChildConnections.get(0); 470 } 471 472 /** 473 * @hide 474 * @deprecated Use {@link #setConnectionTime}. 475 */ 476 @Deprecated 477 @SystemApi 478 public final void setConnectTimeMillis(long connectTimeMillis) { 479 setConnectionTime(connectTimeMillis); 480 } 481 482 /** 483 * Sets the connection start time of the {@code Conference}. 484 * 485 * @param connectionTimeMillis The connection time, in milliseconds. 486 */ 487 public final void setConnectionTime(long connectionTimeMillis) { 488 mConnectTimeMillis = connectionTimeMillis; 489 } 490 491 /** 492 * @hide 493 * @deprecated Use {@link #getConnectionTime}. 494 */ 495 @Deprecated 496 @SystemApi 497 public final long getConnectTimeMillis() { 498 return getConnectionTime(); 499 } 500 501 /** 502 * Retrieves the connection start time of the {@code Conference}, if specified. A value of 503 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time 504 * of the conference. 505 * 506 * @return The time at which the {@code Conference} was connected. 507 */ 508 public final long getConnectionTime() { 509 return mConnectTimeMillis; 510 } 511 512 /** 513 * Inform this Conference that the state of its audio output has been changed externally. 514 * 515 * @param state The new audio state. 516 * @hide 517 */ 518 final void setAudioState(AudioState state) { 519 Log.d(this, "setAudioState %s", state); 520 mAudioState = state; 521 onAudioStateChanged(state); 522 } 523 524 private void setState(int newState) { 525 if (newState != Connection.STATE_ACTIVE && 526 newState != Connection.STATE_HOLDING && 527 newState != Connection.STATE_DISCONNECTED) { 528 Log.w(this, "Unsupported state transition for Conference call.", 529 Connection.stateToString(newState)); 530 return; 531 } 532 533 if (mState != newState) { 534 int oldState = mState; 535 mState = newState; 536 for (Listener l : mListeners) { 537 l.onStateChanged(this, oldState, newState); 538 } 539 } 540 } 541 542 private final void clearConferenceableList() { 543 for (Connection c : mConferenceableConnections) { 544 c.removeConnectionListener(mConnectionDeathListener); 545 } 546 mConferenceableConnections.clear(); 547 } 548 549 @Override 550 public String toString() { 551 return String.format(Locale.US, 552 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]", 553 Connection.stateToString(mState), 554 Call.Details.capabilitiesToString(mConnectionCapabilities), 555 getVideoState(), 556 getVideoProvider(), 557 super.toString()); 558 } 559 560 /** 561 * Sets the label and icon status to display in the InCall UI. 562 * 563 * @param statusHints The status label and icon to set. 564 */ 565 public final void setStatusHints(StatusHints statusHints) { 566 mStatusHints = statusHints; 567 for (Listener l : mListeners) { 568 l.onStatusHintsChanged(this, statusHints); 569 } 570 } 571 572 /** 573 * @return The status hints for this conference. 574 */ 575 public final StatusHints getStatusHints() { 576 return mStatusHints; 577 } 578} 579