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