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