RemoteConference.java revision 720c664401081ca00e56c7eef12641ae792da530
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 com.android.internal.telecom.IConnectionService; 20 21import android.annotation.Nullable; 22import android.annotation.SystemApi; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.RemoteException; 26 27import java.util.ArrayList; 28import java.util.Collections; 29import java.util.List; 30import java.util.Set; 31import java.util.concurrent.CopyOnWriteArrayList; 32import java.util.concurrent.CopyOnWriteArraySet; 33 34/** 35 * A conference provided to a {@link ConnectionService} by another {@code ConnectionService} through 36 * {@link ConnectionService#conferenceRemoteConnections}. Once created, a {@code RemoteConference} 37 * can be used to control the conference call or monitor changes through 38 * {@link RemoteConnection.Callback}. 39 * 40 * @see ConnectionService#onRemoteConferenceAdded 41 */ 42public final class RemoteConference { 43 44 /** 45 * Callback base class for {@link RemoteConference}. 46 */ 47 public abstract static class Callback { 48 /** 49 * Invoked when the state of this {@code RemoteConferece} has changed. See 50 * {@link #getState()}. 51 * 52 * @param conference The {@code RemoteConference} invoking this method. 53 * @param oldState The previous state of the {@code RemoteConference}. 54 * @param newState The new state of the {@code RemoteConference}. 55 */ 56 public void onStateChanged(RemoteConference conference, int oldState, int newState) {} 57 58 /** 59 * Invoked when this {@code RemoteConference} is disconnected. 60 * 61 * @param conference The {@code RemoteConference} invoking this method. 62 * @param disconnectCause The ({@see DisconnectCause}) associated with this failed 63 * conference. 64 */ 65 public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {} 66 67 /** 68 * Invoked when a {@link RemoteConnection} is added to the conference call. 69 * 70 * @param conference The {@code RemoteConference} invoking this method. 71 * @param connection The {@link RemoteConnection} being added. 72 */ 73 public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {} 74 75 /** 76 * Invoked when a {@link RemoteConnection} is removed from the conference call. 77 * 78 * @param conference The {@code RemoteConference} invoking this method. 79 * @param connection The {@link RemoteConnection} being removed. 80 */ 81 public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {} 82 83 /** 84 * Indicates that the call capabilities of this {@code RemoteConference} have changed. 85 * See {@link #getConnectionCapabilities()}. 86 * 87 * @param conference The {@code RemoteConference} invoking this method. 88 * @param connectionCapabilities The new capabilities of the {@code RemoteConference}. 89 */ 90 public void onConnectionCapabilitiesChanged( 91 RemoteConference conference, 92 int connectionCapabilities) {} 93 94 /** 95 * Indicates that the call properties of this {@code RemoteConference} have changed. 96 * See {@link #getConnectionProperties()}. 97 * 98 * @param conference The {@code RemoteConference} invoking this method. 99 * @param connectionProperties The new properties of the {@code RemoteConference}. 100 */ 101 public void onConnectionPropertiesChanged( 102 RemoteConference conference, 103 int connectionProperties) {} 104 105 106 /** 107 * Invoked when the set of {@link RemoteConnection}s which can be added to this conference 108 * call have changed. 109 * 110 * @param conference The {@code RemoteConference} invoking this method. 111 * @param conferenceableConnections The list of conferenceable {@link RemoteConnection}s. 112 */ 113 public void onConferenceableConnectionsChanged( 114 RemoteConference conference, 115 List<RemoteConnection> conferenceableConnections) {} 116 117 /** 118 * Indicates that this {@code RemoteConference} has been destroyed. No further requests 119 * should be made to the {@code RemoteConference}, and references to it should be cleared. 120 * 121 * @param conference The {@code RemoteConference} invoking this method. 122 */ 123 public void onDestroyed(RemoteConference conference) {} 124 125 /** 126 * Handles changes to the {@code RemoteConference} extras. 127 * 128 * @param conference The {@code RemoteConference} invoking this method. 129 * @param extras The extras containing other information associated with the conference. 130 */ 131 public void onExtrasChanged(RemoteConference conference, @Nullable Bundle extras) {} 132 } 133 134 private final String mId; 135 private final IConnectionService mConnectionService; 136 137 private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>(); 138 private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>(); 139 private final List<RemoteConnection> mUnmodifiableChildConnections = 140 Collections.unmodifiableList(mChildConnections); 141 private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>(); 142 private final List<RemoteConnection> mUnmodifiableConferenceableConnections = 143 Collections.unmodifiableList(mConferenceableConnections); 144 145 private int mState = Connection.STATE_NEW; 146 private DisconnectCause mDisconnectCause; 147 private int mConnectionCapabilities; 148 private int mConnectionProperties; 149 private Bundle mExtras; 150 151 /** @hide */ 152 RemoteConference(String id, IConnectionService connectionService) { 153 mId = id; 154 mConnectionService = connectionService; 155 } 156 157 /** @hide */ 158 String getId() { 159 return mId; 160 } 161 162 /** @hide */ 163 void setDestroyed() { 164 for (RemoteConnection connection : mChildConnections) { 165 connection.setConference(null); 166 } 167 for (CallbackRecord<Callback> record : mCallbackRecords) { 168 final RemoteConference conference = this; 169 final Callback callback = record.getCallback(); 170 record.getHandler().post(new Runnable() { 171 @Override 172 public void run() { 173 callback.onDestroyed(conference); 174 } 175 }); 176 } 177 } 178 179 /** @hide */ 180 void setState(final int newState) { 181 if (newState != Connection.STATE_ACTIVE && 182 newState != Connection.STATE_HOLDING && 183 newState != Connection.STATE_DISCONNECTED) { 184 Log.w(this, "Unsupported state transition for Conference call.", 185 Connection.stateToString(newState)); 186 return; 187 } 188 189 if (mState != newState) { 190 final int oldState = mState; 191 mState = newState; 192 for (CallbackRecord<Callback> record : mCallbackRecords) { 193 final RemoteConference conference = this; 194 final Callback callback = record.getCallback(); 195 record.getHandler().post(new Runnable() { 196 @Override 197 public void run() { 198 callback.onStateChanged(conference, oldState, newState); 199 } 200 }); 201 } 202 } 203 } 204 205 /** @hide */ 206 void addConnection(final RemoteConnection connection) { 207 if (!mChildConnections.contains(connection)) { 208 mChildConnections.add(connection); 209 connection.setConference(this); 210 for (CallbackRecord<Callback> record : mCallbackRecords) { 211 final RemoteConference conference = this; 212 final Callback callback = record.getCallback(); 213 record.getHandler().post(new Runnable() { 214 @Override 215 public void run() { 216 callback.onConnectionAdded(conference, connection); 217 } 218 }); 219 } 220 } 221 } 222 223 /** @hide */ 224 void removeConnection(final RemoteConnection connection) { 225 if (mChildConnections.contains(connection)) { 226 mChildConnections.remove(connection); 227 connection.setConference(null); 228 for (CallbackRecord<Callback> record : mCallbackRecords) { 229 final RemoteConference conference = this; 230 final Callback callback = record.getCallback(); 231 record.getHandler().post(new Runnable() { 232 @Override 233 public void run() { 234 callback.onConnectionRemoved(conference, connection); 235 } 236 }); 237 } 238 } 239 } 240 241 /** @hide */ 242 void setConnectionCapabilities(final int connectionCapabilities) { 243 if (mConnectionCapabilities != connectionCapabilities) { 244 mConnectionCapabilities = connectionCapabilities; 245 for (CallbackRecord<Callback> record : mCallbackRecords) { 246 final RemoteConference conference = this; 247 final Callback callback = record.getCallback(); 248 record.getHandler().post(new Runnable() { 249 @Override 250 public void run() { 251 callback.onConnectionCapabilitiesChanged( 252 conference, mConnectionCapabilities); 253 } 254 }); 255 } 256 } 257 } 258 259 /** @hide */ 260 void setConnectionProperties(final int connectionProperties) { 261 if (mConnectionProperties != connectionProperties) { 262 mConnectionProperties = connectionProperties; 263 for (CallbackRecord<Callback> record : mCallbackRecords) { 264 final RemoteConference conference = this; 265 final Callback callback = record.getCallback(); 266 record.getHandler().post(new Runnable() { 267 @Override 268 public void run() { 269 callback.onConnectionPropertiesChanged( 270 conference, mConnectionProperties); 271 } 272 }); 273 } 274 } 275 } 276 277 /** @hide */ 278 void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) { 279 mConferenceableConnections.clear(); 280 mConferenceableConnections.addAll(conferenceableConnections); 281 for (CallbackRecord<Callback> record : mCallbackRecords) { 282 final RemoteConference conference = this; 283 final Callback callback = record.getCallback(); 284 record.getHandler().post(new Runnable() { 285 @Override 286 public void run() { 287 callback.onConferenceableConnectionsChanged( 288 conference, mUnmodifiableConferenceableConnections); 289 } 290 }); 291 } 292 } 293 294 /** @hide */ 295 void setDisconnected(final DisconnectCause disconnectCause) { 296 if (mState != Connection.STATE_DISCONNECTED) { 297 mDisconnectCause = disconnectCause; 298 setState(Connection.STATE_DISCONNECTED); 299 for (CallbackRecord<Callback> record : mCallbackRecords) { 300 final RemoteConference conference = this; 301 final Callback callback = record.getCallback(); 302 record.getHandler().post(new Runnable() { 303 @Override 304 public void run() { 305 callback.onDisconnected(conference, disconnectCause); 306 } 307 }); 308 } 309 } 310 } 311 312 /** @hide */ 313 void putExtras(final Bundle extras) { 314 if (mExtras == null) { 315 mExtras = new Bundle(); 316 } 317 mExtras.putAll(extras); 318 319 notifyExtrasChanged(); 320 } 321 322 /** @hide */ 323 void removeExtras(List<String> keys) { 324 if (mExtras == null || keys == null || keys.isEmpty()) { 325 return; 326 } 327 for (String key : keys) { 328 mExtras.remove(key); 329 } 330 331 notifyExtrasChanged(); 332 } 333 334 private void notifyExtrasChanged() { 335 for (CallbackRecord<Callback> record : mCallbackRecords) { 336 final RemoteConference conference = this; 337 final Callback callback = record.getCallback(); 338 record.getHandler().post(new Runnable() { 339 @Override 340 public void run() { 341 callback.onExtrasChanged(conference, mExtras); 342 } 343 }); 344 } 345 } 346 347 /** 348 * Returns the list of {@link RemoteConnection}s contained in this conference. 349 * 350 * @return A list of child connections. 351 */ 352 public final List<RemoteConnection> getConnections() { 353 return mUnmodifiableChildConnections; 354 } 355 356 /** 357 * Gets the state of the conference call. See {@link Connection} for valid values. 358 * 359 * @return A constant representing the state the conference call is currently in. 360 */ 361 public final int getState() { 362 return mState; 363 } 364 365 /** 366 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class 367 * {@link Connection} for valid values. 368 * 369 * @return A bitmask of the capabilities of the conference call. 370 */ 371 public final int getConnectionCapabilities() { 372 return mConnectionCapabilities; 373 } 374 375 /** 376 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class 377 * {@link Connection} for valid values. 378 * 379 * @return A bitmask of the properties of the conference call. 380 */ 381 public final int getConnectionProperties() { 382 return mConnectionProperties; 383 } 384 385 /** 386 * Obtain the extras associated with this {@code RemoteConnection}. 387 * 388 * @return The extras for this connection. 389 */ 390 public final Bundle getExtras() { 391 return mExtras; 392 } 393 394 /** 395 * Disconnects the conference call as well as the child {@link RemoteConnection}s. 396 */ 397 public void disconnect() { 398 try { 399 mConnectionService.disconnect(mId); 400 } catch (RemoteException e) { 401 } 402 } 403 404 /** 405 * Removes the specified {@link RemoteConnection} from the conference. This causes the 406 * {@link RemoteConnection} to become a standalone connection. This is a no-op if the 407 * {@link RemoteConnection} does not belong to this conference. 408 * 409 * @param connection The remote-connection to remove. 410 */ 411 public void separate(RemoteConnection connection) { 412 if (mChildConnections.contains(connection)) { 413 try { 414 mConnectionService.splitFromConference(connection.getId()); 415 } catch (RemoteException e) { 416 } 417 } 418 } 419 420 /** 421 * Merges all {@link RemoteConnection}s of this conference into a single call. This should be 422 * invoked only if the conference contains the capability 423 * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said 424 * capability indicates that the connections of this conference, despite being part of the 425 * same conference object, are yet to have their audio streams merged; this is a common pattern 426 * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls. 427 * Invoking this method will cause the unmerged child connections to merge their audio 428 * streams. 429 */ 430 public void merge() { 431 try { 432 mConnectionService.mergeConference(mId); 433 } catch (RemoteException e) { 434 } 435 } 436 437 /** 438 * Swaps the active audio stream between the conference's child {@link RemoteConnection}s. 439 * This should be invoked only if the conference contains the capability 440 * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by 441 * {@link ConnectionService}s that create conferences for connections that do not yet have 442 * their audio streams merged; this is a common pattern for CDMA conference calls, but the 443 * capability is not used for GSM and SIP conference calls. Invoking this method will change the 444 * active audio stream to a different child connection. 445 */ 446 public void swap() { 447 try { 448 mConnectionService.swapConference(mId); 449 } catch (RemoteException e) { 450 } 451 } 452 453 /** 454 * Puts the conference on hold. 455 */ 456 public void hold() { 457 try { 458 mConnectionService.hold(mId); 459 } catch (RemoteException e) { 460 } 461 } 462 463 /** 464 * Unholds the conference call. 465 */ 466 public void unhold() { 467 try { 468 mConnectionService.unhold(mId); 469 } catch (RemoteException e) { 470 } 471 } 472 473 /** 474 * Returns the {@link DisconnectCause} for the conference if it is in the state 475 * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will 476 * return null. 477 * 478 * @return The disconnect cause. 479 */ 480 public DisconnectCause getDisconnectCause() { 481 return mDisconnectCause; 482 } 483 484 /** 485 * Requests that the conference start playing the specified DTMF tone. 486 * 487 * @param digit The digit for which to play a DTMF tone. 488 */ 489 public void playDtmfTone(char digit) { 490 try { 491 mConnectionService.playDtmfTone(mId, digit); 492 } catch (RemoteException e) { 493 } 494 } 495 496 /** 497 * Stops the most recent request to play a DTMF tone. 498 * 499 * @see #playDtmfTone 500 */ 501 public void stopDtmfTone() { 502 try { 503 mConnectionService.stopDtmfTone(mId); 504 } catch (RemoteException e) { 505 } 506 } 507 508 /** 509 * Request to change the conference's audio routing to the specified state. The specified state 510 * can include audio routing (Bluetooth, Speaker, etc) and muting state. 511 * 512 * @see android.telecom.AudioState 513 * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead. 514 * @hide 515 */ 516 @SystemApi 517 @Deprecated 518 public void setAudioState(AudioState state) { 519 setCallAudioState(new CallAudioState(state)); 520 } 521 522 /** 523 * Request to change the conference's audio routing to the specified state. The specified state 524 * can include audio routing (Bluetooth, Speaker, etc) and muting state. 525 */ 526 public void setCallAudioState(CallAudioState state) { 527 try { 528 mConnectionService.onCallAudioStateChanged(mId, state); 529 } catch (RemoteException e) { 530 } 531 } 532 533 534 /** 535 * Returns a list of independent connections that can me merged with this conference. 536 * 537 * @return A list of conferenceable connections. 538 */ 539 public List<RemoteConnection> getConferenceableConnections() { 540 return mUnmodifiableConferenceableConnections; 541 } 542 543 /** 544 * Register a callback through which to receive state updates for this conference. 545 * 546 * @param callback The callback to notify of state changes. 547 */ 548 public final void registerCallback(Callback callback) { 549 registerCallback(callback, new Handler()); 550 } 551 552 /** 553 * Registers a callback through which to receive state updates for this conference. 554 * Callbacks will be notified using the specified handler, if provided. 555 * 556 * @param callback The callback to notify of state changes. 557 * @param handler The handler on which to execute the callbacks. 558 */ 559 public final void registerCallback(Callback callback, Handler handler) { 560 unregisterCallback(callback); 561 if (callback != null && handler != null) { 562 mCallbackRecords.add(new CallbackRecord(callback, handler)); 563 } 564 } 565 566 /** 567 * Unregisters a previously registered callback. 568 * 569 * @see #registerCallback 570 * 571 * @param callback The callback to unregister. 572 */ 573 public final void unregisterCallback(Callback callback) { 574 if (callback != null) { 575 for (CallbackRecord<Callback> record : mCallbackRecords) { 576 if (record.getCallback() == callback) { 577 mCallbackRecords.remove(record); 578 break; 579 } 580 } 581 } 582 } 583} 584