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.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.hardware.camera2.CameraManager;
25 import android.net.Uri;
26 import android.os.BadParcelableException;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.telecom.Logging.Session;
32 import android.view.Surface;
33 
34 import com.android.internal.telecom.IConnectionService;
35 import com.android.internal.telecom.IVideoCallback;
36 import com.android.internal.telecom.IVideoProvider;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Set;
42 import java.util.concurrent.ConcurrentHashMap;
43 
44 /**
45  * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
46  * running in a different process.
47  *
48  * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
49  * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
50  */
51 public final class RemoteConnection {
52 
53     /**
54      * Callback base class for {@link RemoteConnection}.
55      */
56     public static abstract class Callback {
57         /**
58          * Invoked when the state of this {@code RemoteConnection} has changed. See
59          * {@link #getState()}.
60          *
61          * @param connection The {@code RemoteConnection} invoking this method.
62          * @param state The new state of the {@code RemoteConnection}.
63          */
onStateChanged(RemoteConnection connection, int state)64         public void onStateChanged(RemoteConnection connection, int state) {}
65 
66         /**
67          * Invoked when this {@code RemoteConnection} is disconnected.
68          *
69          * @param connection The {@code RemoteConnection} invoking this method.
70          * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
71          *     connection.
72          */
onDisconnected( RemoteConnection connection, DisconnectCause disconnectCause)73         public void onDisconnected(
74                 RemoteConnection connection,
75                 DisconnectCause disconnectCause) {}
76 
77         /**
78          * Invoked when this {@code RemoteConnection} is requesting ringback. See
79          * {@link #isRingbackRequested()}.
80          *
81          * @param connection The {@code RemoteConnection} invoking this method.
82          * @param ringback Whether the {@code RemoteConnection} is requesting ringback.
83          */
onRingbackRequested(RemoteConnection connection, boolean ringback)84         public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
85 
86         /**
87          * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
88          * See {@link #getConnectionCapabilities()}.
89          *
90          * @param connection The {@code RemoteConnection} invoking this method.
91          * @param connectionCapabilities The new capabilities of the {@code RemoteConnection}.
92          */
onConnectionCapabilitiesChanged( RemoteConnection connection, int connectionCapabilities)93         public void onConnectionCapabilitiesChanged(
94                 RemoteConnection connection,
95                 int connectionCapabilities) {}
96 
97         /**
98          * Indicates that the call properties of this {@code RemoteConnection} have changed.
99          * See {@link #getConnectionProperties()}.
100          *
101          * @param connection The {@code RemoteConnection} invoking this method.
102          * @param connectionProperties The new properties of the {@code RemoteConnection}.
103          */
onConnectionPropertiesChanged( RemoteConnection connection, int connectionProperties)104         public void onConnectionPropertiesChanged(
105                 RemoteConnection connection,
106                 int connectionProperties) {}
107 
108         /**
109          * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
110          * pause character. This causes the post-dial signals to stop pending user confirmation. An
111          * implementation should present this choice to the user and invoke
112          * {@link RemoteConnection#postDialContinue(boolean)} when the user makes the choice.
113          *
114          * @param connection The {@code RemoteConnection} invoking this method.
115          * @param remainingPostDialSequence The post-dial characters that remain to be sent.
116          */
onPostDialWait(RemoteConnection connection, String remainingPostDialSequence)117         public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {}
118 
119         /**
120          * Invoked when the post-dial sequence in the outgoing {@code Connection} has processed
121          * a character.
122          *
123          * @param connection The {@code RemoteConnection} invoking this method.
124          * @param nextChar The character being processed.
125          */
onPostDialChar(RemoteConnection connection, char nextChar)126         public void onPostDialChar(RemoteConnection connection, char nextChar) {}
127 
128         /**
129          * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed.
130          * See {@link #isVoipAudioMode()}.
131          *
132          * @param connection The {@code RemoteConnection} invoking this method.
133          * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
134          */
onVoipAudioChanged(RemoteConnection connection, boolean isVoip)135         public void onVoipAudioChanged(RemoteConnection connection, boolean isVoip) {}
136 
137         /**
138          * Indicates that the status hints of this {@code RemoteConnection} have changed. See
139          * {@link #getStatusHints()} ()}.
140          *
141          * @param connection The {@code RemoteConnection} invoking this method.
142          * @param statusHints The new status hints of the {@code RemoteConnection}.
143          */
onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints)144         public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {}
145 
146         /**
147          * Indicates that the address (e.g., phone number) of this {@code RemoteConnection} has
148          * changed. See {@link #getAddress()} and {@link #getAddressPresentation()}.
149          *
150          * @param connection The {@code RemoteConnection} invoking this method.
151          * @param address The new address of the {@code RemoteConnection}.
152          * @param presentation The presentation requirements for the address.
153          *        See {@link TelecomManager} for valid values.
154          */
onAddressChanged(RemoteConnection connection, Uri address, int presentation)155         public void onAddressChanged(RemoteConnection connection, Uri address, int presentation) {}
156 
157         /**
158          * Indicates that the caller display name of this {@code RemoteConnection} has changed.
159          * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}.
160          *
161          * @param connection The {@code RemoteConnection} invoking this method.
162          * @param callerDisplayName The new caller display name of the {@code RemoteConnection}.
163          * @param presentation The presentation requirements for the handle.
164          *        See {@link TelecomManager} for valid values.
165          */
onCallerDisplayNameChanged( RemoteConnection connection, String callerDisplayName, int presentation)166         public void onCallerDisplayNameChanged(
167                 RemoteConnection connection, String callerDisplayName, int presentation) {}
168 
169         /**
170          * Indicates that the video state of this {@code RemoteConnection} has changed.
171          * See {@link #getVideoState()}.
172          *
173          * @param connection The {@code RemoteConnection} invoking this method.
174          * @param videoState The new video state of the {@code RemoteConnection}.
175          */
onVideoStateChanged(RemoteConnection connection, int videoState)176         public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
177 
178         /**
179          * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
180          * should be made to the {@code RemoteConnection}, and references to it should be cleared.
181          *
182          * @param connection The {@code RemoteConnection} invoking this method.
183          */
onDestroyed(RemoteConnection connection)184         public void onDestroyed(RemoteConnection connection) {}
185 
186         /**
187          * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection}
188          * may be asked to create a conference has changed.
189          *
190          * @param connection The {@code RemoteConnection} invoking this method.
191          * @param conferenceableConnections The {@code RemoteConnection}s with which this
192          *         {@code RemoteConnection} may be asked to create a conference.
193          */
onConferenceableConnectionsChanged( RemoteConnection connection, List<RemoteConnection> conferenceableConnections)194         public void onConferenceableConnectionsChanged(
195                 RemoteConnection connection,
196                 List<RemoteConnection> conferenceableConnections) {}
197 
198         /**
199          * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
200          * has changed.
201          *
202          * @param connection The {@code RemoteConnection} invoking this method.
203          * @param videoProvider The new {@code VideoProvider} associated with this
204          *         {@code RemoteConnection}.
205          */
onVideoProviderChanged( RemoteConnection connection, VideoProvider videoProvider)206         public void onVideoProviderChanged(
207                 RemoteConnection connection, VideoProvider videoProvider) {}
208 
209         /**
210          * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
211          * of has changed.
212          *
213          * @param connection The {@code RemoteConnection} invoking this method.
214          * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is
215          *         a part, which may be {@code null}.
216          */
onConferenceChanged( RemoteConnection connection, RemoteConference conference)217         public void onConferenceChanged(
218                 RemoteConnection connection,
219                 RemoteConference conference) {}
220 
221         /**
222          * Handles changes to the {@code RemoteConnection} extras.
223          *
224          * @param connection The {@code RemoteConnection} invoking this method.
225          * @param extras The extras containing other information associated with the connection.
226          */
onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras)227         public void onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras) {}
228 
229         /**
230          * Handles a connection event propagated to this {@link RemoteConnection}.
231          * <p>
232          * Connection events originate from {@link Connection#sendConnectionEvent(String, Bundle)}.
233          *
234          * @param connection The {@code RemoteConnection} invoking this method.
235          * @param event The connection event.
236          * @param extras Extras associated with the event.
237          */
onConnectionEvent(RemoteConnection connection, String event, Bundle extras)238         public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
239 
240         /**
241          * Indicates that a RTT session was successfully established on this
242          * {@link RemoteConnection}. See {@link Connection#sendRttInitiationSuccess()}.
243          * @hide
244          * @param connection The {@code RemoteConnection} invoking this method.
245          */
onRttInitiationSuccess(RemoteConnection connection)246         public void onRttInitiationSuccess(RemoteConnection connection) {}
247 
248         /**
249          * Indicates that a RTT session failed to be established on this
250          * {@link RemoteConnection}. See {@link Connection#sendRttInitiationFailure()}.
251          * @hide
252          * @param connection The {@code RemoteConnection} invoking this method.
253          * @param reason One of the reason codes defined in {@link Connection.RttModifyStatus},
254          *               with the exception of
255          *               {@link Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
256          */
onRttInitiationFailure(RemoteConnection connection, int reason)257         public void onRttInitiationFailure(RemoteConnection connection, int reason) {}
258 
259         /**
260          * Indicates that an established RTT session was terminated remotely on this
261          * {@link RemoteConnection}. See {@link Connection#sendRttSessionRemotelyTerminated()}
262          * @hide
263          * @param connection The {@code RemoteConnection} invoking this method.
264          */
onRttSessionRemotelyTerminated(RemoteConnection connection)265         public void onRttSessionRemotelyTerminated(RemoteConnection connection) {}
266 
267         /**
268          * Indicates that the remote user on this {@link RemoteConnection} has requested an upgrade
269          * to an RTT session. See {@link Connection#sendRemoteRttRequest()}
270          * @hide
271          * @param connection The {@code RemoteConnection} invoking this method.
272          */
onRemoteRttRequest(RemoteConnection connection)273         public void onRemoteRttRequest(RemoteConnection connection) {}
274     }
275 
276     /**
277      * {@link RemoteConnection.VideoProvider} associated with a {@link RemoteConnection}.  Used to
278      * receive video related events and control the video associated with a
279      * {@link RemoteConnection}.
280      *
281      * @see Connection.VideoProvider
282      */
283     public static class VideoProvider {
284 
285         /**
286          * Callback class used by the {@link RemoteConnection.VideoProvider} to relay events from
287          * the {@link Connection.VideoProvider}.
288          */
289         public abstract static class Callback {
290             /**
291              * Reports a session modification request received from the
292              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
293              *
294              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
295              * @param videoProfile The requested video call profile.
296              * @see InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)
297              * @see Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)
298              */
onSessionModifyRequestReceived( VideoProvider videoProvider, VideoProfile videoProfile)299             public void onSessionModifyRequestReceived(
300                     VideoProvider videoProvider,
301                     VideoProfile videoProfile) {}
302 
303             /**
304              * Reports a session modification response received from the
305              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
306              *
307              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
308              * @param status Status of the session modify request.
309              * @param requestedProfile The original request which was sent to the peer device.
310              * @param responseProfile The actual profile changes made by the peer device.
311              * @see InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
312              *      VideoProfile, VideoProfile)
313              * @see Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
314              *      VideoProfile)
315              */
onSessionModifyResponseReceived( VideoProvider videoProvider, int status, VideoProfile requestedProfile, VideoProfile responseProfile)316             public void onSessionModifyResponseReceived(
317                     VideoProvider videoProvider,
318                     int status,
319                     VideoProfile requestedProfile,
320                     VideoProfile responseProfile) {}
321 
322             /**
323              * Reports a call session event received from the {@link Connection.VideoProvider}
324              * associated with a {@link RemoteConnection}.
325              *
326              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
327              * @param event The event.
328              * @see InCallService.VideoCall.Callback#onCallSessionEvent(int)
329              * @see Connection.VideoProvider#handleCallSessionEvent(int)
330              */
onCallSessionEvent(VideoProvider videoProvider, int event)331             public void onCallSessionEvent(VideoProvider videoProvider, int event) {}
332 
333             /**
334              * Reports a change in the peer video dimensions received from the
335              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
336              *
337              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
338              * @param width  The updated peer video width.
339              * @param height The updated peer video height.
340              * @see InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)
341              * @see Connection.VideoProvider#changePeerDimensions(int, int)
342              */
onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height)343             public void onPeerDimensionsChanged(VideoProvider videoProvider, int width,
344                     int height) {}
345 
346             /**
347              * Reports a change in the data usage (in bytes) received from the
348              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
349              *
350              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
351              * @param dataUsage The updated data usage (in bytes).
352              * @see InCallService.VideoCall.Callback#onCallDataUsageChanged(long)
353              * @see Connection.VideoProvider#setCallDataUsage(long)
354              */
onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage)355             public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
356 
357             /**
358              * Reports a change in the capabilities of the current camera, received from the
359              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
360              *
361              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
362              * @param cameraCapabilities The changed camera capabilities.
363              * @see InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
364              *      VideoProfile.CameraCapabilities)
365              * @see Connection.VideoProvider#changeCameraCapabilities(
366              *      VideoProfile.CameraCapabilities)
367              */
onCameraCapabilitiesChanged( VideoProvider videoProvider, VideoProfile.CameraCapabilities cameraCapabilities)368             public void onCameraCapabilitiesChanged(
369                     VideoProvider videoProvider,
370                     VideoProfile.CameraCapabilities cameraCapabilities) {}
371 
372             /**
373              * Reports a change in the video quality received from the
374              * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
375              *
376              * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
377              * @param videoQuality  The updated peer video quality.
378              * @see InCallService.VideoCall.Callback#onVideoQualityChanged(int)
379              * @see Connection.VideoProvider#changeVideoQuality(int)
380              */
onVideoQualityChanged(VideoProvider videoProvider, int videoQuality)381             public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
382         }
383 
384         private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
385             @Override
386             public void receiveSessionModifyRequest(VideoProfile videoProfile) {
387                 for (Callback l : mCallbacks) {
388                     l.onSessionModifyRequestReceived(VideoProvider.this, videoProfile);
389                 }
390             }
391 
392             @Override
393             public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
394                     VideoProfile responseProfile) {
395                 for (Callback l : mCallbacks) {
396                     l.onSessionModifyResponseReceived(
397                             VideoProvider.this,
398                             status,
399                             requestedProfile,
400                             responseProfile);
401                 }
402             }
403 
404             @Override
405             public void handleCallSessionEvent(int event) {
406                 for (Callback l : mCallbacks) {
407                     l.onCallSessionEvent(VideoProvider.this, event);
408                 }
409             }
410 
411             @Override
412             public void changePeerDimensions(int width, int height) {
413                 for (Callback l : mCallbacks) {
414                     l.onPeerDimensionsChanged(VideoProvider.this, width, height);
415                 }
416             }
417 
418             @Override
419             public void changeCallDataUsage(long dataUsage) {
420                 for (Callback l : mCallbacks) {
421                     l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
422                 }
423             }
424 
425             @Override
426             public void changeCameraCapabilities(
427                     VideoProfile.CameraCapabilities cameraCapabilities) {
428                 for (Callback l : mCallbacks) {
429                     l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
430                 }
431             }
432 
433             @Override
434             public void changeVideoQuality(int videoQuality) {
435                 for (Callback l : mCallbacks) {
436                     l.onVideoQualityChanged(VideoProvider.this, videoQuality);
437                 }
438             }
439 
440             @Override
441             public IBinder asBinder() {
442                 return null;
443             }
444         };
445 
446         private final VideoCallbackServant mVideoCallbackServant =
447                 new VideoCallbackServant(mVideoCallbackDelegate);
448 
449         private final IVideoProvider mVideoProviderBinder;
450 
451         private final String mCallingPackage;
452 
453         private final int mTargetSdkVersion;
454 
455         /**
456          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
457          * load factor before resizing, 1 means we only expect a single thread to
458          * access the map so make only a single shard
459          */
460         private final Set<Callback> mCallbacks = Collections.newSetFromMap(
461                 new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
462 
VideoProvider(IVideoProvider videoProviderBinder, String callingPackage, int targetSdkVersion)463         VideoProvider(IVideoProvider videoProviderBinder, String callingPackage,
464                       int targetSdkVersion) {
465 
466             mVideoProviderBinder = videoProviderBinder;
467             mCallingPackage = callingPackage;
468             mTargetSdkVersion = targetSdkVersion;
469             try {
470                 mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
471             } catch (RemoteException e) {
472             }
473         }
474 
475         /**
476          * Registers a callback to receive commands and state changes for video calls.
477          *
478          * @param l The video call callback.
479          */
registerCallback(Callback l)480         public void registerCallback(Callback l) {
481             mCallbacks.add(l);
482         }
483 
484         /**
485          * Clears the video call callback set via {@link #registerCallback}.
486          *
487          * @param l The video call callback to clear.
488          */
unregisterCallback(Callback l)489         public void unregisterCallback(Callback l) {
490             mCallbacks.remove(l);
491         }
492 
493         /**
494          * Sets the camera to be used for the outgoing video for the
495          * {@link RemoteConnection.VideoProvider}.
496          *
497          * @param cameraId The id of the camera (use ids as reported by
498          * {@link CameraManager#getCameraIdList()}).
499          * @see Connection.VideoProvider#onSetCamera(String)
500          */
setCamera(String cameraId)501         public void setCamera(String cameraId) {
502             try {
503                 mVideoProviderBinder.setCamera(cameraId, mCallingPackage, mTargetSdkVersion);
504             } catch (RemoteException e) {
505             }
506         }
507 
508         /**
509          * Sets the surface to be used for displaying a preview of what the user's camera is
510          * currently capturing for the {@link RemoteConnection.VideoProvider}.
511          *
512          * @param surface The {@link Surface}.
513          * @see Connection.VideoProvider#onSetPreviewSurface(Surface)
514          */
setPreviewSurface(Surface surface)515         public void setPreviewSurface(Surface surface) {
516             try {
517                 mVideoProviderBinder.setPreviewSurface(surface);
518             } catch (RemoteException e) {
519             }
520         }
521 
522         /**
523          * Sets the surface to be used for displaying the video received from the remote device for
524          * the {@link RemoteConnection.VideoProvider}.
525          *
526          * @param surface The {@link Surface}.
527          * @see Connection.VideoProvider#onSetDisplaySurface(Surface)
528          */
setDisplaySurface(Surface surface)529         public void setDisplaySurface(Surface surface) {
530             try {
531                 mVideoProviderBinder.setDisplaySurface(surface);
532             } catch (RemoteException e) {
533             }
534         }
535 
536         /**
537          * Sets the device orientation, in degrees, for the {@link RemoteConnection.VideoProvider}.
538          * Assumes that a standard portrait orientation of the device is 0 degrees.
539          *
540          * @param rotation The device orientation, in degrees.
541          * @see Connection.VideoProvider#onSetDeviceOrientation(int)
542          */
setDeviceOrientation(int rotation)543         public void setDeviceOrientation(int rotation) {
544             try {
545                 mVideoProviderBinder.setDeviceOrientation(rotation);
546             } catch (RemoteException e) {
547             }
548         }
549 
550         /**
551          * Sets camera zoom ratio for the {@link RemoteConnection.VideoProvider}.
552          *
553          * @param value The camera zoom ratio.
554          * @see Connection.VideoProvider#onSetZoom(float)
555          */
setZoom(float value)556         public void setZoom(float value) {
557             try {
558                 mVideoProviderBinder.setZoom(value);
559             } catch (RemoteException e) {
560             }
561         }
562 
563         /**
564          * Issues a request to modify the properties of the current video session for the
565          * {@link RemoteConnection.VideoProvider}.
566          *
567          * @param fromProfile The video profile prior to the request.
568          * @param toProfile The video profile with the requested changes made.
569          * @see Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)
570          */
sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)571         public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
572             try {
573                 mVideoProviderBinder.sendSessionModifyRequest(fromProfile, toProfile);
574             } catch (RemoteException e) {
575             }
576         }
577 
578         /**
579          * Provides a response to a request to change the current call video session
580          * properties for the {@link RemoteConnection.VideoProvider}.
581          *
582          * @param responseProfile The response call video properties.
583          * @see Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)
584          */
sendSessionModifyResponse(VideoProfile responseProfile)585         public void sendSessionModifyResponse(VideoProfile responseProfile) {
586             try {
587                 mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
588             } catch (RemoteException e) {
589             }
590         }
591 
592         /**
593          * Issues a request to retrieve the capabilities of the current camera for the
594          * {@link RemoteConnection.VideoProvider}.
595          *
596          * @see Connection.VideoProvider#onRequestCameraCapabilities()
597          */
requestCameraCapabilities()598         public void requestCameraCapabilities() {
599             try {
600                 mVideoProviderBinder.requestCameraCapabilities();
601             } catch (RemoteException e) {
602             }
603         }
604 
605         /**
606          * Issues a request to retrieve the data usage (in bytes) of the video portion of the
607          * {@link RemoteConnection} for the {@link RemoteConnection.VideoProvider}.
608          *
609          * @see Connection.VideoProvider#onRequestConnectionDataUsage()
610          */
requestCallDataUsage()611         public void requestCallDataUsage() {
612             try {
613                 mVideoProviderBinder.requestCallDataUsage();
614             } catch (RemoteException e) {
615             }
616         }
617 
618         /**
619          * Sets the {@link Uri} of an image to be displayed to the peer device when the video signal
620          * is paused, for the {@link RemoteConnection.VideoProvider}.
621          *
622          * @see Connection.VideoProvider#onSetPauseImage(Uri)
623          */
setPauseImage(Uri uri)624         public void setPauseImage(Uri uri) {
625             try {
626                 mVideoProviderBinder.setPauseImage(uri);
627             } catch (RemoteException e) {
628             }
629         }
630     }
631 
632     private IConnectionService mConnectionService;
633     private final String mConnectionId;
634     /**
635      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
636      * load factor before resizing, 1 means we only expect a single thread to
637      * access the map so make only a single shard
638      */
639     private final Set<CallbackRecord> mCallbackRecords = Collections.newSetFromMap(
640             new ConcurrentHashMap<CallbackRecord, Boolean>(8, 0.9f, 1));
641     private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
642     private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
643             Collections.unmodifiableList(mConferenceableConnections);
644 
645     private int mState = Connection.STATE_NEW;
646     private DisconnectCause mDisconnectCause;
647     private boolean mRingbackRequested;
648     private boolean mConnected;
649     private int mConnectionCapabilities;
650     private int mConnectionProperties;
651     private int mVideoState;
652     private VideoProvider mVideoProvider;
653     private boolean mIsVoipAudioMode;
654     private StatusHints mStatusHints;
655     private Uri mAddress;
656     private int mAddressPresentation;
657     private String mCallerDisplayName;
658     private int mCallerDisplayNamePresentation;
659     private RemoteConference mConference;
660     private Bundle mExtras;
661     private String mCallingPackageAbbreviation;
662 
663     /**
664      * @hide
665      */
RemoteConnection( String id, IConnectionService connectionService, ConnectionRequest request)666     RemoteConnection(
667             String id,
668             IConnectionService connectionService,
669             ConnectionRequest request) {
670         mConnectionId = id;
671         mConnectionService = connectionService;
672         mConnected = true;
673         mState = Connection.STATE_INITIALIZING;
674         if (request != null && request.getExtras() != null
675                 && request.getExtras().containsKey(
676                         Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
677             String callingPackage = request.getExtras().getString(
678                     Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME);
679             mCallingPackageAbbreviation = Log.getPackageAbbreviation(callingPackage);
680         }
681     }
682 
683     /**
684      * @hide
685      */
RemoteConnection(String callId, IConnectionService connectionService, ParcelableConnection connection, String callingPackage, int targetSdkVersion)686     RemoteConnection(String callId, IConnectionService connectionService,
687             ParcelableConnection connection, String callingPackage, int targetSdkVersion) {
688         mConnectionId = callId;
689         mConnectionService = connectionService;
690         mConnected = true;
691         mState = connection.getState();
692         mDisconnectCause = connection.getDisconnectCause();
693         mRingbackRequested = connection.isRingbackRequested();
694         mConnectionCapabilities = connection.getConnectionCapabilities();
695         mConnectionProperties = connection.getConnectionProperties();
696         mVideoState = connection.getVideoState();
697         IVideoProvider videoProvider = connection.getVideoProvider();
698         if (videoProvider != null) {
699             mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage,
700                     targetSdkVersion);
701         } else {
702             mVideoProvider = null;
703         }
704         mIsVoipAudioMode = connection.getIsVoipAudioMode();
705         mStatusHints = connection.getStatusHints();
706         mAddress = connection.getHandle();
707         mAddressPresentation = connection.getHandlePresentation();
708         mCallerDisplayName = connection.getCallerDisplayName();
709         mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation();
710         mConference = null;
711         putExtras(connection.getExtras());
712 
713         // Stash the original connection ID as it exists in the source ConnectionService.
714         // Telecom will use this to avoid adding duplicates later.
715         // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information.
716         Bundle newExtras = new Bundle();
717         newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
718         putExtras(newExtras);
719         mCallingPackageAbbreviation = Log.getPackageAbbreviation(callingPackage);
720     }
721 
722     /**
723      * Create a RemoteConnection which is used for failed connections. Note that using it for any
724      * "real" purpose will almost certainly fail. Callers should note the failure and act
725      * accordingly (moving on to another RemoteConnection, for example)
726      *
727      * @param disconnectCause The reason for the failed connection.
728      * @hide
729      */
RemoteConnection(DisconnectCause disconnectCause)730     RemoteConnection(DisconnectCause disconnectCause) {
731         mConnectionId = "NULL";
732         mConnected = false;
733         mState = Connection.STATE_DISCONNECTED;
734         mDisconnectCause = disconnectCause;
735     }
736 
737     /**
738      * Adds a callback to this {@code RemoteConnection}.
739      *
740      * @param callback A {@code Callback}.
741      */
registerCallback(Callback callback)742     public void registerCallback(Callback callback) {
743         registerCallback(callback, new Handler());
744     }
745 
746     /**
747      * Adds a callback to this {@code RemoteConnection}.
748      *
749      * @param callback A {@code Callback}.
750      * @param handler A {@code Handler} which command and status changes will be delivered to.
751      */
registerCallback(Callback callback, Handler handler)752     public void registerCallback(Callback callback, Handler handler) {
753         unregisterCallback(callback);
754         if (callback != null && handler != null) {
755             mCallbackRecords.add(new CallbackRecord(callback, handler));
756         }
757     }
758 
759     /**
760      * Removes a callback from this {@code RemoteConnection}.
761      *
762      * @param callback A {@code Callback}.
763      */
unregisterCallback(Callback callback)764     public void unregisterCallback(Callback callback) {
765         if (callback != null) {
766             for (CallbackRecord record : mCallbackRecords) {
767                 if (record.getCallback() == callback) {
768                     mCallbackRecords.remove(record);
769                     break;
770                 }
771             }
772         }
773     }
774 
775     /**
776      * Obtains the state of this {@code RemoteConnection}.
777      *
778      * @return A state value, chosen from the {@code STATE_*} constants.
779      */
getState()780     public int getState() {
781         return mState;
782     }
783 
784     /**
785      * Obtains the reason why this {@code RemoteConnection} may have been disconnected.
786      *
787      * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, the
788      *         disconnect cause expressed as a code chosen from among those declared in
789      *         {@link DisconnectCause}.
790      */
getDisconnectCause()791     public DisconnectCause getDisconnectCause() {
792         return mDisconnectCause;
793     }
794 
795     /**
796      * Obtains the capabilities of this {@code RemoteConnection}.
797      *
798      * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
799      *         the {@code CAPABILITY_*} constants in class {@link Connection}.
800      */
getConnectionCapabilities()801     public int getConnectionCapabilities() {
802         return mConnectionCapabilities;
803     }
804 
805     /**
806      * Obtains the properties of this {@code RemoteConnection}.
807      *
808      * @return A bitmask of the properties of the {@code RemoteConnection}, as defined in the
809      *         {@code PROPERTY_*} constants in class {@link Connection}.
810      */
getConnectionProperties()811     public int getConnectionProperties() {
812         return mConnectionProperties;
813     }
814 
815     /**
816      * Determines if the audio mode of this {@code RemoteConnection} is VOIP.
817      *
818      * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
819      */
isVoipAudioMode()820     public boolean isVoipAudioMode() {
821         return mIsVoipAudioMode;
822     }
823 
824     /**
825      * Obtains status hints pertaining to this {@code RemoteConnection}.
826      *
827      * @return The current {@link StatusHints} of this {@code RemoteConnection},
828      *         or {@code null} if none have been set.
829      */
getStatusHints()830     public StatusHints getStatusHints() {
831         return mStatusHints;
832     }
833 
834     /**
835      * Obtains the address of this {@code RemoteConnection}.
836      *
837      * @return The address (e.g., phone number) to which the {@code RemoteConnection}
838      *         is currently connected.
839      */
getAddress()840     public Uri getAddress() {
841         return mAddress;
842     }
843 
844     /**
845      * Obtains the presentation requirements for the address of this {@code RemoteConnection}.
846      *
847      * @return The presentation requirements for the address. See
848      *         {@link TelecomManager} for valid values.
849      */
getAddressPresentation()850     public int getAddressPresentation() {
851         return mAddressPresentation;
852     }
853 
854     /**
855      * Obtains the display name for this {@code RemoteConnection}'s caller.
856      *
857      * @return The display name for the caller.
858      */
getCallerDisplayName()859     public CharSequence getCallerDisplayName() {
860         return mCallerDisplayName;
861     }
862 
863     /**
864      * Obtains the presentation requirements for this {@code RemoteConnection}'s
865      * caller's display name.
866      *
867      * @return The presentation requirements for the caller display name. See
868      *         {@link TelecomManager} for valid values.
869      */
getCallerDisplayNamePresentation()870     public int getCallerDisplayNamePresentation() {
871         return mCallerDisplayNamePresentation;
872     }
873 
874     /**
875      * Obtains the video state of this {@code RemoteConnection}.
876      *
877      * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile}.
878      */
getVideoState()879     public int getVideoState() {
880         return mVideoState;
881     }
882 
883     /**
884      * Obtains the video provider of this {@code RemoteConnection}.
885      * @return The video provider associated with this {@code RemoteConnection}.
886      */
getVideoProvider()887     public final VideoProvider getVideoProvider() {
888         return mVideoProvider;
889     }
890 
891     /**
892      * Obtain the extras associated with this {@code RemoteConnection}.
893      *
894      * @return The extras for this connection.
895      */
getExtras()896     public final Bundle getExtras() {
897         return mExtras;
898     }
899 
900     /**
901      * Determines whether this {@code RemoteConnection} is requesting ringback.
902      *
903      * @return Whether the {@code RemoteConnection} is requesting that the framework play a
904      *         ringback tone on its behalf.
905      */
isRingbackRequested()906     public boolean isRingbackRequested() {
907         return mRingbackRequested;
908     }
909 
910     /**
911      * Instructs this {@code RemoteConnection} to abort.
912      */
abort()913     public void abort() {
914         Log.startSession("RC.a", getActiveOwnerInfo());
915         try {
916             if (mConnected) {
917                 mConnectionService.abort(mConnectionId, Log.getExternalSession(
918                         mCallingPackageAbbreviation));
919             }
920         } catch (RemoteException ignored) {
921         } finally {
922             Log.endSession();
923         }
924     }
925 
926     /**
927      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
928      */
answer()929     public void answer() {
930         Log.startSession("RC.an", getActiveOwnerInfo());
931         try {
932             if (mConnected) {
933                mConnectionService.answer(mConnectionId, Log.getExternalSession(
934                        mCallingPackageAbbreviation));
935             }
936         } catch (RemoteException ignored) {
937         } finally {
938             Log.endSession();
939         }
940     }
941 
942     /**
943      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
944      * @param videoState The video state in which to answer the call.
945      * @hide
946      */
answer(int videoState)947     public void answer(int videoState) {
948         Log.startSession("RC.an2", getActiveOwnerInfo());
949         try {
950             if (mConnected) {
951                 mConnectionService.answerVideo(mConnectionId, videoState,
952                         Log.getExternalSession(mCallingPackageAbbreviation));
953             }
954         } catch (RemoteException ignored) {
955         } finally {
956             Log.endSession();
957         }
958     }
959 
960     /**
961      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
962      */
reject()963     public void reject() {
964         Log.startSession("RC.r", getActiveOwnerInfo());
965         try {
966             if (mConnected) {
967                 mConnectionService.reject(mConnectionId, Log.getExternalSession(
968                         mCallingPackageAbbreviation));
969             }
970         } catch (RemoteException ignored) {
971         } finally {
972             Log.endSession();
973         }
974     }
975 
976     /**
977      * Instructs this {@code RemoteConnection} to go on hold.
978      */
hold()979     public void hold() {
980         Log.startSession("RC.h", getActiveOwnerInfo());
981         try {
982             if (mConnected) {
983                 mConnectionService.hold(mConnectionId, Log.getExternalSession(
984                         mCallingPackageAbbreviation));
985             }
986         } catch (RemoteException ignored) {
987         } finally {
988             Log.endSession();
989         }
990     }
991 
992     /**
993      * Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
994      */
unhold()995     public void unhold() {
996         Log.startSession("RC.u", getActiveOwnerInfo());
997         try {
998             if (mConnected) {
999                 mConnectionService.unhold(mConnectionId, Log.getExternalSession(
1000                         mCallingPackageAbbreviation));
1001             }
1002         } catch (RemoteException ignored) {
1003         } finally {
1004             Log.endSession();
1005         }
1006     }
1007 
1008     /**
1009      * Instructs this {@code RemoteConnection} to disconnect.
1010      */
disconnect()1011     public void disconnect() {
1012         Log.startSession("RC.d", getActiveOwnerInfo());
1013         try {
1014             if (mConnected) {
1015                 mConnectionService.disconnect(mConnectionId, Log.getExternalSession(
1016                         mCallingPackageAbbreviation));
1017             }
1018         } catch (RemoteException ignored) {
1019         } finally {
1020             Log.endSession();
1021         }
1022     }
1023 
1024     /**
1025      * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling
1026      * (DTMF) tone.
1027      *
1028      * Any other currently playing DTMF tone in the specified call is immediately stopped.
1029      *
1030      * @param digit A character representing the DTMF digit for which to play the tone. This
1031      *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
1032      */
playDtmfTone(char digit)1033     public void playDtmfTone(char digit) {
1034         Log.startSession("RC.pDT", getActiveOwnerInfo());
1035         try {
1036             if (mConnected) {
1037                 mConnectionService.playDtmfTone(mConnectionId, digit, null /*Session.Info*/);
1038             }
1039         } catch (RemoteException ignored) {
1040         } finally {
1041             Log.endSession();
1042         }
1043     }
1044 
1045     /**
1046      * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling
1047      * (DTMF) tone currently playing.
1048      *
1049      * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
1050      * currently playing, this method will do nothing.
1051      */
stopDtmfTone()1052     public void stopDtmfTone() {
1053         Log.startSession("RC.sDT", getActiveOwnerInfo());
1054         try {
1055             if (mConnected) {
1056                 mConnectionService.stopDtmfTone(mConnectionId, null /*Session.Info*/);
1057             }
1058         } catch (RemoteException ignored) {
1059         } finally {
1060             Log.endSession();
1061         }
1062     }
1063 
1064     /**
1065      * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string.
1066      *
1067      * A post-dial DTMF string is a string of digits following the first instance of either
1068      * {@link TelecomManager#DTMF_CHARACTER_WAIT} or {@link TelecomManager#DTMF_CHARACTER_PAUSE}.
1069      * These digits are immediately sent as DTMF tones to the recipient as soon as the
1070      * connection is made.
1071      *
1072      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
1073      * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period
1074      * of time.
1075      *
1076      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
1077      * {@code RemoteConnection} will pause playing the tones and notify callbacks via
1078      * {@link Callback#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app
1079      * should display to the user an indication of this state and an affordance to continue
1080      * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
1081      * app should invoke the {@link #postDialContinue(boolean)} method.
1082      *
1083      * @param proceed Whether or not to continue with the post-dial sequence.
1084      */
postDialContinue(boolean proceed)1085     public void postDialContinue(boolean proceed) {
1086         Log.startSession("RC.pDC", getActiveOwnerInfo());
1087         try {
1088             if (mConnected) {
1089                 mConnectionService.onPostDialContinue(mConnectionId, proceed,
1090                         null /*Session.Info*/);
1091             }
1092         } catch (RemoteException ignored) {
1093             // bliss
1094         } finally {
1095             Log.endSession();
1096         }
1097     }
1098 
1099     /**
1100      * Instructs this {@link RemoteConnection} to pull itself to the local device.
1101      * <p>
1102      * See {@link Call#pullExternalCall()} for more information.
1103      */
pullExternalCall()1104     public void pullExternalCall() {
1105         Log.startSession("RC.pEC", getActiveOwnerInfo());
1106         try {
1107             if (mConnected) {
1108                 mConnectionService.pullExternalCall(mConnectionId, null /*Session.Info*/);
1109             }
1110         } catch (RemoteException ignored) {
1111         } finally {
1112             Log.endSession();
1113         }
1114     }
1115 
1116     /**
1117      * Instructs this {@link RemoteConnection} to initiate a conference with a list of
1118      * participants.
1119      * <p>
1120      *
1121      * @param participants with which conference call will be formed.
1122      */
addConferenceParticipants(@onNull List<Uri> participants)1123     public void addConferenceParticipants(@NonNull List<Uri> participants) {
1124         try {
1125             if (mConnected) {
1126                 mConnectionService.addConferenceParticipants(mConnectionId, participants,
1127                         null /*Session.Info*/);
1128             }
1129         } catch (RemoteException ignored) {
1130         }
1131     }
1132 
1133     /**
1134      * Set the audio state of this {@code RemoteConnection}.
1135      *
1136      * @param state The audio state of this {@code RemoteConnection}.
1137      * @hide
1138      * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
1139      */
1140     @SystemApi
1141     @Deprecated
setAudioState(AudioState state)1142     public void setAudioState(AudioState state) {
1143         setCallAudioState(new CallAudioState(state));
1144     }
1145 
1146     /**
1147      * Set the audio state of this {@code RemoteConnection}.
1148      *
1149      * @param state The audio state of this {@code RemoteConnection}.
1150      */
setCallAudioState(CallAudioState state)1151     public void setCallAudioState(CallAudioState state) {
1152         Log.startSession("RC.sCAS", getActiveOwnerInfo());
1153         try {
1154             if (mConnected) {
1155                 mConnectionService.onCallAudioStateChanged(mConnectionId, state,
1156                         null /*Session.Info*/);
1157             }
1158         } catch (RemoteException ignored) {
1159         } finally {
1160             Log.endSession();
1161         }
1162     }
1163 
1164     /**
1165      * Notifies this {@link RemoteConnection} that the user has requested an RTT session.
1166      * @param rttTextStream The object that should be used to send text to or receive text from
1167      *                      the in-call app.
1168      * @hide
1169      */
startRtt(@onNull Connection.RttTextStream rttTextStream)1170     public void startRtt(@NonNull Connection.RttTextStream rttTextStream) {
1171         Log.startSession("RC.sR", getActiveOwnerInfo());
1172         try {
1173             if (mConnected) {
1174                 mConnectionService.startRtt(mConnectionId, rttTextStream.getFdFromInCall(),
1175                         rttTextStream.getFdToInCall(), null /*Session.Info*/);
1176             }
1177         } catch (RemoteException ignored) {
1178         } finally {
1179             Log.endSession();
1180         }
1181     }
1182 
1183     /**
1184      * Notifies this {@link RemoteConnection} that it should terminate any existing RTT
1185      * session. No response to Telecom is needed for this method.
1186      * @hide
1187      */
stopRtt()1188     public void stopRtt() {
1189         Log.startSession("RC.stR", getActiveOwnerInfo());
1190         try {
1191             if (mConnected) {
1192                 mConnectionService.stopRtt(mConnectionId, null /*Session.Info*/);
1193             }
1194         } catch (RemoteException ignored) {
1195         } finally {
1196             Log.endSession();
1197         }
1198     }
1199 
1200     /**
1201      * Notifies this {@link RemoteConnection} that call filtering has completed, as well as
1202      * the results of a contacts lookup for the remote party.
1203      *
1204      * @param completionInfo Info provided by Telecom on the results of call filtering.
1205      * @hide
1206      */
1207     @SystemApi
1208     @RequiresPermission(Manifest.permission.READ_CONTACTS)
onCallFilteringCompleted( @onNull Connection.CallFilteringCompletionInfo completionInfo)1209     public void onCallFilteringCompleted(
1210             @NonNull Connection.CallFilteringCompletionInfo completionInfo) {
1211         Log.startSession("RC.oCFC", getActiveOwnerInfo());
1212         try {
1213             if (mConnected) {
1214                 mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo,
1215                         null /*Session.Info*/);
1216             }
1217         } catch (RemoteException ignored) {
1218         } finally {
1219             Log.endSession();
1220         }
1221     }
1222 
1223     /**
1224      * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
1225      * upgrade request sent via {@link Connection#sendRemoteRttRequest}.
1226      * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
1227      * and rejection is indicated by {@code rttTextStream} being {@code null}
1228      * @hide
1229      * @param rttTextStream The object that should be used to send text to or receive text from
1230      *                      the in-call app.
1231      */
sendRttUpgradeResponse(@ullable Connection.RttTextStream rttTextStream)1232     public void sendRttUpgradeResponse(@Nullable Connection.RttTextStream rttTextStream) {
1233         Log.startSession("RC.sRUR", getActiveOwnerInfo());
1234         try {
1235             if (mConnected) {
1236                 if (rttTextStream == null) {
1237                     mConnectionService.respondToRttUpgradeRequest(mConnectionId,
1238                             null, null, null /*Session.Info*/);
1239                 } else {
1240                     mConnectionService.respondToRttUpgradeRequest(mConnectionId,
1241                             rttTextStream.getFdFromInCall(), rttTextStream.getFdToInCall(),
1242                             null /*Session.Info*/);
1243                 }
1244             }
1245         } catch (RemoteException ignored) {
1246         } finally {
1247             Log.endSession();
1248         }
1249     }
1250 
1251     /**
1252      * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
1253      * successfully asked to create a conference with.
1254      *
1255      * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be
1256      *         merged into a {@link RemoteConference}.
1257      */
getConferenceableConnections()1258     public List<RemoteConnection> getConferenceableConnections() {
1259         return mUnmodifiableconferenceableConnections;
1260     }
1261 
1262     /**
1263      * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part
1264      * of, or {@code null} if there is no such {@code RemoteConference}.
1265      *
1266      * @return A {@code RemoteConference} or {@code null};
1267      */
getConference()1268     public RemoteConference getConference() {
1269         return mConference;
1270     }
1271 
1272     /**
1273      * Get the owner info for the currently active session.  We want to make sure that any owner
1274      * info from the original call into the connection manager gets retained so that the full
1275      * context of the calls can be traced down to Telephony.
1276      * Example: Telecom will provide owner info in it's external session info that indicates
1277      * 'cast' as the calling owner.
1278      * @return The active owner
1279      */
getActiveOwnerInfo()1280     private String getActiveOwnerInfo() {
1281         Session.Info info = Log.getExternalSession();
1282         if (info == null) {
1283             return null;
1284         }
1285         return info.ownerInfo;
1286     }
1287 
1288     /** {@hide} */
getId()1289     String getId() {
1290         return mConnectionId;
1291     }
1292 
1293     /** {@hide} */
getConnectionService()1294     IConnectionService getConnectionService() {
1295         return mConnectionService;
1296     }
1297 
1298     /**
1299      * @hide
1300      */
setState(final int state)1301     void setState(final int state) {
1302         if (mState != state) {
1303             mState = state;
1304             for (CallbackRecord record: mCallbackRecords) {
1305                 final RemoteConnection connection = this;
1306                 final Callback callback = record.getCallback();
1307                 record.getHandler().post(new Runnable() {
1308                     @Override
1309                     public void run() {
1310                         callback.onStateChanged(connection, state);
1311                     }
1312                 });
1313             }
1314         }
1315     }
1316 
1317     /**
1318      * @hide
1319      */
setDisconnected(final DisconnectCause disconnectCause)1320     void setDisconnected(final DisconnectCause disconnectCause) {
1321         if (mState != Connection.STATE_DISCONNECTED) {
1322             mState = Connection.STATE_DISCONNECTED;
1323             mDisconnectCause = disconnectCause;
1324 
1325             for (CallbackRecord record : mCallbackRecords) {
1326                 final RemoteConnection connection = this;
1327                 final Callback callback = record.getCallback();
1328                 record.getHandler().post(new Runnable() {
1329                     @Override
1330                     public void run() {
1331                         callback.onDisconnected(connection, disconnectCause);
1332                     }
1333                 });
1334             }
1335         }
1336     }
1337 
1338     /**
1339      * @hide
1340      */
setRingbackRequested(final boolean ringback)1341     void setRingbackRequested(final boolean ringback) {
1342         if (mRingbackRequested != ringback) {
1343             mRingbackRequested = ringback;
1344             for (CallbackRecord record : mCallbackRecords) {
1345                 final RemoteConnection connection = this;
1346                 final Callback callback = record.getCallback();
1347                 record.getHandler().post(new Runnable() {
1348                     @Override
1349                     public void run() {
1350                         callback.onRingbackRequested(connection, ringback);
1351                     }
1352                 });
1353             }
1354         }
1355     }
1356 
1357     /**
1358      * @hide
1359      */
setConnectionCapabilities(final int connectionCapabilities)1360     void setConnectionCapabilities(final int connectionCapabilities) {
1361         mConnectionCapabilities = connectionCapabilities;
1362         for (CallbackRecord record : mCallbackRecords) {
1363             final RemoteConnection connection = this;
1364             final Callback callback = record.getCallback();
1365             record.getHandler().post(new Runnable() {
1366                 @Override
1367                 public void run() {
1368                     callback.onConnectionCapabilitiesChanged(connection, connectionCapabilities);
1369                 }
1370             });
1371         }
1372     }
1373 
1374     /**
1375      * @hide
1376      */
setConnectionProperties(final int connectionProperties)1377     void setConnectionProperties(final int connectionProperties) {
1378         mConnectionProperties = connectionProperties;
1379         for (CallbackRecord record : mCallbackRecords) {
1380             final RemoteConnection connection = this;
1381             final Callback callback = record.getCallback();
1382             record.getHandler().post(new Runnable() {
1383                 @Override
1384                 public void run() {
1385                     callback.onConnectionPropertiesChanged(connection, connectionProperties);
1386                 }
1387             });
1388         }
1389     }
1390 
1391     /**
1392      * @hide
1393      */
setDestroyed()1394     void setDestroyed() {
1395         if (!mCallbackRecords.isEmpty()) {
1396             // Make sure that the callbacks are notified that the call is destroyed first.
1397             if (mState != Connection.STATE_DISCONNECTED) {
1398                 setDisconnected(
1399                         new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
1400             }
1401 
1402             for (CallbackRecord record : mCallbackRecords) {
1403                 final RemoteConnection connection = this;
1404                 final Callback callback = record.getCallback();
1405                 record.getHandler().post(new Runnable() {
1406                     @Override
1407                     public void run() {
1408                         callback.onDestroyed(connection);
1409                     }
1410                 });
1411             }
1412             mCallbackRecords.clear();
1413 
1414             mConnected = false;
1415         }
1416     }
1417 
1418     /**
1419      * @hide
1420      */
setPostDialWait(final String remainingDigits)1421     void setPostDialWait(final String remainingDigits) {
1422         for (CallbackRecord record : mCallbackRecords) {
1423             final RemoteConnection connection = this;
1424             final Callback callback = record.getCallback();
1425             record.getHandler().post(new Runnable() {
1426                 @Override
1427                 public void run() {
1428                     callback.onPostDialWait(connection, remainingDigits);
1429                 }
1430             });
1431         }
1432     }
1433 
1434     /**
1435      * @hide
1436      */
onPostDialChar(final char nextChar)1437     void onPostDialChar(final char nextChar) {
1438         for (CallbackRecord record : mCallbackRecords) {
1439             final RemoteConnection connection = this;
1440             final Callback callback = record.getCallback();
1441             record.getHandler().post(new Runnable() {
1442                 @Override
1443                 public void run() {
1444                     callback.onPostDialChar(connection, nextChar);
1445                 }
1446             });
1447         }
1448     }
1449 
1450     /**
1451      * @hide
1452      */
setVideoState(final int videoState)1453     void setVideoState(final int videoState) {
1454         mVideoState = videoState;
1455         for (CallbackRecord record : mCallbackRecords) {
1456             final RemoteConnection connection = this;
1457             final Callback callback = record.getCallback();
1458             record.getHandler().post(new Runnable() {
1459                 @Override
1460                 public void run() {
1461                     callback.onVideoStateChanged(connection, videoState);
1462                 }
1463             });
1464         }
1465     }
1466 
1467     /**
1468      * @hide
1469      */
setVideoProvider(final VideoProvider videoProvider)1470     void setVideoProvider(final VideoProvider videoProvider) {
1471         mVideoProvider = videoProvider;
1472         for (CallbackRecord record : mCallbackRecords) {
1473             final RemoteConnection connection = this;
1474             final Callback callback = record.getCallback();
1475             record.getHandler().post(new Runnable() {
1476                 @Override
1477                 public void run() {
1478                     callback.onVideoProviderChanged(connection, videoProvider);
1479                 }
1480             });
1481         }
1482     }
1483 
1484     /** @hide */
setIsVoipAudioMode(final boolean isVoip)1485     void setIsVoipAudioMode(final boolean isVoip) {
1486         mIsVoipAudioMode = isVoip;
1487         for (CallbackRecord record : mCallbackRecords) {
1488             final RemoteConnection connection = this;
1489             final Callback callback = record.getCallback();
1490             record.getHandler().post(new Runnable() {
1491                 @Override
1492                 public void run() {
1493                     callback.onVoipAudioChanged(connection, isVoip);
1494                 }
1495             });
1496         }
1497     }
1498 
1499     /** @hide */
setStatusHints(final StatusHints statusHints)1500     void setStatusHints(final StatusHints statusHints) {
1501         mStatusHints = statusHints;
1502         for (CallbackRecord record : mCallbackRecords) {
1503             final RemoteConnection connection = this;
1504             final Callback callback = record.getCallback();
1505             record.getHandler().post(new Runnable() {
1506                 @Override
1507                 public void run() {
1508                     callback.onStatusHintsChanged(connection, statusHints);
1509                 }
1510             });
1511         }
1512     }
1513 
1514     /** @hide */
setAddress(final Uri address, final int presentation)1515     void setAddress(final Uri address, final int presentation) {
1516         mAddress = address;
1517         mAddressPresentation = presentation;
1518         for (CallbackRecord record : mCallbackRecords) {
1519             final RemoteConnection connection = this;
1520             final Callback callback = record.getCallback();
1521             record.getHandler().post(new Runnable() {
1522                 @Override
1523                 public void run() {
1524                     callback.onAddressChanged(connection, address, presentation);
1525                 }
1526             });
1527         }
1528     }
1529 
1530     /** @hide */
setCallerDisplayName(final String callerDisplayName, final int presentation)1531     void setCallerDisplayName(final String callerDisplayName, final int presentation) {
1532         mCallerDisplayName = callerDisplayName;
1533         mCallerDisplayNamePresentation = presentation;
1534         for (CallbackRecord record : mCallbackRecords) {
1535             final RemoteConnection connection = this;
1536             final Callback callback = record.getCallback();
1537             record.getHandler().post(new Runnable() {
1538                 @Override
1539                 public void run() {
1540                     callback.onCallerDisplayNameChanged(
1541                             connection, callerDisplayName, presentation);
1542                 }
1543             });
1544         }
1545     }
1546 
1547     /** @hide */
setConferenceableConnections(final List<RemoteConnection> conferenceableConnections)1548     void setConferenceableConnections(final List<RemoteConnection> conferenceableConnections) {
1549         mConferenceableConnections.clear();
1550         mConferenceableConnections.addAll(conferenceableConnections);
1551         for (CallbackRecord record : mCallbackRecords) {
1552             final RemoteConnection connection = this;
1553             final Callback callback = record.getCallback();
1554             record.getHandler().post(new Runnable() {
1555                 @Override
1556                 public void run() {
1557                     callback.onConferenceableConnectionsChanged(
1558                             connection, mUnmodifiableconferenceableConnections);
1559                 }
1560             });
1561         }
1562     }
1563 
1564     /** @hide */
setConference(final RemoteConference conference)1565     void setConference(final RemoteConference conference) {
1566         if (mConference != conference) {
1567             mConference = conference;
1568             for (CallbackRecord record : mCallbackRecords) {
1569                 final RemoteConnection connection = this;
1570                 final Callback callback = record.getCallback();
1571                 record.getHandler().post(new Runnable() {
1572                     @Override
1573                     public void run() {
1574                         callback.onConferenceChanged(connection, conference);
1575                     }
1576                 });
1577             }
1578         }
1579     }
1580 
1581     /** @hide */
putExtras(final Bundle extras)1582     void putExtras(final Bundle extras) {
1583         if (extras == null) {
1584             return;
1585         }
1586         if (mExtras == null) {
1587             mExtras = new Bundle();
1588         }
1589         try {
1590             mExtras.putAll(extras);
1591         } catch (BadParcelableException bpe) {
1592             Log.w(this, "putExtras: could not unmarshal extras; exception = " + bpe);
1593         }
1594 
1595         notifyExtrasChanged();
1596     }
1597 
1598     /** @hide */
removeExtras(List<String> keys)1599     void removeExtras(List<String> keys) {
1600         if (mExtras == null || keys == null || keys.isEmpty()) {
1601             return;
1602         }
1603         for (String key : keys) {
1604             mExtras.remove(key);
1605         }
1606 
1607         notifyExtrasChanged();
1608     }
1609 
notifyExtrasChanged()1610     private void notifyExtrasChanged() {
1611         for (CallbackRecord record : mCallbackRecords) {
1612             final RemoteConnection connection = this;
1613             final Callback callback = record.getCallback();
1614             record.getHandler().post(new Runnable() {
1615                 @Override
1616                 public void run() {
1617                     callback.onExtrasChanged(connection, mExtras);
1618                 }
1619             });
1620         }
1621     }
1622 
1623     /** @hide */
onConnectionEvent(final String event, final Bundle extras)1624     void onConnectionEvent(final String event, final Bundle extras) {
1625         for (CallbackRecord record : mCallbackRecords) {
1626             final RemoteConnection connection = this;
1627             final Callback callback = record.getCallback();
1628             record.getHandler().post(new Runnable() {
1629                 @Override
1630                 public void run() {
1631                     callback.onConnectionEvent(connection, event, extras);
1632                 }
1633             });
1634         }
1635     }
1636 
1637     /** @hide */
onRttInitiationSuccess()1638     void onRttInitiationSuccess() {
1639         for (CallbackRecord record : mCallbackRecords) {
1640             final RemoteConnection connection = this;
1641             final Callback callback = record.getCallback();
1642             record.getHandler().post(
1643                     () -> callback.onRttInitiationSuccess(connection));
1644         }
1645     }
1646 
1647     /** @hide */
onRttInitiationFailure(int reason)1648     void onRttInitiationFailure(int reason) {
1649         for (CallbackRecord record : mCallbackRecords) {
1650             final RemoteConnection connection = this;
1651             final Callback callback = record.getCallback();
1652             record.getHandler().post(
1653                     () -> callback.onRttInitiationFailure(connection, reason));
1654         }
1655     }
1656 
1657     /** @hide */
onRttSessionRemotelyTerminated()1658     void onRttSessionRemotelyTerminated() {
1659         for (CallbackRecord record : mCallbackRecords) {
1660             final RemoteConnection connection = this;
1661             final Callback callback = record.getCallback();
1662             record.getHandler().post(
1663                     () -> callback.onRttSessionRemotelyTerminated(connection));
1664         }
1665     }
1666 
1667     /** @hide */
onRemoteRttRequest()1668     void onRemoteRttRequest() {
1669         for (CallbackRecord record : mCallbackRecords) {
1670             final RemoteConnection connection = this;
1671             final Callback callback = record.getCallback();
1672             record.getHandler().post(
1673                     () -> callback.onRemoteRttRequest(connection));
1674         }
1675     }
1676 
1677     /**
1678     /**
1679      * Create a RemoteConnection represents a failure, and which will be in
1680      * {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
1681      * certainly result in bad things happening. Do not do this.
1682      *
1683      * @return a failed {@link RemoteConnection}
1684      *
1685      * @hide
1686      */
failure(DisconnectCause disconnectCause)1687     public static RemoteConnection failure(DisconnectCause disconnectCause) {
1688         return new RemoteConnection(disconnectCause);
1689     }
1690 
1691     private static final class CallbackRecord extends Callback {
1692         private final Callback mCallback;
1693         private final Handler mHandler;
1694 
CallbackRecord(Callback callback, Handler handler)1695         public CallbackRecord(Callback callback, Handler handler) {
1696             mCallback = callback;
1697             mHandler = handler;
1698         }
1699 
getCallback()1700         public Callback getCallback() {
1701             return mCallback;
1702         }
1703 
getHandler()1704         public Handler getHandler() {
1705             return mHandler;
1706         }
1707     }
1708 }
1709