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 handleCreateConnectionComplete(String callId, ConnectionRequest request, ParcelableConnection connection)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 setActive(String callId)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 setRinging(String callId)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 setVideoProvider(String callId, IVideoProvider videoProvider)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 setDialing(String callId)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 setPulling(String callId)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 setDisconnected(String callId, DisconnectCause disconnectCause)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 setOnHold(String callId)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 setRingbackRequested(String callId, boolean ringback)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 removeCall(String callId)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 setConnectionCapabilities(String callId, int connectionCapabilities)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 setConnectionProperties(String callId, int connectionProperties)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 setIsConferenced(String callId, String conferenceCallId)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 setConferenceMergeFailed(String callId)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 addConferenceCall(String callId, ParcelableConference parcelableConference)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 onPostDialWait(String callId, String remaining)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 onPostDialChar(String callId, char nextChar)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 queryRemoteConnectionServices(RemoteServiceCallback callback)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 setVideoState(String callId, int videoState)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 setIsVoipAudioMode(String callId, boolean isVoip)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 setStatusHints(String callId, StatusHints statusHints)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 putExtras(String callId, Bundle extras)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 removeExtras(String callId, List<String> keys)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 setAddress(String callId, Uri address, int presentation)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 setCallerDisplayName( String callId, String callerDisplayName, int presentation)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 setConferenceableConnections( String callId, List<String> conferenceableCallIds)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 addExistingConnection(String callId, ParcelableConnection connection)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 onConnectionEvent(String callId, String event, Bundle extras)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 */ ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle)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}. */ addConnectionServiceAdapter(IConnectionServiceAdapter adapter)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}. */ removeConnectionServiceAdapter(IConnectionServiceAdapter adapter)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 createConnection(final Call call, final CreateConnectionResponse response)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) */ abort(Call call)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) */ silence(Call call)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) */ hold(Call call)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) */ unhold(Call call)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 onCallAudioStateChanged(Call activeCall, CallAudioState audioState)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) */ disconnect(Call call)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) */ answer(Call call, int videoState)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) */ reject(Call call, boolean rejectWithMessage, String message)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) */ playDtmfTone(Call call, char digit)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) */ stopDtmfTone(Call call)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 addCall(Call call)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 */ replaceCall(Call newCall, Call callToReplace)945 void replaceCall(Call newCall, Call callToReplace) { 946 Preconditions.checkState(callToReplace.getConnectionService() == this); 947 mCallIdMapper.replaceCall(newCall, callToReplace); 948 } 949 removeCall(Call call)950 void removeCall(Call call) { 951 removeCall(call, new DisconnectCause(DisconnectCause.ERROR)); 952 } 953 removeCall(String callId, DisconnectCause disconnectCause)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 removeCall(Call call, DisconnectCause disconnectCause)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 onPostDialContinue(Call call, boolean proceed)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 conference(final Call call, Call otherCall)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 splitFromConference(Call call)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 mergeConference(Call call)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 swapConference(Call call)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 pullExternalCall(Call call)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 sendCallEvent(Call call, String event, Bundle extras)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 onExtrasChanged(Call call, Bundle extras)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 setServiceInterface(IBinder binder)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 removeServiceInterface()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 handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)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 */ handleConnectionServiceDeath()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 logIncoming(String msg, Object... params)1119 private void logIncoming(String msg, Object... params) { 1120 Log.d(this, "ConnectionService -> Telecom: " + msg, params); 1121 } 1122 logOutgoing(String msg, Object... params)1123 private void logOutgoing(String msg, Object... params) { 1124 Log.d(this, "Telecom -> ConnectionService: " + msg, params); 1125 } 1126 queryRemoteConnectionServices(final UserHandle userHandle, final RemoteServiceCallback callback)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 setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)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 noRemoteServices(RemoteServiceCallback callback)1201 private void noRemoteServices(RemoteServiceCallback callback) { 1202 setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST); 1203 } 1204 } 1205