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