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