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