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