Conference.java revision 46f7f5dce42d645353a0f3eb0dbdd25b3a6c72fb
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 java.util.ArrayList; 20import java.util.Collections; 21import java.util.List; 22import java.util.Set; 23import java.util.concurrent.CopyOnWriteArrayList; 24import java.util.concurrent.CopyOnWriteArraySet; 25 26/** 27 * Represents a conference call which can contain any number of {@link Connection} objects. 28 */ 29public abstract class Conference { 30 31 /** @hide */ 32 public abstract static class Listener { 33 public void onStateChanged(Conference conference, int oldState, int newState) {} 34 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {} 35 public void onConnectionAdded(Conference conference, Connection connection) {} 36 public void onConnectionRemoved(Conference conference, Connection connection) {} 37 public void onConferenceableConnectionsChanged( 38 Conference conference, List<Connection> conferenceableConnections) {} 39 public void onDestroyed(Conference conference) {} 40 public void onCapabilitiesChanged(Conference conference, int capabilities) {} 41 } 42 43 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); 44 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>(); 45 private final List<Connection> mUnmodifiableChildConnections = 46 Collections.unmodifiableList(mChildConnections); 47 private final List<Connection> mConferenceableConnections = new ArrayList<>(); 48 private final List<Connection> mUnmodifiableConferenceableConnections = 49 Collections.unmodifiableList(mConferenceableConnections); 50 51 private PhoneAccountHandle mPhoneAccount; 52 private AudioState mAudioState; 53 private int mState = Connection.STATE_NEW; 54 private DisconnectCause mDisconnectCause; 55 private int mCapabilities; 56 private String mDisconnectMessage; 57 58 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { 59 @Override 60 public void onDestroyed(Connection c) { 61 if (mConferenceableConnections.remove(c)) { 62 fireOnConferenceableConnectionsChanged(); 63 } 64 } 65 }; 66 67 /** 68 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle} 69 * 70 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference. 71 */ 72 public Conference(PhoneAccountHandle phoneAccount) { 73 mPhoneAccount = phoneAccount; 74 } 75 76 /** 77 * Returns the {@link PhoneAccountHandle} the conference call is being placed through. 78 * 79 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference. 80 */ 81 public final PhoneAccountHandle getPhoneAccountHandle() { 82 return mPhoneAccount; 83 } 84 85 /** 86 * Returns the list of connections currently associated with the conference call. 87 * 88 * @return A list of {@code Connection} objects which represent the children of the conference. 89 */ 90 public final List<Connection> getConnections() { 91 return mUnmodifiableChildConnections; 92 } 93 94 /** 95 * Gets the state of the conference call. See {@link Connection} for valid values. 96 * 97 * @return A constant representing the state the conference call is currently in. 98 */ 99 public final int getState() { 100 return mState; 101 } 102 103 /** 104 * Returns the capabilities of a conference. See {@link PhoneCapabilities} for valid values. 105 * 106 * @return A bitmask of the {@code PhoneCapabilities} of the conference call. 107 */ 108 public final int getCapabilities() { 109 return mCapabilities; 110 } 111 112 /** 113 * @return The audio state of the conference, describing how its audio is currently 114 * being routed by the system. This is {@code null} if this Conference 115 * does not directly know about its audio state. 116 */ 117 public final AudioState getAudioState() { 118 return mAudioState; 119 } 120 121 /** 122 * Invoked when the Conference and all it's {@link Connection}s should be disconnected. 123 */ 124 public void onDisconnect() {} 125 126 /** 127 * Invoked when the specified {@link Connection} should be separated from the conference call. 128 * 129 * @param connection The connection to separate. 130 */ 131 public void onSeparate(Connection connection) {} 132 133 /** 134 * Invoked when the specified {@link Connection} should merged with the conference call. 135 * 136 * @param connection The {@code Connection} to merge. 137 */ 138 public void onMerge(Connection connection) {} 139 140 /** 141 * Invoked when the conference should be put on hold. 142 */ 143 public void onHold() {} 144 145 /** 146 * Invoked when the conference should be moved from hold to active. 147 */ 148 public void onUnhold() {} 149 150 /** 151 * Invoked when the child calls should be merged. Only invoked if the conference contains the 152 * capability {@link PhoneCapabilities#MERGE_CONFERENCE}. 153 */ 154 public void onMerge() {} 155 156 /** 157 * Invoked when the child calls should be swapped. Only invoked if the conference contains the 158 * capability {@link PhoneCapabilities#SWAP_CONFERENCE}. 159 */ 160 public void onSwap() {} 161 162 /** 163 * Notifies this conference of a request to play a DTMF tone. 164 * 165 * @param c A DTMF character. 166 */ 167 public void onPlayDtmfTone(char c) {} 168 169 /** 170 * Notifies this conference of a request to stop any currently playing DTMF tones. 171 */ 172 public void onStopDtmfTone() {} 173 174 /** 175 * Notifies this conference that the {@link #getAudioState()} property has a new value. 176 * 177 * @param state The new call audio state. 178 */ 179 public void onAudioStateChanged(AudioState state) {} 180 181 /** 182 * Notifies this conference that a connection has been added to it. 183 * 184 * @param connection The newly added connection. 185 */ 186 public void onConnectionAdded(Connection connection) {} 187 188 /** 189 * Sets state to be on hold. 190 */ 191 public final void setOnHold() { 192 setState(Connection.STATE_HOLDING); 193 } 194 195 /** 196 * Sets state to be active. 197 */ 198 public final void setActive() { 199 setState(Connection.STATE_ACTIVE); 200 } 201 202 /** 203 * Sets state to disconnected. 204 * 205 * @param disconnectCause The reason for the disconnection, as described by 206 * {@link android.telecom.DisconnectCause}. 207 */ 208 public final void setDisconnected(DisconnectCause disconnectCause) { 209 mDisconnectCause = disconnectCause;; 210 setState(Connection.STATE_DISCONNECTED); 211 for (Listener l : mListeners) { 212 l.onDisconnected(this, mDisconnectCause); 213 } 214 } 215 216 /** 217 * @return The {@link DisconnectCause} for this connection. 218 */ 219 public final DisconnectCause getDisconnectCause() { 220 return mDisconnectCause; 221 } 222 223 /** 224 * Sets the capabilities of a conference. See {@link PhoneCapabilities} for valid values. 225 * 226 * @param capabilities A bitmask of the {@code PhoneCapabilities} of the conference call. 227 */ 228 public final void setCapabilities(int capabilities) { 229 if (capabilities != mCapabilities) { 230 mCapabilities = capabilities; 231 232 for (Listener l : mListeners) { 233 l.onCapabilitiesChanged(this, mCapabilities); 234 } 235 } 236 } 237 238 /** 239 * Adds the specified connection as a child of this conference. 240 * 241 * @param connection The connection to add. 242 * @return True if the connection was successfully added. 243 */ 244 public final boolean addConnection(Connection connection) { 245 if (connection != null && !mChildConnections.contains(connection)) { 246 if (connection.setConference(this)) { 247 mChildConnections.add(connection); 248 onConnectionAdded(connection); 249 for (Listener l : mListeners) { 250 l.onConnectionAdded(this, connection); 251 } 252 return true; 253 } 254 } 255 return false; 256 } 257 258 /** 259 * Removes the specified connection as a child of this conference. 260 * 261 * @param connection The connection to remove. 262 */ 263 public final void removeConnection(Connection connection) { 264 Log.d(this, "removing %s from %s", connection, mChildConnections); 265 if (connection != null && mChildConnections.remove(connection)) { 266 connection.resetConference(); 267 for (Listener l : mListeners) { 268 l.onConnectionRemoved(this, connection); 269 } 270 } 271 } 272 273 /** 274 * Sets the connections with which this connection can be conferenced. 275 * 276 * @param conferenceableConnections The set of connections this connection can conference with. 277 */ 278 public final void setConferenceableConnections(List<Connection> conferenceableConnections) { 279 clearConferenceableList(); 280 for (Connection c : conferenceableConnections) { 281 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a 282 // small amount of items here. 283 if (!mConferenceableConnections.contains(c)) { 284 c.addConnectionListener(mConnectionDeathListener); 285 mConferenceableConnections.add(c); 286 } 287 } 288 fireOnConferenceableConnectionsChanged(); 289 } 290 291 private final void fireOnConferenceableConnectionsChanged() { 292 for (Listener l : mListeners) { 293 l.onConferenceableConnectionsChanged(this, getConferenceableConnections()); 294 } 295 } 296 297 /** 298 * Returns the connections with which this connection can be conferenced. 299 */ 300 public final List<Connection> getConferenceableConnections() { 301 return mUnmodifiableConferenceableConnections; 302 } 303 304 /** 305 * Tears down the conference object and any of its current connections. 306 */ 307 public final void destroy() { 308 Log.d(this, "destroying conference : %s", this); 309 // Tear down the children. 310 for (Connection connection : mChildConnections) { 311 Log.d(this, "removing connection %s", connection); 312 removeConnection(connection); 313 } 314 315 // If not yet disconnected, set the conference call as disconnected first. 316 if (mState != Connection.STATE_DISCONNECTED) { 317 Log.d(this, "setting to disconnected"); 318 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 319 } 320 321 // ...and notify. 322 for (Listener l : mListeners) { 323 l.onDestroyed(this); 324 } 325 } 326 327 /** 328 * Add a listener to be notified of a state change. 329 * 330 * @param listener The new listener. 331 * @return This conference. 332 * @hide 333 */ 334 public final Conference addListener(Listener listener) { 335 mListeners.add(listener); 336 return this; 337 } 338 339 /** 340 * Removes the specified listener. 341 * 342 * @param listener The listener to remove. 343 * @return This conference. 344 * @hide 345 */ 346 public final Conference removeListener(Listener listener) { 347 mListeners.remove(listener); 348 return this; 349 } 350 351 /** 352 * Retrieves the primary connection associated with the conference. The primary connection is 353 * the connection from which the conference will retrieve its current state. 354 * 355 * @return The primary connection. 356 */ 357 public Connection getPrimaryConnection() { 358 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { 359 return null; 360 } 361 return mUnmodifiableChildConnections.get(0); 362 } 363 364 /** 365 * Inform this Conference that the state of its audio output has been changed externally. 366 * 367 * @param state The new audio state. 368 * @hide 369 */ 370 final void setAudioState(AudioState state) { 371 Log.d(this, "setAudioState %s", state); 372 mAudioState = state; 373 onAudioStateChanged(state); 374 } 375 376 private void setState(int newState) { 377 if (newState != Connection.STATE_ACTIVE && 378 newState != Connection.STATE_HOLDING && 379 newState != Connection.STATE_DISCONNECTED) { 380 Log.w(this, "Unsupported state transition for Conference call.", 381 Connection.stateToString(newState)); 382 return; 383 } 384 385 if (mState != newState) { 386 int oldState = mState; 387 mState = newState; 388 for (Listener l : mListeners) { 389 l.onStateChanged(this, oldState, newState); 390 } 391 } 392 } 393 394 private final void clearConferenceableList() { 395 for (Connection c : mConferenceableConnections) { 396 c.removeConnectionListener(mConnectionDeathListener); 397 } 398 mConferenceableConnections.clear(); 399 } 400} 401