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