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.SystemApi;
     24 import android.net.Uri;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.view.Surface;
     28 
     29 import java.util.ArrayList;
     30 import java.util.Collections;
     31 import java.util.List;
     32 import java.util.Set;
     33 import java.util.concurrent.ConcurrentHashMap;
     34 
     35 /**
     36  * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
     37  * running in a different process.
     38  *
     39  * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
     40  * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
     41  * @hide
     42  */
     43 @SystemApi
     44 public final class RemoteConnection {
     45 
     46     public static abstract class Callback {
     47         /**
     48          * Invoked when the state of this {@code RemoteConnection} has changed. See
     49          * {@link #getState()}.
     50          *
     51          * @param connection The {@code RemoteConnection} invoking this method.
     52          * @param state The new state of the {@code RemoteConnection}.
     53          */
     54         public void onStateChanged(RemoteConnection connection, int state) {}
     55 
     56         /**
     57          * Invoked when this {@code RemoteConnection} is disconnected.
     58          *
     59          * @param connection The {@code RemoteConnection} invoking this method.
     60          * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
     61          *     connection.
     62          */
     63         public void onDisconnected(
     64                 RemoteConnection connection,
     65                 DisconnectCause disconnectCause) {}
     66 
     67         /**
     68          * Invoked when this {@code RemoteConnection} is requesting ringback. See
     69          * {@link #isRingbackRequested()}.
     70          *
     71          * @param connection The {@code RemoteConnection} invoking this method.
     72          * @param ringback Whether the {@code RemoteConnection} is requesting ringback.
     73          */
     74         public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
     75 
     76         /** @hide */
     77         @Deprecated public void onCallCapabilitiesChanged(
     78                 RemoteConnection connection,
     79                 int callCapabilities) {}
     80 
     81         /**
     82          * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
     83          * See {@link #getConnectionCapabilities()}.
     84          *
     85          * @param connection The {@code RemoteConnection} invoking this method.
     86          * @param connectionCapabilities The new capabilities of the {@code RemoteConnection}.
     87          */
     88         public void onConnectionCapabilitiesChanged(
     89                 RemoteConnection connection,
     90                 int connectionCapabilities) {}
     91 
     92         /**
     93          * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
     94          * pause character. This causes the post-dial signals to stop pending user confirmation. An
     95          * implementation should present this choice to the user and invoke
     96          * {@link RemoteConnection#postDialContinue(boolean)} when the user makes the choice.
     97          *
     98          * @param connection The {@code RemoteConnection} invoking this method.
     99          * @param remainingPostDialSequence The post-dial characters that remain to be sent.
    100          */
    101         public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {}
    102 
    103         /**
    104          * Invoked when the post-dial sequence in the outgoing {@code Connection} has processed
    105          * a character.
    106          *
    107          * @param connection The {@code RemoteConnection} invoking this method.
    108          * @param nextChar The character being processed.
    109          */
    110         public void onPostDialChar(RemoteConnection connection, char nextChar) {}
    111 
    112         /**
    113          * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed.
    114          * See {@link #isVoipAudioMode()}.
    115          *
    116          * @param connection The {@code RemoteConnection} invoking this method.
    117          * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
    118          */
    119         public void onVoipAudioChanged(RemoteConnection connection, boolean isVoip) {}
    120 
    121         /**
    122          * Indicates that the status hints of this {@code RemoteConnection} have changed. See
    123          * {@link #getStatusHints()} ()}.
    124          *
    125          * @param connection The {@code RemoteConnection} invoking this method.
    126          * @param statusHints The new status hints of the {@code RemoteConnection}.
    127          */
    128         public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {}
    129 
    130         /**
    131          * Indicates that the address (e.g., phone number) of this {@code RemoteConnection} has
    132          * changed. See {@link #getAddress()} and {@link #getAddressPresentation()}.
    133          *
    134          * @param connection The {@code RemoteConnection} invoking this method.
    135          * @param address The new address of the {@code RemoteConnection}.
    136          * @param presentation The presentation requirements for the address.
    137          *        See {@link TelecomManager} for valid values.
    138          */
    139         public void onAddressChanged(RemoteConnection connection, Uri address, int presentation) {}
    140 
    141         /**
    142          * Indicates that the caller display name of this {@code RemoteConnection} has changed.
    143          * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}.
    144          *
    145          * @param connection The {@code RemoteConnection} invoking this method.
    146          * @param callerDisplayName The new caller display name of the {@code RemoteConnection}.
    147          * @param presentation The presentation requirements for the handle.
    148          *        See {@link TelecomManager} for valid values.
    149          */
    150         public void onCallerDisplayNameChanged(
    151                 RemoteConnection connection, String callerDisplayName, int presentation) {}
    152 
    153         /**
    154          * Indicates that the video state of this {@code RemoteConnection} has changed.
    155          * See {@link #getVideoState()}.
    156          *
    157          * @param connection The {@code RemoteConnection} invoking this method.
    158          * @param videoState The new video state of the {@code RemoteConnection}.
    159          * @hide
    160          */
    161         public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
    162 
    163         /**
    164          * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
    165          * should be made to the {@code RemoteConnection}, and references to it should be cleared.
    166          *
    167          * @param connection The {@code RemoteConnection} invoking this method.
    168          */
    169         public void onDestroyed(RemoteConnection connection) {}
    170 
    171         /**
    172          * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection}
    173          * may be asked to create a conference has changed.
    174          *
    175          * @param connection The {@code RemoteConnection} invoking this method.
    176          * @param conferenceableConnections The {@code RemoteConnection}s with which this
    177          *         {@code RemoteConnection} may be asked to create a conference.
    178          */
    179         public void onConferenceableConnectionsChanged(
    180                 RemoteConnection connection,
    181                 List<RemoteConnection> conferenceableConnections) {}
    182 
    183         /**
    184          * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
    185          * has changed.
    186          *
    187          * @param connection The {@code RemoteConnection} invoking this method.
    188          * @param videoProvider The new {@code VideoProvider} associated with this
    189          *         {@code RemoteConnection}.
    190          * @hide
    191          */
    192         public void onVideoProviderChanged(
    193                 RemoteConnection connection, VideoProvider videoProvider) {}
    194 
    195         /**
    196          * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
    197          * of has changed.
    198          *
    199          * @param connection The {@code RemoteConnection} invoking this method.
    200          * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is
    201          *         a part, which may be {@code null}.
    202          */
    203         public void onConferenceChanged(
    204                 RemoteConnection connection,
    205                 RemoteConference conference) {}
    206     }
    207 
    208     /** {@hide} */
    209     public static class VideoProvider {
    210 
    211         public abstract static class Listener {
    212             public void onReceiveSessionModifyRequest(
    213                     VideoProvider videoProvider,
    214                     VideoProfile videoProfile) {}
    215 
    216             public void onReceiveSessionModifyResponse(
    217                     VideoProvider videoProvider,
    218                     int status,
    219                     VideoProfile requestedProfile,
    220                     VideoProfile responseProfile) {}
    221 
    222             public void onHandleCallSessionEvent(VideoProvider videoProvider, int event) {}
    223 
    224             public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
    225 
    226             public void onCallDataUsageChanged(VideoProvider videoProvider, int dataUsage) {}
    227 
    228             public void onCameraCapabilitiesChanged(
    229                     VideoProvider videoProvider,
    230                     CameraCapabilities cameraCapabilities) {}
    231         }
    232 
    233         private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
    234             @Override
    235             public void receiveSessionModifyRequest(VideoProfile videoProfile) {
    236                 for (Listener l : mListeners) {
    237                     l.onReceiveSessionModifyRequest(VideoProvider.this, videoProfile);
    238                 }
    239             }
    240 
    241             @Override
    242             public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
    243                     VideoProfile responseProfile) {
    244                 for (Listener l : mListeners) {
    245                     l.onReceiveSessionModifyResponse(
    246                             VideoProvider.this,
    247                             status,
    248                             requestedProfile,
    249                             responseProfile);
    250                 }
    251             }
    252 
    253             @Override
    254             public void handleCallSessionEvent(int event) {
    255                 for (Listener l : mListeners) {
    256                     l.onHandleCallSessionEvent(VideoProvider.this, event);
    257                 }
    258             }
    259 
    260             @Override
    261             public void changePeerDimensions(int width, int height) {
    262                 for (Listener l : mListeners) {
    263                     l.onPeerDimensionsChanged(VideoProvider.this, width, height);
    264                 }
    265             }
    266 
    267             @Override
    268             public void changeCallDataUsage(int dataUsage) {
    269                 for (Listener l : mListeners) {
    270                     l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
    271                 }
    272             }
    273 
    274             @Override
    275             public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
    276                 for (Listener l : mListeners) {
    277                     l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
    278                 }
    279             }
    280 
    281             @Override
    282             public IBinder asBinder() {
    283                 return null;
    284             }
    285         };
    286 
    287         private final VideoCallbackServant mVideoCallbackServant =
    288                 new VideoCallbackServant(mVideoCallbackDelegate);
    289 
    290         private final IVideoProvider mVideoProviderBinder;
    291 
    292         /**
    293          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    294          * load factor before resizing, 1 means we only expect a single thread to
    295          * access the map so make only a single shard
    296          */
    297         private final Set<Listener> mListeners = Collections.newSetFromMap(
    298                 new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
    299 
    300         public VideoProvider(IVideoProvider videoProviderBinder) {
    301             mVideoProviderBinder = videoProviderBinder;
    302             try {
    303                 mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
    304             } catch (RemoteException e) {
    305             }
    306         }
    307 
    308         public void addListener(Listener l) {
    309             mListeners.add(l);
    310         }
    311 
    312         public void removeListener(Listener l) {
    313             mListeners.remove(l);
    314         }
    315 
    316         public void setCamera(String cameraId) {
    317             try {
    318                 mVideoProviderBinder.setCamera(cameraId);
    319             } catch (RemoteException e) {
    320             }
    321         }
    322 
    323         public void setPreviewSurface(Surface surface) {
    324             try {
    325                 mVideoProviderBinder.setPreviewSurface(surface);
    326             } catch (RemoteException e) {
    327             }
    328         }
    329 
    330         public void setDisplaySurface(Surface surface) {
    331             try {
    332                 mVideoProviderBinder.setDisplaySurface(surface);
    333             } catch (RemoteException e) {
    334             }
    335         }
    336 
    337         public void setDeviceOrientation(int rotation) {
    338             try {
    339                 mVideoProviderBinder.setDeviceOrientation(rotation);
    340             } catch (RemoteException e) {
    341             }
    342         }
    343 
    344         public void setZoom(float value) {
    345             try {
    346                 mVideoProviderBinder.setZoom(value);
    347             } catch (RemoteException e) {
    348             }
    349         }
    350 
    351         public void sendSessionModifyRequest(VideoProfile reqProfile) {
    352             try {
    353                 mVideoProviderBinder.sendSessionModifyRequest(reqProfile);
    354             } catch (RemoteException e) {
    355             }
    356         }
    357 
    358         public void sendSessionModifyResponse(VideoProfile responseProfile) {
    359             try {
    360                 mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
    361             } catch (RemoteException e) {
    362             }
    363         }
    364 
    365         public void requestCameraCapabilities() {
    366             try {
    367                 mVideoProviderBinder.requestCameraCapabilities();
    368             } catch (RemoteException e) {
    369             }
    370         }
    371 
    372         public void requestCallDataUsage() {
    373             try {
    374                 mVideoProviderBinder.requestCallDataUsage();
    375             } catch (RemoteException e) {
    376             }
    377         }
    378 
    379         public void setPauseImage(String uri) {
    380             try {
    381                 mVideoProviderBinder.setPauseImage(uri);
    382             } catch (RemoteException e) {
    383             }
    384         }
    385     }
    386 
    387     private IConnectionService mConnectionService;
    388     private final String mConnectionId;
    389     /**
    390      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    391      * load factor before resizing, 1 means we only expect a single thread to
    392      * access the map so make only a single shard
    393      */
    394     private final Set<Callback> mCallbacks = Collections.newSetFromMap(
    395             new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
    396     private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
    397     private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
    398             Collections.unmodifiableList(mConferenceableConnections);
    399 
    400     private int mState = Connection.STATE_NEW;
    401     private DisconnectCause mDisconnectCause;
    402     private boolean mRingbackRequested;
    403     private boolean mConnected;
    404     private int mConnectionCapabilities;
    405     private int mVideoState;
    406     private VideoProvider mVideoProvider;
    407     private boolean mIsVoipAudioMode;
    408     private StatusHints mStatusHints;
    409     private Uri mAddress;
    410     private int mAddressPresentation;
    411     private String mCallerDisplayName;
    412     private int mCallerDisplayNamePresentation;
    413     private RemoteConference mConference;
    414 
    415     /**
    416      * @hide
    417      */
    418     RemoteConnection(
    419             String id,
    420             IConnectionService connectionService,
    421             ConnectionRequest request) {
    422         mConnectionId = id;
    423         mConnectionService = connectionService;
    424         mConnected = true;
    425         mState = Connection.STATE_INITIALIZING;
    426     }
    427 
    428     /**
    429      * @hide
    430      */
    431     RemoteConnection(String callId, IConnectionService connectionService,
    432             ParcelableConnection connection) {
    433         mConnectionId = callId;
    434         mConnectionService = connectionService;
    435         mConnected = true;
    436         mState = connection.getState();
    437         mDisconnectCause = connection.getDisconnectCause();
    438         mRingbackRequested = connection.isRingbackRequested();
    439         mConnectionCapabilities = connection.getConnectionCapabilities();
    440         mVideoState = connection.getVideoState();
    441         mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
    442         mIsVoipAudioMode = connection.getIsVoipAudioMode();
    443         mStatusHints = connection.getStatusHints();
    444         mAddress = connection.getHandle();
    445         mAddressPresentation = connection.getHandlePresentation();
    446         mCallerDisplayName = connection.getCallerDisplayName();
    447         mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation();
    448         mConference = null;
    449     }
    450 
    451     /**
    452      * Create a RemoteConnection which is used for failed connections. Note that using it for any
    453      * "real" purpose will almost certainly fail. Callers should note the failure and act
    454      * accordingly (moving on to another RemoteConnection, for example)
    455      *
    456      * @param disconnectCause The reason for the failed connection.
    457      * @hide
    458      */
    459     RemoteConnection(DisconnectCause disconnectCause) {
    460         mConnectionId = "NULL";
    461         mConnected = false;
    462         mState = Connection.STATE_DISCONNECTED;
    463         mDisconnectCause = disconnectCause;
    464     }
    465 
    466     /**
    467      * Adds a callback to this {@code RemoteConnection}.
    468      *
    469      * @param callback A {@code Callback}.
    470      */
    471     public void registerCallback(Callback callback) {
    472         mCallbacks.add(callback);
    473     }
    474 
    475     /**
    476      * Removes a callback from this {@code RemoteConnection}.
    477      *
    478      * @param callback A {@code Callback}.
    479      */
    480     public void unregisterCallback(Callback callback) {
    481         if (callback != null) {
    482             mCallbacks.remove(callback);
    483         }
    484     }
    485 
    486     /**
    487      * Obtains the state of this {@code RemoteConnection}.
    488      *
    489      * @return A state value, chosen from the {@code STATE_*} constants.
    490      */
    491     public int getState() {
    492         return mState;
    493     }
    494 
    495     /**
    496      * Obtains the reason why this {@code RemoteConnection} may have been disconnected.
    497      *
    498      * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, the
    499      *         disconnect cause expressed as a code chosen from among those declared in
    500      *         {@link DisconnectCause}.
    501      */
    502     public DisconnectCause getDisconnectCause() {
    503         return mDisconnectCause;
    504     }
    505 
    506     /**
    507      * Obtains the capabilities of this {@code RemoteConnection}.
    508      *
    509      * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
    510      *         the {@code CAPABILITY_*} constants in class {@link Connection}.
    511      */
    512     public int getConnectionCapabilities() {
    513         return mConnectionCapabilities;
    514     }
    515 
    516     /**
    517      * Determines if the audio mode of this {@code RemoteConnection} is VOIP.
    518      *
    519      * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
    520      */
    521     public boolean isVoipAudioMode() {
    522         return mIsVoipAudioMode;
    523     }
    524 
    525     /**
    526      * Obtains status hints pertaining to this {@code RemoteConnection}.
    527      *
    528      * @return The current {@link StatusHints} of this {@code RemoteConnection},
    529      *         or {@code null} if none have been set.
    530      */
    531     public StatusHints getStatusHints() {
    532         return mStatusHints;
    533     }
    534 
    535     /**
    536      * Obtains the address of this {@code RemoteConnection}.
    537      *
    538      * @return The address (e.g., phone number) to which the {@code RemoteConnection}
    539      *         is currently connected.
    540      */
    541     public Uri getAddress() {
    542         return mAddress;
    543     }
    544 
    545     /**
    546      * Obtains the presentation requirements for the address of this {@code RemoteConnection}.
    547      *
    548      * @return The presentation requirements for the address. See
    549      *         {@link TelecomManager} for valid values.
    550      */
    551     public int getAddressPresentation() {
    552         return mAddressPresentation;
    553     }
    554 
    555     /**
    556      * Obtains the display name for this {@code RemoteConnection}'s caller.
    557      *
    558      * @return The display name for the caller.
    559      */
    560     public CharSequence getCallerDisplayName() {
    561         return mCallerDisplayName;
    562     }
    563 
    564     /**
    565      * Obtains the presentation requirements for this {@code RemoteConnection}'s
    566      * caller's display name.
    567      *
    568      * @return The presentation requirements for the caller display name. See
    569      *         {@link TelecomManager} for valid values.
    570      */
    571     public int getCallerDisplayNamePresentation() {
    572         return mCallerDisplayNamePresentation;
    573     }
    574 
    575     /**
    576      * Obtains the video state of this {@code RemoteConnection}.
    577      *
    578      * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile.VideoState}.
    579      * @hide
    580      */
    581     public int getVideoState() {
    582         return mVideoState;
    583     }
    584 
    585     /**
    586      * Obtains the video provider of this {@code RemoteConnection}.
    587      *
    588      * @return The video provider associated with this {@code RemoteConnection}.
    589      * @hide
    590      */
    591     public final VideoProvider getVideoProvider() {
    592         return mVideoProvider;
    593     }
    594 
    595     /**
    596      * Determines whether this {@code RemoteConnection} is requesting ringback.
    597      *
    598      * @return Whether the {@code RemoteConnection} is requesting that the framework play a
    599      *         ringback tone on its behalf.
    600      */
    601     public boolean isRingbackRequested() {
    602         return false;
    603     }
    604 
    605     /**
    606      * Instructs this {@code RemoteConnection} to abort.
    607      */
    608     public void abort() {
    609         try {
    610             if (mConnected) {
    611                 mConnectionService.abort(mConnectionId);
    612             }
    613         } catch (RemoteException ignored) {
    614         }
    615     }
    616 
    617     /**
    618      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
    619      */
    620     public void answer() {
    621         try {
    622             if (mConnected) {
    623                 mConnectionService.answer(mConnectionId);
    624             }
    625         } catch (RemoteException ignored) {
    626         }
    627     }
    628 
    629     /**
    630      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
    631      * @param videoState The video state in which to answer the call.
    632      * @hide
    633      */
    634     public void answer(int videoState) {
    635         try {
    636             if (mConnected) {
    637                 mConnectionService.answerVideo(mConnectionId, videoState);
    638             }
    639         } catch (RemoteException ignored) {
    640         }
    641     }
    642 
    643     /**
    644      * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
    645      */
    646     public void reject() {
    647         try {
    648             if (mConnected) {
    649                 mConnectionService.reject(mConnectionId);
    650             }
    651         } catch (RemoteException ignored) {
    652         }
    653     }
    654 
    655     /**
    656      * Instructs this {@code RemoteConnection} to go on hold.
    657      */
    658     public void hold() {
    659         try {
    660             if (mConnected) {
    661                 mConnectionService.hold(mConnectionId);
    662             }
    663         } catch (RemoteException ignored) {
    664         }
    665     }
    666 
    667     /**
    668      * Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
    669      */
    670     public void unhold() {
    671         try {
    672             if (mConnected) {
    673                 mConnectionService.unhold(mConnectionId);
    674             }
    675         } catch (RemoteException ignored) {
    676         }
    677     }
    678 
    679     /**
    680      * Instructs this {@code RemoteConnection} to disconnect.
    681      */
    682     public void disconnect() {
    683         try {
    684             if (mConnected) {
    685                 mConnectionService.disconnect(mConnectionId);
    686             }
    687         } catch (RemoteException ignored) {
    688         }
    689     }
    690 
    691     /**
    692      * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling
    693      * (DTMF) tone.
    694      *
    695      * Any other currently playing DTMF tone in the specified call is immediately stopped.
    696      *
    697      * @param digit A character representing the DTMF digit for which to play the tone. This
    698      *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
    699      */
    700     public void playDtmfTone(char digit) {
    701         try {
    702             if (mConnected) {
    703                 mConnectionService.playDtmfTone(mConnectionId, digit);
    704             }
    705         } catch (RemoteException ignored) {
    706         }
    707     }
    708 
    709     /**
    710      * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling
    711      * (DTMF) tone currently playing.
    712      *
    713      * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
    714      * currently playing, this method will do nothing.
    715      */
    716     public void stopDtmfTone() {
    717         try {
    718             if (mConnected) {
    719                 mConnectionService.stopDtmfTone(mConnectionId);
    720             }
    721         } catch (RemoteException ignored) {
    722         }
    723     }
    724 
    725     /**
    726      * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string.
    727      *
    728      * A post-dial DTMF string is a string of digits following the first instance of either
    729      * {@link TelecomManager#DTMF_CHARACTER_WAIT} or {@link TelecomManager#DTMF_CHARACTER_PAUSE}.
    730      * These digits are immediately sent as DTMF tones to the recipient as soon as the
    731      * connection is made.
    732      *
    733      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
    734      * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period
    735      * of time.
    736      *
    737      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
    738      * {@code RemoteConnection} will pause playing the tones and notify callbacks via
    739      * {@link Callback#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app
    740      * should display to the user an indication of this state and an affordance to continue
    741      * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
    742      * app should invoke the {@link #postDialContinue(boolean)} method.
    743      *
    744      * @param proceed Whether or not to continue with the post-dial sequence.
    745      */
    746     public void postDialContinue(boolean proceed) {
    747         try {
    748             if (mConnected) {
    749                 mConnectionService.onPostDialContinue(mConnectionId, proceed);
    750             }
    751         } catch (RemoteException ignored) {
    752         }
    753     }
    754 
    755     /**
    756      * Set the audio state of this {@code RemoteConnection}.
    757      *
    758      * @param state The audio state of this {@code RemoteConnection}.
    759      */
    760     public void setAudioState(AudioState state) {
    761         try {
    762             if (mConnected) {
    763                 mConnectionService.onAudioStateChanged(mConnectionId, state);
    764             }
    765         } catch (RemoteException ignored) {
    766         }
    767     }
    768 
    769     /**
    770      * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
    771      * successfully asked to create a conference with.
    772      *
    773      * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be
    774      *         merged into a {@link RemoteConference}.
    775      */
    776     public List<RemoteConnection> getConferenceableConnections() {
    777         return mUnmodifiableconferenceableConnections;
    778     }
    779 
    780     /**
    781      * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part
    782      * of, or {@code null} if there is no such {@code RemoteConference}.
    783      *
    784      * @return A {@code RemoteConference} or {@code null};
    785      */
    786     public RemoteConference getConference() {
    787         return mConference;
    788     }
    789 
    790     /** {@hide} */
    791     String getId() {
    792         return mConnectionId;
    793     }
    794 
    795     /** {@hide} */
    796     IConnectionService getConnectionService() {
    797         return mConnectionService;
    798     }
    799 
    800     /**
    801      * @hide
    802      */
    803     void setState(int state) {
    804         if (mState != state) {
    805             mState = state;
    806             for (Callback c: mCallbacks) {
    807                 c.onStateChanged(this, state);
    808             }
    809         }
    810     }
    811 
    812     /**
    813      * @hide
    814      */
    815     void setDisconnected(DisconnectCause disconnectCause) {
    816         if (mState != Connection.STATE_DISCONNECTED) {
    817             mState = Connection.STATE_DISCONNECTED;
    818             mDisconnectCause = disconnectCause;
    819 
    820             for (Callback c : mCallbacks) {
    821                 c.onDisconnected(this, mDisconnectCause);
    822             }
    823         }
    824     }
    825 
    826     /**
    827      * @hide
    828      */
    829     void setRingbackRequested(boolean ringback) {
    830         if (mRingbackRequested != ringback) {
    831             mRingbackRequested = ringback;
    832             for (Callback c : mCallbacks) {
    833                 c.onRingbackRequested(this, ringback);
    834             }
    835         }
    836     }
    837 
    838     /**
    839      * @hide
    840      */
    841     void setConnectionCapabilities(int connectionCapabilities) {
    842         mConnectionCapabilities = connectionCapabilities;
    843         for (Callback c : mCallbacks) {
    844             c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
    845             c.onCallCapabilitiesChanged(this, connectionCapabilities);
    846         }
    847     }
    848 
    849     /**
    850      * @hide
    851      */
    852     void setDestroyed() {
    853         if (!mCallbacks.isEmpty()) {
    854             // Make sure that the callbacks are notified that the call is destroyed first.
    855             if (mState != Connection.STATE_DISCONNECTED) {
    856                 setDisconnected(
    857                         new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
    858             }
    859 
    860             for (Callback c : mCallbacks) {
    861                 c.onDestroyed(this);
    862             }
    863             mCallbacks.clear();
    864 
    865             mConnected = false;
    866         }
    867     }
    868 
    869     /**
    870      * @hide
    871      */
    872     void setPostDialWait(String remainingDigits) {
    873         for (Callback c : mCallbacks) {
    874             c.onPostDialWait(this, remainingDigits);
    875         }
    876     }
    877 
    878     /**
    879      * @hide
    880      */
    881     void onPostDialChar(char nextChar) {
    882         for (Callback c : mCallbacks) {
    883             c.onPostDialChar(this, nextChar);
    884         }
    885     }
    886 
    887     /**
    888      * @hide
    889      */
    890     void setVideoState(int videoState) {
    891         mVideoState = videoState;
    892         for (Callback c : mCallbacks) {
    893             c.onVideoStateChanged(this, videoState);
    894         }
    895     }
    896 
    897     /**
    898      * @hide
    899      */
    900     void setVideoProvider(VideoProvider videoProvider) {
    901         mVideoProvider = videoProvider;
    902         for (Callback c : mCallbacks) {
    903             c.onVideoProviderChanged(this, videoProvider);
    904         }
    905     }
    906 
    907     /** @hide */
    908     void setIsVoipAudioMode(boolean isVoip) {
    909         mIsVoipAudioMode = isVoip;
    910         for (Callback c : mCallbacks) {
    911             c.onVoipAudioChanged(this, isVoip);
    912         }
    913     }
    914 
    915     /** @hide */
    916     void setStatusHints(StatusHints statusHints) {
    917         mStatusHints = statusHints;
    918         for (Callback c : mCallbacks) {
    919             c.onStatusHintsChanged(this, statusHints);
    920         }
    921     }
    922 
    923     /** @hide */
    924     void setAddress(Uri address, int presentation) {
    925         mAddress = address;
    926         mAddressPresentation = presentation;
    927         for (Callback c : mCallbacks) {
    928             c.onAddressChanged(this, address, presentation);
    929         }
    930     }
    931 
    932     /** @hide */
    933     void setCallerDisplayName(String callerDisplayName, int presentation) {
    934         mCallerDisplayName = callerDisplayName;
    935         mCallerDisplayNamePresentation = presentation;
    936         for (Callback c : mCallbacks) {
    937             c.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
    938         }
    939     }
    940 
    941     /** @hide */
    942     void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
    943         mConferenceableConnections.clear();
    944         mConferenceableConnections.addAll(conferenceableConnections);
    945         for (Callback c : mCallbacks) {
    946             c.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections);
    947         }
    948     }
    949 
    950     /** @hide */
    951     void setConference(RemoteConference conference) {
    952         if (mConference != conference) {
    953             mConference = conference;
    954             for (Callback c : mCallbacks) {
    955                 c.onConferenceChanged(this, conference);
    956             }
    957         }
    958     }
    959 
    960     /**
    961      * Create a RemoteConnection represents a failure, and which will be in
    962      * {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
    963      * certainly result in bad things happening. Do not do this.
    964      *
    965      * @return a failed {@link RemoteConnection}
    966      *
    967      * @hide
    968      */
    969     public static RemoteConnection failure(DisconnectCause disconnectCause) {
    970         return new RemoteConnection(disconnectCause);
    971     }
    972 }
    973