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