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