Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (c) 2013 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 com.android.ims.internal;
     18 
     19 import android.os.Message;
     20 import android.os.RemoteException;
     21 import android.telecom.Connection;
     22 
     23 import java.util.Objects;
     24 import android.util.Log;
     25 import com.android.ims.ImsCallProfile;
     26 import com.android.ims.ImsConferenceState;
     27 import com.android.ims.ImsReasonInfo;
     28 import com.android.ims.ImsStreamMediaProfile;
     29 import com.android.ims.ImsSuppServiceNotification;
     30 
     31 /**
     32  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
     33  * It directly communicates with IMS service which implements the IMS protocol behavior.
     34  *
     35  * @hide
     36  */
     37 public class ImsCallSession {
     38     private static final String TAG = "ImsCallSession";
     39 
     40     /**
     41      * Defines IMS call session state.
     42      */
     43     public static class State {
     44         public static final int IDLE = 0;
     45         public static final int INITIATED = 1;
     46         public static final int NEGOTIATING = 2;
     47         public static final int ESTABLISHING = 3;
     48         public static final int ESTABLISHED = 4;
     49 
     50         public static final int RENEGOTIATING = 5;
     51         public static final int REESTABLISHING = 6;
     52 
     53         public static final int TERMINATING = 7;
     54         public static final int TERMINATED = 8;
     55 
     56         public static final int INVALID = (-1);
     57 
     58         /**
     59          * Converts the state to string.
     60          */
     61         public static String toString(int state) {
     62             switch (state) {
     63                 case IDLE:
     64                     return "IDLE";
     65                 case INITIATED:
     66                     return "INITIATED";
     67                 case NEGOTIATING:
     68                     return "NEGOTIATING";
     69                 case ESTABLISHING:
     70                     return "ESTABLISHING";
     71                 case ESTABLISHED:
     72                     return "ESTABLISHED";
     73                 case RENEGOTIATING:
     74                     return "RENEGOTIATING";
     75                 case REESTABLISHING:
     76                     return "REESTABLISHING";
     77                 case TERMINATING:
     78                     return "TERMINATING";
     79                 case TERMINATED:
     80                     return "TERMINATED";
     81                 default:
     82                     return "UNKNOWN";
     83             }
     84         }
     85 
     86         private State() {
     87         }
     88     }
     89 
     90     /**
     91      * Listener for events relating to an IMS session, such as when a session is being
     92      * recieved ("on ringing") or a call is outgoing ("on calling").
     93      * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
     94      */
     95     public static class Listener {
     96         /**
     97          * Called when a request is sent out to initiate a new session
     98          * and 1xx response is received from the network.
     99          *
    100          * @param session the session object that carries out the IMS session
    101          */
    102         public void callSessionProgressing(ImsCallSession session,
    103                 ImsStreamMediaProfile profile) {
    104             // no-op
    105         }
    106 
    107         /**
    108          * Called when the session is established.
    109          *
    110          * @param session the session object that carries out the IMS session
    111          */
    112         public void callSessionStarted(ImsCallSession session,
    113                 ImsCallProfile profile) {
    114             // no-op
    115         }
    116 
    117         /**
    118          * Called when the session establishment is failed.
    119          *
    120          * @param session the session object that carries out the IMS session
    121          * @param reasonInfo detailed reason of the session establishment failure
    122          */
    123         public void callSessionStartFailed(ImsCallSession session,
    124                 ImsReasonInfo reasonInfo) {
    125         }
    126 
    127         /**
    128          * Called when the session is terminated.
    129          *
    130          * @param session the session object that carries out the IMS session
    131          * @param reasonInfo detailed reason of the session termination
    132          */
    133         public void callSessionTerminated(ImsCallSession session,
    134                 ImsReasonInfo reasonInfo) {
    135         }
    136 
    137         /**
    138          * Called when the session is in hold.
    139          *
    140          * @param session the session object that carries out the IMS session
    141          */
    142         public void callSessionHeld(ImsCallSession session,
    143                 ImsCallProfile profile) {
    144         }
    145 
    146         /**
    147          * Called when the session hold is failed.
    148          *
    149          * @param session the session object that carries out the IMS session
    150          * @param reasonInfo detailed reason of the session hold failure
    151          */
    152         public void callSessionHoldFailed(ImsCallSession session,
    153                 ImsReasonInfo reasonInfo) {
    154         }
    155 
    156         /**
    157          * Called when the session hold is received from the remote user.
    158          *
    159          * @param session the session object that carries out the IMS session
    160          */
    161         public void callSessionHoldReceived(ImsCallSession session,
    162                 ImsCallProfile profile) {
    163         }
    164 
    165         /**
    166          * Called when the session resume is done.
    167          *
    168          * @param session the session object that carries out the IMS session
    169          */
    170         public void callSessionResumed(ImsCallSession session,
    171                 ImsCallProfile profile) {
    172         }
    173 
    174         /**
    175          * Called when the session resume is failed.
    176          *
    177          * @param session the session object that carries out the IMS session
    178          * @param reasonInfo detailed reason of the session resume failure
    179          */
    180         public void callSessionResumeFailed(ImsCallSession session,
    181                 ImsReasonInfo reasonInfo) {
    182         }
    183 
    184         /**
    185          * Called when the session resume is received from the remote user.
    186          *
    187          * @param session the session object that carries out the IMS session
    188          */
    189         public void callSessionResumeReceived(ImsCallSession session,
    190                 ImsCallProfile profile) {
    191         }
    192 
    193         /**
    194          * Called when the session merge has been started.  At this point, the {@code newSession}
    195          * represents the session which has been initiated to the IMS conference server for the
    196          * new merged conference.
    197          *
    198          * @param session the session object that carries out the IMS session
    199          * @param newSession the session object that is merged with an active & hold session
    200          */
    201         public void callSessionMergeStarted(ImsCallSession session,
    202                 ImsCallSession newSession, ImsCallProfile profile) {
    203         }
    204 
    205         /**
    206          * Called when the session merge is successful and the merged session is active.
    207          *
    208          * @param session the session object that carries out the IMS session
    209          */
    210         public void callSessionMergeComplete(ImsCallSession session) {
    211         }
    212 
    213         /**
    214          * Called when the session merge has failed.
    215          *
    216          * @param session the session object that carries out the IMS session
    217          * @param reasonInfo detailed reason of the call merge failure
    218          */
    219         public void callSessionMergeFailed(ImsCallSession session,
    220                 ImsReasonInfo reasonInfo) {
    221         }
    222 
    223         /**
    224          * Called when the session is updated (except for hold/unhold).
    225          *
    226          * @param call the call object that carries out the IMS call
    227          */
    228         public void callSessionUpdated(ImsCallSession session,
    229                 ImsCallProfile profile) {
    230         }
    231 
    232         /**
    233          * Called when the session update is failed.
    234          *
    235          * @param session the session object that carries out the IMS session
    236          * @param reasonInfo detailed reason of the session update failure
    237          */
    238         public void callSessionUpdateFailed(ImsCallSession session,
    239                 ImsReasonInfo reasonInfo) {
    240         }
    241 
    242         /**
    243          * Called when the session update is received from the remote user.
    244          *
    245          * @param session the session object that carries out the IMS session
    246          */
    247         public void callSessionUpdateReceived(ImsCallSession session,
    248                 ImsCallProfile profile) {
    249             // no-op
    250         }
    251 
    252         /**
    253          * Called when the session is extended to the conference session.
    254          *
    255          * @param session the session object that carries out the IMS session
    256          * @param newSession the session object that is extended to the conference
    257          *      from the active session
    258          */
    259         public void callSessionConferenceExtended(ImsCallSession session,
    260                 ImsCallSession newSession, ImsCallProfile profile) {
    261         }
    262 
    263         /**
    264          * Called when the conference extension is failed.
    265          *
    266          * @param session the session object that carries out the IMS session
    267          * @param reasonInfo detailed reason of the conference extension failure
    268          */
    269         public void callSessionConferenceExtendFailed(ImsCallSession session,
    270                 ImsReasonInfo reasonInfo) {
    271         }
    272 
    273         /**
    274          * Called when the conference extension is received from the remote user.
    275          *
    276          * @param session the session object that carries out the IMS session
    277          */
    278         public void callSessionConferenceExtendReceived(ImsCallSession session,
    279                 ImsCallSession newSession, ImsCallProfile profile) {
    280             // no-op
    281         }
    282 
    283         /**
    284          * Called when the invitation request of the participants is delivered to the conference
    285          * server.
    286          *
    287          * @param session the session object that carries out the IMS session
    288          */
    289         public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
    290             // no-op
    291         }
    292 
    293         /**
    294          * Called when the invitation request of the participants is failed.
    295          *
    296          * @param session the session object that carries out the IMS session
    297          * @param reasonInfo detailed reason of the conference invitation failure
    298          */
    299         public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
    300                 ImsReasonInfo reasonInfo) {
    301             // no-op
    302         }
    303 
    304         /**
    305          * Called when the removal request of the participants is delivered to the conference
    306          * server.
    307          *
    308          * @param session the session object that carries out the IMS session
    309          */
    310         public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
    311             // no-op
    312         }
    313 
    314         /**
    315          * Called when the removal request of the participants is failed.
    316          *
    317          * @param session the session object that carries out the IMS session
    318          * @param reasonInfo detailed reason of the conference removal failure
    319          */
    320         public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
    321                 ImsReasonInfo reasonInfo) {
    322             // no-op
    323         }
    324 
    325         /**
    326          * Called when the conference state is updated.
    327          *
    328          * @param session the session object that carries out the IMS session
    329          */
    330         public void callSessionConferenceStateUpdated(ImsCallSession session,
    331                 ImsConferenceState state) {
    332             // no-op
    333         }
    334 
    335         /**
    336          * Called when the USSD message is received from the network.
    337          *
    338          * @param mode mode of the USSD message (REQUEST / NOTIFY)
    339          * @param ussdMessage USSD message
    340          */
    341         public void callSessionUssdMessageReceived(ImsCallSession session,
    342                 int mode, String ussdMessage) {
    343             // no-op
    344         }
    345 
    346         /**
    347          * Called when session access technology changes
    348          *
    349          * @param session IMS session object
    350          * @param srcAccessTech original access technology
    351          * @param targetAccessTech new access technology
    352          * @param reasonInfo
    353          */
    354         public void callSessionHandover(ImsCallSession session,
    355                                  int srcAccessTech, int targetAccessTech,
    356                                  ImsReasonInfo reasonInfo) {
    357             // no-op
    358         }
    359 
    360         /**
    361          * Called when session access technology change fails
    362          *
    363          * @param session IMS session object
    364          * @param srcAccessTech original access technology
    365          * @param targetAccessTech new access technology
    366          * @param reasonInfo handover failure reason
    367          */
    368         public void callSessionHandoverFailed(ImsCallSession session,
    369                                        int srcAccessTech, int targetAccessTech,
    370                                        ImsReasonInfo reasonInfo) {
    371             // no-op
    372         }
    373 
    374         /**
    375          * Called when TTY mode of remote party changed
    376          *
    377          * @param session IMS session object
    378          * @param mode TTY mode of remote party
    379          */
    380         public void callSessionTtyModeReceived(ImsCallSession session,
    381                                        int mode) {
    382             // no-op
    383         }
    384 
    385         /**
    386          * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
    387          *
    388          * @param session The call session.
    389          * @param isMultiParty {@code true} if the session became multiparty, {@code false}
    390          *      otherwise.
    391          */
    392         public void callSessionMultipartyStateChanged(ImsCallSession session,
    393                 boolean isMultiParty) {
    394             // no-op
    395         }
    396 
    397         /**
    398          * Called when the session supplementary service is received
    399          *
    400          * @param session the session object that carries out the IMS session
    401          */
    402         public void callSessionSuppServiceReceived(ImsCallSession session,
    403                 ImsSuppServiceNotification suppServiceInfo) {
    404         }
    405     }
    406 
    407     private final IImsCallSession miSession;
    408     private boolean mClosed = false;
    409     private Listener mListener;
    410 
    411     public ImsCallSession(IImsCallSession iSession) {
    412         miSession = iSession;
    413 
    414         if (iSession != null) {
    415             try {
    416                 iSession.setListener(new IImsCallSessionListenerProxy());
    417             } catch (RemoteException e) {
    418             }
    419         } else {
    420             mClosed = true;
    421         }
    422     }
    423 
    424     public ImsCallSession(IImsCallSession iSession, Listener listener) {
    425         this(iSession);
    426         setListener(listener);
    427     }
    428 
    429     /**
    430      * Closes this object. This object is not usable after being closed.
    431      */
    432     public synchronized void close() {
    433         if (mClosed) {
    434             return;
    435         }
    436 
    437         try {
    438             miSession.close();
    439             mClosed = true;
    440         } catch (RemoteException e) {
    441         }
    442     }
    443 
    444     /**
    445      * Gets the call ID of the session.
    446      *
    447      * @return the call ID
    448      */
    449     public String getCallId() {
    450         if (mClosed) {
    451             return null;
    452         }
    453 
    454         try {
    455             return miSession.getCallId();
    456         } catch (RemoteException e) {
    457             return null;
    458         }
    459     }
    460 
    461     /**
    462      * Gets the call profile that this session is associated with
    463      *
    464      * @return the call profile that this session is associated with
    465      */
    466     public ImsCallProfile getCallProfile() {
    467         if (mClosed) {
    468             return null;
    469         }
    470 
    471         try {
    472             return miSession.getCallProfile();
    473         } catch (RemoteException e) {
    474             return null;
    475         }
    476     }
    477 
    478     /**
    479      * Gets the local call profile that this session is associated with
    480      *
    481      * @return the local call profile that this session is associated with
    482      */
    483     public ImsCallProfile getLocalCallProfile() {
    484         if (mClosed) {
    485             return null;
    486         }
    487 
    488         try {
    489             return miSession.getLocalCallProfile();
    490         } catch (RemoteException e) {
    491             return null;
    492         }
    493     }
    494 
    495     /**
    496      * Gets the remote call profile that this session is associated with
    497      *
    498      * @return the remote call profile that this session is associated with
    499      */
    500     public ImsCallProfile getRemoteCallProfile() {
    501         if (mClosed) {
    502             return null;
    503         }
    504 
    505         try {
    506             return miSession.getRemoteCallProfile();
    507         } catch (RemoteException e) {
    508             return null;
    509         }
    510     }
    511 
    512     /**
    513      * Gets the video call provider for the session.
    514      *
    515      * @return The video call provider.
    516      */
    517     public IImsVideoCallProvider getVideoCallProvider() {
    518         if (mClosed) {
    519             return null;
    520         }
    521 
    522         try {
    523             return miSession.getVideoCallProvider();
    524         } catch (RemoteException e) {
    525             return null;
    526         }
    527     }
    528 
    529     /**
    530      * Gets the value associated with the specified property of this session.
    531      *
    532      * @return the string value associated with the specified property
    533      */
    534     public String getProperty(String name) {
    535         if (mClosed) {
    536             return null;
    537         }
    538 
    539         try {
    540             return miSession.getProperty(name);
    541         } catch (RemoteException e) {
    542             return null;
    543         }
    544     }
    545 
    546     /**
    547      * Gets the session state.
    548      * The value returned must be one of the states in {@link State}.
    549      *
    550      * @return the session state
    551      */
    552     public int getState() {
    553         if (mClosed) {
    554             return State.INVALID;
    555         }
    556 
    557         try {
    558             return miSession.getState();
    559         } catch (RemoteException e) {
    560             return State.INVALID;
    561         }
    562     }
    563 
    564     /**
    565      * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
    566      * closed state).
    567      *
    568      * @return {@code True} if the session is alive.
    569      */
    570     public boolean isAlive() {
    571         if (mClosed) {
    572             return false;
    573         }
    574 
    575         int state = getState();
    576         switch (state) {
    577             case State.IDLE:
    578             case State.INITIATED:
    579             case State.NEGOTIATING:
    580             case State.ESTABLISHING:
    581             case State.ESTABLISHED:
    582             case State.RENEGOTIATING:
    583             case State.REESTABLISHING:
    584                 return true;
    585             default:
    586                 return false;
    587         }
    588     }
    589 
    590     /**
    591      * Gets the native IMS call session.
    592      * @hide
    593      */
    594     public IImsCallSession getSession() {
    595         return miSession;
    596     }
    597 
    598     /**
    599      * Checks if the session is in call.
    600      *
    601      * @return true if the session is in call
    602      */
    603     public boolean isInCall() {
    604         if (mClosed) {
    605             return false;
    606         }
    607 
    608         try {
    609             return miSession.isInCall();
    610         } catch (RemoteException e) {
    611             return false;
    612         }
    613     }
    614 
    615     /**
    616      * Sets the listener to listen to the session events. A {@link ImsCallSession}
    617      * can only hold one listener at a time. Subsequent calls to this method
    618      * override the previous listener.
    619      *
    620      * @param listener to listen to the session events of this object
    621      */
    622     public void setListener(Listener listener) {
    623         mListener = listener;
    624     }
    625 
    626     /**
    627      * Mutes or unmutes the mic for the active call.
    628      *
    629      * @param muted true if the call is muted, false otherwise
    630      */
    631     public void setMute(boolean muted) {
    632         if (mClosed) {
    633             return;
    634         }
    635 
    636         try {
    637             miSession.setMute(muted);
    638         } catch (RemoteException e) {
    639         }
    640     }
    641 
    642     /**
    643      * Initiates an IMS call with the specified target and call profile.
    644      * The session listener is called back upon defined session events.
    645      * The method is only valid to call when the session state is in
    646      * {@link ImsCallSession#State#IDLE}.
    647      *
    648      * @param callee dialed string to make the call to
    649      * @param profile call profile to make the call with the specified service type,
    650      *      call type and media information
    651      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
    652      */
    653     public void start(String callee, ImsCallProfile profile) {
    654         if (mClosed) {
    655             return;
    656         }
    657 
    658         try {
    659             miSession.start(callee, profile);
    660         } catch (RemoteException e) {
    661         }
    662     }
    663 
    664     /**
    665      * Initiates an IMS conference call with the specified target and call profile.
    666      * The session listener is called back upon defined session events.
    667      * The method is only valid to call when the session state is in
    668      * {@link ImsCallSession#State#IDLE}.
    669      *
    670      * @param participants participant list to initiate an IMS conference call
    671      * @param profile call profile to make the call with the specified service type,
    672      *      call type and media information
    673      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
    674      */
    675     public void start(String[] participants, ImsCallProfile profile) {
    676         if (mClosed) {
    677             return;
    678         }
    679 
    680         try {
    681             miSession.startConference(participants, profile);
    682         } catch (RemoteException e) {
    683         }
    684     }
    685 
    686     /**
    687      * Accepts an incoming call or session update.
    688      *
    689      * @param callType call type specified in {@link ImsCallProfile} to be answered
    690      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
    691      * @see Listener#callSessionStarted
    692      */
    693     public void accept(int callType, ImsStreamMediaProfile profile) {
    694         if (mClosed) {
    695             return;
    696         }
    697 
    698         try {
    699             miSession.accept(callType, profile);
    700         } catch (RemoteException e) {
    701         }
    702     }
    703 
    704     /**
    705      * Rejects an incoming call or session update.
    706      *
    707      * @param reason reason code to reject an incoming call
    708      * @see Listener#callSessionStartFailed
    709      */
    710     public void reject(int reason) {
    711         if (mClosed) {
    712             return;
    713         }
    714 
    715         try {
    716             miSession.reject(reason);
    717         } catch (RemoteException e) {
    718         }
    719     }
    720 
    721     /**
    722      * Terminates a call.
    723      *
    724      * @see Listener#callSessionTerminated
    725      */
    726     public void terminate(int reason) {
    727         if (mClosed) {
    728             return;
    729         }
    730 
    731         try {
    732             miSession.terminate(reason);
    733         } catch (RemoteException e) {
    734         }
    735     }
    736 
    737     /**
    738      * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
    739      *
    740      * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
    741      * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
    742      */
    743     public void hold(ImsStreamMediaProfile profile) {
    744         if (mClosed) {
    745             return;
    746         }
    747 
    748         try {
    749             miSession.hold(profile);
    750         } catch (RemoteException e) {
    751         }
    752     }
    753 
    754     /**
    755      * Continues a call that's on hold. When it succeeds,
    756      * {@link Listener#callSessionResumed} is called.
    757      *
    758      * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
    759      * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
    760      */
    761     public void resume(ImsStreamMediaProfile profile) {
    762         if (mClosed) {
    763             return;
    764         }
    765 
    766         try {
    767             miSession.resume(profile);
    768         } catch (RemoteException e) {
    769         }
    770     }
    771 
    772     /**
    773      * Merges the active & hold call. When it succeeds,
    774      * {@link Listener#callSessionMergeStarted} is called.
    775      *
    776      * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
    777      */
    778     public void merge() {
    779         if (mClosed) {
    780             return;
    781         }
    782 
    783         try {
    784             miSession.merge();
    785         } catch (RemoteException e) {
    786         }
    787     }
    788 
    789     /**
    790      * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
    791      *
    792      * @param callType call type specified in {@link ImsCallProfile} to be updated
    793      * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
    794      * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
    795      */
    796     public void update(int callType, ImsStreamMediaProfile profile) {
    797         if (mClosed) {
    798             return;
    799         }
    800 
    801         try {
    802             miSession.update(callType, profile);
    803         } catch (RemoteException e) {
    804         }
    805     }
    806 
    807     /**
    808      * Extends this call to the conference call with the specified recipients.
    809      *
    810      * @participants participant list to be invited to the conference call after extending the call
    811      * @see Listener#sessionConferenceExtened, Listener#sessionConferenceExtendFailed
    812      */
    813     public void extendToConference(String[] participants) {
    814         if (mClosed) {
    815             return;
    816         }
    817 
    818         try {
    819             miSession.extendToConference(participants);
    820         } catch (RemoteException e) {
    821         }
    822     }
    823 
    824     /**
    825      * Requests the conference server to invite an additional participants to the conference.
    826      *
    827      * @participants participant list to be invited to the conference call
    828      * @see Listener#sessionInviteParticipantsRequestDelivered,
    829      *      Listener#sessionInviteParticipantsRequestFailed
    830      */
    831     public void inviteParticipants(String[] participants) {
    832         if (mClosed) {
    833             return;
    834         }
    835 
    836         try {
    837             miSession.inviteParticipants(participants);
    838         } catch (RemoteException e) {
    839         }
    840     }
    841 
    842     /**
    843      * Requests the conference server to remove the specified participants from the conference.
    844      *
    845      * @param participants participant list to be removed from the conference call
    846      * @see Listener#sessionRemoveParticipantsRequestDelivered,
    847      *      Listener#sessionRemoveParticipantsRequestFailed
    848      */
    849     public void removeParticipants(String[] participants) {
    850         if (mClosed) {
    851             return;
    852         }
    853 
    854         try {
    855             miSession.removeParticipants(participants);
    856         } catch (RemoteException e) {
    857         }
    858     }
    859 
    860 
    861     /**
    862      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
    863      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
    864      * and event flash to 16. Currently, event flash is not supported.
    865      *
    866      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
    867      */
    868     public void sendDtmf(char c, Message result) {
    869         if (mClosed) {
    870             return;
    871         }
    872 
    873         try {
    874             miSession.sendDtmf(c, result);
    875         } catch (RemoteException e) {
    876         }
    877     }
    878 
    879     /**
    880      * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
    881      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
    882      * and event flash to 16. Currently, event flash is not supported.
    883      *
    884      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
    885      */
    886     public void startDtmf(char c) {
    887         if (mClosed) {
    888             return;
    889         }
    890 
    891         try {
    892             miSession.startDtmf(c);
    893         } catch (RemoteException e) {
    894         }
    895     }
    896 
    897     /**
    898      * Stops a DTMF code.
    899      */
    900     public void stopDtmf() {
    901         if (mClosed) {
    902             return;
    903         }
    904 
    905         try {
    906             miSession.stopDtmf();
    907         } catch (RemoteException e) {
    908         }
    909     }
    910 
    911     /**
    912      * Sends an USSD message.
    913      *
    914      * @param ussdMessage USSD message to send
    915      */
    916     public void sendUssd(String ussdMessage) {
    917         if (mClosed) {
    918             return;
    919         }
    920 
    921         try {
    922             miSession.sendUssd(ussdMessage);
    923         } catch (RemoteException e) {
    924         }
    925     }
    926 
    927     /**
    928      * Determines if the session is multiparty.
    929      *
    930      * @return {@code True} if the session is multiparty.
    931      */
    932     public boolean isMultiparty() {
    933         if (mClosed) {
    934             return false;
    935         }
    936 
    937         try {
    938             return miSession.isMultiparty();
    939         } catch (RemoteException e) {
    940             return false;
    941         }
    942     }
    943 
    944     /**
    945      * A listener type for receiving notification on IMS call session events.
    946      * When an event is generated for an {@link IImsCallSession},
    947      * the application is notified by having one of the methods called on
    948      * the {@link IImsCallSessionListener}.
    949      */
    950     private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
    951         /**
    952          * Notifies the result of the basic session operation (setup / terminate).
    953          */
    954         @Override
    955         public void callSessionProgressing(IImsCallSession session,
    956                 ImsStreamMediaProfile profile) {
    957             if (mListener != null) {
    958                 mListener.callSessionProgressing(ImsCallSession.this, profile);
    959             }
    960         }
    961 
    962         @Override
    963         public void callSessionStarted(IImsCallSession session,
    964                 ImsCallProfile profile) {
    965             if (mListener != null) {
    966                 mListener.callSessionStarted(ImsCallSession.this, profile);
    967             }
    968         }
    969 
    970         @Override
    971         public void callSessionStartFailed(IImsCallSession session,
    972                 ImsReasonInfo reasonInfo) {
    973             if (mListener != null) {
    974                 mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
    975             }
    976         }
    977 
    978         @Override
    979         public void callSessionTerminated(IImsCallSession session,
    980                 ImsReasonInfo reasonInfo) {
    981             if (mListener != null) {
    982                 mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
    983             }
    984         }
    985 
    986         /**
    987          * Notifies the result of the call hold/resume operation.
    988          */
    989         @Override
    990         public void callSessionHeld(IImsCallSession session,
    991                 ImsCallProfile profile) {
    992             if (mListener != null) {
    993                 mListener.callSessionHeld(ImsCallSession.this, profile);
    994             }
    995         }
    996 
    997         @Override
    998         public void callSessionHoldFailed(IImsCallSession session,
    999                 ImsReasonInfo reasonInfo) {
   1000             if (mListener != null) {
   1001                 mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
   1002             }
   1003         }
   1004 
   1005         @Override
   1006         public void callSessionHoldReceived(IImsCallSession session,
   1007                 ImsCallProfile profile) {
   1008             if (mListener != null) {
   1009                 mListener.callSessionHoldReceived(ImsCallSession.this, profile);
   1010             }
   1011         }
   1012 
   1013         @Override
   1014         public void callSessionResumed(IImsCallSession session,
   1015                 ImsCallProfile profile) {
   1016             if (mListener != null) {
   1017                 mListener.callSessionResumed(ImsCallSession.this, profile);
   1018             }
   1019         }
   1020 
   1021         @Override
   1022         public void callSessionResumeFailed(IImsCallSession session,
   1023                 ImsReasonInfo reasonInfo) {
   1024             if (mListener != null) {
   1025                 mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
   1026             }
   1027         }
   1028 
   1029         @Override
   1030         public void callSessionResumeReceived(IImsCallSession session,
   1031                 ImsCallProfile profile) {
   1032             if (mListener != null) {
   1033                 mListener.callSessionResumeReceived(ImsCallSession.this, profile);
   1034             }
   1035         }
   1036 
   1037         /**
   1038          * Notifies the start of a call merge operation.
   1039          *
   1040          * @param session The call session.
   1041          * @param newSession The merged call session.
   1042          * @param profile The call profile.
   1043          */
   1044         @Override
   1045         public void callSessionMergeStarted(IImsCallSession session,
   1046                 IImsCallSession newSession, ImsCallProfile profile) {
   1047             // This callback can be used for future use to add additional
   1048             // functionality that may be needed between conference start and complete
   1049             Log.d(TAG, "callSessionMergeStarted");
   1050         }
   1051 
   1052         /**
   1053          * Notifies the successful completion of a call merge operation.
   1054          *
   1055          * @param session The call session.
   1056          */
   1057         @Override
   1058         public void callSessionMergeComplete(IImsCallSession newSession) {
   1059             if (mListener != null) {
   1060                 if (newSession != null) {
   1061                     // Check if the active session is the same session that was
   1062                     // active before the merge request was sent.
   1063                     ImsCallSession validActiveSession = ImsCallSession.this;
   1064                     try {
   1065                         if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
   1066                             // New session created after conference
   1067                             validActiveSession = new ImsCallSession(newSession);
   1068                         }
   1069                     } catch (RemoteException rex) {
   1070                         Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
   1071                     }
   1072                     mListener.callSessionMergeComplete(validActiveSession);
   1073                } else {
   1074                    // Session already exists. Hence no need to pass
   1075                    mListener.callSessionMergeComplete(null);
   1076                }
   1077             }
   1078         }
   1079 
   1080         /**
   1081          * Notifies of a failure to perform a call merge operation.
   1082          *
   1083          * @param session The call session.
   1084          * @param reasonInfo The merge failure reason.
   1085          */
   1086         @Override
   1087         public void callSessionMergeFailed(IImsCallSession session,
   1088                 ImsReasonInfo reasonInfo) {
   1089             if (mListener != null) {
   1090                 mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
   1091             }
   1092         }
   1093 
   1094         /**
   1095          * Notifies the result of call upgrade / downgrade or any other call updates.
   1096          */
   1097         @Override
   1098         public void callSessionUpdated(IImsCallSession session,
   1099                 ImsCallProfile profile) {
   1100             if (mListener != null) {
   1101                 mListener.callSessionUpdated(ImsCallSession.this, profile);
   1102             }
   1103         }
   1104 
   1105         @Override
   1106         public void callSessionUpdateFailed(IImsCallSession session,
   1107                 ImsReasonInfo reasonInfo) {
   1108             if (mListener != null) {
   1109                 mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
   1110             }
   1111         }
   1112 
   1113         @Override
   1114         public void callSessionUpdateReceived(IImsCallSession session,
   1115                 ImsCallProfile profile) {
   1116             if (mListener != null) {
   1117                 mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
   1118             }
   1119         }
   1120 
   1121         /**
   1122          * Notifies the result of conference extension.
   1123          */
   1124         @Override
   1125         public void callSessionConferenceExtended(IImsCallSession session,
   1126                 IImsCallSession newSession, ImsCallProfile profile) {
   1127             if (mListener != null) {
   1128                 mListener.callSessionConferenceExtended(ImsCallSession.this,
   1129                         new ImsCallSession(newSession), profile);
   1130             }
   1131         }
   1132 
   1133         @Override
   1134         public void callSessionConferenceExtendFailed(IImsCallSession session,
   1135                 ImsReasonInfo reasonInfo) {
   1136             if (mListener != null) {
   1137                 mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
   1138             }
   1139         }
   1140 
   1141         @Override
   1142         public void callSessionConferenceExtendReceived(IImsCallSession session,
   1143                 IImsCallSession newSession, ImsCallProfile profile) {
   1144             if (mListener != null) {
   1145                 mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
   1146                         new ImsCallSession(newSession), profile);
   1147             }
   1148         }
   1149 
   1150         /**
   1151          * Notifies the result of the participant invitation / removal to/from
   1152          * the conference session.
   1153          */
   1154         @Override
   1155         public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
   1156             if (mListener != null) {
   1157                 mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
   1158             }
   1159         }
   1160 
   1161         @Override
   1162         public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
   1163                 ImsReasonInfo reasonInfo) {
   1164             if (mListener != null) {
   1165                 mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
   1166                         reasonInfo);
   1167             }
   1168         }
   1169 
   1170         @Override
   1171         public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
   1172             if (mListener != null) {
   1173                 mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
   1174             }
   1175         }
   1176 
   1177         @Override
   1178         public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
   1179                 ImsReasonInfo reasonInfo) {
   1180             if (mListener != null) {
   1181                 mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
   1182                         reasonInfo);
   1183             }
   1184         }
   1185 
   1186         /**
   1187          * Notifies the changes of the conference info. in the conference session.
   1188          */
   1189         @Override
   1190         public void callSessionConferenceStateUpdated(IImsCallSession session,
   1191                 ImsConferenceState state) {
   1192             if (mListener != null) {
   1193                 mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
   1194             }
   1195         }
   1196 
   1197         /**
   1198          * Notifies the incoming USSD message.
   1199          */
   1200         @Override
   1201         public void callSessionUssdMessageReceived(IImsCallSession session,
   1202                 int mode, String ussdMessage) {
   1203             if (mListener != null) {
   1204                 mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
   1205             }
   1206         }
   1207 
   1208         /**
   1209          * Notifies of handover information for this call
   1210          */
   1211         @Override
   1212         public void callSessionHandover(IImsCallSession session,
   1213                                  int srcAccessTech, int targetAccessTech,
   1214                                  ImsReasonInfo reasonInfo) {
   1215             if (mListener != null) {
   1216                 mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
   1217                         targetAccessTech, reasonInfo);
   1218             }
   1219         }
   1220 
   1221         /**
   1222          * Notifies of handover failure info for this call
   1223          */
   1224         @Override
   1225         public void callSessionHandoverFailed(IImsCallSession session,
   1226                                        int srcAccessTech, int targetAccessTech,
   1227                                        ImsReasonInfo reasonInfo) {
   1228             if (mListener != null) {
   1229                 mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
   1230                         targetAccessTech, reasonInfo);
   1231             }
   1232         }
   1233 
   1234         /**
   1235          * Notifies the TTY mode received from remote party.
   1236          */
   1237         @Override
   1238         public void callSessionTtyModeReceived(IImsCallSession session,
   1239                 int mode) {
   1240             if (mListener != null) {
   1241                 mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
   1242             }
   1243         }
   1244 
   1245         /**
   1246          * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
   1247          *
   1248          * @param session The call session.
   1249          * @param isMultiParty {@code true} if the session became multiparty, {@code false}
   1250          *      otherwise.
   1251          */
   1252         public void callSessionMultipartyStateChanged(IImsCallSession session,
   1253                 boolean isMultiParty) {
   1254 
   1255             if (mListener != null) {
   1256                 mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
   1257             }
   1258         }
   1259 
   1260         @Override
   1261         public void callSessionSuppServiceReceived(IImsCallSession session,
   1262                 ImsSuppServiceNotification suppServiceInfo ) {
   1263             if (mListener != null) {
   1264                 mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
   1265             }
   1266         }
   1267 
   1268     }
   1269 
   1270     /**
   1271      * Provides a string representation of the {@link ImsCallSession}.  Primarily intended for
   1272      * use in log statements.
   1273      *
   1274      * @return String representation of session.
   1275      */
   1276     @Override
   1277     public String toString() {
   1278         StringBuilder sb = new StringBuilder();
   1279         sb.append("[ImsCallSession objId:");
   1280         sb.append(System.identityHashCode(this));
   1281         sb.append(" state:");
   1282         sb.append(State.toString(getState()));
   1283         sb.append(" callId:");
   1284         sb.append(getCallId());
   1285         sb.append("]");
   1286         return sb.toString();
   1287     }
   1288 }
   1289