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.annotation.CallbackExecutor;
20 import android.annotation.NonNull;
21 import android.location.Location;
22 import android.net.Uri;
23 import android.os.Binder;
24 import android.os.Bundle;
25 import android.os.IBinder.DeathRecipient;
26 import android.os.OutcomeReceiver;
27 import android.os.RemoteException;
28 import android.os.ResultReceiver;
29 
30 import com.android.internal.telecom.IConnectionServiceAdapter;
31 import com.android.internal.telecom.RemoteServiceCallback;
32 
33 import java.util.Collections;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.Executor;
39 
40 /**
41  * Provides methods for IConnectionService implementations to interact with the system phone app.
42  *
43  * @hide
44  */
45 final class ConnectionServiceAdapter implements DeathRecipient {
46     /**
47      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
48      * load factor before resizing, 1 means we only expect a single thread to
49      * access the map so make only a single shard
50      */
51     private final Set<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap(
52             new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1));
53 
ConnectionServiceAdapter()54     ConnectionServiceAdapter() {
55     }
56 
addAdapter(IConnectionServiceAdapter adapter)57     void addAdapter(IConnectionServiceAdapter adapter) {
58         for (IConnectionServiceAdapter it : mAdapters) {
59             if (it.asBinder() == adapter.asBinder()) {
60                 Log.w(this, "Ignoring duplicate adapter addition.");
61                 return;
62             }
63         }
64         if (mAdapters.add(adapter)) {
65             try {
66                 adapter.asBinder().linkToDeath(this, 0);
67             } catch (RemoteException e) {
68                 mAdapters.remove(adapter);
69             }
70         }
71     }
72 
removeAdapter(IConnectionServiceAdapter adapter)73     void removeAdapter(IConnectionServiceAdapter adapter) {
74         if (adapter != null) {
75             for (IConnectionServiceAdapter it : mAdapters) {
76                 if (it.asBinder() == adapter.asBinder() && mAdapters.remove(it)) {
77                     adapter.asBinder().unlinkToDeath(this, 0);
78                     break;
79                 }
80             }
81         }
82     }
83 
84     /** ${inheritDoc} */
85     @Override
binderDied()86     public void binderDied() {
87         Iterator<IConnectionServiceAdapter> it = mAdapters.iterator();
88         while (it.hasNext()) {
89             IConnectionServiceAdapter adapter = it.next();
90             if (!adapter.asBinder().isBinderAlive()) {
91                 it.remove();
92                 adapter.asBinder().unlinkToDeath(this, 0);
93             }
94         }
95     }
96 
handleCreateConnectionComplete( String id, ConnectionRequest request, ParcelableConnection connection)97     void handleCreateConnectionComplete(
98             String id,
99             ConnectionRequest request,
100             ParcelableConnection connection) {
101         for (IConnectionServiceAdapter adapter : mAdapters) {
102             try {
103                 adapter.handleCreateConnectionComplete(id, request, connection,
104                         Log.getExternalSession());
105             } catch (RemoteException e) {
106             }
107         }
108     }
109 
handleCreateConferenceComplete( String id, ConnectionRequest request, ParcelableConference conference)110     void handleCreateConferenceComplete(
111             String id,
112             ConnectionRequest request,
113             ParcelableConference conference) {
114         for (IConnectionServiceAdapter adapter : mAdapters) {
115             try {
116                 adapter.handleCreateConferenceComplete(id, request, conference,
117                         Log.getExternalSession());
118             } catch (RemoteException e) {
119             }
120         }
121     }
122 
123     /**
124      * Sets a call's state to active (e.g., an ongoing call where two parties can actively
125      * communicate).
126      *
127      * @param callId The unique ID of the call whose state is changing to active.
128      */
setActive(String callId)129     void setActive(String callId) {
130         for (IConnectionServiceAdapter adapter : mAdapters) {
131             try {
132                 adapter.setActive(callId, Log.getExternalSession());
133             } catch (RemoteException e) {
134             }
135         }
136     }
137 
138     /**
139      * Sets a call's state to ringing (e.g., an inbound ringing call).
140      *
141      * @param callId The unique ID of the call whose state is changing to ringing.
142      */
setRinging(String callId)143     void setRinging(String callId) {
144         for (IConnectionServiceAdapter adapter : mAdapters) {
145             try {
146                 adapter.setRinging(callId, Log.getExternalSession());
147             } catch (RemoteException e) {
148             }
149         }
150     }
151 
152     /**
153      * Sets a call's state to dialing (e.g., dialing an outbound call).
154      *
155      * @param callId The unique ID of the call whose state is changing to dialing.
156      */
setDialing(String callId)157     void setDialing(String callId) {
158         for (IConnectionServiceAdapter adapter : mAdapters) {
159             try {
160                 adapter.setDialing(callId, Log.getExternalSession());
161             } catch (RemoteException e) {
162             }
163         }
164     }
165 
166     /**
167      * Sets a call's state to pulling (e.g. a call with {@link Connection#PROPERTY_IS_EXTERNAL_CALL}
168      * is being pulled to the local device.
169      *
170      * @param callId The unique ID of the call whose state is changing to dialing.
171      */
setPulling(String callId)172     void setPulling(String callId) {
173         for (IConnectionServiceAdapter adapter : mAdapters) {
174             try {
175                 adapter.setPulling(callId, Log.getExternalSession());
176             } catch (RemoteException e) {
177             }
178         }
179     }
180 
181     /**
182      * Sets a call's state to disconnected.
183      *
184      * @param callId The unique ID of the call whose state is changing to disconnected.
185      * @param disconnectCause The reason for the disconnection, as described by
186      *            {@link android.telecomm.DisconnectCause}.
187      */
setDisconnected(String callId, DisconnectCause disconnectCause)188     void setDisconnected(String callId, DisconnectCause disconnectCause) {
189         for (IConnectionServiceAdapter adapter : mAdapters) {
190             try {
191                 adapter.setDisconnected(callId, disconnectCause, Log.getExternalSession());
192             } catch (RemoteException e) {
193             }
194         }
195     }
196 
197     /**
198      * Sets a call's state to be on hold.
199      *
200      * @param callId - The unique ID of the call whose state is changing to be on hold.
201      */
setOnHold(String callId)202     void setOnHold(String callId) {
203         for (IConnectionServiceAdapter adapter : mAdapters) {
204             try {
205                 adapter.setOnHold(callId, Log.getExternalSession());
206             } catch (RemoteException e) {
207             }
208         }
209     }
210 
211     /**
212      * Asks Telecom to start or stop a ringback tone for a call.
213      *
214      * @param callId The unique ID of the call whose ringback is being changed.
215      * @param ringback Whether Telecom should start playing a ringback tone.
216      */
setRingbackRequested(String callId, boolean ringback)217     void setRingbackRequested(String callId, boolean ringback) {
218         for (IConnectionServiceAdapter adapter : mAdapters) {
219             try {
220                 adapter.setRingbackRequested(callId, ringback, Log.getExternalSession());
221             } catch (RemoteException e) {
222             }
223         }
224     }
225 
setConnectionCapabilities(String callId, int capabilities)226     void setConnectionCapabilities(String callId, int capabilities) {
227         for (IConnectionServiceAdapter adapter : mAdapters) {
228             try {
229                 adapter.setConnectionCapabilities(callId, capabilities, Log.getExternalSession());
230             } catch (RemoteException ignored) {
231             }
232         }
233     }
234 
setConnectionProperties(String callId, int properties)235     void setConnectionProperties(String callId, int properties) {
236         for (IConnectionServiceAdapter adapter : mAdapters) {
237             try {
238                 adapter.setConnectionProperties(callId, properties, Log.getExternalSession());
239             } catch (RemoteException ignored) {
240             }
241         }
242     }
243 
244     /**
245      * Indicates whether or not the specified call is currently conferenced into the specified
246      * conference call.
247      *
248      * @param callId The unique ID of the call being conferenced.
249      * @param conferenceCallId The unique ID of the conference call. Null if call is not
250      *            conferenced.
251      */
setIsConferenced(String callId, String conferenceCallId)252     void setIsConferenced(String callId, String conferenceCallId) {
253         for (IConnectionServiceAdapter adapter : mAdapters) {
254             try {
255                 Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId);
256                 adapter.setIsConferenced(callId, conferenceCallId, Log.getExternalSession());
257             } catch (RemoteException ignored) {
258             }
259         }
260     }
261 
262     /**
263      * Indicates that the merge request on this call has failed.
264      *
265      * @param callId The unique ID of the call being conferenced.
266      */
onConferenceMergeFailed(String callId)267     void onConferenceMergeFailed(String callId) {
268         for (IConnectionServiceAdapter adapter : mAdapters) {
269             try {
270                 Log.d(this, "merge failed for call %s", callId);
271                 adapter.setConferenceMergeFailed(callId, Log.getExternalSession());
272             } catch (RemoteException ignored) {
273             }
274         }
275     }
276 
277     /**
278         * Resets the cdma connection time.
279         */
resetConnectionTime(String callId)280     void resetConnectionTime(String callId) {
281         for (IConnectionServiceAdapter adapter : mAdapters) {
282             try {
283                 adapter.resetConnectionTime(callId, Log.getExternalSession());
284             } catch (RemoteException e) {
285             }
286         }
287     }
288 
289     /**
290      * Indicates that the call no longer exists. Can be used with either a call or a conference
291      * call.
292      *
293      * @param callId The unique ID of the call.
294      */
removeCall(String callId)295     void removeCall(String callId) {
296         for (IConnectionServiceAdapter adapter : mAdapters) {
297             try {
298                 adapter.removeCall(callId, Log.getExternalSession());
299             } catch (RemoteException ignored) {
300             }
301         }
302     }
303 
onPostDialWait(String callId, String remaining)304     void onPostDialWait(String callId, String remaining) {
305         for (IConnectionServiceAdapter adapter : mAdapters) {
306             try {
307                 adapter.onPostDialWait(callId, remaining, Log.getExternalSession());
308             } catch (RemoteException ignored) {
309             }
310         }
311     }
312 
onPostDialChar(String callId, char nextChar)313     void onPostDialChar(String callId, char nextChar) {
314         for (IConnectionServiceAdapter adapter : mAdapters) {
315             try {
316                 adapter.onPostDialChar(callId, nextChar, Log.getExternalSession());
317             } catch (RemoteException ignored) {
318             }
319         }
320     }
321 
322     /**
323      * Indicates that a new conference call has been created.
324      *
325      * @param callId The unique ID of the conference call.
326      */
addConferenceCall(String callId, ParcelableConference parcelableConference)327     void addConferenceCall(String callId, ParcelableConference parcelableConference) {
328         for (IConnectionServiceAdapter adapter : mAdapters) {
329             try {
330                 adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession());
331             } catch (RemoteException ignored) {
332             }
333         }
334     }
335 
336     /**
337      * Retrieves a list of remote connection services usable to place calls.
338      */
queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage)339     void queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage) {
340         // Only supported when there is only one adapter.
341         if (mAdapters.size() == 1) {
342             try {
343                 mAdapters.iterator().next().queryRemoteConnectionServices(callback, callingPackage,
344                         Log.getExternalSession());
345             } catch (RemoteException e) {
346                 Log.e(this, e, "Exception trying to query for remote CSs");
347             }
348         } else {
349             try {
350                 // This is not an error condition, so just pass back an empty list.
351                 // This happens when querying from a remote connection service, not the connection
352                 // manager itself.
353                 callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
354             } catch (RemoteException e) {
355                 Log.e(this, e, "Exception trying to query for remote CSs");
356             }
357         }
358     }
359 
360     /**
361      * Sets the call video provider for a call.
362      *
363      * @param callId The unique ID of the call to set with the given call video provider.
364      * @param videoProvider The call video provider instance to set on the call.
365      */
setVideoProvider( String callId, Connection.VideoProvider videoProvider)366     void setVideoProvider(
367             String callId, Connection.VideoProvider videoProvider) {
368         for (IConnectionServiceAdapter adapter : mAdapters) {
369             try {
370                 adapter.setVideoProvider(
371                         callId,
372                         videoProvider == null ? null : videoProvider.getInterface(),
373                         Log.getExternalSession());
374             } catch (RemoteException e) {
375             }
376         }
377     }
378 
379     /**
380      * Requests that the framework use VOIP audio mode for this connection.
381      *
382      * @param callId The unique ID of the call to set with the given call video provider.
383      * @param isVoip True if the audio mode is VOIP.
384      */
setIsVoipAudioMode(String callId, boolean isVoip)385     void setIsVoipAudioMode(String callId, boolean isVoip) {
386         for (IConnectionServiceAdapter adapter : mAdapters) {
387             try {
388                 adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession());
389             } catch (RemoteException e) {
390             }
391         }
392     }
393 
setStatusHints(String callId, StatusHints statusHints)394     void setStatusHints(String callId, StatusHints statusHints) {
395         for (IConnectionServiceAdapter adapter : mAdapters) {
396             try {
397                 adapter.setStatusHints(callId, statusHints, Log.getExternalSession());
398             } catch (RemoteException e) {
399             }
400         }
401     }
402 
setAddress(String callId, Uri address, int presentation)403     void setAddress(String callId, Uri address, int presentation) {
404         for (IConnectionServiceAdapter adapter : mAdapters) {
405             try {
406                 adapter.setAddress(callId, address, presentation, Log.getExternalSession());
407             } catch (RemoteException e) {
408             }
409         }
410     }
411 
setCallerDisplayName(String callId, String callerDisplayName, int presentation)412     void setCallerDisplayName(String callId, String callerDisplayName, int presentation) {
413         for (IConnectionServiceAdapter adapter : mAdapters) {
414             try {
415                 adapter.setCallerDisplayName(callId, callerDisplayName, presentation,
416                         Log.getExternalSession());
417             } catch (RemoteException e) {
418             }
419         }
420     }
421 
422     /**
423      * Sets the video state associated with a call.
424      *
425      * Valid values: {@link VideoProfile#STATE_BIDIRECTIONAL},
426      * {@link VideoProfile#STATE_AUDIO_ONLY},
427      * {@link VideoProfile#STATE_TX_ENABLED},
428      * {@link VideoProfile#STATE_RX_ENABLED}.
429      *
430      * @param callId The unique ID of the call to set the video state for.
431      * @param videoState The video state.
432      */
setVideoState(String callId, int videoState)433     void setVideoState(String callId, int videoState) {
434         Log.v(this, "setVideoState: %d", videoState);
435         for (IConnectionServiceAdapter adapter : mAdapters) {
436             try {
437                 adapter.setVideoState(callId, videoState, Log.getExternalSession());
438             } catch (RemoteException ignored) {
439             }
440         }
441     }
442 
setConferenceableConnections(String callId, List<String> conferenceableCallIds)443     void setConferenceableConnections(String callId, List<String> conferenceableCallIds) {
444         Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds);
445         for (IConnectionServiceAdapter adapter : mAdapters) {
446             try {
447                 adapter.setConferenceableConnections(callId, conferenceableCallIds,
448                         Log.getExternalSession());
449             } catch (RemoteException ignored) {
450             }
451         }
452     }
453 
454     /**
455      * Informs telecom of an existing connection which was added by the {@link ConnectionService}.
456      *
457      * @param callId The unique ID of the call being added.
458      * @param connection The connection.
459      */
addExistingConnection(String callId, ParcelableConnection connection)460     void addExistingConnection(String callId, ParcelableConnection connection) {
461         Log.v(this, "addExistingConnection: %s", callId);
462         for (IConnectionServiceAdapter adapter : mAdapters) {
463             try {
464                 adapter.addExistingConnection(callId, connection, Log.getExternalSession());
465             } catch (RemoteException ignored) {
466             }
467         }
468     }
469 
470     /**
471      * Adds some extras associated with a {@code Connection}.
472      *
473      * @param callId The unique ID of the call.
474      * @param extras The extras to add.
475      */
putExtras(String callId, Bundle extras)476     void putExtras(String callId, Bundle extras) {
477         Log.v(this, "putExtras: %s", callId);
478         for (IConnectionServiceAdapter adapter : mAdapters) {
479             try {
480                 adapter.putExtras(callId, extras, Log.getExternalSession());
481             } catch (RemoteException ignored) {
482             }
483         }
484     }
485 
486     /**
487      * Adds an extra associated with a {@code Connection}.
488      *
489      * @param callId The unique ID of the call.
490      * @param key The extra key.
491      * @param value The extra value.
492      */
putExtra(String callId, String key, boolean value)493     void putExtra(String callId, String key, boolean value) {
494         Log.v(this, "putExtra: %s %s=%b", callId, key, value);
495         for (IConnectionServiceAdapter adapter : mAdapters) {
496             try {
497                 Bundle bundle = new Bundle();
498                 bundle.putBoolean(key, value);
499                 adapter.putExtras(callId, bundle, Log.getExternalSession());
500             } catch (RemoteException ignored) {
501             }
502         }
503     }
504 
505     /**
506      * Adds an extra associated with a {@code Connection}.
507      *
508      * @param callId The unique ID of the call.
509      * @param key The extra key.
510      * @param value The extra value.
511      */
putExtra(String callId, String key, int value)512     void putExtra(String callId, String key, int value) {
513         Log.v(this, "putExtra: %s %s=%d", callId, key, value);
514         for (IConnectionServiceAdapter adapter : mAdapters) {
515             try {
516                 Bundle bundle = new Bundle();
517                 bundle.putInt(key, value);
518                 adapter.putExtras(callId, bundle, Log.getExternalSession());
519             } catch (RemoteException ignored) {
520             }
521         }
522     }
523 
524     /**
525      * Adds an extra associated with a {@code Connection}.
526      *
527      * @param callId The unique ID of the call.
528      * @param key The extra key.
529      * @param value The extra value.
530      */
putExtra(String callId, String key, String value)531     void putExtra(String callId, String key, String value) {
532         Log.v(this, "putExtra: %s %s=%s", callId, key, value);
533         for (IConnectionServiceAdapter adapter : mAdapters) {
534             try {
535                 Bundle bundle = new Bundle();
536                 bundle.putString(key, value);
537                 adapter.putExtras(callId, bundle, Log.getExternalSession());
538             } catch (RemoteException ignored) {
539             }
540         }
541     }
542 
543     /**
544      * Removes extras associated with a {@code Connection}.
545      *  @param callId The unique ID of the call.
546      * @param keys The extra keys to remove.
547      */
removeExtras(String callId, List<String> keys)548     void removeExtras(String callId, List<String> keys) {
549         Log.v(this, "removeExtras: %s %s", callId, keys);
550         for (IConnectionServiceAdapter adapter : mAdapters) {
551             try {
552                 adapter.removeExtras(callId, keys, Log.getExternalSession());
553             } catch (RemoteException ignored) {
554             }
555         }
556     }
557 
558     /**
559      * Sets the audio route associated with a {@link Connection}.
560      *
561      * @param callId The unique ID of the call.
562      * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
563      */
setAudioRoute(String callId, int audioRoute, String bluetoothAddress)564     void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
565         Log.v(this, "setAudioRoute: %s %s %s", callId,
566                 CallAudioState.audioRouteToString(audioRoute),
567                 bluetoothAddress);
568         for (IConnectionServiceAdapter adapter : mAdapters) {
569             try {
570                 adapter.setAudioRoute(callId, audioRoute,
571                         bluetoothAddress, Log.getExternalSession());
572             } catch (RemoteException ignored) {
573             }
574         }
575     }
576 
577     /**
578      * Sets the call endpoint associated with a {@link Connection}.
579      *
580      * @param callId The unique ID of the call.
581      * @param endpoint The new call endpoint (see {@link CallEndpoint}).
582      * @param executor The executor of where the callback will execute.
583      * @param callback The callback to notify the result of the endpoint change.
584      */
requestCallEndpointChange(String callId, CallEndpoint endpoint, Executor executor, OutcomeReceiver<Void, CallEndpointException> callback)585     void requestCallEndpointChange(String callId, CallEndpoint endpoint, Executor executor,
586             OutcomeReceiver<Void, CallEndpointException> callback) {
587         Log.v(this, "requestCallEndpointChange");
588         for (IConnectionServiceAdapter adapter : mAdapters) {
589             try {
590                 adapter.requestCallEndpointChange(callId, endpoint, new ResultReceiver(null) {
591                     @Override
592                     protected void onReceiveResult(int resultCode, Bundle result) {
593                         super.onReceiveResult(resultCode, result);
594                         final long identity = Binder.clearCallingIdentity();
595                         try {
596                             if (resultCode == CallEndpoint.ENDPOINT_OPERATION_SUCCESS) {
597                                 executor.execute(() -> callback.onResult(null));
598                             } else {
599                                 executor.execute(() -> callback.onError(result.getParcelable(
600                                         CallEndpointException.CHANGE_ERROR,
601                                         CallEndpointException.class)));
602                             }
603                         } finally {
604                             Binder.restoreCallingIdentity(identity);
605                         }
606                     }}, Log.getExternalSession());
607             } catch (RemoteException ignored) {
608                 Log.d(this, "Remote exception calling requestCallEndpointChange");
609             }
610         }
611     }
612 
613     /**
614      * Informs Telecom of a connection level event.
615      *
616      * @param callId The unique ID of the call.
617      * @param event The event.
618      * @param extras Extras associated with the event.
619      */
onConnectionEvent(String callId, String event, Bundle extras)620     void onConnectionEvent(String callId, String event, Bundle extras) {
621         Log.v(this, "onConnectionEvent: %s", event);
622         for (IConnectionServiceAdapter adapter : mAdapters) {
623             try {
624                 adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession());
625             } catch (RemoteException ignored) {
626             }
627         }
628     }
629 
630     /**
631      * Notifies Telecom that an RTT session was successfully established.
632      *
633      * @param callId The unique ID of the call.
634      */
onRttInitiationSuccess(String callId)635     void onRttInitiationSuccess(String callId) {
636         Log.v(this, "onRttInitiationSuccess: %s", callId);
637         for (IConnectionServiceAdapter adapter : mAdapters) {
638             try {
639                 adapter.onRttInitiationSuccess(callId, Log.getExternalSession());
640             } catch (RemoteException ignored) {
641             }
642         }
643     }
644 
645     /**
646      * Notifies Telecom that a requested RTT session failed to be established.
647      *
648      * @param callId The unique ID of the call.
649      */
onRttInitiationFailure(String callId, int reason)650     void onRttInitiationFailure(String callId, int reason) {
651         Log.v(this, "onRttInitiationFailure: %s", callId);
652         for (IConnectionServiceAdapter adapter : mAdapters) {
653             try {
654                 adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession());
655             } catch (RemoteException ignored) {
656             }
657         }
658     }
659 
660     /**
661      * Notifies Telecom that an established RTT session was terminated by the remote user on
662      * the call.
663      *
664      * @param callId The unique ID of the call.
665      */
onRttSessionRemotelyTerminated(String callId)666     void onRttSessionRemotelyTerminated(String callId) {
667         Log.v(this, "onRttSessionRemotelyTerminated: %s", callId);
668         for (IConnectionServiceAdapter adapter : mAdapters) {
669             try {
670                 adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession());
671             } catch (RemoteException ignored) {
672             }
673         }
674     }
675 
676     /**
677      * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT
678      * session for this call.
679      *
680      * @param callId The unique ID of the call.
681      */
onRemoteRttRequest(String callId)682     void onRemoteRttRequest(String callId) {
683         Log.v(this, "onRemoteRttRequest: %s", callId);
684         for (IConnectionServiceAdapter adapter : mAdapters) {
685             try {
686                 adapter.onRemoteRttRequest(callId, Log.getExternalSession());
687             } catch (RemoteException ignored) {
688             }
689         }
690     }
691 
692     /**
693      * Notifies Telecom that a call's PhoneAccountHandle has changed.
694      *
695      * @param callId The unique ID of the call.
696      * @param pHandle The new PhoneAccountHandle associated with the call.
697      */
onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle)698     void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle) {
699         for (IConnectionServiceAdapter adapter : mAdapters) {
700             try {
701                 Log.d(this, "onPhoneAccountChanged %s", callId);
702                 adapter.onPhoneAccountChanged(callId, pHandle, Log.getExternalSession());
703             } catch (RemoteException ignored) {
704             }
705         }
706     }
707 
708     /**
709      * Notifies Telecom that the {@link ConnectionService} has released the call resource.
710      */
onConnectionServiceFocusReleased()711     void onConnectionServiceFocusReleased() {
712         for (IConnectionServiceAdapter adapter : mAdapters) {
713             try {
714                 Log.d(this, "onConnectionServiceFocusReleased");
715                 adapter.onConnectionServiceFocusReleased(Log.getExternalSession());
716             } catch (RemoteException ignored) {
717             }
718         }
719     }
720 
721     /**
722      * Sets whether a conference is treated as a conference or a single party call.
723      * See {@link Conference#setConferenceState(boolean)} for more information.
724      *
725      * @param callId The ID of the telecom call.
726      * @param isConference {@code true} if this call should be treated as a conference,
727      * {@code false} otherwise.
728      */
setConferenceState(String callId, boolean isConference)729     void setConferenceState(String callId, boolean isConference) {
730         Log.v(this, "setConferenceState: %s %b", callId, isConference);
731         for (IConnectionServiceAdapter adapter : mAdapters) {
732             try {
733                 adapter.setConferenceState(callId, isConference, Log.getExternalSession());
734             } catch (RemoteException ignored) {
735             }
736         }
737     }
738 
739     /**
740      * Sets the direction of a call. Setting a new direction of an existing call is usually only
741      * applicable during single caller emulation during conferencing, see
742      * {@link Conference#setConferenceState(boolean)} for more information.
743      * @param callId The identifier of the call.
744      * @param direction The new direction of the call.
745      */
setCallDirection(String callId, @Call.Details.CallDirection int direction)746     void setCallDirection(String callId, @Call.Details.CallDirection int direction) {
747         for (IConnectionServiceAdapter a : mAdapters) {
748             try {
749                 a.setCallDirection(callId, direction, Log.getExternalSession());
750             } catch (RemoteException e) {
751             }
752         }
753     }
754 
755     /**
756      * Query location information.
757      * Only SIM call managers can call this method for Connections representing Emergency calls.
758      * If the previous request is not completed, the new request will be rejected.
759      *
760      * @param timeoutMillis long: Timeout in millis waiting for query response.
761      * @param provider String: the location provider name, This value cannot be null.
762      * @param executor The executor of where the callback will execute.
763      * @param callback The callback to notify the result of queryLocation.
764      */
queryLocation(String callId, long timeoutMillis, @NonNull String provider, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Location, QueryLocationException> callback)765     void queryLocation(String callId, long timeoutMillis, @NonNull String provider,
766             @NonNull @CallbackExecutor Executor executor,
767             @NonNull OutcomeReceiver<Location, QueryLocationException> callback) {
768         Log.v(this, "queryLocation: %s %d", callId, timeoutMillis);
769         for (IConnectionServiceAdapter adapter : mAdapters) {
770             try {
771                 adapter.queryLocation(callId, timeoutMillis, provider,
772                         new ResultReceiver(null) {
773                             @Override
774                             protected void onReceiveResult(int resultCode, Bundle result) {
775                                 super.onReceiveResult(resultCode, result);
776 
777                                 if (resultCode == 1 /* success */) {
778                                     executor.execute(() -> callback.onResult(result.getParcelable(
779                                             Connection.EXTRA_KEY_QUERY_LOCATION, Location.class)));
780                                 } else {
781                                     executor.execute(() -> callback.onError(result.getParcelable(
782                                             QueryLocationException.QUERY_LOCATION_ERROR,
783                                             QueryLocationException.class)));
784                                 }
785                             }
786                         },
787                         Log.getExternalSession());
788             } catch (RemoteException e) {
789                 Log.d(this, "queryLocation: Exception e : " + e);
790                 executor.execute(() -> callback.onError(new QueryLocationException(
791                         e.getMessage(), QueryLocationException.ERROR_SERVICE_UNAVAILABLE)));
792             }
793         }
794     }
795 }
796