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