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