1 /* 2 * Copyright (C) 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 android.telecom; 18 19 import android.net.Uri; 20 import android.os.Bundle; 21 import android.os.IBinder; 22 import android.os.IBinder.DeathRecipient; 23 import android.os.RemoteException; 24 import android.os.ResultReceiver; 25 import android.telecom.Logging.Session; 26 27 import com.android.internal.telecom.IConnectionService; 28 import com.android.internal.telecom.IConnectionServiceAdapter; 29 import com.android.internal.telecom.IVideoProvider; 30 import com.android.internal.telecom.RemoteServiceCallback; 31 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.UUID; 39 40 /** 41 * Remote connection service which other connection services can use to place calls on their behalf. 42 * 43 * @hide 44 */ 45 final class RemoteConnectionService { 46 47 // Note: Casting null to avoid ambiguous constructor reference. 48 private static final RemoteConnection NULL_CONNECTION = 49 new RemoteConnection("NULL", null, (ConnectionRequest) null); 50 51 private static final RemoteConference NULL_CONFERENCE = 52 new RemoteConference("NULL", null); 53 54 private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() { 55 @Override 56 public void handleCreateConnectionComplete( 57 String id, 58 ConnectionRequest request, 59 ParcelableConnection parcel, 60 Session.Info info) { 61 RemoteConnection connection = 62 findConnectionForAction(id, "handleCreateConnectionSuccessful"); 63 if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) { 64 mPendingConnections.remove(connection); 65 // Unconditionally initialize the connection ... 66 connection.setConnectionCapabilities(parcel.getConnectionCapabilities()); 67 connection.setConnectionProperties(parcel.getConnectionProperties()); 68 if (parcel.getHandle() != null 69 || parcel.getState() != Connection.STATE_DISCONNECTED) { 70 connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation()); 71 } 72 if (parcel.getCallerDisplayName() != null 73 || parcel.getState() != Connection.STATE_DISCONNECTED) { 74 connection.setCallerDisplayName( 75 parcel.getCallerDisplayName(), 76 parcel.getCallerDisplayNamePresentation()); 77 } 78 // Set state after handle so that the client can identify the connection. 79 if (parcel.getState() == Connection.STATE_DISCONNECTED) { 80 connection.setDisconnected(parcel.getDisconnectCause()); 81 } else { 82 connection.setState(parcel.getState()); 83 } 84 List<RemoteConnection> conferenceable = new ArrayList<>(); 85 for (String confId : parcel.getConferenceableConnectionIds()) { 86 if (mConnectionById.containsKey(confId)) { 87 conferenceable.add(mConnectionById.get(confId)); 88 } 89 } 90 connection.setConferenceableConnections(conferenceable); 91 connection.setVideoState(parcel.getVideoState()); 92 if (connection.getState() == Connection.STATE_DISCONNECTED) { 93 // ... then, if it was created in a disconnected state, that indicates 94 // failure on the providing end, so immediately mark it destroyed 95 connection.setDestroyed(); 96 } 97 connection.setStatusHints(parcel.getStatusHints()); 98 connection.setIsVoipAudioMode(parcel.getIsVoipAudioMode()); 99 connection.setRingbackRequested(parcel.isRingbackRequested()); 100 connection.putExtras(parcel.getExtras()); 101 } 102 } 103 104 @Override 105 public void handleCreateConferenceComplete( 106 String id, 107 ConnectionRequest request, 108 ParcelableConference parcel, 109 Session.Info info) { 110 } 111 112 @Override 113 public void setActive(String callId, Session.Info sessionInfo) { 114 if (mConnectionById.containsKey(callId)) { 115 findConnectionForAction(callId, "setActive") 116 .setState(Connection.STATE_ACTIVE); 117 } else { 118 findConferenceForAction(callId, "setActive") 119 .setState(Connection.STATE_ACTIVE); 120 } 121 } 122 123 @Override 124 public void setRinging(String callId, Session.Info sessionInfo) { 125 findConnectionForAction(callId, "setRinging") 126 .setState(Connection.STATE_RINGING); 127 } 128 129 @Override 130 public void setDialing(String callId, Session.Info sessionInfo) { 131 findConnectionForAction(callId, "setDialing") 132 .setState(Connection.STATE_DIALING); 133 } 134 135 @Override 136 public void setPulling(String callId, Session.Info sessionInfo) { 137 findConnectionForAction(callId, "setPulling") 138 .setState(Connection.STATE_PULLING_CALL); 139 } 140 141 @Override 142 public void setDisconnected(String callId, DisconnectCause disconnectCause, 143 Session.Info sessionInfo) { 144 if (mConnectionById.containsKey(callId)) { 145 findConnectionForAction(callId, "setDisconnected") 146 .setDisconnected(disconnectCause); 147 } else { 148 findConferenceForAction(callId, "setDisconnected") 149 .setDisconnected(disconnectCause); 150 } 151 } 152 153 @Override 154 public void setOnHold(String callId, Session.Info sessionInfo) { 155 if (mConnectionById.containsKey(callId)) { 156 findConnectionForAction(callId, "setOnHold") 157 .setState(Connection.STATE_HOLDING); 158 } else { 159 findConferenceForAction(callId, "setOnHold") 160 .setState(Connection.STATE_HOLDING); 161 } 162 } 163 164 @Override 165 public void setRingbackRequested(String callId, boolean ringing, Session.Info sessionInfo) { 166 findConnectionForAction(callId, "setRingbackRequested") 167 .setRingbackRequested(ringing); 168 } 169 170 @Override 171 public void setConnectionCapabilities(String callId, int connectionCapabilities, 172 Session.Info sessionInfo) { 173 if (mConnectionById.containsKey(callId)) { 174 findConnectionForAction(callId, "setConnectionCapabilities") 175 .setConnectionCapabilities(connectionCapabilities); 176 } else { 177 findConferenceForAction(callId, "setConnectionCapabilities") 178 .setConnectionCapabilities(connectionCapabilities); 179 } 180 } 181 182 @Override 183 public void setConnectionProperties(String callId, int connectionProperties, 184 Session.Info sessionInfo) { 185 if (mConnectionById.containsKey(callId)) { 186 findConnectionForAction(callId, "setConnectionProperties") 187 .setConnectionProperties(connectionProperties); 188 } else { 189 findConferenceForAction(callId, "setConnectionProperties") 190 .setConnectionProperties(connectionProperties); 191 } 192 } 193 194 @Override 195 public void setIsConferenced(String callId, String conferenceCallId, 196 Session.Info sessionInfo) { 197 // Note: callId should not be null; conferenceCallId may be null 198 RemoteConnection connection = 199 findConnectionForAction(callId, "setIsConferenced"); 200 if (connection != NULL_CONNECTION) { 201 if (conferenceCallId == null) { 202 // 'connection' is being split from its conference 203 if (connection.getConference() != null) { 204 connection.getConference().removeConnection(connection); 205 } 206 } else { 207 RemoteConference conference = 208 findConferenceForAction(conferenceCallId, "setIsConferenced"); 209 if (conference != NULL_CONFERENCE) { 210 conference.addConnection(connection); 211 } 212 } 213 } 214 } 215 216 @Override 217 public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) { 218 // Nothing to do here. 219 // The event has already been handled and there is no state to update 220 // in the underlying connection or conference objects 221 } 222 223 @Override 224 public void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle, 225 Session.Info sessionInfo) { 226 } 227 228 @Override 229 public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {} 230 231 @Override 232 public void addConferenceCall( 233 final String callId, ParcelableConference parcel, Session.Info sessionInfo) { 234 RemoteConference conference = new RemoteConference(callId, 235 mOutgoingConnectionServiceRpc); 236 237 for (String id : parcel.getConnectionIds()) { 238 RemoteConnection c = mConnectionById.get(id); 239 if (c != null) { 240 conference.addConnection(c); 241 } 242 } 243 // We used to skip adding empty conferences; however in the world of IMS conference 244 // calls we need to add them to the remote connection service because they will always 245 // start with no participants. 246 247 conference.setState(parcel.getState()); 248 conference.setConnectionCapabilities(parcel.getConnectionCapabilities()); 249 conference.setConnectionProperties(parcel.getConnectionProperties()); 250 conference.putExtras(parcel.getExtras()); 251 mConferenceById.put(callId, conference); 252 253 // Stash the original connection ID as it exists in the source ConnectionService. 254 // Telecom will use this to avoid adding duplicates later. 255 // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information. 256 Bundle newExtras = new Bundle(); 257 newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); 258 // Track the fact this request was relayed through the remote connection service. 259 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, 260 parcel.getPhoneAccount()); 261 conference.putExtras(newExtras); 262 263 conference.registerCallback(new RemoteConference.Callback() { 264 @Override 265 public void onDestroyed(RemoteConference c) { 266 mConferenceById.remove(callId); 267 maybeDisconnectAdapter(); 268 } 269 }); 270 271 mOurConnectionServiceImpl.addRemoteConference(conference); 272 } 273 274 @Override 275 public void removeCall(String callId, Session.Info sessionInfo) { 276 if (mConnectionById.containsKey(callId)) { 277 findConnectionForAction(callId, "removeCall") 278 .setDestroyed(); 279 } else { 280 findConferenceForAction(callId, "removeCall") 281 .setDestroyed(); 282 } 283 } 284 285 @Override 286 public void onPostDialWait(String callId, String remaining, Session.Info sessionInfo) { 287 findConnectionForAction(callId, "onPostDialWait") 288 .setPostDialWait(remaining); 289 } 290 291 @Override 292 public void onPostDialChar(String callId, char nextChar, Session.Info sessionInfo) { 293 findConnectionForAction(callId, "onPostDialChar") 294 .onPostDialChar(nextChar); 295 } 296 297 @Override 298 public void queryRemoteConnectionServices(RemoteServiceCallback callback, 299 String callingPackage, Session.Info sessionInfo) { 300 // Not supported from remote connection service. 301 } 302 303 @Override 304 public void setVideoProvider(String callId, IVideoProvider videoProvider, 305 Session.Info sessionInfo) { 306 307 String callingPackage = mOurConnectionServiceImpl.getApplicationContext() 308 .getOpPackageName(); 309 int targetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo().targetSdkVersion; 310 RemoteConnection.VideoProvider remoteVideoProvider = null; 311 if (videoProvider != null) { 312 remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider, 313 callingPackage, targetSdkVersion); 314 } 315 findConnectionForAction(callId, "setVideoProvider") 316 .setVideoProvider(remoteVideoProvider); 317 } 318 319 @Override 320 public void setVideoState(String callId, int videoState, Session.Info sessionInfo) { 321 findConnectionForAction(callId, "setVideoState") 322 .setVideoState(videoState); 323 } 324 325 @Override 326 public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) { 327 findConnectionForAction(callId, "setIsVoipAudioMode") 328 .setIsVoipAudioMode(isVoip); 329 } 330 331 @Override 332 public void setStatusHints(String callId, StatusHints statusHints, 333 Session.Info sessionInfo) { 334 findConnectionForAction(callId, "setStatusHints") 335 .setStatusHints(statusHints); 336 } 337 338 @Override 339 public void setAddress(String callId, Uri address, int presentation, 340 Session.Info sessionInfo) { 341 findConnectionForAction(callId, "setAddress") 342 .setAddress(address, presentation); 343 } 344 345 @Override 346 public void setCallerDisplayName(String callId, String callerDisplayName, 347 int presentation, Session.Info sessionInfo) { 348 findConnectionForAction(callId, "setCallerDisplayName") 349 .setCallerDisplayName(callerDisplayName, presentation); 350 } 351 352 @Override 353 public IBinder asBinder() { 354 throw new UnsupportedOperationException(); 355 } 356 357 @Override 358 public final void setConferenceableConnections(String callId, 359 List<String> conferenceableConnectionIds, Session.Info sessionInfo) { 360 List<RemoteConnection> conferenceable = new ArrayList<>(); 361 for (String id : conferenceableConnectionIds) { 362 if (mConnectionById.containsKey(id)) { 363 conferenceable.add(mConnectionById.get(id)); 364 } 365 } 366 367 if (hasConnection(callId)) { 368 findConnectionForAction(callId, "setConferenceableConnections") 369 .setConferenceableConnections(conferenceable); 370 } else { 371 findConferenceForAction(callId, "setConferenceableConnections") 372 .setConferenceableConnections(conferenceable); 373 } 374 } 375 376 @Override 377 public void addExistingConnection(String callId, ParcelableConnection connection, 378 Session.Info sessionInfo) { 379 Log.i(RemoteConnectionService.this, "addExistingConnection: callId=%s, conn=%s", callId, 380 connection); 381 String callingPackage = mOurConnectionServiceImpl.getApplicationContext(). 382 getOpPackageName(); 383 int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo() 384 .targetSdkVersion; 385 RemoteConnection remoteConnection = new RemoteConnection(callId, 386 mOutgoingConnectionServiceRpc, connection, callingPackage, 387 callingTargetSdkVersion); 388 // Track that it is via a remote connection. 389 Bundle newExtras = new Bundle(); 390 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, 391 connection.getPhoneAccount()); 392 if (connection.getParentCallId() != null) { 393 RemoteConference parentConf = mConferenceById.get(connection.getParentCallId()); 394 // If there is a parent being set, we need to stash the conference ID here. 395 // Telephony can add an existing connection while specifying a parent conference. 396 // There is no equivalent version of that operation as part of the remote connection 397 // API, so we will stash the pre-defined parent's ID in the extras. When the 398 // connectionmanager copies over the extras from the remote connection to the 399 // actual one, it'll get passed to Telecom so that it can make the association. 400 if (parentConf != null) { 401 newExtras.putString(Connection.EXTRA_ADD_TO_CONFERENCE_ID, parentConf.getId()); 402 Log.i(this, "addExistingConnection: stash parent of %s as %s", 403 connection.getParentCallId(), parentConf.getId()); 404 } 405 } 406 remoteConnection.putExtras(newExtras); 407 mConnectionById.put(callId, remoteConnection); 408 remoteConnection.registerCallback(new RemoteConnection.Callback() { 409 @Override 410 public void onDestroyed(RemoteConnection connection) { 411 mConnectionById.remove(callId); 412 maybeDisconnectAdapter(); 413 } 414 }); 415 mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnection); 416 } 417 418 @Override 419 public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) { 420 if (hasConnection(callId)) { 421 findConnectionForAction(callId, "putExtras").putExtras(extras); 422 } else { 423 findConferenceForAction(callId, "putExtras").putExtras(extras); 424 } 425 } 426 427 @Override 428 public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) { 429 if (hasConnection(callId)) { 430 findConnectionForAction(callId, "removeExtra").removeExtras(keys); 431 } else { 432 findConferenceForAction(callId, "removeExtra").removeExtras(keys); 433 } 434 } 435 436 @Override 437 public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress, 438 Session.Info sessionInfo) { 439 if (hasConnection(callId)) { 440 // TODO(3pcalls): handle this for remote connections. 441 // Likely we don't want to do anything since it doesn't make sense for self-managed 442 // connections to go through a connection mgr. 443 } 444 } 445 446 @Override 447 public void onConnectionEvent(String callId, String event, Bundle extras, 448 Session.Info sessionInfo) { 449 if (mConnectionById.containsKey(callId)) { 450 findConnectionForAction(callId, "onConnectionEvent").onConnectionEvent(event, 451 extras); 452 } 453 } 454 455 @Override 456 public void onRttInitiationSuccess(String callId, Session.Info sessionInfo) 457 throws RemoteException { 458 if (hasConnection(callId)) { 459 findConnectionForAction(callId, "onRttInitiationSuccess") 460 .onRttInitiationSuccess(); 461 } else { 462 Log.w(this, "onRttInitiationSuccess called on a remote conference"); 463 } 464 } 465 466 @Override 467 public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo) 468 throws RemoteException { 469 if (hasConnection(callId)) { 470 findConnectionForAction(callId, "onRttInitiationFailure") 471 .onRttInitiationFailure(reason); 472 } else { 473 Log.w(this, "onRttInitiationFailure called on a remote conference"); 474 } 475 } 476 477 @Override 478 public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo) 479 throws RemoteException { 480 if (hasConnection(callId)) { 481 findConnectionForAction(callId, "onRttSessionRemotelyTerminated") 482 .onRttSessionRemotelyTerminated(); 483 } else { 484 Log.w(this, "onRttSessionRemotelyTerminated called on a remote conference"); 485 } 486 } 487 488 @Override 489 public void onRemoteRttRequest(String callId, Session.Info sessionInfo) 490 throws RemoteException { 491 if (hasConnection(callId)) { 492 findConnectionForAction(callId, "onRemoteRttRequest") 493 .onRemoteRttRequest(); 494 } else { 495 Log.w(this, "onRemoteRttRequest called on a remote conference"); 496 } 497 } 498 499 @Override 500 public void resetConnectionTime(String callId, Session.Info sessionInfo) { 501 // Do nothing 502 } 503 504 @Override 505 public void setConferenceState(String callId, boolean isConference, 506 Session.Info sessionInfo) { 507 // Do nothing 508 } 509 510 @Override 511 public void setCallDirection(String callId, int direction, Session.Info sessionInfo) { 512 // Do nothing 513 } 514 515 @Override 516 public void requestCallEndpointChange(String callId, CallEndpoint endpoint, 517 ResultReceiver callback, Session.Info sessionInfo) { 518 // Do nothing 519 } 520 521 @Override 522 public void queryLocation(String callId, long timeoutMillis, String provider, 523 ResultReceiver callback, Session.Info sessionInfo) { 524 // Do nothing 525 } 526 }; 527 528 private final ConnectionServiceAdapterServant mServant = 529 new ConnectionServiceAdapterServant(mServantDelegate); 530 531 private final DeathRecipient mDeathRecipient = new DeathRecipient() { 532 @Override 533 public void binderDied() { 534 for (RemoteConnection c : mConnectionById.values()) { 535 c.setDestroyed(); 536 } 537 for (RemoteConference c : mConferenceById.values()) { 538 c.setDestroyed(); 539 } 540 mConnectionById.clear(); 541 mConferenceById.clear(); 542 mPendingConnections.clear(); 543 mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0); 544 } 545 }; 546 547 private final IConnectionService mOutgoingConnectionServiceRpc; 548 private final ConnectionService mOurConnectionServiceImpl; 549 private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); 550 private final Map<String, RemoteConference> mConferenceById = new HashMap<>(); 551 private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); 552 RemoteConnectionService( IConnectionService outgoingConnectionServiceRpc, ConnectionService ourConnectionServiceImpl)553 RemoteConnectionService( 554 IConnectionService outgoingConnectionServiceRpc, 555 ConnectionService ourConnectionServiceImpl) throws RemoteException { 556 mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc; 557 mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0); 558 mOurConnectionServiceImpl = ourConnectionServiceImpl; 559 } 560 561 @Override toString()562 public String toString() { 563 return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]"; 564 } 565 createRemoteConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request, boolean isIncoming)566 final RemoteConnection createRemoteConnection( 567 PhoneAccountHandle connectionManagerPhoneAccount, 568 ConnectionRequest request, 569 boolean isIncoming) { 570 final String id = UUID.randomUUID().toString(); 571 Bundle extras = new Bundle(); 572 if (request.getExtras() != null) { 573 extras.putAll(request.getExtras()); 574 } 575 // We will set the package name for the originator of the remote request; this lets the 576 // receiving ConnectionService know that the request originated from a remote connection 577 // service so that it can provide tracking information for Telecom. 578 extras.putString(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, 579 mOurConnectionServiceImpl.getApplicationContext().getOpPackageName()); 580 581 final ConnectionRequest newRequest = new ConnectionRequest.Builder() 582 .setAccountHandle(request.getAccountHandle()) 583 .setAddress(request.getAddress()) 584 .setExtras(extras) 585 .setVideoState(request.getVideoState()) 586 .setRttPipeFromInCall(request.getRttPipeFromInCall()) 587 .setRttPipeToInCall(request.getRttPipeToInCall()) 588 .build(); 589 try { 590 if (mConnectionById.isEmpty()) { 591 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(), 592 null /*Session.Info*/); 593 } 594 RemoteConnection connection = 595 new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest); 596 mPendingConnections.add(connection); 597 mConnectionById.put(id, connection); 598 mOutgoingConnectionServiceRpc.createConnection( 599 connectionManagerPhoneAccount, 600 id, 601 newRequest, 602 isIncoming, 603 false /* isUnknownCall */, 604 null /*Session.info*/); 605 connection.registerCallback(new RemoteConnection.Callback() { 606 @Override 607 public void onDestroyed(RemoteConnection connection) { 608 mConnectionById.remove(id); 609 maybeDisconnectAdapter(); 610 } 611 }); 612 return connection; 613 } catch (RemoteException e) { 614 return RemoteConnection.failure( 615 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 616 } 617 } 618 createRemoteConference( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request, boolean isIncoming)619 RemoteConference createRemoteConference( 620 PhoneAccountHandle connectionManagerPhoneAccount, 621 ConnectionRequest request, 622 boolean isIncoming) { 623 final String id = UUID.randomUUID().toString(); 624 try { 625 if (mConferenceById.isEmpty()) { 626 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(), 627 null /*Session.Info*/); 628 } 629 RemoteConference conference = new RemoteConference(id, mOutgoingConnectionServiceRpc); 630 mOutgoingConnectionServiceRpc.createConference(connectionManagerPhoneAccount, 631 id, 632 request, 633 isIncoming, 634 false /* isUnknownCall */, 635 null /*Session.info*/); 636 conference.registerCallback(new RemoteConference.Callback() { 637 @Override 638 public void onDestroyed(RemoteConference conference) { 639 mConferenceById.remove(id); 640 maybeDisconnectAdapter(); 641 } 642 }); 643 conference.putExtras(request.getExtras()); 644 return conference; 645 } catch (RemoteException e) { 646 return RemoteConference.failure( 647 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 648 } 649 } 650 hasConnection(String callId)651 private boolean hasConnection(String callId) { 652 return mConnectionById.containsKey(callId); 653 } 654 findConnectionForAction( String callId, String action)655 private RemoteConnection findConnectionForAction( 656 String callId, String action) { 657 if (mConnectionById.containsKey(callId)) { 658 return mConnectionById.get(callId); 659 } 660 Log.w(this, "%s - Cannot find Connection %s", action, callId); 661 return NULL_CONNECTION; 662 } 663 findConferenceForAction( String callId, String action)664 private RemoteConference findConferenceForAction( 665 String callId, String action) { 666 if (mConferenceById.containsKey(callId)) { 667 return mConferenceById.get(callId); 668 } 669 Log.w(this, "%s - Cannot find Conference %s", action, callId); 670 return NULL_CONFERENCE; 671 } 672 maybeDisconnectAdapter()673 private void maybeDisconnectAdapter() { 674 if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) { 675 try { 676 mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub(), 677 null /*Session.info*/); 678 } catch (RemoteException e) { 679 } 680 } 681 } 682 } 683