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