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