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 17 package com.android.server.telecom; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.net.Uri; 22 import android.os.Binder; 23 import android.os.Bundle; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.os.UserHandle; 27 import android.telecom.CallAudioState; 28 import android.telecom.Connection; 29 import android.telecom.ConnectionRequest; 30 import android.telecom.ConnectionService; 31 import android.telecom.DisconnectCause; 32 import android.telecom.GatewayInfo; 33 import android.telecom.ParcelableConference; 34 import android.telecom.ParcelableConnection; 35 import android.telecom.PhoneAccountHandle; 36 import android.telecom.StatusHints; 37 import android.telecom.TelecomManager; 38 import android.telecom.VideoProfile; 39 40 import com.android.internal.telecom.IConnectionService; 41 import com.android.internal.telecom.IConnectionServiceAdapter; 42 import com.android.internal.telecom.IVideoProvider; 43 import com.android.internal.telecom.RemoteServiceCallback; 44 import com.android.internal.util.Preconditions; 45 46 import java.util.ArrayList; 47 import java.util.Collections; 48 import java.util.HashMap; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Set; 52 import 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 */ 60 final class ConnectionServiceWrapper extends ServiceBinder { 61 62 private final class Adapter extends IConnectionServiceAdapter.Stub { 63 64 @Override handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)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 setActive(String callId)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 setRinging(String callId)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 setVideoProvider(String callId, IVideoProvider videoProvider)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 setDialing(String callId)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 setDisconnected(String callId, DisconnectCause disconnectCause)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 setOnHold(String callId)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 setRingbackRequested(String callId, boolean ringback)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 removeCall(String callId)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 setConnectionCapabilities(String callId, int connectionCapabilities)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 setIsConferenced(String callId, String conferenceCallId)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 setConferenceMergeFailed(String callId)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 addConferenceCall(String callId, ParcelableConference parcelableConference)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 onPostDialWait(String callId, String remaining)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 onPostDialChar(String callId, char nextChar)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 queryRemoteConnectionServices(RemoteServiceCallback callback)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 setVideoState(String callId, int videoState)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 setIsVoipAudioMode(String callId, boolean isVoip)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 setStatusHints(String callId, StatusHints statusHints)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 setExtras(String callId, Bundle extras)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 setAddress(String callId, Uri address, int presentation)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 setCallerDisplayName( String callId, String callerDisplayName, int presentation)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 setConferenceableConnections( String callId, List<String> conferenceableCallIds)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 addExistingConnection(String callId, ParcelableConnection connection)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 */ ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle)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}. */ addConnectionServiceAdapter(IConnectionServiceAdapter adapter)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 */ createConnection(final Call call, final CreateConnectionResponse response)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) */ abort(Call call)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#hold(String) */ hold(Call call)713 void hold(Call call) { 714 final String callId = mCallIdMapper.getCallId(call); 715 if (callId != null && isServiceValid("hold")) { 716 try { 717 logOutgoing("hold %s", callId); 718 mServiceInterface.hold(callId); 719 } catch (RemoteException e) { 720 } 721 } 722 } 723 724 /** @see IConnectionService#unhold(String) */ unhold(Call call)725 void unhold(Call call) { 726 final String callId = mCallIdMapper.getCallId(call); 727 if (callId != null && isServiceValid("unhold")) { 728 try { 729 logOutgoing("unhold %s", callId); 730 mServiceInterface.unhold(callId); 731 } catch (RemoteException e) { 732 } 733 } 734 } 735 736 /** @see IConnectionService#onCallAudioStateChanged(String,CallAudioState) */ onCallAudioStateChanged(Call activeCall, CallAudioState audioState)737 void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) { 738 final String callId = mCallIdMapper.getCallId(activeCall); 739 if (callId != null && isServiceValid("onCallAudioStateChanged")) { 740 try { 741 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState); 742 mServiceInterface.onCallAudioStateChanged(callId, audioState); 743 } catch (RemoteException e) { 744 } 745 } 746 } 747 748 /** @see IConnectionService#disconnect(String) */ disconnect(Call call)749 void disconnect(Call call) { 750 final String callId = mCallIdMapper.getCallId(call); 751 if (callId != null && isServiceValid("disconnect")) { 752 try { 753 logOutgoing("disconnect %s", callId); 754 mServiceInterface.disconnect(callId); 755 } catch (RemoteException e) { 756 } 757 } 758 } 759 760 /** @see IConnectionService#answer(String) */ answer(Call call, int videoState)761 void answer(Call call, int videoState) { 762 final String callId = mCallIdMapper.getCallId(call); 763 if (callId != null && isServiceValid("answer")) { 764 try { 765 logOutgoing("answer %s %d", callId, videoState); 766 if (VideoProfile.isAudioOnly(videoState)) { 767 mServiceInterface.answer(callId); 768 } else { 769 mServiceInterface.answerVideo(callId, videoState); 770 } 771 } catch (RemoteException e) { 772 } 773 } 774 } 775 776 /** @see IConnectionService#reject(String) */ reject(Call call)777 void reject(Call call) { 778 final String callId = mCallIdMapper.getCallId(call); 779 if (callId != null && isServiceValid("reject")) { 780 try { 781 logOutgoing("reject %s", callId); 782 mServiceInterface.reject(callId); 783 } catch (RemoteException e) { 784 } 785 } 786 } 787 788 /** @see IConnectionService#playDtmfTone(String,char) */ playDtmfTone(Call call, char digit)789 void playDtmfTone(Call call, char digit) { 790 final String callId = mCallIdMapper.getCallId(call); 791 if (callId != null && isServiceValid("playDtmfTone")) { 792 try { 793 logOutgoing("playDtmfTone %s %c", callId, digit); 794 mServiceInterface.playDtmfTone(callId, digit); 795 } catch (RemoteException e) { 796 } 797 } 798 } 799 800 /** @see IConnectionService#stopDtmfTone(String) */ stopDtmfTone(Call call)801 void stopDtmfTone(Call call) { 802 final String callId = mCallIdMapper.getCallId(call); 803 if (callId != null && isServiceValid("stopDtmfTone")) { 804 try { 805 logOutgoing("stopDtmfTone %s",callId); 806 mServiceInterface.stopDtmfTone(callId); 807 } catch (RemoteException e) { 808 } 809 } 810 } 811 addCall(Call call)812 void addCall(Call call) { 813 if (mCallIdMapper.getCallId(call) == null) { 814 mCallIdMapper.addCall(call); 815 } 816 } 817 818 /** 819 * Associates newCall with this connection service by replacing callToReplace. 820 */ replaceCall(Call newCall, Call callToReplace)821 void replaceCall(Call newCall, Call callToReplace) { 822 Preconditions.checkState(callToReplace.getConnectionService() == this); 823 mCallIdMapper.replaceCall(newCall, callToReplace); 824 } 825 removeCall(Call call)826 void removeCall(Call call) { 827 removeCall(call, new DisconnectCause(DisconnectCause.ERROR)); 828 } 829 removeCall(String callId, DisconnectCause disconnectCause)830 void removeCall(String callId, DisconnectCause disconnectCause) { 831 CreateConnectionResponse response = mPendingResponses.remove(callId); 832 if (response != null) { 833 response.handleCreateConnectionFailure(disconnectCause); 834 } 835 836 mCallIdMapper.removeCall(callId); 837 } 838 removeCall(Call call, DisconnectCause disconnectCause)839 void removeCall(Call call, DisconnectCause disconnectCause) { 840 CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call)); 841 if (response != null) { 842 response.handleCreateConnectionFailure(disconnectCause); 843 } 844 845 mCallIdMapper.removeCall(call); 846 } 847 onPostDialContinue(Call call, boolean proceed)848 void onPostDialContinue(Call call, boolean proceed) { 849 final String callId = mCallIdMapper.getCallId(call); 850 if (callId != null && isServiceValid("onPostDialContinue")) { 851 try { 852 logOutgoing("onPostDialContinue %s %b", callId, proceed); 853 mServiceInterface.onPostDialContinue(callId, proceed); 854 } catch (RemoteException ignored) { 855 } 856 } 857 } 858 conference(final Call call, Call otherCall)859 void conference(final Call call, Call otherCall) { 860 final String callId = mCallIdMapper.getCallId(call); 861 final String otherCallId = mCallIdMapper.getCallId(otherCall); 862 if (callId != null && otherCallId != null && isServiceValid("conference")) { 863 try { 864 logOutgoing("conference %s %s", callId, otherCallId); 865 mServiceInterface.conference(callId, otherCallId); 866 } catch (RemoteException ignored) { 867 } 868 } 869 } 870 splitFromConference(Call call)871 void splitFromConference(Call call) { 872 final String callId = mCallIdMapper.getCallId(call); 873 if (callId != null && isServiceValid("splitFromConference")) { 874 try { 875 logOutgoing("splitFromConference %s", callId); 876 mServiceInterface.splitFromConference(callId); 877 } catch (RemoteException ignored) { 878 } 879 } 880 } 881 mergeConference(Call call)882 void mergeConference(Call call) { 883 final String callId = mCallIdMapper.getCallId(call); 884 if (callId != null && isServiceValid("mergeConference")) { 885 try { 886 logOutgoing("mergeConference %s", callId); 887 mServiceInterface.mergeConference(callId); 888 } catch (RemoteException ignored) { 889 } 890 } 891 } 892 swapConference(Call call)893 void swapConference(Call call) { 894 final String callId = mCallIdMapper.getCallId(call); 895 if (callId != null && isServiceValid("swapConference")) { 896 try { 897 logOutgoing("swapConference %s", callId); 898 mServiceInterface.swapConference(callId); 899 } catch (RemoteException ignored) { 900 } 901 } 902 } 903 904 /** {@inheritDoc} */ 905 @Override setServiceInterface(IBinder binder)906 protected void setServiceInterface(IBinder binder) { 907 if (binder == null) { 908 // We have lost our service connection. Notify the world that this service is done. 909 // We must notify the adapter before CallsManager. The adapter will force any pending 910 // outgoing calls to try the next service. This needs to happen before CallsManager 911 // tries to clean up any calls still associated with this service. 912 handleConnectionServiceDeath(); 913 mCallsManager.handleConnectionServiceDeath(this); 914 mServiceInterface = null; 915 } else { 916 mServiceInterface = IConnectionService.Stub.asInterface(binder); 917 addConnectionServiceAdapter(mAdapter); 918 } 919 } 920 handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)921 private void handleCreateConnectionComplete( 922 String callId, 923 ConnectionRequest request, 924 ParcelableConnection connection) { 925 // TODO: Note we are not using parameter "request", which is a side effect of our tacit 926 // assumption that we have at most one outgoing connection attempt per ConnectionService. 927 // This may not continue to be the case. 928 if (connection.getState() == Connection.STATE_DISCONNECTED) { 929 // A connection that begins in the DISCONNECTED state is an indication of 930 // failure to connect; we handle all failures uniformly 931 removeCall(callId, connection.getDisconnectCause()); 932 } else { 933 // Successful connection 934 if (mPendingResponses.containsKey(callId)) { 935 mPendingResponses.remove(callId) 936 .handleCreateConnectionSuccess(mCallIdMapper, connection); 937 } 938 } 939 } 940 941 /** 942 * Called when the associated connection service dies. 943 */ handleConnectionServiceDeath()944 private void handleConnectionServiceDeath() { 945 if (!mPendingResponses.isEmpty()) { 946 CreateConnectionResponse[] responses = mPendingResponses.values().toArray( 947 new CreateConnectionResponse[mPendingResponses.values().size()]); 948 mPendingResponses.clear(); 949 for (int i = 0; i < responses.length; i++) { 950 responses[i].handleCreateConnectionFailure( 951 new DisconnectCause(DisconnectCause.ERROR)); 952 } 953 } 954 mCallIdMapper.clear(); 955 } 956 logIncoming(String msg, Object... params)957 private void logIncoming(String msg, Object... params) { 958 Log.d(this, "ConnectionService -> Telecom: " + msg, params); 959 } 960 logOutgoing(String msg, Object... params)961 private void logOutgoing(String msg, Object... params) { 962 Log.d(this, "Telecom -> ConnectionService: " + msg, params); 963 } 964 queryRemoteConnectionServices(final RemoteServiceCallback callback)965 private void queryRemoteConnectionServices(final RemoteServiceCallback callback) { 966 // Only give remote connection services to this connection service if it is listed as 967 // the connection manager. 968 PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(); 969 Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager); 970 if (simCallManager == null || 971 !simCallManager.getComponentName().equals(getComponentName())) { 972 noRemoteServices(callback); 973 return; 974 } 975 976 // Make a list of ConnectionServices that are listed as being associated with SIM accounts 977 final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap( 978 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1)); 979 for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts()) { 980 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 981 handle.getComponentName(), handle.getUserHandle()); 982 if (service != null) { 983 simServices.add(service); 984 } 985 } 986 987 final List<ComponentName> simServiceComponentNames = new ArrayList<>(); 988 final List<IBinder> simServiceBinders = new ArrayList<>(); 989 990 Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices); 991 992 for (ConnectionServiceWrapper simService : simServices) { 993 if (simService == this) { 994 // Only happens in the unlikely case that a SIM service is also a SIM call manager 995 continue; 996 } 997 998 final ConnectionServiceWrapper currentSimService = simService; 999 1000 currentSimService.mBinder.bind(new BindCallback() { 1001 @Override 1002 public void onSuccess() { 1003 Log.d(this, "Adding simService %s", currentSimService.getComponentName()); 1004 simServiceComponentNames.add(currentSimService.getComponentName()); 1005 simServiceBinders.add(currentSimService.mServiceInterface.asBinder()); 1006 maybeComplete(); 1007 } 1008 1009 @Override 1010 public void onFailure() { 1011 Log.d(this, "Failed simService %s", currentSimService.getComponentName()); 1012 // We know maybeComplete() will always be a no-op from now on, so go ahead and 1013 // signal failure of the entire request 1014 noRemoteServices(callback); 1015 } 1016 1017 private void maybeComplete() { 1018 if (simServiceComponentNames.size() == simServices.size()) { 1019 setRemoteServices(callback, simServiceComponentNames, simServiceBinders); 1020 } 1021 } 1022 }, null); 1023 } 1024 } 1025 setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)1026 private void setRemoteServices( 1027 RemoteServiceCallback callback, 1028 List<ComponentName> componentNames, 1029 List<IBinder> binders) { 1030 try { 1031 callback.onResult(componentNames, binders); 1032 } catch (RemoteException e) { 1033 Log.e(this, e, "Contacting ConnectionService %s", 1034 ConnectionServiceWrapper.this.getComponentName()); 1035 } 1036 } 1037 noRemoteServices(RemoteServiceCallback callback)1038 private void noRemoteServices(RemoteServiceCallback callback) { 1039 setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST); 1040 } 1041 } 1042