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 17 package android.telecom; 18 19 import android.net.Uri; 20 import android.os.Bundle; 21 import android.os.IBinder.DeathRecipient; 22 import android.os.RemoteException; 23 24 import com.android.internal.telecom.IConnectionServiceAdapter; 25 import com.android.internal.telecom.RemoteServiceCallback; 26 27 import java.util.Collections; 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.Set; 31 import java.util.concurrent.ConcurrentHashMap; 32 33 /** 34 * Provides methods for IConnectionService implementations to interact with the system phone app. 35 * 36 * @hide 37 */ 38 final class ConnectionServiceAdapter implements DeathRecipient { 39 /** 40 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 41 * load factor before resizing, 1 means we only expect a single thread to 42 * access the map so make only a single shard 43 */ 44 private final Set<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap( 45 new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1)); 46 47 ConnectionServiceAdapter() { 48 } 49 50 void addAdapter(IConnectionServiceAdapter adapter) { 51 for (IConnectionServiceAdapter it : mAdapters) { 52 if (it.asBinder() == adapter.asBinder()) { 53 Log.w(this, "Ignoring duplicate adapter addition."); 54 return; 55 } 56 } 57 if (mAdapters.add(adapter)) { 58 try { 59 adapter.asBinder().linkToDeath(this, 0); 60 } catch (RemoteException e) { 61 mAdapters.remove(adapter); 62 } 63 } 64 } 65 66 void removeAdapter(IConnectionServiceAdapter adapter) { 67 if (adapter != null) { 68 for (IConnectionServiceAdapter it : mAdapters) { 69 if (it.asBinder() == adapter.asBinder() && mAdapters.remove(it)) { 70 adapter.asBinder().unlinkToDeath(this, 0); 71 break; 72 } 73 } 74 } 75 } 76 77 /** ${inheritDoc} */ 78 @Override 79 public void binderDied() { 80 Iterator<IConnectionServiceAdapter> it = mAdapters.iterator(); 81 while (it.hasNext()) { 82 IConnectionServiceAdapter adapter = it.next(); 83 if (!adapter.asBinder().isBinderAlive()) { 84 it.remove(); 85 adapter.asBinder().unlinkToDeath(this, 0); 86 } 87 } 88 } 89 90 void handleCreateConnectionComplete( 91 String id, 92 ConnectionRequest request, 93 ParcelableConnection connection) { 94 for (IConnectionServiceAdapter adapter : mAdapters) { 95 try { 96 adapter.handleCreateConnectionComplete(id, request, connection, 97 Log.getExternalSession()); 98 } catch (RemoteException e) { 99 } 100 } 101 } 102 103 /** 104 * Sets a call's state to active (e.g., an ongoing call where two parties can actively 105 * communicate). 106 * 107 * @param callId The unique ID of the call whose state is changing to active. 108 */ 109 void setActive(String callId) { 110 for (IConnectionServiceAdapter adapter : mAdapters) { 111 try { 112 adapter.setActive(callId, Log.getExternalSession()); 113 } catch (RemoteException e) { 114 } 115 } 116 } 117 118 /** 119 * Sets a call's state to ringing (e.g., an inbound ringing call). 120 * 121 * @param callId The unique ID of the call whose state is changing to ringing. 122 */ 123 void setRinging(String callId) { 124 for (IConnectionServiceAdapter adapter : mAdapters) { 125 try { 126 adapter.setRinging(callId, Log.getExternalSession()); 127 } catch (RemoteException e) { 128 } 129 } 130 } 131 132 /** 133 * Sets a call's state to dialing (e.g., dialing an outbound call). 134 * 135 * @param callId The unique ID of the call whose state is changing to dialing. 136 */ 137 void setDialing(String callId) { 138 for (IConnectionServiceAdapter adapter : mAdapters) { 139 try { 140 adapter.setDialing(callId, Log.getExternalSession()); 141 } catch (RemoteException e) { 142 } 143 } 144 } 145 146 /** 147 * Sets a call's state to pulling (e.g. a call with {@link Connection#PROPERTY_IS_EXTERNAL_CALL} 148 * is being pulled to the local device. 149 * 150 * @param callId The unique ID of the call whose state is changing to dialing. 151 */ 152 void setPulling(String callId) { 153 for (IConnectionServiceAdapter adapter : mAdapters) { 154 try { 155 adapter.setPulling(callId, Log.getExternalSession()); 156 } catch (RemoteException e) { 157 } 158 } 159 } 160 161 /** 162 * Sets a call's state to disconnected. 163 * 164 * @param callId The unique ID of the call whose state is changing to disconnected. 165 * @param disconnectCause The reason for the disconnection, as described by 166 * {@link android.telecomm.DisconnectCause}. 167 */ 168 void setDisconnected(String callId, DisconnectCause disconnectCause) { 169 for (IConnectionServiceAdapter adapter : mAdapters) { 170 try { 171 adapter.setDisconnected(callId, disconnectCause, Log.getExternalSession()); 172 } catch (RemoteException e) { 173 } 174 } 175 } 176 177 /** 178 * Sets a call's state to be on hold. 179 * 180 * @param callId - The unique ID of the call whose state is changing to be on hold. 181 */ 182 void setOnHold(String callId) { 183 for (IConnectionServiceAdapter adapter : mAdapters) { 184 try { 185 adapter.setOnHold(callId, Log.getExternalSession()); 186 } catch (RemoteException e) { 187 } 188 } 189 } 190 191 /** 192 * Asks Telecom to start or stop a ringback tone for a call. 193 * 194 * @param callId The unique ID of the call whose ringback is being changed. 195 * @param ringback Whether Telecom should start playing a ringback tone. 196 */ 197 void setRingbackRequested(String callId, boolean ringback) { 198 for (IConnectionServiceAdapter adapter : mAdapters) { 199 try { 200 adapter.setRingbackRequested(callId, ringback, Log.getExternalSession()); 201 } catch (RemoteException e) { 202 } 203 } 204 } 205 206 void setConnectionCapabilities(String callId, int capabilities) { 207 for (IConnectionServiceAdapter adapter : mAdapters) { 208 try { 209 adapter.setConnectionCapabilities(callId, capabilities, Log.getExternalSession()); 210 } catch (RemoteException ignored) { 211 } 212 } 213 } 214 215 void setConnectionProperties(String callId, int properties) { 216 for (IConnectionServiceAdapter adapter : mAdapters) { 217 try { 218 adapter.setConnectionProperties(callId, properties, Log.getExternalSession()); 219 } catch (RemoteException ignored) { 220 } 221 } 222 } 223 224 /** 225 * Indicates whether or not the specified call is currently conferenced into the specified 226 * conference call. 227 * 228 * @param callId The unique ID of the call being conferenced. 229 * @param conferenceCallId The unique ID of the conference call. Null if call is not 230 * conferenced. 231 */ 232 void setIsConferenced(String callId, String conferenceCallId) { 233 for (IConnectionServiceAdapter adapter : mAdapters) { 234 try { 235 Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId); 236 adapter.setIsConferenced(callId, conferenceCallId, Log.getExternalSession()); 237 } catch (RemoteException ignored) { 238 } 239 } 240 } 241 242 /** 243 * Indicates that the merge request on this call has failed. 244 * 245 * @param callId The unique ID of the call being conferenced. 246 */ 247 void onConferenceMergeFailed(String callId) { 248 for (IConnectionServiceAdapter adapter : mAdapters) { 249 try { 250 Log.d(this, "merge failed for call %s", callId); 251 adapter.setConferenceMergeFailed(callId, Log.getExternalSession()); 252 } catch (RemoteException ignored) { 253 } 254 } 255 } 256 257 /** 258 * Resets the cdma connection time. 259 */ 260 void resetConnectionTime(String callId) { 261 for (IConnectionServiceAdapter adapter : mAdapters) { 262 try { 263 adapter.resetConnectionTime(callId, Log.getExternalSession()); 264 } catch (RemoteException e) { 265 } 266 } 267 } 268 269 /** 270 * Indicates that the call no longer exists. Can be used with either a call or a conference 271 * call. 272 * 273 * @param callId The unique ID of the call. 274 */ 275 void removeCall(String callId) { 276 for (IConnectionServiceAdapter adapter : mAdapters) { 277 try { 278 adapter.removeCall(callId, Log.getExternalSession()); 279 } catch (RemoteException ignored) { 280 } 281 } 282 } 283 284 void onPostDialWait(String callId, String remaining) { 285 for (IConnectionServiceAdapter adapter : mAdapters) { 286 try { 287 adapter.onPostDialWait(callId, remaining, Log.getExternalSession()); 288 } catch (RemoteException ignored) { 289 } 290 } 291 } 292 293 void onPostDialChar(String callId, char nextChar) { 294 for (IConnectionServiceAdapter adapter : mAdapters) { 295 try { 296 adapter.onPostDialChar(callId, nextChar, Log.getExternalSession()); 297 } catch (RemoteException ignored) { 298 } 299 } 300 } 301 302 /** 303 * Indicates that a new conference call has been created. 304 * 305 * @param callId The unique ID of the conference call. 306 */ 307 void addConferenceCall(String callId, ParcelableConference parcelableConference) { 308 for (IConnectionServiceAdapter adapter : mAdapters) { 309 try { 310 adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession()); 311 } catch (RemoteException ignored) { 312 } 313 } 314 } 315 316 /** 317 * Retrieves a list of remote connection services usable to place calls. 318 */ 319 void queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage) { 320 // Only supported when there is only one adapter. 321 if (mAdapters.size() == 1) { 322 try { 323 mAdapters.iterator().next().queryRemoteConnectionServices(callback, callingPackage, 324 Log.getExternalSession()); 325 } catch (RemoteException e) { 326 Log.e(this, e, "Exception trying to query for remote CSs"); 327 } 328 } else { 329 try { 330 // This is not an error condition, so just pass back an empty list. 331 // This happens when querying from a remote connection service, not the connection 332 // manager itself. 333 callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 334 } catch (RemoteException e) { 335 Log.e(this, e, "Exception trying to query for remote CSs"); 336 } 337 } 338 } 339 340 /** 341 * Sets the call video provider for a call. 342 * 343 * @param callId The unique ID of the call to set with the given call video provider. 344 * @param videoProvider The call video provider instance to set on the call. 345 */ 346 void setVideoProvider( 347 String callId, Connection.VideoProvider videoProvider) { 348 for (IConnectionServiceAdapter adapter : mAdapters) { 349 try { 350 adapter.setVideoProvider( 351 callId, 352 videoProvider == null ? null : videoProvider.getInterface(), 353 Log.getExternalSession()); 354 } catch (RemoteException e) { 355 } 356 } 357 } 358 359 /** 360 * Requests that the framework use VOIP audio mode for this connection. 361 * 362 * @param callId The unique ID of the call to set with the given call video provider. 363 * @param isVoip True if the audio mode is VOIP. 364 */ 365 void setIsVoipAudioMode(String callId, boolean isVoip) { 366 for (IConnectionServiceAdapter adapter : mAdapters) { 367 try { 368 adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession()); 369 } catch (RemoteException e) { 370 } 371 } 372 } 373 374 void setStatusHints(String callId, StatusHints statusHints) { 375 for (IConnectionServiceAdapter adapter : mAdapters) { 376 try { 377 adapter.setStatusHints(callId, statusHints, Log.getExternalSession()); 378 } catch (RemoteException e) { 379 } 380 } 381 } 382 383 void setAddress(String callId, Uri address, int presentation) { 384 for (IConnectionServiceAdapter adapter : mAdapters) { 385 try { 386 adapter.setAddress(callId, address, presentation, Log.getExternalSession()); 387 } catch (RemoteException e) { 388 } 389 } 390 } 391 392 void setCallerDisplayName(String callId, String callerDisplayName, int presentation) { 393 for (IConnectionServiceAdapter adapter : mAdapters) { 394 try { 395 adapter.setCallerDisplayName(callId, callerDisplayName, presentation, 396 Log.getExternalSession()); 397 } catch (RemoteException e) { 398 } 399 } 400 } 401 402 /** 403 * Sets the video state associated with a call. 404 * 405 * Valid values: {@link VideoProfile#STATE_BIDIRECTIONAL}, 406 * {@link VideoProfile#STATE_AUDIO_ONLY}, 407 * {@link VideoProfile#STATE_TX_ENABLED}, 408 * {@link VideoProfile#STATE_RX_ENABLED}. 409 * 410 * @param callId The unique ID of the call to set the video state for. 411 * @param videoState The video state. 412 */ 413 void setVideoState(String callId, int videoState) { 414 Log.v(this, "setVideoState: %d", videoState); 415 for (IConnectionServiceAdapter adapter : mAdapters) { 416 try { 417 adapter.setVideoState(callId, videoState, Log.getExternalSession()); 418 } catch (RemoteException ignored) { 419 } 420 } 421 } 422 423 void setConferenceableConnections(String callId, List<String> conferenceableCallIds) { 424 Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds); 425 for (IConnectionServiceAdapter adapter : mAdapters) { 426 try { 427 adapter.setConferenceableConnections(callId, conferenceableCallIds, 428 Log.getExternalSession()); 429 } catch (RemoteException ignored) { 430 } 431 } 432 } 433 434 /** 435 * Informs telecom of an existing connection which was added by the {@link ConnectionService}. 436 * 437 * @param callId The unique ID of the call being added. 438 * @param connection The connection. 439 */ 440 void addExistingConnection(String callId, ParcelableConnection connection) { 441 Log.v(this, "addExistingConnection: %s", callId); 442 for (IConnectionServiceAdapter adapter : mAdapters) { 443 try { 444 adapter.addExistingConnection(callId, connection, Log.getExternalSession()); 445 } catch (RemoteException ignored) { 446 } 447 } 448 } 449 450 /** 451 * Adds some extras associated with a {@code Connection}. 452 * 453 * @param callId The unique ID of the call. 454 * @param extras The extras to add. 455 */ 456 void putExtras(String callId, Bundle extras) { 457 Log.v(this, "putExtras: %s", callId); 458 for (IConnectionServiceAdapter adapter : mAdapters) { 459 try { 460 adapter.putExtras(callId, extras, Log.getExternalSession()); 461 } catch (RemoteException ignored) { 462 } 463 } 464 } 465 466 /** 467 * Adds an extra associated with a {@code Connection}. 468 * 469 * @param callId The unique ID of the call. 470 * @param key The extra key. 471 * @param value The extra value. 472 */ 473 void putExtra(String callId, String key, boolean value) { 474 Log.v(this, "putExtra: %s %s=%b", callId, key, value); 475 for (IConnectionServiceAdapter adapter : mAdapters) { 476 try { 477 Bundle bundle = new Bundle(); 478 bundle.putBoolean(key, value); 479 adapter.putExtras(callId, bundle, Log.getExternalSession()); 480 } catch (RemoteException ignored) { 481 } 482 } 483 } 484 485 /** 486 * Adds an extra associated with a {@code Connection}. 487 * 488 * @param callId The unique ID of the call. 489 * @param key The extra key. 490 * @param value The extra value. 491 */ 492 void putExtra(String callId, String key, int value) { 493 Log.v(this, "putExtra: %s %s=%d", callId, key, value); 494 for (IConnectionServiceAdapter adapter : mAdapters) { 495 try { 496 Bundle bundle = new Bundle(); 497 bundle.putInt(key, value); 498 adapter.putExtras(callId, bundle, Log.getExternalSession()); 499 } catch (RemoteException ignored) { 500 } 501 } 502 } 503 504 /** 505 * Adds an extra associated with a {@code Connection}. 506 * 507 * @param callId The unique ID of the call. 508 * @param key The extra key. 509 * @param value The extra value. 510 */ 511 void putExtra(String callId, String key, String value) { 512 Log.v(this, "putExtra: %s %s=%s", callId, key, value); 513 for (IConnectionServiceAdapter adapter : mAdapters) { 514 try { 515 Bundle bundle = new Bundle(); 516 bundle.putString(key, value); 517 adapter.putExtras(callId, bundle, Log.getExternalSession()); 518 } catch (RemoteException ignored) { 519 } 520 } 521 } 522 523 /** 524 * Removes extras associated with a {@code Connection}. 525 * @param callId The unique ID of the call. 526 * @param keys The extra keys to remove. 527 */ 528 void removeExtras(String callId, List<String> keys) { 529 Log.v(this, "removeExtras: %s %s", callId, keys); 530 for (IConnectionServiceAdapter adapter : mAdapters) { 531 try { 532 adapter.removeExtras(callId, keys, Log.getExternalSession()); 533 } catch (RemoteException ignored) { 534 } 535 } 536 } 537 538 /** 539 * Sets the audio route associated with a {@link Connection}. 540 * 541 * @param callId The unique ID of the call. 542 * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}). 543 */ 544 void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) { 545 Log.v(this, "setAudioRoute: %s %s %s", callId, 546 CallAudioState.audioRouteToString(audioRoute), 547 bluetoothAddress); 548 for (IConnectionServiceAdapter adapter : mAdapters) { 549 try { 550 adapter.setAudioRoute(callId, audioRoute, 551 bluetoothAddress, Log.getExternalSession()); 552 } catch (RemoteException ignored) { 553 } 554 } 555 } 556 557 558 /** 559 * Informs Telecom of a connection level event. 560 * 561 * @param callId The unique ID of the call. 562 * @param event The event. 563 * @param extras Extras associated with the event. 564 */ 565 void onConnectionEvent(String callId, String event, Bundle extras) { 566 Log.v(this, "onConnectionEvent: %s", event); 567 for (IConnectionServiceAdapter adapter : mAdapters) { 568 try { 569 adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession()); 570 } catch (RemoteException ignored) { 571 } 572 } 573 } 574 575 /** 576 * Notifies Telecom that an RTT session was successfully established. 577 * 578 * @param callId The unique ID of the call. 579 */ 580 void onRttInitiationSuccess(String callId) { 581 Log.v(this, "onRttInitiationSuccess: %s", callId); 582 for (IConnectionServiceAdapter adapter : mAdapters) { 583 try { 584 adapter.onRttInitiationSuccess(callId, Log.getExternalSession()); 585 } catch (RemoteException ignored) { 586 } 587 } 588 } 589 590 /** 591 * Notifies Telecom that a requested RTT session failed to be established. 592 * 593 * @param callId The unique ID of the call. 594 */ 595 void onRttInitiationFailure(String callId, int reason) { 596 Log.v(this, "onRttInitiationFailure: %s", callId); 597 for (IConnectionServiceAdapter adapter : mAdapters) { 598 try { 599 adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession()); 600 } catch (RemoteException ignored) { 601 } 602 } 603 } 604 605 /** 606 * Notifies Telecom that an established RTT session was terminated by the remote user on 607 * the call. 608 * 609 * @param callId The unique ID of the call. 610 */ 611 void onRttSessionRemotelyTerminated(String callId) { 612 Log.v(this, "onRttSessionRemotelyTerminated: %s", callId); 613 for (IConnectionServiceAdapter adapter : mAdapters) { 614 try { 615 adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession()); 616 } catch (RemoteException ignored) { 617 } 618 } 619 } 620 621 /** 622 * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT 623 * session for this call. 624 * 625 * @param callId The unique ID of the call. 626 */ 627 void onRemoteRttRequest(String callId) { 628 Log.v(this, "onRemoteRttRequest: %s", callId); 629 for (IConnectionServiceAdapter adapter : mAdapters) { 630 try { 631 adapter.onRemoteRttRequest(callId, Log.getExternalSession()); 632 } catch (RemoteException ignored) { 633 } 634 } 635 } 636 637 /** 638 * Notifies Telecom that a call's PhoneAccountHandle has changed. 639 * 640 * @param callId The unique ID of the call. 641 * @param pHandle The new PhoneAccountHandle associated with the call. 642 */ 643 void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle) { 644 for (IConnectionServiceAdapter adapter : mAdapters) { 645 try { 646 Log.d(this, "onPhoneAccountChanged %s", callId); 647 adapter.onPhoneAccountChanged(callId, pHandle, Log.getExternalSession()); 648 } catch (RemoteException ignored) { 649 } 650 } 651 } 652 653 /** 654 * Notifies Telecom that the {@link ConnectionService} has released the call resource. 655 */ 656 void onConnectionServiceFocusReleased() { 657 for (IConnectionServiceAdapter adapter : mAdapters) { 658 try { 659 Log.d(this, "onConnectionServiceFocusReleased"); 660 adapter.onConnectionServiceFocusReleased(Log.getExternalSession()); 661 } catch (RemoteException ignored) { 662 } 663 } 664 } 665 666 /** 667 * Sets whether a conference is treated as a conference or a single party call. 668 * See {@link Conference#setConferenceState(boolean)} for more information. 669 * 670 * @param callId The ID of the telecom call. 671 * @param isConference {@code true} if this call should be treated as a conference, 672 * {@code false} otherwise. 673 */ 674 void setConferenceState(String callId, boolean isConference) { 675 Log.v(this, "setConferenceState: %s %b", callId, isConference); 676 for (IConnectionServiceAdapter adapter : mAdapters) { 677 try { 678 adapter.setConferenceState(callId, isConference, Log.getExternalSession()); 679 } catch (RemoteException ignored) { 680 } 681 } 682 } 683 } 684