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