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