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