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 * Indicates that the call no longer exists. Can be used with either a call or a conference 259 * call. 260 * 261 * @param callId The unique ID of the call. 262 */ 263 void removeCall(String callId) { 264 for (IConnectionServiceAdapter adapter : mAdapters) { 265 try { 266 adapter.removeCall(callId, Log.getExternalSession()); 267 } catch (RemoteException ignored) { 268 } 269 } 270 } 271 272 void onPostDialWait(String callId, String remaining) { 273 for (IConnectionServiceAdapter adapter : mAdapters) { 274 try { 275 adapter.onPostDialWait(callId, remaining, Log.getExternalSession()); 276 } catch (RemoteException ignored) { 277 } 278 } 279 } 280 281 void onPostDialChar(String callId, char nextChar) { 282 for (IConnectionServiceAdapter adapter : mAdapters) { 283 try { 284 adapter.onPostDialChar(callId, nextChar, Log.getExternalSession()); 285 } catch (RemoteException ignored) { 286 } 287 } 288 } 289 290 /** 291 * Indicates that a new conference call has been created. 292 * 293 * @param callId The unique ID of the conference call. 294 */ 295 void addConferenceCall(String callId, ParcelableConference parcelableConference) { 296 for (IConnectionServiceAdapter adapter : mAdapters) { 297 try { 298 adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession()); 299 } catch (RemoteException ignored) { 300 } 301 } 302 } 303 304 /** 305 * Retrieves a list of remote connection services usable to place calls. 306 */ 307 void queryRemoteConnectionServices(RemoteServiceCallback callback) { 308 // Only supported when there is only one adapter. 309 if (mAdapters.size() == 1) { 310 try { 311 mAdapters.iterator().next().queryRemoteConnectionServices(callback, 312 Log.getExternalSession()); 313 } catch (RemoteException e) { 314 Log.e(this, e, "Exception trying to query for remote CSs"); 315 } 316 } 317 } 318 319 /** 320 * Sets the call video provider for a call. 321 * 322 * @param callId The unique ID of the call to set with the given call video provider. 323 * @param videoProvider The call video provider instance to set on the call. 324 */ 325 void setVideoProvider( 326 String callId, Connection.VideoProvider videoProvider) { 327 for (IConnectionServiceAdapter adapter : mAdapters) { 328 try { 329 adapter.setVideoProvider( 330 callId, 331 videoProvider == null ? null : videoProvider.getInterface(), 332 Log.getExternalSession()); 333 } catch (RemoteException e) { 334 } 335 } 336 } 337 338 /** 339 * Requests that the framework use VOIP audio mode for this connection. 340 * 341 * @param callId The unique ID of the call to set with the given call video provider. 342 * @param isVoip True if the audio mode is VOIP. 343 */ 344 void setIsVoipAudioMode(String callId, boolean isVoip) { 345 for (IConnectionServiceAdapter adapter : mAdapters) { 346 try { 347 adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession()); 348 } catch (RemoteException e) { 349 } 350 } 351 } 352 353 void setStatusHints(String callId, StatusHints statusHints) { 354 for (IConnectionServiceAdapter adapter : mAdapters) { 355 try { 356 adapter.setStatusHints(callId, statusHints, Log.getExternalSession()); 357 } catch (RemoteException e) { 358 } 359 } 360 } 361 362 void setAddress(String callId, Uri address, int presentation) { 363 for (IConnectionServiceAdapter adapter : mAdapters) { 364 try { 365 adapter.setAddress(callId, address, presentation, Log.getExternalSession()); 366 } catch (RemoteException e) { 367 } 368 } 369 } 370 371 void setCallerDisplayName(String callId, String callerDisplayName, int presentation) { 372 for (IConnectionServiceAdapter adapter : mAdapters) { 373 try { 374 adapter.setCallerDisplayName(callId, callerDisplayName, presentation, 375 Log.getExternalSession()); 376 } catch (RemoteException e) { 377 } 378 } 379 } 380 381 /** 382 * Sets the video state associated with a call. 383 * 384 * Valid values: {@link VideoProfile#STATE_BIDIRECTIONAL}, 385 * {@link VideoProfile#STATE_AUDIO_ONLY}, 386 * {@link VideoProfile#STATE_TX_ENABLED}, 387 * {@link VideoProfile#STATE_RX_ENABLED}. 388 * 389 * @param callId The unique ID of the call to set the video state for. 390 * @param videoState The video state. 391 */ 392 void setVideoState(String callId, int videoState) { 393 Log.v(this, "setVideoState: %d", videoState); 394 for (IConnectionServiceAdapter adapter : mAdapters) { 395 try { 396 adapter.setVideoState(callId, videoState, Log.getExternalSession()); 397 } catch (RemoteException ignored) { 398 } 399 } 400 } 401 402 void setConferenceableConnections(String callId, List<String> conferenceableCallIds) { 403 Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds); 404 for (IConnectionServiceAdapter adapter : mAdapters) { 405 try { 406 adapter.setConferenceableConnections(callId, conferenceableCallIds, 407 Log.getExternalSession()); 408 } catch (RemoteException ignored) { 409 } 410 } 411 } 412 413 /** 414 * Informs telecom of an existing connection which was added by the {@link ConnectionService}. 415 * 416 * @param callId The unique ID of the call being added. 417 * @param connection The connection. 418 */ 419 void addExistingConnection(String callId, ParcelableConnection connection) { 420 Log.v(this, "addExistingConnection: %s", callId); 421 for (IConnectionServiceAdapter adapter : mAdapters) { 422 try { 423 adapter.addExistingConnection(callId, connection, Log.getExternalSession()); 424 } catch (RemoteException ignored) { 425 } 426 } 427 } 428 429 /** 430 * Adds some extras associated with a {@code Connection}. 431 * 432 * @param callId The unique ID of the call. 433 * @param extras The extras to add. 434 */ 435 void putExtras(String callId, Bundle extras) { 436 Log.v(this, "putExtras: %s", callId); 437 for (IConnectionServiceAdapter adapter : mAdapters) { 438 try { 439 adapter.putExtras(callId, extras, Log.getExternalSession()); 440 } catch (RemoteException ignored) { 441 } 442 } 443 } 444 445 /** 446 * Adds an extra associated with a {@code Connection}. 447 * 448 * @param callId The unique ID of the call. 449 * @param key The extra key. 450 * @param value The extra value. 451 */ 452 void putExtra(String callId, String key, boolean value) { 453 Log.v(this, "putExtra: %s %s=%b", callId, key, value); 454 for (IConnectionServiceAdapter adapter : mAdapters) { 455 try { 456 Bundle bundle = new Bundle(); 457 bundle.putBoolean(key, value); 458 adapter.putExtras(callId, bundle, Log.getExternalSession()); 459 } catch (RemoteException ignored) { 460 } 461 } 462 } 463 464 /** 465 * Adds an extra associated with a {@code Connection}. 466 * 467 * @param callId The unique ID of the call. 468 * @param key The extra key. 469 * @param value The extra value. 470 */ 471 void putExtra(String callId, String key, int value) { 472 Log.v(this, "putExtra: %s %s=%d", callId, key, value); 473 for (IConnectionServiceAdapter adapter : mAdapters) { 474 try { 475 Bundle bundle = new Bundle(); 476 bundle.putInt(key, value); 477 adapter.putExtras(callId, bundle, Log.getExternalSession()); 478 } catch (RemoteException ignored) { 479 } 480 } 481 } 482 483 /** 484 * Adds an extra associated with a {@code Connection}. 485 * 486 * @param callId The unique ID of the call. 487 * @param key The extra key. 488 * @param value The extra value. 489 */ 490 void putExtra(String callId, String key, String value) { 491 Log.v(this, "putExtra: %s %s=%s", callId, key, value); 492 for (IConnectionServiceAdapter adapter : mAdapters) { 493 try { 494 Bundle bundle = new Bundle(); 495 bundle.putString(key, value); 496 adapter.putExtras(callId, bundle, Log.getExternalSession()); 497 } catch (RemoteException ignored) { 498 } 499 } 500 } 501 502 /** 503 * Removes extras associated with a {@code Connection}. 504 * @param callId The unique ID of the call. 505 * @param keys The extra keys to remove. 506 */ 507 void removeExtras(String callId, List<String> keys) { 508 Log.v(this, "removeExtras: %s %s", callId, keys); 509 for (IConnectionServiceAdapter adapter : mAdapters) { 510 try { 511 adapter.removeExtras(callId, keys, Log.getExternalSession()); 512 } catch (RemoteException ignored) { 513 } 514 } 515 } 516 517 /** 518 * Sets the audio route associated with a {@link Connection}. 519 * 520 * @param callId The unique ID of the call. 521 * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}). 522 */ 523 void setAudioRoute(String callId, int audioRoute) { 524 Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute)); 525 for (IConnectionServiceAdapter adapter : mAdapters) { 526 try { 527 adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession()); 528 } catch (RemoteException ignored) { 529 } 530 } 531 } 532 533 534 /** 535 * Informs Telecom of a connection level event. 536 * 537 * @param callId The unique ID of the call. 538 * @param event The event. 539 * @param extras Extras associated with the event. 540 */ 541 void onConnectionEvent(String callId, String event, Bundle extras) { 542 Log.v(this, "onConnectionEvent: %s", event); 543 for (IConnectionServiceAdapter adapter : mAdapters) { 544 try { 545 adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession()); 546 } catch (RemoteException ignored) { 547 } 548 } 549 } 550 551 /** 552 * Notifies Telecom that an RTT session was successfully established. 553 * 554 * @param callId The unique ID of the call. 555 */ 556 void onRttInitiationSuccess(String callId) { 557 Log.v(this, "onRttInitiationSuccess: %s", callId); 558 for (IConnectionServiceAdapter adapter : mAdapters) { 559 try { 560 adapter.onRttInitiationSuccess(callId, Log.getExternalSession()); 561 } catch (RemoteException ignored) { 562 } 563 } 564 } 565 566 /** 567 * Notifies Telecom that a requested RTT session failed to be established. 568 * 569 * @param callId The unique ID of the call. 570 */ 571 void onRttInitiationFailure(String callId, int reason) { 572 Log.v(this, "onRttInitiationFailure: %s", callId); 573 for (IConnectionServiceAdapter adapter : mAdapters) { 574 try { 575 adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession()); 576 } catch (RemoteException ignored) { 577 } 578 } 579 } 580 581 /** 582 * Notifies Telecom that an established RTT session was terminated by the remote user on 583 * the call. 584 * 585 * @param callId The unique ID of the call. 586 */ 587 void onRttSessionRemotelyTerminated(String callId) { 588 Log.v(this, "onRttSessionRemotelyTerminated: %s", callId); 589 for (IConnectionServiceAdapter adapter : mAdapters) { 590 try { 591 adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession()); 592 } catch (RemoteException ignored) { 593 } 594 } 595 } 596 597 /** 598 * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT 599 * session for this call. 600 * 601 * @param callId The unique ID of the call. 602 */ 603 void onRemoteRttRequest(String callId) { 604 Log.v(this, "onRemoteRttRequest: %s", callId); 605 for (IConnectionServiceAdapter adapter : mAdapters) { 606 try { 607 adapter.onRemoteRttRequest(callId, Log.getExternalSession()); 608 } catch (RemoteException ignored) { 609 } 610 } 611 } 612 } 613