ConnectionServiceWrapper.java revision d1b36695f77189f008e2de82b4391e5e49048c18
1/* 2 * Copyright 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 com.android.server.telecom; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.net.Uri; 22import android.os.Binder; 23import android.os.Bundle; 24import android.os.IBinder; 25import android.os.RemoteException; 26import android.os.UserHandle; 27import android.telecom.CallAudioState; 28import android.telecom.Connection; 29import android.telecom.ConnectionRequest; 30import android.telecom.ConnectionService; 31import android.telecom.DisconnectCause; 32import android.telecom.GatewayInfo; 33import android.telecom.Log; 34import android.telecom.ParcelableConference; 35import android.telecom.ParcelableConnection; 36import android.telecom.PhoneAccountHandle; 37import android.telecom.StatusHints; 38import android.telecom.TelecomManager; 39import android.telecom.VideoProfile; 40 41import com.android.internal.annotations.VisibleForTesting; 42import com.android.internal.telecom.IConnectionService; 43import com.android.internal.telecom.IConnectionServiceAdapter; 44import com.android.internal.telecom.IVideoProvider; 45import com.android.internal.telecom.RemoteServiceCallback; 46import com.android.internal.util.Preconditions; 47 48import java.util.ArrayList; 49import java.util.Collections; 50import java.util.HashMap; 51import java.util.List; 52import java.util.Map; 53import java.util.Set; 54import java.util.concurrent.ConcurrentHashMap; 55 56/** 57 * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps 58 * track of when the object can safely be unbound. Other classes should not use 59 * {@link IConnectionService} directly and instead should use this class to invoke methods of 60 * {@link IConnectionService}. 61 */ 62@VisibleForTesting 63public class ConnectionServiceWrapper extends ServiceBinder { 64 65 private final class Adapter extends IConnectionServiceAdapter.Stub { 66 67 @Override 68 public void handleCreateConnectionComplete(String callId, ConnectionRequest request, 69 ParcelableConnection connection) { 70 Log.startSession("CSW.hCCC"); 71 long token = Binder.clearCallingIdentity(); 72 try { 73 synchronized (mLock) { 74 logIncoming("handleCreateConnectionComplete %s", callId); 75 ConnectionServiceWrapper.this 76 .handleCreateConnectionComplete(callId, request, connection); 77 } 78 } finally { 79 Binder.restoreCallingIdentity(token); 80 Log.endSession(); 81 } 82 } 83 84 @Override 85 public void setActive(String callId) { 86 Log.startSession("CSW.sA"); 87 long token = Binder.clearCallingIdentity(); 88 try { 89 synchronized (mLock) { 90 logIncoming("setActive %s", callId); 91 Call call = mCallIdMapper.getCall(callId); 92 if (call != null) { 93 mCallsManager.markCallAsActive(call); 94 } else { 95 // Log.w(this, "setActive, unknown call id: %s", msg.obj); 96 } 97 } 98 } finally { 99 Binder.restoreCallingIdentity(token); 100 Log.endSession(); 101 } 102 } 103 104 @Override 105 public void setRinging(String callId) { 106 Log.startSession("CSW.sR"); 107 long token = Binder.clearCallingIdentity(); 108 try { 109 synchronized (mLock) { 110 logIncoming("setRinging %s", callId); 111 Call call = mCallIdMapper.getCall(callId); 112 if (call != null) { 113 mCallsManager.markCallAsRinging(call); 114 } else { 115 // Log.w(this, "setRinging, unknown call id: %s", msg.obj); 116 } 117 } 118 } finally { 119 Binder.restoreCallingIdentity(token); 120 Log.endSession(); 121 } 122 } 123 124 @Override 125 public void setVideoProvider(String callId, IVideoProvider videoProvider) { 126 Log.startSession("CSW.sVP"); 127 long token = Binder.clearCallingIdentity(); 128 try { 129 synchronized (mLock) { 130 logIncoming("setVideoProvider %s", callId); 131 Call call = mCallIdMapper.getCall(callId); 132 if (call != null) { 133 call.setVideoProvider(videoProvider); 134 } 135 } 136 } finally { 137 Binder.restoreCallingIdentity(token); 138 Log.endSession(); 139 } 140 } 141 142 @Override 143 public void setDialing(String callId) { 144 Log.startSession("CSW.sD"); 145 long token = Binder.clearCallingIdentity(); 146 try { 147 synchronized (mLock) { 148 logIncoming("setDialing %s", callId); 149 Call call = mCallIdMapper.getCall(callId); 150 if (call != null) { 151 mCallsManager.markCallAsDialing(call); 152 } else { 153 // Log.w(this, "setDialing, unknown call id: %s", msg.obj); 154 } 155 } 156 } finally { 157 Binder.restoreCallingIdentity(token); 158 Log.endSession(); 159 } 160 } 161 162 @Override 163 public void setDisconnected(String callId, DisconnectCause disconnectCause) { 164 Log.startSession("CSW.sD"); 165 long token = Binder.clearCallingIdentity(); 166 try { 167 synchronized (mLock) { 168 logIncoming("setDisconnected %s %s", callId, disconnectCause); 169 Call call = mCallIdMapper.getCall(callId); 170 Log.d(this, "disconnect call %s %s", disconnectCause, call); 171 if (call != null) { 172 mCallsManager.markCallAsDisconnected(call, disconnectCause); 173 } else { 174 // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1); 175 } 176 } 177 } finally { 178 Binder.restoreCallingIdentity(token); 179 Log.endSession(); 180 } 181 } 182 183 @Override 184 public void setOnHold(String callId) { 185 Log.startSession("CSW.sOH"); 186 long token = Binder.clearCallingIdentity(); 187 try { 188 synchronized (mLock) { 189 logIncoming("setOnHold %s", callId); 190 Call call = mCallIdMapper.getCall(callId); 191 if (call != null) { 192 mCallsManager.markCallAsOnHold(call); 193 } else { 194 // Log.w(this, "setOnHold, unknown call id: %s", msg.obj); 195 } 196 } 197 } finally { 198 Binder.restoreCallingIdentity(token); 199 Log.endSession(); 200 } 201 } 202 203 @Override 204 public void setRingbackRequested(String callId, boolean ringback) { 205 Log.startSession("CSW.SRR"); 206 long token = Binder.clearCallingIdentity(); 207 try { 208 synchronized (mLock) { 209 logIncoming("setRingbackRequested %s %b", callId, ringback); 210 Call call = mCallIdMapper.getCall(callId); 211 if (call != null) { 212 call.setRingbackRequested(ringback); 213 } else { 214 // Log.w(this, "setRingback, unknown call id: %s", args.arg1); 215 } 216 } 217 } finally { 218 Binder.restoreCallingIdentity(token); 219 Log.endSession(); 220 } 221 } 222 223 @Override 224 public void removeCall(String callId) { 225 Log.startSession("CSW.rC"); 226 long token = Binder.clearCallingIdentity(); 227 try { 228 synchronized (mLock) { 229 logIncoming("removeCall %s", callId); 230 Call call = mCallIdMapper.getCall(callId); 231 if (call != null) { 232 if (call.isAlive()) { 233 mCallsManager.markCallAsDisconnected( 234 call, new DisconnectCause(DisconnectCause.REMOTE)); 235 } else { 236 mCallsManager.markCallAsRemoved(call); 237 } 238 } 239 } 240 } finally { 241 Binder.restoreCallingIdentity(token); 242 Log.endSession(); 243 } 244 } 245 246 @Override 247 public void setConnectionCapabilities(String callId, int connectionCapabilities) { 248 Log.startSession("CSW.sCC"); 249 long token = Binder.clearCallingIdentity(); 250 try { 251 synchronized (mLock) { 252 logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities); 253 Call call = mCallIdMapper.getCall(callId); 254 if (call != null) { 255 call.setConnectionCapabilities(connectionCapabilities); 256 } else { 257 // Log.w(ConnectionServiceWrapper.this, 258 // "setConnectionCapabilities, unknown call id: %s", msg.obj); 259 } 260 } 261 } finally { 262 Binder.restoreCallingIdentity(token); 263 Log.endSession(); 264 } 265 } 266 267 @Override 268 public void setConnectionProperties(String callId, int connectionProperties) { 269 Log.startSession("CSW.sCP"); 270 long token = Binder.clearCallingIdentity(); 271 try { 272 synchronized (mLock) { 273 logIncoming("setConnectionProperties %s %d", callId, connectionProperties); 274 Call call = mCallIdMapper.getCall(callId); 275 if (call != null) { 276 call.setConnectionProperties(connectionProperties); 277 } 278 } 279 } finally { 280 Binder.restoreCallingIdentity(token); 281 Log.endSession(); 282 } 283 } 284 285 @Override 286 public void setIsConferenced(String callId, String conferenceCallId) { 287 Log.startSession("CSW.sIC"); 288 long token = Binder.clearCallingIdentity(); 289 try { 290 synchronized (mLock) { 291 logIncoming("setIsConferenced %s %s", callId, conferenceCallId); 292 Call childCall = mCallIdMapper.getCall(callId); 293 if (childCall != null) { 294 if (conferenceCallId == null) { 295 Log.d(this, "unsetting parent: %s", conferenceCallId); 296 childCall.setParentCall(null); 297 } else { 298 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId); 299 childCall.setParentCall(conferenceCall); 300 } 301 } else { 302 // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1); 303 } 304 } 305 } finally { 306 Binder.restoreCallingIdentity(token); 307 Log.endSession(); 308 } 309 } 310 311 @Override 312 public void setConferenceMergeFailed(String callId) { 313 Log.startSession("CSW.sCMF"); 314 long token = Binder.clearCallingIdentity(); 315 try { 316 synchronized (mLock) { 317 logIncoming("setConferenceMergeFailed %s", callId); 318 // TODO: we should move the UI for indication a merge failure here 319 // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can 320 // deliver the message anyway that they want. b/20530631. 321 Call call = mCallIdMapper.getCall(callId); 322 if (call != null) { 323 // Just refresh the connection capabilities so that the UI 324 // is forced to reenable the merge button as the capability 325 // is still on the connection. Note when b/20530631 is fixed, we need 326 // to revisit this fix to remove this hacky way of unhiding the merge 327 // button (side effect of reprocessing the capabilities) and plumb 328 // the failure event all the way to InCallUI instead of stopping 329 // it here. That way we can also handle the UI of notifying that 330 // the merged has failed. 331 call.setConnectionCapabilities(call.getConnectionCapabilities(), true); 332 } else { 333 Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId); 334 } 335 } 336 } finally { 337 Binder.restoreCallingIdentity(token); 338 Log.endSession(); 339 } 340 } 341 342 @Override 343 public void addConferenceCall(String callId, ParcelableConference parcelableConference) { 344 Log.startSession("CSW.aCC"); 345 long token = Binder.clearCallingIdentity(); 346 try { 347 synchronized (mLock) { 348 if (mCallIdMapper.getCall(callId) != null) { 349 Log.w(this, "Attempting to add a conference call using an existing " + 350 "call id %s", callId); 351 return; 352 } 353 354 // Make sure that there's at least one valid call. For remote connections 355 // we'll get a add conference msg from both the remote connection service 356 // and from the real connection service. 357 boolean hasValidCalls = false; 358 for (String connId : parcelableConference.getConnectionIds()) { 359 if (mCallIdMapper.getCall(connId) != null) { 360 hasValidCalls = true; 361 } 362 } 363 // But don't bail out if the connection count is 0, because that is a valid 364 // IMS conference state. 365 if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) { 366 Log.d(this, "Attempting to add a conference with no valid calls"); 367 return; 368 } 369 370 // need to create a new Call 371 PhoneAccountHandle phAcc = null; 372 if (parcelableConference != null && 373 parcelableConference.getPhoneAccount() != null) { 374 phAcc = parcelableConference.getPhoneAccount(); 375 } 376 Call conferenceCall = mCallsManager.createConferenceCall(callId, 377 phAcc, parcelableConference); 378 mCallIdMapper.addCall(conferenceCall, callId); 379 conferenceCall.setConnectionService(ConnectionServiceWrapper.this); 380 381 Log.d(this, "adding children to conference %s phAcc %s", 382 parcelableConference.getConnectionIds(), phAcc); 383 for (String connId : parcelableConference.getConnectionIds()) { 384 Call childCall = mCallIdMapper.getCall(connId); 385 Log.d(this, "found child: %s", connId); 386 if (childCall != null) { 387 childCall.setParentCall(conferenceCall); 388 } 389 } 390 } 391 } finally { 392 Binder.restoreCallingIdentity(token); 393 Log.endSession(); 394 } 395 } 396 397 @Override 398 public void onPostDialWait(String callId, String remaining) throws RemoteException { 399 Log.startSession("CSW.oPDW"); 400 long token = Binder.clearCallingIdentity(); 401 try { 402 synchronized (mLock) { 403 logIncoming("onPostDialWait %s %s", callId, remaining); 404 Call call = mCallIdMapper.getCall(callId); 405 if (call != null) { 406 call.onPostDialWait(remaining); 407 } else { 408 // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1); 409 } 410 } 411 } finally { 412 Binder.restoreCallingIdentity(token); 413 Log.endSession(); 414 } 415 } 416 417 @Override 418 public void onPostDialChar(String callId, char nextChar) throws RemoteException { 419 Log.startSession("CSW.oPDC"); 420 long token = Binder.clearCallingIdentity(); 421 try { 422 synchronized (mLock) { 423 logIncoming("onPostDialChar %s %s", callId, nextChar); 424 Call call = mCallIdMapper.getCall(callId); 425 if (call != null) { 426 call.onPostDialChar(nextChar); 427 } else { 428 // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1); 429 } 430 } 431 } finally { 432 Binder.restoreCallingIdentity(token); 433 Log.endSession(); 434 } 435 } 436 437 @Override 438 public void queryRemoteConnectionServices(RemoteServiceCallback callback) { 439 final UserHandle callingUserHandle = Binder.getCallingUserHandle(); 440 Log.startSession("CSW.qRCS"); 441 long token = Binder.clearCallingIdentity(); 442 try { 443 synchronized (mLock) { 444 logIncoming("queryRemoteConnectionServices %s", callback); 445 ConnectionServiceWrapper.this 446 .queryRemoteConnectionServices(callingUserHandle, callback); 447 } 448 } finally { 449 Binder.restoreCallingIdentity(token); 450 Log.endSession(); 451 } 452 } 453 454 @Override 455 public void setVideoState(String callId, int videoState) { 456 Log.startSession("CSW.sVS"); 457 long token = Binder.clearCallingIdentity(); 458 try { 459 synchronized (mLock) { 460 logIncoming("setVideoState %s %d", callId, videoState); 461 Call call = mCallIdMapper.getCall(callId); 462 if (call != null) { 463 call.setVideoState(videoState); 464 } 465 } 466 } finally { 467 Binder.restoreCallingIdentity(token); 468 Log.endSession(); 469 } 470 } 471 472 @Override 473 public void setIsVoipAudioMode(String callId, boolean isVoip) { 474 Log.startSession("CSW.sIVAM"); 475 long token = Binder.clearCallingIdentity(); 476 try { 477 synchronized (mLock) { 478 logIncoming("setIsVoipAudioMode %s %b", callId, isVoip); 479 Call call = mCallIdMapper.getCall(callId); 480 if (call != null) { 481 call.setIsVoipAudioMode(isVoip); 482 } 483 } 484 } finally { 485 Binder.restoreCallingIdentity(token); 486 Log.endSession(); 487 } 488 } 489 490 @Override 491 public void setStatusHints(String callId, StatusHints statusHints) { 492 Log.startSession("CSW.sSH"); 493 long token = Binder.clearCallingIdentity(); 494 try { 495 synchronized (mLock) { 496 logIncoming("setStatusHints %s %s", callId, statusHints); 497 Call call = mCallIdMapper.getCall(callId); 498 if (call != null) { 499 call.setStatusHints(statusHints); 500 } 501 } 502 } finally { 503 Binder.restoreCallingIdentity(token); 504 Log.endSession(); 505 } 506 } 507 508 @Override 509 public void putExtras(String callId, Bundle extras) { 510 Log.startSession("CSW.pE"); 511 long token = Binder.clearCallingIdentity(); 512 try { 513 synchronized (mLock) { 514 Bundle.setDefusable(extras, true); 515 Call call = mCallIdMapper.getCall(callId); 516 if (call != null) { 517 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras); 518 } 519 } 520 } finally { 521 Binder.restoreCallingIdentity(token); 522 Log.endSession(); 523 } 524 } 525 526 @Override 527 public void removeExtras(String callId, List<String> keys) { 528 Log.startSession("CSW.rE"); 529 long token = Binder.clearCallingIdentity(); 530 try { 531 synchronized (mLock) { 532 logIncoming("removeExtra %s %s", callId, keys); 533 Call call = mCallIdMapper.getCall(callId); 534 if (call != null) { 535 call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys); 536 } 537 } 538 } finally { 539 Binder.restoreCallingIdentity(token); 540 Log.endSession(); 541 } 542 } 543 544 @Override 545 public void setAddress(String callId, Uri address, int presentation) { 546 Log.startSession("CSW.sA"); 547 long token = Binder.clearCallingIdentity(); 548 try { 549 synchronized (mLock) { 550 logIncoming("setAddress %s %s %d", callId, address, presentation); 551 Call call = mCallIdMapper.getCall(callId); 552 if (call != null) { 553 call.setHandle(address, presentation); 554 } 555 } 556 } finally { 557 Binder.restoreCallingIdentity(token); 558 Log.endSession(); 559 } 560 } 561 562 @Override 563 public void setCallerDisplayName( 564 String callId, String callerDisplayName, int presentation) { 565 Log.startSession("CSW.sCDN"); 566 long token = Binder.clearCallingIdentity(); 567 try { 568 synchronized (mLock) { 569 logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, 570 presentation); 571 Call call = mCallIdMapper.getCall(callId); 572 if (call != null) { 573 call.setCallerDisplayName(callerDisplayName, presentation); 574 } 575 } 576 } finally { 577 Binder.restoreCallingIdentity(token); 578 Log.endSession(); 579 } 580 } 581 582 @Override 583 public void setConferenceableConnections( 584 String callId, List<String> conferenceableCallIds) { 585 Log.startSession("CSW.sCC"); 586 long token = Binder.clearCallingIdentity(); 587 try { 588 synchronized (mLock) { 589 logIncoming("setConferenceableConnections %s %s", callId, 590 conferenceableCallIds); 591 Call call = mCallIdMapper.getCall(callId); 592 if (call != null) { 593 List<Call> conferenceableCalls = 594 new ArrayList<>(conferenceableCallIds.size()); 595 for (String otherId : conferenceableCallIds) { 596 Call otherCall = mCallIdMapper.getCall(otherId); 597 if (otherCall != null && otherCall != call) { 598 conferenceableCalls.add(otherCall); 599 } 600 } 601 call.setConferenceableCalls(conferenceableCalls); 602 } 603 } 604 } finally { 605 Binder.restoreCallingIdentity(token); 606 Log.endSession(); 607 } 608 } 609 610 @Override 611 public void addExistingConnection(String callId, ParcelableConnection connection) { 612 Log.startSession("CSW.aEC"); 613 long token = Binder.clearCallingIdentity(); 614 try { 615 synchronized (mLock) { 616 logIncoming("addExistingConnection %s %s", callId, connection); 617 Call existingCall = mCallsManager 618 .createCallForExistingConnection(callId, connection); 619 mCallIdMapper.addCall(existingCall, callId); 620 existingCall.setConnectionService(ConnectionServiceWrapper.this); 621 } 622 } finally { 623 Binder.restoreCallingIdentity(token); 624 Log.endSession(); 625 } 626 } 627 628 @Override 629 public void onConnectionEvent(String callId, String event, Bundle extras) { 630 Log.startSession("CSW.oCE"); 631 long token = Binder.clearCallingIdentity(); 632 try { 633 synchronized (mLock) { 634 Bundle.setDefusable(extras, true); 635 Call call = mCallIdMapper.getCall(callId); 636 if (call != null) { 637 call.onConnectionEvent(event, extras); 638 } 639 } 640 } finally { 641 Binder.restoreCallingIdentity(token); 642 Log.endSession(); 643 } 644 } 645 } 646 647 private final Adapter mAdapter = new Adapter(); 648 private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId); 649 private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>(); 650 651 private Binder2 mBinder = new Binder2(); 652 private IConnectionService mServiceInterface; 653 private final ConnectionServiceRepository mConnectionServiceRepository; 654 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 655 private final CallsManager mCallsManager; 656 657 /** 658 * Creates a connection service. 659 * 660 * @param componentName The component name of the service with which to bind. 661 * @param connectionServiceRepository Connection service repository. 662 * @param phoneAccountRegistrar Phone account registrar 663 * @param callsManager Calls manager 664 * @param context The context. 665 * @param userHandle The {@link UserHandle} to use when binding. 666 */ 667 ConnectionServiceWrapper( 668 ComponentName componentName, 669 ConnectionServiceRepository connectionServiceRepository, 670 PhoneAccountRegistrar phoneAccountRegistrar, 671 CallsManager callsManager, 672 Context context, 673 TelecomSystem.SyncRoot lock, 674 UserHandle userHandle) { 675 super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle); 676 mConnectionServiceRepository = connectionServiceRepository; 677 phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() { 678 // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections 679 // To do this, we must proxy remote ConnectionService objects 680 }); 681 mPhoneAccountRegistrar = phoneAccountRegistrar; 682 mCallsManager = callsManager; 683 } 684 685 /** See {@link IConnectionService#addConnectionServiceAdapter}. */ 686 private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 687 if (isServiceValid("addConnectionServiceAdapter")) { 688 try { 689 logOutgoing("addConnectionServiceAdapter %s", adapter); 690 mServiceInterface.addConnectionServiceAdapter(adapter); 691 } catch (RemoteException e) { 692 } 693 } 694 } 695 696 /** See {@link IConnectionService#removeConnectionServiceAdapter}. */ 697 private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 698 if (isServiceValid("removeConnectionServiceAdapter")) { 699 try { 700 logOutgoing("removeConnectionServiceAdapter %s", adapter); 701 mServiceInterface.removeConnectionServiceAdapter(adapter); 702 } catch (RemoteException e) { 703 } 704 } 705 } 706 707 /** 708 * Creates a new connection for a new outgoing call or to attach to an existing incoming call. 709 */ 710 @VisibleForTesting 711 public void createConnection(final Call call, final CreateConnectionResponse response) { 712 Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); 713 BindCallback callback = new BindCallback() { 714 @Override 715 public void onSuccess() { 716 String callId = mCallIdMapper.getCallId(call); 717 mPendingResponses.put(callId, response); 718 719 GatewayInfo gatewayInfo = call.getGatewayInfo(); 720 Bundle extras = call.getIntentExtras(); 721 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && 722 gatewayInfo.getOriginalAddress() != null) { 723 extras = (Bundle) extras.clone(); 724 extras.putString( 725 TelecomManager.GATEWAY_PROVIDER_PACKAGE, 726 gatewayInfo.getGatewayProviderPackageName()); 727 extras.putParcelable( 728 TelecomManager.GATEWAY_ORIGINAL_ADDRESS, 729 gatewayInfo.getOriginalAddress()); 730 } 731 732 Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.piiHandle(call.getHandle())); 733 try { 734 mServiceInterface.createConnection( 735 call.getConnectionManagerPhoneAccount(), 736 callId, 737 new ConnectionRequest( 738 call.getTargetPhoneAccount(), 739 call.getHandle(), 740 extras, 741 call.getVideoState(), 742 callId), 743 call.shouldAttachToExistingConnection(), 744 call.isUnknown()); 745 } catch (RemoteException e) { 746 Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); 747 mPendingResponses.remove(callId).handleCreateConnectionFailure( 748 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 749 } 750 } 751 752 @Override 753 public void onFailure() { 754 Log.e(this, new Exception(), "Failure to call %s", getComponentName()); 755 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); 756 } 757 }; 758 759 mBinder.bind(callback, call); 760 } 761 762 /** @see IConnectionService#abort(String) */ 763 void abort(Call call) { 764 // Clear out any pending outgoing call data 765 final String callId = mCallIdMapper.getCallId(call); 766 767 // If still bound, tell the connection service to abort. 768 if (callId != null && isServiceValid("abort")) { 769 try { 770 logOutgoing("abort %s", callId); 771 mServiceInterface.abort(callId); 772 } catch (RemoteException e) { 773 } 774 } 775 776 removeCall(call, new DisconnectCause(DisconnectCause.LOCAL)); 777 } 778 779 /** @see IConnectionService#silence(String) */ 780 void silence(Call call) { 781 final String callId = mCallIdMapper.getCallId(call); 782 if (callId != null && isServiceValid("silence")) { 783 try { 784 logOutgoing("silence %s", callId); 785 mServiceInterface.silence(callId); 786 } catch (RemoteException e) { 787 } 788 } 789 } 790 791 /** @see IConnectionService#hold(String) */ 792 void hold(Call call) { 793 final String callId = mCallIdMapper.getCallId(call); 794 if (callId != null && isServiceValid("hold")) { 795 try { 796 logOutgoing("hold %s", callId); 797 mServiceInterface.hold(callId); 798 } catch (RemoteException e) { 799 } 800 } 801 } 802 803 /** @see IConnectionService#unhold(String) */ 804 void unhold(Call call) { 805 final String callId = mCallIdMapper.getCallId(call); 806 if (callId != null && isServiceValid("unhold")) { 807 try { 808 logOutgoing("unhold %s", callId); 809 mServiceInterface.unhold(callId); 810 } catch (RemoteException e) { 811 } 812 } 813 } 814 815 /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState) */ 816 @VisibleForTesting 817 public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) { 818 final String callId = mCallIdMapper.getCallId(activeCall); 819 if (callId != null && isServiceValid("onCallAudioStateChanged")) { 820 try { 821 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState); 822 mServiceInterface.onCallAudioStateChanged(callId, audioState); 823 } catch (RemoteException e) { 824 } 825 } 826 } 827 828 /** @see IConnectionService#disconnect(String) */ 829 void disconnect(Call call) { 830 final String callId = mCallIdMapper.getCallId(call); 831 if (callId != null && isServiceValid("disconnect")) { 832 try { 833 logOutgoing("disconnect %s", callId); 834 mServiceInterface.disconnect(callId); 835 } catch (RemoteException e) { 836 } 837 } 838 } 839 840 /** @see IConnectionService#answer(String) */ 841 void answer(Call call, int videoState) { 842 final String callId = mCallIdMapper.getCallId(call); 843 if (callId != null && isServiceValid("answer")) { 844 try { 845 logOutgoing("answer %s %d", callId, videoState); 846 if (VideoProfile.isAudioOnly(videoState)) { 847 mServiceInterface.answer(callId); 848 } else { 849 mServiceInterface.answerVideo(callId, videoState); 850 } 851 } catch (RemoteException e) { 852 } 853 } 854 } 855 856 /** @see IConnectionService#reject(String) */ 857 void reject(Call call, boolean rejectWithMessage, String message) { 858 final String callId = mCallIdMapper.getCallId(call); 859 if (callId != null && isServiceValid("reject")) { 860 try { 861 logOutgoing("reject %s", callId); 862 863 if (rejectWithMessage && call.can( 864 Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) { 865 mServiceInterface.rejectWithMessage(callId, message); 866 } else { 867 mServiceInterface.reject(callId); 868 } 869 } catch (RemoteException e) { 870 } 871 } 872 } 873 874 /** @see IConnectionService#playDtmfTone(String, char) */ 875 void playDtmfTone(Call call, char digit) { 876 final String callId = mCallIdMapper.getCallId(call); 877 if (callId != null && isServiceValid("playDtmfTone")) { 878 try { 879 logOutgoing("playDtmfTone %s %c", callId, digit); 880 mServiceInterface.playDtmfTone(callId, digit); 881 } catch (RemoteException e) { 882 } 883 } 884 } 885 886 /** @see IConnectionService#stopDtmfTone(String) */ 887 void stopDtmfTone(Call call) { 888 final String callId = mCallIdMapper.getCallId(call); 889 if (callId != null && isServiceValid("stopDtmfTone")) { 890 try { 891 logOutgoing("stopDtmfTone %s", callId); 892 mServiceInterface.stopDtmfTone(callId); 893 } catch (RemoteException e) { 894 } 895 } 896 } 897 898 void addCall(Call call) { 899 if (mCallIdMapper.getCallId(call) == null) { 900 mCallIdMapper.addCall(call); 901 } 902 } 903 904 /** 905 * Associates newCall with this connection service by replacing callToReplace. 906 */ 907 void replaceCall(Call newCall, Call callToReplace) { 908 Preconditions.checkState(callToReplace.getConnectionService() == this); 909 mCallIdMapper.replaceCall(newCall, callToReplace); 910 } 911 912 void removeCall(Call call) { 913 removeCall(call, new DisconnectCause(DisconnectCause.ERROR)); 914 } 915 916 void removeCall(String callId, DisconnectCause disconnectCause) { 917 CreateConnectionResponse response = mPendingResponses.remove(callId); 918 if (response != null) { 919 response.handleCreateConnectionFailure(disconnectCause); 920 } 921 922 mCallIdMapper.removeCall(callId); 923 } 924 925 void removeCall(Call call, DisconnectCause disconnectCause) { 926 CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call)); 927 if (response != null) { 928 response.handleCreateConnectionFailure(disconnectCause); 929 } 930 931 mCallIdMapper.removeCall(call); 932 } 933 934 void onPostDialContinue(Call call, boolean proceed) { 935 final String callId = mCallIdMapper.getCallId(call); 936 if (callId != null && isServiceValid("onPostDialContinue")) { 937 try { 938 logOutgoing("onPostDialContinue %s %b", callId, proceed); 939 mServiceInterface.onPostDialContinue(callId, proceed); 940 } catch (RemoteException ignored) { 941 } 942 } 943 } 944 945 void conference(final Call call, Call otherCall) { 946 final String callId = mCallIdMapper.getCallId(call); 947 final String otherCallId = mCallIdMapper.getCallId(otherCall); 948 if (callId != null && otherCallId != null && isServiceValid("conference")) { 949 try { 950 logOutgoing("conference %s %s", callId, otherCallId); 951 mServiceInterface.conference(callId, otherCallId); 952 } catch (RemoteException ignored) { 953 } 954 } 955 } 956 957 void splitFromConference(Call call) { 958 final String callId = mCallIdMapper.getCallId(call); 959 if (callId != null && isServiceValid("splitFromConference")) { 960 try { 961 logOutgoing("splitFromConference %s", callId); 962 mServiceInterface.splitFromConference(callId); 963 } catch (RemoteException ignored) { 964 } 965 } 966 } 967 968 void mergeConference(Call call) { 969 final String callId = mCallIdMapper.getCallId(call); 970 if (callId != null && isServiceValid("mergeConference")) { 971 try { 972 logOutgoing("mergeConference %s", callId); 973 mServiceInterface.mergeConference(callId); 974 } catch (RemoteException ignored) { 975 } 976 } 977 } 978 979 void swapConference(Call call) { 980 final String callId = mCallIdMapper.getCallId(call); 981 if (callId != null && isServiceValid("swapConference")) { 982 try { 983 logOutgoing("swapConference %s", callId); 984 mServiceInterface.swapConference(callId); 985 } catch (RemoteException ignored) { 986 } 987 } 988 } 989 990 void pullExternalCall(Call call) { 991 final String callId = mCallIdMapper.getCallId(call); 992 if (callId != null && isServiceValid("pullExternalCall")) { 993 try { 994 logOutgoing("pullExternalCall %s", callId); 995 mServiceInterface.pullExternalCall(callId); 996 } catch (RemoteException ignored) { 997 } 998 } 999 } 1000 1001 void sendCallEvent(Call call, String event, Bundle extras) { 1002 final String callId = mCallIdMapper.getCallId(call); 1003 if (callId != null && isServiceValid("sendCallEvent")) { 1004 try { 1005 logOutgoing("sendCallEvent %s %s", callId, event); 1006 mServiceInterface.sendCallEvent(callId, event, extras); 1007 } catch (RemoteException ignored) { 1008 } 1009 } 1010 } 1011 1012 void onExtrasChanged(Call call, Bundle extras) { 1013 final String callId = mCallIdMapper.getCallId(call); 1014 if (callId != null && isServiceValid("onExtrasChanged")) { 1015 try { 1016 logOutgoing("onExtrasChanged %s %s", callId, extras); 1017 mServiceInterface.onExtrasChanged(callId, extras); 1018 } catch (RemoteException ignored) { 1019 } 1020 } 1021 } 1022 1023 /** {@inheritDoc} */ 1024 @Override 1025 protected void setServiceInterface(IBinder binder) { 1026 mServiceInterface = IConnectionService.Stub.asInterface(binder); 1027 Log.v(this, "Adding Connection Service Adapter."); 1028 addConnectionServiceAdapter(mAdapter); 1029 } 1030 1031 /** {@inheritDoc} */ 1032 @Override 1033 protected void removeServiceInterface() { 1034 Log.v(this, "Removing Connection Service Adapter."); 1035 removeConnectionServiceAdapter(mAdapter); 1036 // We have lost our service connection. Notify the world that this service is done. 1037 // We must notify the adapter before CallsManager. The adapter will force any pending 1038 // outgoing calls to try the next service. This needs to happen before CallsManager 1039 // tries to clean up any calls still associated with this service. 1040 handleConnectionServiceDeath(); 1041 mCallsManager.handleConnectionServiceDeath(this); 1042 mServiceInterface = null; 1043 } 1044 1045 private void handleCreateConnectionComplete( 1046 String callId, 1047 ConnectionRequest request, 1048 ParcelableConnection connection) { 1049 // TODO: Note we are not using parameter "request", which is a side effect of our tacit 1050 // assumption that we have at most one outgoing connection attempt per ConnectionService. 1051 // This may not continue to be the case. 1052 if (connection.getState() == Connection.STATE_DISCONNECTED) { 1053 // A connection that begins in the DISCONNECTED state is an indication of 1054 // failure to connect; we handle all failures uniformly 1055 removeCall(callId, connection.getDisconnectCause()); 1056 } else { 1057 // Successful connection 1058 if (mPendingResponses.containsKey(callId)) { 1059 mPendingResponses.remove(callId) 1060 .handleCreateConnectionSuccess(mCallIdMapper, connection); 1061 } 1062 } 1063 } 1064 1065 /** 1066 * Called when the associated connection service dies. 1067 */ 1068 private void handleConnectionServiceDeath() { 1069 if (!mPendingResponses.isEmpty()) { 1070 CreateConnectionResponse[] responses = mPendingResponses.values().toArray( 1071 new CreateConnectionResponse[mPendingResponses.values().size()]); 1072 mPendingResponses.clear(); 1073 for (int i = 0; i < responses.length; i++) { 1074 responses[i].handleCreateConnectionFailure( 1075 new DisconnectCause(DisconnectCause.ERROR)); 1076 } 1077 } 1078 mCallIdMapper.clear(); 1079 } 1080 1081 private void logIncoming(String msg, Object... params) { 1082 Log.d(this, "ConnectionService -> Telecom: " + msg, params); 1083 } 1084 1085 private void logOutgoing(String msg, Object... params) { 1086 Log.d(this, "Telecom -> ConnectionService: " + msg, params); 1087 } 1088 1089 private void queryRemoteConnectionServices(final UserHandle userHandle, 1090 final RemoteServiceCallback callback) { 1091 // Only give remote connection services to this connection service if it is listed as 1092 // the connection manager. 1093 PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle); 1094 Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager); 1095 if (simCallManager == null || 1096 !simCallManager.getComponentName().equals(getComponentName())) { 1097 noRemoteServices(callback); 1098 return; 1099 } 1100 1101 // Make a list of ConnectionServices that are listed as being associated with SIM accounts 1102 final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap( 1103 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1)); 1104 for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) { 1105 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 1106 handle.getComponentName(), handle.getUserHandle()); 1107 if (service != null) { 1108 simServices.add(service); 1109 } 1110 } 1111 1112 final List<ComponentName> simServiceComponentNames = new ArrayList<>(); 1113 final List<IBinder> simServiceBinders = new ArrayList<>(); 1114 1115 Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices); 1116 1117 for (ConnectionServiceWrapper simService : simServices) { 1118 if (simService == this) { 1119 // Only happens in the unlikely case that a SIM service is also a SIM call manager 1120 continue; 1121 } 1122 1123 final ConnectionServiceWrapper currentSimService = simService; 1124 1125 currentSimService.mBinder.bind(new BindCallback() { 1126 @Override 1127 public void onSuccess() { 1128 Log.d(this, "Adding simService %s", currentSimService.getComponentName()); 1129 simServiceComponentNames.add(currentSimService.getComponentName()); 1130 simServiceBinders.add(currentSimService.mServiceInterface.asBinder()); 1131 maybeComplete(); 1132 } 1133 1134 @Override 1135 public void onFailure() { 1136 Log.d(this, "Failed simService %s", currentSimService.getComponentName()); 1137 // We know maybeComplete() will always be a no-op from now on, so go ahead and 1138 // signal failure of the entire request 1139 noRemoteServices(callback); 1140 } 1141 1142 private void maybeComplete() { 1143 if (simServiceComponentNames.size() == simServices.size()) { 1144 setRemoteServices(callback, simServiceComponentNames, simServiceBinders); 1145 } 1146 } 1147 }, null); 1148 } 1149 } 1150 1151 private void setRemoteServices( 1152 RemoteServiceCallback callback, 1153 List<ComponentName> componentNames, 1154 List<IBinder> binders) { 1155 try { 1156 callback.onResult(componentNames, binders); 1157 } catch (RemoteException e) { 1158 Log.e(this, e, "Contacting ConnectionService %s", 1159 ConnectionServiceWrapper.this.getComponentName()); 1160 } 1161 } 1162 1163 private void noRemoteServices(RemoteServiceCallback callback) { 1164 setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST); 1165 } 1166} 1167