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