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