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