ConnectionServiceWrapper.java revision 4b1617a620039fd0d8321028b46c27b486b90763
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 setIsConferenced(String callId, String conferenceCallId) { 268 Log.startSession("CSW.sIC"); 269 long token = Binder.clearCallingIdentity(); 270 try { 271 synchronized (mLock) { 272 logIncoming("setIsConferenced %s %s", callId, conferenceCallId); 273 Call childCall = mCallIdMapper.getCall(callId); 274 if (childCall != null) { 275 if (conferenceCallId == null) { 276 Log.d(this, "unsetting parent: %s", conferenceCallId); 277 childCall.setParentCall(null); 278 } else { 279 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId); 280 childCall.setParentCall(conferenceCall); 281 } 282 } else { 283 // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1); 284 } 285 } 286 } finally { 287 Binder.restoreCallingIdentity(token); 288 Log.endSession(); 289 } 290 } 291 292 @Override 293 public void setConferenceMergeFailed(String callId) { 294 Log.startSession("CSW.sCMF"); 295 long token = Binder.clearCallingIdentity(); 296 try { 297 synchronized (mLock) { 298 logIncoming("setConferenceMergeFailed %s", callId); 299 // TODO: we should move the UI for indication a merge failure here 300 // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can 301 // deliver the message anyway that they want. b/20530631. 302 Call call = mCallIdMapper.getCall(callId); 303 if (call != null) { 304 // Just refresh the connection capabilities so that the UI 305 // is forced to reenable the merge button as the capability 306 // is still on the connection. Note when b/20530631 is fixed, we need 307 // to revisit this fix to remove this hacky way of unhiding the merge 308 // button (side effect of reprocessing the capabilities) and plumb 309 // the failure event all the way to InCallUI instead of stopping 310 // it here. That way we can also handle the UI of notifying that 311 // the merged has failed. 312 call.setConnectionCapabilities(call.getConnectionCapabilities(), true); 313 } else { 314 Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId); 315 } 316 } 317 } finally { 318 Binder.restoreCallingIdentity(token); 319 Log.endSession(); 320 } 321 } 322 323 @Override 324 public void addConferenceCall(String callId, ParcelableConference parcelableConference) { 325 Log.startSession("CSW.aCC"); 326 long token = Binder.clearCallingIdentity(); 327 try { 328 synchronized (mLock) { 329 if (mCallIdMapper.getCall(callId) != null) { 330 Log.w(this, "Attempting to add a conference call using an existing " + 331 "call id %s", callId); 332 return; 333 } 334 335 // Make sure that there's at least one valid call. For remote connections 336 // we'll get a add conference msg from both the remote connection service 337 // and from the real connection service. 338 boolean hasValidCalls = false; 339 for (String connId : parcelableConference.getConnectionIds()) { 340 if (mCallIdMapper.getCall(connId) != null) { 341 hasValidCalls = true; 342 } 343 } 344 // But don't bail out if the connection count is 0, because that is a valid 345 // IMS conference state. 346 if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) { 347 Log.d(this, "Attempting to add a conference with no valid calls"); 348 return; 349 } 350 351 // need to create a new Call 352 PhoneAccountHandle phAcc = null; 353 if (parcelableConference != null && 354 parcelableConference.getPhoneAccount() != null) { 355 phAcc = parcelableConference.getPhoneAccount(); 356 } 357 Call conferenceCall = mCallsManager.createConferenceCall(callId, 358 phAcc, parcelableConference); 359 mCallIdMapper.addCall(conferenceCall, callId); 360 conferenceCall.setConnectionService(ConnectionServiceWrapper.this); 361 362 Log.d(this, "adding children to conference %s phAcc %s", 363 parcelableConference.getConnectionIds(), phAcc); 364 for (String connId : parcelableConference.getConnectionIds()) { 365 Call childCall = mCallIdMapper.getCall(connId); 366 Log.d(this, "found child: %s", connId); 367 if (childCall != null) { 368 childCall.setParentCall(conferenceCall); 369 } 370 } 371 } 372 } finally { 373 Binder.restoreCallingIdentity(token); 374 Log.endSession(); 375 } 376 } 377 378 @Override 379 public void onPostDialWait(String callId, String remaining) throws RemoteException { 380 Log.startSession("CSW.oPDW"); 381 long token = Binder.clearCallingIdentity(); 382 try { 383 synchronized (mLock) { 384 logIncoming("onPostDialWait %s %s", callId, remaining); 385 Call call = mCallIdMapper.getCall(callId); 386 if (call != null) { 387 call.onPostDialWait(remaining); 388 } else { 389 // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1); 390 } 391 } 392 } finally { 393 Binder.restoreCallingIdentity(token); 394 Log.endSession(); 395 } 396 } 397 398 @Override 399 public void onPostDialChar(String callId, char nextChar) throws RemoteException { 400 Log.startSession("CSW.oPDC"); 401 long token = Binder.clearCallingIdentity(); 402 try { 403 synchronized (mLock) { 404 logIncoming("onPostDialChar %s %s", callId, nextChar); 405 Call call = mCallIdMapper.getCall(callId); 406 if (call != null) { 407 call.onPostDialChar(nextChar); 408 } else { 409 // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1); 410 } 411 } 412 } finally { 413 Binder.restoreCallingIdentity(token); 414 Log.endSession(); 415 } 416 } 417 418 @Override 419 public void queryRemoteConnectionServices(RemoteServiceCallback callback) { 420 final UserHandle callingUserHandle = Binder.getCallingUserHandle(); 421 Log.startSession("CSW.qRCS"); 422 long token = Binder.clearCallingIdentity(); 423 try { 424 synchronized (mLock) { 425 logIncoming("queryRemoteConnectionServices %s", callback); 426 ConnectionServiceWrapper.this 427 .queryRemoteConnectionServices(callingUserHandle, callback); 428 } 429 } finally { 430 Binder.restoreCallingIdentity(token); 431 Log.endSession(); 432 } 433 } 434 435 @Override 436 public void setVideoState(String callId, int videoState) { 437 Log.startSession("CSW.sVS"); 438 long token = Binder.clearCallingIdentity(); 439 try { 440 synchronized (mLock) { 441 logIncoming("setVideoState %s %d", callId, videoState); 442 Call call = mCallIdMapper.getCall(callId); 443 if (call != null) { 444 call.setVideoState(videoState); 445 } 446 } 447 } finally { 448 Binder.restoreCallingIdentity(token); 449 Log.endSession(); 450 } 451 } 452 453 @Override 454 public void setIsVoipAudioMode(String callId, boolean isVoip) { 455 Log.startSession("CSW.sIVAM"); 456 long token = Binder.clearCallingIdentity(); 457 try { 458 synchronized (mLock) { 459 logIncoming("setIsVoipAudioMode %s %b", callId, isVoip); 460 Call call = mCallIdMapper.getCall(callId); 461 if (call != null) { 462 call.setIsVoipAudioMode(isVoip); 463 } 464 } 465 } finally { 466 Binder.restoreCallingIdentity(token); 467 Log.endSession(); 468 } 469 } 470 471 @Override 472 public void setStatusHints(String callId, StatusHints statusHints) { 473 Log.startSession("CSW.sSH"); 474 long token = Binder.clearCallingIdentity(); 475 try { 476 synchronized (mLock) { 477 logIncoming("setStatusHints %s %s", callId, statusHints); 478 Call call = mCallIdMapper.getCall(callId); 479 if (call != null) { 480 call.setStatusHints(statusHints); 481 } 482 } 483 } finally { 484 Binder.restoreCallingIdentity(token); 485 Log.endSession(); 486 } 487 } 488 489 @Override 490 public void setExtras(String callId, Bundle extras) { 491 Log.startSession("CSW.sE"); 492 long token = Binder.clearCallingIdentity(); 493 try { 494 synchronized (mLock) { 495 logIncoming("setExtras %s %s", callId, extras); 496 Call call = mCallIdMapper.getCall(callId); 497 if (call != null) { 498 call.setExtras(extras); 499 } 500 } 501 } finally { 502 Binder.restoreCallingIdentity(token); 503 Log.endSession(); 504 } 505 } 506 507 @Override 508 public void setAddress(String callId, Uri address, int presentation) { 509 Log.startSession("CSW.sA"); 510 long token = Binder.clearCallingIdentity(); 511 try { 512 synchronized (mLock) { 513 logIncoming("setAddress %s %s %d", callId, address, presentation); 514 Call call = mCallIdMapper.getCall(callId); 515 if (call != null) { 516 call.setHandle(address, presentation); 517 } 518 } 519 } finally { 520 Binder.restoreCallingIdentity(token); 521 Log.endSession(); 522 } 523 } 524 525 @Override 526 public void setCallerDisplayName( 527 String callId, String callerDisplayName, int presentation) { 528 Log.startSession("CSW.sCDN"); 529 long token = Binder.clearCallingIdentity(); 530 try { 531 synchronized (mLock) { 532 logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, 533 presentation); 534 Call call = mCallIdMapper.getCall(callId); 535 if (call != null) { 536 call.setCallerDisplayName(callerDisplayName, presentation); 537 } 538 } 539 } finally { 540 Binder.restoreCallingIdentity(token); 541 Log.endSession(); 542 } 543 } 544 545 @Override 546 public void setConferenceableConnections( 547 String callId, List<String> conferenceableCallIds) { 548 Log.startSession("CSW.sCC"); 549 long token = Binder.clearCallingIdentity(); 550 try { 551 synchronized (mLock) { 552 logIncoming("setConferenceableConnections %s %s", callId, 553 conferenceableCallIds); 554 Call call = mCallIdMapper.getCall(callId); 555 if (call != null) { 556 List<Call> conferenceableCalls = 557 new ArrayList<>(conferenceableCallIds.size()); 558 for (String otherId : conferenceableCallIds) { 559 Call otherCall = mCallIdMapper.getCall(otherId); 560 if (otherCall != null && otherCall != call) { 561 conferenceableCalls.add(otherCall); 562 } 563 } 564 call.setConferenceableCalls(conferenceableCalls); 565 } 566 } 567 } finally { 568 Binder.restoreCallingIdentity(token); 569 Log.endSession(); 570 } 571 } 572 573 @Override 574 public void addExistingConnection(String callId, ParcelableConnection connection) { 575 Log.startSession("CSW.aEC"); 576 long token = Binder.clearCallingIdentity(); 577 try { 578 synchronized (mLock) { 579 logIncoming("addExistingConnection %s %s", callId, connection); 580 Call existingCall = mCallsManager 581 .createCallForExistingConnection(callId, connection); 582 mCallIdMapper.addCall(existingCall, callId); 583 existingCall.setConnectionService(ConnectionServiceWrapper.this); 584 } 585 } finally { 586 Binder.restoreCallingIdentity(token); 587 Log.endSession(); 588 } 589 } 590 591 @Override 592 public void onConnectionEvent(String callId, String event) { 593 Log.startSession("CSW.oCE"); 594 long token = Binder.clearCallingIdentity(); 595 try { 596 synchronized (mLock) { 597 Call call = mCallIdMapper.getCall(callId); 598 if (call != null) { 599 call.onConnectionEvent(event); 600 } 601 } 602 } finally { 603 Binder.restoreCallingIdentity(token); 604 Log.endSession(); 605 } 606 } 607 } 608 609 private final Adapter mAdapter = new Adapter(); 610 private final CallIdMapper mCallIdMapper = new CallIdMapper(); 611 private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>(); 612 613 private Binder2 mBinder = new Binder2(); 614 private IConnectionService mServiceInterface; 615 private final ConnectionServiceRepository mConnectionServiceRepository; 616 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 617 private final CallsManager mCallsManager; 618 619 /** 620 * Creates a connection service. 621 * 622 * @param componentName The component name of the service with which to bind. 623 * @param connectionServiceRepository Connection service repository. 624 * @param phoneAccountRegistrar Phone account registrar 625 * @param callsManager Calls manager 626 * @param context The context. 627 * @param userHandle The {@link UserHandle} to use when binding. 628 */ 629 ConnectionServiceWrapper( 630 ComponentName componentName, 631 ConnectionServiceRepository connectionServiceRepository, 632 PhoneAccountRegistrar phoneAccountRegistrar, 633 CallsManager callsManager, 634 Context context, 635 TelecomSystem.SyncRoot lock, 636 UserHandle userHandle) { 637 super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle); 638 mConnectionServiceRepository = connectionServiceRepository; 639 phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() { 640 // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections 641 // To do this, we must proxy remote ConnectionService objects 642 }); 643 mPhoneAccountRegistrar = phoneAccountRegistrar; 644 mCallsManager = callsManager; 645 } 646 647 /** See {@link IConnectionService#addConnectionServiceAdapter}. */ 648 private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 649 if (isServiceValid("addConnectionServiceAdapter")) { 650 try { 651 logOutgoing("addConnectionServiceAdapter %s", adapter); 652 mServiceInterface.addConnectionServiceAdapter(adapter); 653 } catch (RemoteException e) { 654 } 655 } 656 } 657 658 /** 659 * Creates a new connection for a new outgoing call or to attach to an existing incoming call. 660 */ 661 @VisibleForTesting 662 public void createConnection(final Call call, final CreateConnectionResponse response) { 663 Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); 664 BindCallback callback = new BindCallback() { 665 @Override 666 public void onSuccess() { 667 String callId = mCallIdMapper.getCallId(call); 668 mPendingResponses.put(callId, response); 669 670 GatewayInfo gatewayInfo = call.getGatewayInfo(); 671 Bundle extras = call.getIntentExtras(); 672 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && 673 gatewayInfo.getOriginalAddress() != null) { 674 extras = (Bundle) extras.clone(); 675 extras.putString( 676 TelecomManager.GATEWAY_PROVIDER_PACKAGE, 677 gatewayInfo.getGatewayProviderPackageName()); 678 extras.putParcelable( 679 TelecomManager.GATEWAY_ORIGINAL_ADDRESS, 680 gatewayInfo.getOriginalAddress()); 681 } 682 683 Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle())); 684 try { 685 mServiceInterface.createConnection( 686 call.getConnectionManagerPhoneAccount(), 687 callId, 688 new ConnectionRequest( 689 call.getTargetPhoneAccount(), 690 call.getHandle(), 691 extras, 692 call.getVideoState(), 693 callId), 694 call.shouldAttachToExistingConnection(), 695 call.isUnknown()); 696 } catch (RemoteException e) { 697 Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); 698 mPendingResponses.remove(callId).handleCreateConnectionFailure( 699 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 700 } 701 } 702 703 @Override 704 public void onFailure() { 705 Log.e(this, new Exception(), "Failure to call %s", getComponentName()); 706 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); 707 } 708 }; 709 710 mBinder.bind(callback, call); 711 } 712 713 /** @see IConnectionService#abort(String) */ 714 void abort(Call call) { 715 // Clear out any pending outgoing call data 716 final String callId = mCallIdMapper.getCallId(call); 717 718 // If still bound, tell the connection service to abort. 719 if (callId != null && isServiceValid("abort")) { 720 try { 721 logOutgoing("abort %s", callId); 722 mServiceInterface.abort(callId); 723 } catch (RemoteException e) { 724 } 725 } 726 727 removeCall(call, new DisconnectCause(DisconnectCause.LOCAL)); 728 } 729 730 /** @see IConnectionService#silence(String) */ 731 void silence(Call call) { 732 final String callId = mCallIdMapper.getCallId(call); 733 if (callId != null && isServiceValid("silence")) { 734 try { 735 logOutgoing("silence %s", callId); 736 mServiceInterface.silence(callId); 737 } catch (RemoteException e) { 738 } 739 } 740 } 741 742 /** @see IConnectionService#hold(String) */ 743 void hold(Call call) { 744 final String callId = mCallIdMapper.getCallId(call); 745 if (callId != null && isServiceValid("hold")) { 746 try { 747 logOutgoing("hold %s", callId); 748 mServiceInterface.hold(callId); 749 } catch (RemoteException e) { 750 } 751 } 752 } 753 754 /** @see IConnectionService#unhold(String) */ 755 void unhold(Call call) { 756 final String callId = mCallIdMapper.getCallId(call); 757 if (callId != null && isServiceValid("unhold")) { 758 try { 759 logOutgoing("unhold %s", callId); 760 mServiceInterface.unhold(callId); 761 } catch (RemoteException e) { 762 } 763 } 764 } 765 766 /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState) */ 767 @VisibleForTesting 768 public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) { 769 final String callId = mCallIdMapper.getCallId(activeCall); 770 if (callId != null && isServiceValid("onCallAudioStateChanged")) { 771 try { 772 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState); 773 mServiceInterface.onCallAudioStateChanged(callId, audioState); 774 } catch (RemoteException e) { 775 } 776 } 777 } 778 779 /** @see IConnectionService#disconnect(String) */ 780 void disconnect(Call call) { 781 final String callId = mCallIdMapper.getCallId(call); 782 if (callId != null && isServiceValid("disconnect")) { 783 try { 784 logOutgoing("disconnect %s", callId); 785 mServiceInterface.disconnect(callId); 786 } catch (RemoteException e) { 787 } 788 } 789 } 790 791 /** @see IConnectionService#answer(String) */ 792 void answer(Call call, int videoState) { 793 final String callId = mCallIdMapper.getCallId(call); 794 if (callId != null && isServiceValid("answer")) { 795 try { 796 logOutgoing("answer %s %d", callId, videoState); 797 if (VideoProfile.isAudioOnly(videoState)) { 798 mServiceInterface.answer(callId); 799 } else { 800 mServiceInterface.answerVideo(callId, videoState); 801 } 802 } catch (RemoteException e) { 803 } 804 } 805 } 806 807 /** @see IConnectionService#reject(String) */ 808 void reject(Call call, boolean rejectWithMessage, String message) { 809 final String callId = mCallIdMapper.getCallId(call); 810 if (callId != null && isServiceValid("reject")) { 811 try { 812 logOutgoing("reject %s", callId); 813 814 if (rejectWithMessage && call.can( 815 Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) { 816 mServiceInterface.rejectWithMessage(callId, message); 817 } else { 818 mServiceInterface.reject(callId); 819 } 820 } catch (RemoteException e) { 821 } 822 } 823 } 824 825 /** @see IConnectionService#playDtmfTone(String, char) */ 826 void playDtmfTone(Call call, char digit) { 827 final String callId = mCallIdMapper.getCallId(call); 828 if (callId != null && isServiceValid("playDtmfTone")) { 829 try { 830 logOutgoing("playDtmfTone %s %c", callId, digit); 831 mServiceInterface.playDtmfTone(callId, digit); 832 } catch (RemoteException e) { 833 } 834 } 835 } 836 837 /** @see IConnectionService#stopDtmfTone(String) */ 838 void stopDtmfTone(Call call) { 839 final String callId = mCallIdMapper.getCallId(call); 840 if (callId != null && isServiceValid("stopDtmfTone")) { 841 try { 842 logOutgoing("stopDtmfTone %s", callId); 843 mServiceInterface.stopDtmfTone(callId); 844 } catch (RemoteException e) { 845 } 846 } 847 } 848 849 void addCall(Call call) { 850 if (mCallIdMapper.getCallId(call) == null) { 851 mCallIdMapper.addCall(call); 852 } 853 } 854 855 /** 856 * Associates newCall with this connection service by replacing callToReplace. 857 */ 858 void replaceCall(Call newCall, Call callToReplace) { 859 Preconditions.checkState(callToReplace.getConnectionService() == this); 860 mCallIdMapper.replaceCall(newCall, callToReplace); 861 } 862 863 void removeCall(Call call) { 864 removeCall(call, new DisconnectCause(DisconnectCause.ERROR)); 865 } 866 867 void removeCall(String callId, DisconnectCause disconnectCause) { 868 CreateConnectionResponse response = mPendingResponses.remove(callId); 869 if (response != null) { 870 response.handleCreateConnectionFailure(disconnectCause); 871 } 872 873 mCallIdMapper.removeCall(callId); 874 } 875 876 void removeCall(Call call, DisconnectCause disconnectCause) { 877 CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call)); 878 if (response != null) { 879 response.handleCreateConnectionFailure(disconnectCause); 880 } 881 882 mCallIdMapper.removeCall(call); 883 } 884 885 void onPostDialContinue(Call call, boolean proceed) { 886 final String callId = mCallIdMapper.getCallId(call); 887 if (callId != null && isServiceValid("onPostDialContinue")) { 888 try { 889 logOutgoing("onPostDialContinue %s %b", callId, proceed); 890 mServiceInterface.onPostDialContinue(callId, proceed); 891 } catch (RemoteException ignored) { 892 } 893 } 894 } 895 896 void conference(final Call call, Call otherCall) { 897 final String callId = mCallIdMapper.getCallId(call); 898 final String otherCallId = mCallIdMapper.getCallId(otherCall); 899 if (callId != null && otherCallId != null && isServiceValid("conference")) { 900 try { 901 logOutgoing("conference %s %s", callId, otherCallId); 902 mServiceInterface.conference(callId, otherCallId); 903 } catch (RemoteException ignored) { 904 } 905 } 906 } 907 908 void splitFromConference(Call call) { 909 final String callId = mCallIdMapper.getCallId(call); 910 if (callId != null && isServiceValid("splitFromConference")) { 911 try { 912 logOutgoing("splitFromConference %s", callId); 913 mServiceInterface.splitFromConference(callId); 914 } catch (RemoteException ignored) { 915 } 916 } 917 } 918 919 void mergeConference(Call call) { 920 final String callId = mCallIdMapper.getCallId(call); 921 if (callId != null && isServiceValid("mergeConference")) { 922 try { 923 logOutgoing("mergeConference %s", callId); 924 mServiceInterface.mergeConference(callId); 925 } catch (RemoteException ignored) { 926 } 927 } 928 } 929 930 void swapConference(Call call) { 931 final String callId = mCallIdMapper.getCallId(call); 932 if (callId != null && isServiceValid("swapConference")) { 933 try { 934 logOutgoing("swapConference %s", callId); 935 mServiceInterface.swapConference(callId); 936 } catch (RemoteException ignored) { 937 } 938 } 939 } 940 941 /** {@inheritDoc} */ 942 @Override 943 protected void setServiceInterface(IBinder binder) { 944 if (binder == null) { 945 // We have lost our service connection. Notify the world that this service is done. 946 // We must notify the adapter before CallsManager. The adapter will force any pending 947 // outgoing calls to try the next service. This needs to happen before CallsManager 948 // tries to clean up any calls still associated with this service. 949 handleConnectionServiceDeath(); 950 mCallsManager.handleConnectionServiceDeath(this); 951 mServiceInterface = null; 952 } else { 953 mServiceInterface = IConnectionService.Stub.asInterface(binder); 954 addConnectionServiceAdapter(mAdapter); 955 } 956 } 957 958 private void handleCreateConnectionComplete( 959 String callId, 960 ConnectionRequest request, 961 ParcelableConnection connection) { 962 // TODO: Note we are not using parameter "request", which is a side effect of our tacit 963 // assumption that we have at most one outgoing connection attempt per ConnectionService. 964 // This may not continue to be the case. 965 if (connection.getState() == Connection.STATE_DISCONNECTED) { 966 // A connection that begins in the DISCONNECTED state is an indication of 967 // failure to connect; we handle all failures uniformly 968 removeCall(callId, connection.getDisconnectCause()); 969 } else { 970 // Successful connection 971 if (mPendingResponses.containsKey(callId)) { 972 mPendingResponses.remove(callId) 973 .handleCreateConnectionSuccess(mCallIdMapper, connection); 974 } 975 } 976 } 977 978 /** 979 * Called when the associated connection service dies. 980 */ 981 private void handleConnectionServiceDeath() { 982 if (!mPendingResponses.isEmpty()) { 983 CreateConnectionResponse[] responses = mPendingResponses.values().toArray( 984 new CreateConnectionResponse[mPendingResponses.values().size()]); 985 mPendingResponses.clear(); 986 for (int i = 0; i < responses.length; i++) { 987 responses[i].handleCreateConnectionFailure( 988 new DisconnectCause(DisconnectCause.ERROR)); 989 } 990 } 991 mCallIdMapper.clear(); 992 } 993 994 private void logIncoming(String msg, Object... params) { 995 Log.d(this, "ConnectionService -> Telecom: " + msg, params); 996 } 997 998 private void logOutgoing(String msg, Object... params) { 999 Log.d(this, "Telecom -> ConnectionService: " + msg, params); 1000 } 1001 1002 private void queryRemoteConnectionServices(final UserHandle userHandle, 1003 final RemoteServiceCallback callback) { 1004 // Only give remote connection services to this connection service if it is listed as 1005 // the connection manager. 1006 PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle); 1007 Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager); 1008 if (simCallManager == null || 1009 !simCallManager.getComponentName().equals(getComponentName())) { 1010 noRemoteServices(callback); 1011 return; 1012 } 1013 1014 // Make a list of ConnectionServices that are listed as being associated with SIM accounts 1015 final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap( 1016 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1)); 1017 for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) { 1018 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 1019 handle.getComponentName(), handle.getUserHandle()); 1020 if (service != null) { 1021 simServices.add(service); 1022 } 1023 } 1024 1025 final List<ComponentName> simServiceComponentNames = new ArrayList<>(); 1026 final List<IBinder> simServiceBinders = new ArrayList<>(); 1027 1028 Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices); 1029 1030 for (ConnectionServiceWrapper simService : simServices) { 1031 if (simService == this) { 1032 // Only happens in the unlikely case that a SIM service is also a SIM call manager 1033 continue; 1034 } 1035 1036 final ConnectionServiceWrapper currentSimService = simService; 1037 1038 currentSimService.mBinder.bind(new BindCallback() { 1039 @Override 1040 public void onSuccess() { 1041 Log.d(this, "Adding simService %s", currentSimService.getComponentName()); 1042 simServiceComponentNames.add(currentSimService.getComponentName()); 1043 simServiceBinders.add(currentSimService.mServiceInterface.asBinder()); 1044 maybeComplete(); 1045 } 1046 1047 @Override 1048 public void onFailure() { 1049 Log.d(this, "Failed simService %s", currentSimService.getComponentName()); 1050 // We know maybeComplete() will always be a no-op from now on, so go ahead and 1051 // signal failure of the entire request 1052 noRemoteServices(callback); 1053 } 1054 1055 private void maybeComplete() { 1056 if (simServiceComponentNames.size() == simServices.size()) { 1057 setRemoteServices(callback, simServiceComponentNames, simServiceBinders); 1058 } 1059 } 1060 }, null); 1061 } 1062 } 1063 1064 private void setRemoteServices( 1065 RemoteServiceCallback callback, 1066 List<ComponentName> componentNames, 1067 List<IBinder> binders) { 1068 try { 1069 callback.onResult(componentNames, binders); 1070 } catch (RemoteException e) { 1071 Log.e(this, e, "Contacting ConnectionService %s", 1072 ConnectionServiceWrapper.this.getComponentName()); 1073 } 1074 } 1075 1076 private void noRemoteServices(RemoteServiceCallback callback) { 1077 setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST); 1078 } 1079} 1080