• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.telephony.ims;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Message;
22 import android.os.RemoteException;
23 import android.telephony.CallQuality;
24 import android.telephony.ims.aidl.IImsCallSessionListener;
25 import android.util.ArraySet;
26 import android.util.Log;
27 
28 import com.android.ims.internal.IImsCallSession;
29 import com.android.ims.internal.IImsVideoCallProvider;
30 import com.android.internal.telephony.util.TelephonyUtils;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.concurrent.Executor;
36 
37 /**
38  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
39  * It directly communicates with IMS service which implements the IMS protocol behavior.
40  *
41  * @hide
42  */
43 public class ImsCallSession {
44     private static final String TAG = "ImsCallSession";
45 
46     /**
47      * Defines IMS call session state. Please use
48      * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
49      * This is kept around for capability reasons.
50      */
51     public static class State {
52         public static final int IDLE = 0;
53         public static final int INITIATED = 1;
54         public static final int NEGOTIATING = 2;
55         public static final int ESTABLISHING = 3;
56         public static final int ESTABLISHED = 4;
57 
58         public static final int RENEGOTIATING = 5;
59         public static final int REESTABLISHING = 6;
60 
61         public static final int TERMINATING = 7;
62         public static final int TERMINATED = 8;
63 
64         public static final int INVALID = (-1);
65 
66         /**
67          * Converts the state to string.
68          */
toString(int state)69         public static String toString(int state) {
70             switch (state) {
71                 case IDLE:
72                     return "IDLE";
73                 case INITIATED:
74                     return "INITIATED";
75                 case NEGOTIATING:
76                     return "NEGOTIATING";
77                 case ESTABLISHING:
78                     return "ESTABLISHING";
79                 case ESTABLISHED:
80                     return "ESTABLISHED";
81                 case RENEGOTIATING:
82                     return "RENEGOTIATING";
83                 case REESTABLISHING:
84                     return "REESTABLISHING";
85                 case TERMINATING:
86                     return "TERMINATING";
87                 case TERMINATED:
88                     return "TERMINATED";
89                 default:
90                     return "UNKNOWN";
91             }
92         }
93 
State()94         private State() {
95         }
96     }
97 
98     /**
99      * Listener for events relating to an IMS session, such as when a session is being
100      * recieved ("on ringing") or a call is outgoing ("on calling").
101      * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
102      * @hide
103      */
104     public static class Listener {
105         /**
106          * Called when the session is initiating.
107          *
108          * see: {@link ImsCallSessionListener#callSessionInitiating(ImsCallProfile)}
109          */
callSessionInitiating(ImsCallSession session, ImsCallProfile profile)110         public void callSessionInitiating(ImsCallSession session,
111                 ImsCallProfile profile) {
112             // no-op
113         }
114 
115         /**
116          * Called when the session failed before initiating was called.
117          *
118          * see: {@link ImsCallSessionListener#callSessionInitiatingFailed(ImsReasonInfo)}
119          */
callSessionInitiatingFailed(ImsCallSession session, ImsReasonInfo reasonInfo)120         public void callSessionInitiatingFailed(ImsCallSession session,
121                 ImsReasonInfo reasonInfo) {
122             // no-op
123         }
124 
125         /**
126          * Called when the session is progressing.
127          *
128          * see: {@link ImsCallSessionListener#callSessionProgressing(ImsStreamMediaProfile)}
129          */
callSessionProgressing(ImsCallSession session, ImsStreamMediaProfile profile)130         public void callSessionProgressing(ImsCallSession session,
131                 ImsStreamMediaProfile profile) {
132             // no-op
133         }
134 
135         /**
136          * Called when the session is established.
137          *
138          * @param session the session object that carries out the IMS session
139          */
callSessionStarted(ImsCallSession session, ImsCallProfile profile)140         public void callSessionStarted(ImsCallSession session,
141                 ImsCallProfile profile) {
142             // no-op
143         }
144 
145         /**
146          * Called when the session establishment is failed.
147          *
148          * @param session the session object that carries out the IMS session
149          * @param reasonInfo detailed reason of the session establishment failure
150          */
callSessionStartFailed(ImsCallSession session, ImsReasonInfo reasonInfo)151         public void callSessionStartFailed(ImsCallSession session,
152                 ImsReasonInfo reasonInfo) {
153         }
154 
155         /**
156          * Called when the session is terminated.
157          *
158          * @param session the session object that carries out the IMS session
159          * @param reasonInfo detailed reason of the session termination
160          */
callSessionTerminated(ImsCallSession session, ImsReasonInfo reasonInfo)161         public void callSessionTerminated(ImsCallSession session,
162                 ImsReasonInfo reasonInfo) {
163         }
164 
165         /**
166          * Called when the session is in hold.
167          *
168          * @param session the session object that carries out the IMS session
169          */
callSessionHeld(ImsCallSession session, ImsCallProfile profile)170         public void callSessionHeld(ImsCallSession session,
171                 ImsCallProfile profile) {
172         }
173 
174         /**
175          * Called when the session hold is failed.
176          *
177          * @param session the session object that carries out the IMS session
178          * @param reasonInfo detailed reason of the session hold failure
179          */
callSessionHoldFailed(ImsCallSession session, ImsReasonInfo reasonInfo)180         public void callSessionHoldFailed(ImsCallSession session,
181                 ImsReasonInfo reasonInfo) {
182         }
183 
184         /**
185          * Called when the session hold is received from the remote user.
186          *
187          * @param session the session object that carries out the IMS session
188          */
callSessionHoldReceived(ImsCallSession session, ImsCallProfile profile)189         public void callSessionHoldReceived(ImsCallSession session,
190                 ImsCallProfile profile) {
191         }
192 
193         /**
194          * Called when the session resume is done.
195          *
196          * @param session the session object that carries out the IMS session
197          */
callSessionResumed(ImsCallSession session, ImsCallProfile profile)198         public void callSessionResumed(ImsCallSession session,
199                 ImsCallProfile profile) {
200         }
201 
202         /**
203          * Called when the session resume is failed.
204          *
205          * @param session the session object that carries out the IMS session
206          * @param reasonInfo detailed reason of the session resume failure
207          */
callSessionResumeFailed(ImsCallSession session, ImsReasonInfo reasonInfo)208         public void callSessionResumeFailed(ImsCallSession session,
209                 ImsReasonInfo reasonInfo) {
210         }
211 
212         /**
213          * Called when the session resume is received from the remote user.
214          *
215          * @param session the session object that carries out the IMS session
216          */
callSessionResumeReceived(ImsCallSession session, ImsCallProfile profile)217         public void callSessionResumeReceived(ImsCallSession session,
218                 ImsCallProfile profile) {
219         }
220 
221         /**
222          * Called when the session merge has been started.  At this point, the {@code newSession}
223          * represents the session which has been initiated to the IMS conference server for the
224          * new merged conference.
225          *
226          * @param session the session object that carries out the IMS session
227          * @param newSession the session object that is merged with an active & hold session
228          */
callSessionMergeStarted(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)229         public void callSessionMergeStarted(ImsCallSession session,
230                 ImsCallSession newSession, ImsCallProfile profile) {
231         }
232 
233         /**
234          * Called when the session merge is successful and the merged session is active.
235          *
236          * @param session the session object that carries out the IMS session
237          */
callSessionMergeComplete(ImsCallSession session)238         public void callSessionMergeComplete(ImsCallSession session) {
239         }
240 
241         /**
242          * Called when the session merge has failed.
243          *
244          * @param session the session object that carries out the IMS session
245          * @param reasonInfo detailed reason of the call merge failure
246          */
callSessionMergeFailed(ImsCallSession session, ImsReasonInfo reasonInfo)247         public void callSessionMergeFailed(ImsCallSession session,
248                 ImsReasonInfo reasonInfo) {
249         }
250 
251         /**
252          * Called when the session is updated (except for hold/unhold).
253          *
254          * @param session the session object that carries out the IMS session
255          */
callSessionUpdated(ImsCallSession session, ImsCallProfile profile)256         public void callSessionUpdated(ImsCallSession session,
257                 ImsCallProfile profile) {
258         }
259 
260         /**
261          * Called when the session update is failed.
262          *
263          * @param session the session object that carries out the IMS session
264          * @param reasonInfo detailed reason of the session update failure
265          */
callSessionUpdateFailed(ImsCallSession session, ImsReasonInfo reasonInfo)266         public void callSessionUpdateFailed(ImsCallSession session,
267                 ImsReasonInfo reasonInfo) {
268         }
269 
270         /**
271          * Called when the session update is received from the remote user.
272          *
273          * @param session the session object that carries out the IMS session
274          */
callSessionUpdateReceived(ImsCallSession session, ImsCallProfile profile)275         public void callSessionUpdateReceived(ImsCallSession session,
276                 ImsCallProfile profile) {
277             // no-op
278         }
279 
280         /**
281          * Called when the session is extended to the conference session.
282          *
283          * @param session the session object that carries out the IMS session
284          * @param newSession the session object that is extended to the conference
285          *      from the active session
286          */
callSessionConferenceExtended(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)287         public void callSessionConferenceExtended(ImsCallSession session,
288                 ImsCallSession newSession, ImsCallProfile profile) {
289         }
290 
291         /**
292          * Called when the conference extension is failed.
293          *
294          * @param session the session object that carries out the IMS session
295          * @param reasonInfo detailed reason of the conference extension failure
296          */
callSessionConferenceExtendFailed(ImsCallSession session, ImsReasonInfo reasonInfo)297         public void callSessionConferenceExtendFailed(ImsCallSession session,
298                 ImsReasonInfo reasonInfo) {
299         }
300 
301         /**
302          * Called when the conference extension is received from the remote user.
303          *
304          * @param session the session object that carries out the IMS session
305          */
callSessionConferenceExtendReceived(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)306         public void callSessionConferenceExtendReceived(ImsCallSession session,
307                 ImsCallSession newSession, ImsCallProfile profile) {
308             // no-op
309         }
310 
311         /**
312          * Called when the invitation request of the participants is delivered to the conference
313          * server.
314          *
315          * @param session the session object that carries out the IMS session
316          */
callSessionInviteParticipantsRequestDelivered(ImsCallSession session)317         public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
318             // no-op
319         }
320 
321         /**
322          * Called when the invitation request of the participants is failed.
323          *
324          * @param session the session object that carries out the IMS session
325          * @param reasonInfo detailed reason of the conference invitation failure
326          */
callSessionInviteParticipantsRequestFailed(ImsCallSession session, ImsReasonInfo reasonInfo)327         public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
328                 ImsReasonInfo reasonInfo) {
329             // no-op
330         }
331 
332         /**
333          * Called when the removal request of the participants is delivered to the conference
334          * server.
335          *
336          * @param session the session object that carries out the IMS session
337          */
callSessionRemoveParticipantsRequestDelivered(ImsCallSession session)338         public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
339             // no-op
340         }
341 
342         /**
343          * Called when the removal request of the participants is failed.
344          *
345          * @param session the session object that carries out the IMS session
346          * @param reasonInfo detailed reason of the conference removal failure
347          */
callSessionRemoveParticipantsRequestFailed(ImsCallSession session, ImsReasonInfo reasonInfo)348         public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
349                 ImsReasonInfo reasonInfo) {
350             // no-op
351         }
352 
353         /**
354          * Called when the conference state is updated.
355          *
356          * @param session the session object that carries out the IMS session
357          */
callSessionConferenceStateUpdated(ImsCallSession session, ImsConferenceState state)358         public void callSessionConferenceStateUpdated(ImsCallSession session,
359                 ImsConferenceState state) {
360             // no-op
361         }
362 
363         /**
364          * Called when the USSD message is received from the network.
365          *
366          * @param mode mode of the USSD message (REQUEST / NOTIFY)
367          * @param ussdMessage USSD message
368          */
callSessionUssdMessageReceived(ImsCallSession session, int mode, String ussdMessage)369         public void callSessionUssdMessageReceived(ImsCallSession session,
370                 int mode, String ussdMessage) {
371             // no-op
372         }
373 
374         /**
375          * Called when an {@link ImsCallSession} may handover from one network type to another.
376          * For example, the session may handover from WIFI to LTE if conditions are right.
377          * <p>
378          * If handover is attempted,
379          * {@link #callSessionHandover(ImsCallSession, int, int, ImsReasonInfo)} or
380          * {@link #callSessionHandoverFailed(ImsCallSession, int, int, ImsReasonInfo)} will be
381          * called to indicate the success or failure of the handover.
382          *
383          * @param session IMS session object
384          * @param srcNetworkType original network type
385          * @param targetNetworkType new network type
386          */
callSessionMayHandover(ImsCallSession session, int srcNetworkType, int targetNetworkType)387         public void callSessionMayHandover(ImsCallSession session, int srcNetworkType,
388                 int targetNetworkType) {
389             // no-op
390         }
391 
392         /**
393          * Called when session network type changes
394          *
395          * @param session IMS session object
396          * @param srcNetworkType original network type
397          * @param targetNetworkType new network type
398          * @param reasonInfo
399          */
callSessionHandover(ImsCallSession session, int srcNetworkType, int targetNetworkType, ImsReasonInfo reasonInfo)400         public void callSessionHandover(ImsCallSession session,
401                                  int srcNetworkType, int targetNetworkType,
402                                  ImsReasonInfo reasonInfo) {
403             // no-op
404         }
405 
406         /**
407          * Called when session access technology change fails
408          *
409          * @param session IMS session object
410          * @param srcNetworkType original access technology
411          * @param targetNetworkType new access technology
412          * @param reasonInfo handover failure reason
413          */
callSessionHandoverFailed(ImsCallSession session, int srcNetworkType, int targetNetworkType, ImsReasonInfo reasonInfo)414         public void callSessionHandoverFailed(ImsCallSession session,
415                                        int srcNetworkType, int targetNetworkType,
416                                        ImsReasonInfo reasonInfo) {
417             // no-op
418         }
419 
420         /**
421          * Called when TTY mode of remote party changed
422          *
423          * @param session IMS session object
424          * @param mode TTY mode of remote party
425          */
callSessionTtyModeReceived(ImsCallSession session, int mode)426         public void callSessionTtyModeReceived(ImsCallSession session,
427                                        int mode) {
428             // no-op
429         }
430 
431         /**
432          * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
433          *
434          * @param session The call session.
435          * @param isMultiParty {@code true} if the session became multiparty, {@code false}
436          *      otherwise.
437          */
callSessionMultipartyStateChanged(ImsCallSession session, boolean isMultiParty)438         public void callSessionMultipartyStateChanged(ImsCallSession session,
439                 boolean isMultiParty) {
440             // no-op
441         }
442 
443         /**
444          * Called when the session supplementary service is received
445          *
446          * @param session the session object that carries out the IMS session
447          */
callSessionSuppServiceReceived(ImsCallSession session, ImsSuppServiceNotification suppServiceInfo)448         public void callSessionSuppServiceReceived(ImsCallSession session,
449                 ImsSuppServiceNotification suppServiceInfo) {
450         }
451 
452         /**
453          * Received RTT modify request from Remote Party
454          */
callSessionRttModifyRequestReceived(ImsCallSession session, ImsCallProfile callProfile)455         public void callSessionRttModifyRequestReceived(ImsCallSession session,
456             ImsCallProfile callProfile) {
457             // no-op
458         }
459 
460         /**
461          * Received response for RTT modify request
462          */
callSessionRttModifyResponseReceived(int status)463         public void callSessionRttModifyResponseReceived(int status) {
464             // no -op
465         }
466 
467         /**
468          * Device received RTT message from Remote UE
469          */
callSessionRttMessageReceived(String rttMessage)470         public void callSessionRttMessageReceived(String rttMessage) {
471             // no-op
472         }
473 
474         /**
475          * While in call, there has been a change in RTT audio indicator.
476          */
callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)477         public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
478             // no-op
479         }
480 
481         /**
482          * Received success response for call transfer request.
483          */
callSessionTransferred(@onNull ImsCallSession session)484         public void callSessionTransferred(@NonNull ImsCallSession session) {
485             // no-op
486         }
487 
488         /**
489          * Received failure response for call transfer request.
490          */
callSessionTransferFailed(@onNull ImsCallSession session, @Nullable ImsReasonInfo reasonInfo)491         public void callSessionTransferFailed(@NonNull ImsCallSession session,
492                 @Nullable ImsReasonInfo reasonInfo) {
493             // no-op
494         }
495 
496         /**
497          * Informs the framework of a DTMF digit which was received from the network.
498          * <p>
499          * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>,
500          * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to
501          * 12 ~ 15.
502          * @param digit the DTMF digit
503          */
callSessionDtmfReceived(char digit)504         public void callSessionDtmfReceived(char digit) {
505             // no-op
506         }
507 
508         /**
509          * Called when the IMS service reports a change to the call quality.
510          */
callQualityChanged(CallQuality callQuality)511         public void callQualityChanged(CallQuality callQuality) {
512             // no-op
513         }
514 
515         /**
516          * Called when the IMS service reports incoming RTP header extension data.
517          */
callSessionRtpHeaderExtensionsReceived( @onNull Set<RtpHeaderExtension> extensions)518         public void callSessionRtpHeaderExtensionsReceived(
519                 @NonNull Set<RtpHeaderExtension> extensions) {
520             // no-op
521         }
522     }
523 
524     private final IImsCallSession miSession;
525     private boolean mClosed = false;
526     private Listener mListener;
527     private Executor mListenerExecutor = Runnable::run;
528 
529     /** @hide */
ImsCallSession(IImsCallSession iSession)530     public ImsCallSession(IImsCallSession iSession) {
531         miSession = iSession;
532 
533         if (iSession != null) {
534             try {
535                 iSession.setListener(new IImsCallSessionListenerProxy());
536             } catch (RemoteException e) {
537             }
538         } else {
539             mClosed = true;
540         }
541     }
542 
543     /** @hide */
ImsCallSession(IImsCallSession iSession, Listener listener, Executor executor)544     public ImsCallSession(IImsCallSession iSession, Listener listener, Executor executor) {
545         this(iSession);
546         setListener(listener, executor);
547     }
548 
549     /**
550      * Closes this object. This object is not usable after being closed.
551      */
close()552     public void close() {
553         synchronized (this) {
554             if (mClosed) {
555                 return;
556             }
557 
558             try {
559                 miSession.close();
560                 mClosed = true;
561             } catch (RemoteException e) {
562             }
563         }
564     }
565 
566     /**
567      * Gets the call ID of the session.
568      *
569      * @return the call ID
570      */
getCallId()571     public String getCallId() {
572         if (mClosed) {
573             return null;
574         }
575 
576         try {
577             return miSession.getCallId();
578         } catch (RemoteException e) {
579             return null;
580         }
581     }
582 
583     /**
584      * Gets the call profile that this session is associated with
585      *
586      * @return the call profile that this session is associated with
587      */
getCallProfile()588     public ImsCallProfile getCallProfile() {
589         if (mClosed) {
590             return null;
591         }
592 
593         try {
594             return miSession.getCallProfile();
595         } catch (RemoteException e) {
596             return null;
597         }
598     }
599 
600     /**
601      * Gets the local call profile that this session is associated with
602      *
603      * @return the local call profile that this session is associated with
604      */
getLocalCallProfile()605     public ImsCallProfile getLocalCallProfile() {
606         if (mClosed) {
607             return null;
608         }
609 
610         try {
611             return miSession.getLocalCallProfile();
612         } catch (RemoteException e) {
613             return null;
614         }
615     }
616 
617     /**
618      * Gets the remote call profile that this session is associated with
619      *
620      * @return the remote call profile that this session is associated with
621      */
getRemoteCallProfile()622     public ImsCallProfile getRemoteCallProfile() {
623         if (mClosed) {
624             return null;
625         }
626 
627         try {
628             return miSession.getRemoteCallProfile();
629         } catch (RemoteException e) {
630             return null;
631         }
632     }
633 
634     /**
635      * Gets the video call provider for the session.
636      *
637      * @return The video call provider.
638      * @hide
639      */
getVideoCallProvider()640     public IImsVideoCallProvider getVideoCallProvider() {
641         if (mClosed) {
642             return null;
643         }
644 
645         try {
646             return miSession.getVideoCallProvider();
647         } catch (RemoteException e) {
648             return null;
649         }
650     }
651 
652     /**
653      * Gets the value associated with the specified property of this session.
654      *
655      * @return the string value associated with the specified property
656      */
getProperty(String name)657     public String getProperty(String name) {
658         if (mClosed) {
659             return null;
660         }
661 
662         try {
663             return miSession.getProperty(name);
664         } catch (RemoteException e) {
665             return null;
666         }
667     }
668 
669     /**
670      * Gets the session state.
671      * The value returned must be one of the states in {@link State}.
672      *
673      * @return the session state
674      */
getState()675     public int getState() {
676         if (mClosed) {
677             return State.INVALID;
678         }
679 
680         try {
681             return miSession.getState();
682         } catch (RemoteException e) {
683             return State.INVALID;
684         }
685     }
686 
687     /**
688      * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
689      * closed state).
690      *
691      * @return {@code True} if the session is alive.
692      */
isAlive()693     public boolean isAlive() {
694         if (mClosed) {
695             return false;
696         }
697 
698         int state = getState();
699         switch (state) {
700             case State.IDLE:
701             case State.INITIATED:
702             case State.NEGOTIATING:
703             case State.ESTABLISHING:
704             case State.ESTABLISHED:
705             case State.RENEGOTIATING:
706             case State.REESTABLISHING:
707                 return true;
708             default:
709                 return false;
710         }
711     }
712 
713     /**
714      * Gets the native IMS call session.
715      * @hide
716      */
getSession()717     public IImsCallSession getSession() {
718         return miSession;
719     }
720 
721     /**
722      * Checks if the session is in call.
723      *
724      * @return true if the session is in call
725      */
isInCall()726     public boolean isInCall() {
727         if (mClosed) {
728             return false;
729         }
730 
731         try {
732             return miSession.isInCall();
733         } catch (RemoteException e) {
734             return false;
735         }
736     }
737 
738     /**
739      * Sets the listener to listen to the session events. A {@link ImsCallSession}
740      * can only hold one listener at a time. Subsequent calls to this method
741      * override the previous listener.
742      *
743      * @param listener to listen to the session events of this object
744      * @param executor an Executor that will execute callbacks
745      * @hide
746      */
setListener(Listener listener, Executor executor)747     public void setListener(Listener listener, Executor executor) {
748         mListener = listener;
749         if (executor != null) {
750             mListenerExecutor = executor;
751         }
752     }
753 
754     /**
755      * Mutes or unmutes the mic for the active call.
756      *
757      * @param muted true if the call is muted, false otherwise
758      */
setMute(boolean muted)759     public void setMute(boolean muted) {
760         if (mClosed) {
761             return;
762         }
763 
764         try {
765             miSession.setMute(muted);
766         } catch (RemoteException e) {
767         }
768     }
769 
770     /**
771      * Initiates an IMS call with the specified target and call profile.
772      * The session listener is called back upon defined session events.
773      * The method is only valid to call when the session state is in
774      * {@link ImsCallSession.State#IDLE}.
775      *
776      * @param callee dial string to make the call to.  The platform passes the dialed number
777      *               entered by the user as-is.  The {@link ImsService} should ensure that the
778      *               number is formatted in SIP messages appropriately (e.g. using
779      *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
780      * @param profile call profile to make the call with the specified service type,
781      *      call type and media information
782      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
783      */
start(String callee, ImsCallProfile profile)784     public void start(String callee, ImsCallProfile profile) {
785         if (mClosed) {
786             return;
787         }
788 
789         try {
790             miSession.start(callee, profile);
791         } catch (RemoteException e) {
792         }
793     }
794 
795     /**
796      * Initiates an IMS conference call with the specified target and call profile.
797      * The session listener is called back upon defined session events.
798      * The method is only valid to call when the session state is in
799      * {@link ImsCallSession.State#IDLE}.
800      *
801      * @param participants participant list to initiate an IMS conference call.  The platform passes
802      *               the dialed numbers entered by the user as-is.  The {@link ImsService} should
803      *               ensure that the number is formatted in SIP messages appropriately (e.g. using
804      *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
805      * @param profile call profile to make the call with the specified service type,
806      *      call type and media information
807      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
808      */
start(String[] participants, ImsCallProfile profile)809     public void start(String[] participants, ImsCallProfile profile) {
810         if (mClosed) {
811             return;
812         }
813 
814         try {
815             miSession.startConference(participants, profile);
816         } catch (RemoteException e) {
817         }
818     }
819 
820     /**
821      * Accepts an incoming call or session update.
822      *
823      * @param callType call type specified in {@link ImsCallProfile} to be answered
824      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
825      * @see Listener#callSessionStarted
826      */
accept(int callType, ImsStreamMediaProfile profile)827     public void accept(int callType, ImsStreamMediaProfile profile) {
828         if (mClosed) {
829             return;
830         }
831 
832         try {
833             miSession.accept(callType, profile);
834         } catch (RemoteException e) {
835         }
836     }
837 
838     /**
839      * Deflects an incoming call.
840      *
841      * @param number number to be deflected to
842      */
deflect(String number)843     public void deflect(String number) {
844         if (mClosed) {
845             return;
846         }
847 
848         try {
849             miSession.deflect(number);
850         } catch (RemoteException e) {
851         }
852     }
853 
854     /**
855      * Rejects an incoming call or session update.
856      *
857      * @param reason reason code to reject an incoming call
858      * @see Listener#callSessionStartFailed
859      */
reject(int reason)860     public void reject(int reason) {
861         if (mClosed) {
862             return;
863         }
864 
865         try {
866             miSession.reject(reason);
867         } catch (RemoteException e) {
868         }
869     }
870 
871     /**
872      * Transfers an ongoing call.
873      *
874      * @param number number to be transferred to.
875      * @param isConfirmationRequired indicates whether confirmation of the transfer is required.
876      */
transfer(@onNull String number, boolean isConfirmationRequired)877     public void transfer(@NonNull String number, boolean isConfirmationRequired) {
878         if (mClosed) {
879             return;
880         }
881 
882         try {
883             miSession.transfer(number, isConfirmationRequired);
884         } catch (RemoteException e) {
885         }
886     }
887 
888     /**
889      * Transfers a call to another ongoing call.
890      *
891      * @param transferToSession the other ImsCallSession to which this session will be transferred.
892      */
transfer(@onNull ImsCallSession transferToSession)893     public void transfer(@NonNull ImsCallSession transferToSession) {
894         if (mClosed) {
895             return;
896         }
897 
898         try {
899             if (transferToSession != null) {
900                 miSession.consultativeTransfer(transferToSession.getSession());
901             }
902         } catch (RemoteException e) {
903         }
904     }
905 
906     /**
907      * Terminates a call.
908      *
909      * @see Listener#callSessionTerminated
910      */
terminate(int reason)911     public void terminate(int reason) {
912         if (mClosed) {
913             return;
914         }
915 
916         try {
917             miSession.terminate(reason);
918         } catch (RemoteException e) {
919         }
920     }
921 
922     /**
923      * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
924      *
925      * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
926      * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
927      */
hold(ImsStreamMediaProfile profile)928     public void hold(ImsStreamMediaProfile profile) {
929         if (mClosed) {
930             return;
931         }
932 
933         try {
934             miSession.hold(profile);
935         } catch (RemoteException e) {
936         }
937     }
938 
939     /**
940      * Continues a call that's on hold. When it succeeds,
941      * {@link Listener#callSessionResumed} is called.
942      *
943      * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
944      * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
945      */
resume(ImsStreamMediaProfile profile)946     public void resume(ImsStreamMediaProfile profile) {
947         if (mClosed) {
948             return;
949         }
950 
951         try {
952             miSession.resume(profile);
953         } catch (RemoteException e) {
954         }
955     }
956 
957     /**
958      * Merges the active & hold call. When it succeeds,
959      * {@link Listener#callSessionMergeStarted} is called.
960      *
961      * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
962      */
merge()963     public void merge() {
964         if (mClosed) {
965             return;
966         }
967 
968         try {
969             miSession.merge();
970         } catch (RemoteException e) {
971         }
972     }
973 
974     /**
975      * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
976      *
977      * @param callType call type specified in {@link ImsCallProfile} to be updated
978      * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
979      * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
980      */
update(int callType, ImsStreamMediaProfile profile)981     public void update(int callType, ImsStreamMediaProfile profile) {
982         if (mClosed) {
983             return;
984         }
985 
986         try {
987             miSession.update(callType, profile);
988         } catch (RemoteException e) {
989         }
990     }
991 
992     /**
993      * Extends this call to the conference call with the specified recipients.
994      *
995      * @param participants list to be invited to the conference call after extending the call
996      * @see Listener#callSessionConferenceExtended
997      * @see Listener#callSessionConferenceExtendFailed
998      */
extendToConference(String[] participants)999     public void extendToConference(String[] participants) {
1000         if (mClosed) {
1001             return;
1002         }
1003 
1004         try {
1005             miSession.extendToConference(participants);
1006         } catch (RemoteException e) {
1007         }
1008     }
1009 
1010     /**
1011      * Requests the conference server to invite an additional participants to the conference.
1012      *
1013      * @param participants list to be invited to the conference call
1014      * @see Listener#callSessionInviteParticipantsRequestDelivered
1015      * @see Listener#callSessionInviteParticipantsRequestFailed
1016      */
inviteParticipants(String[] participants)1017     public void inviteParticipants(String[] participants) {
1018         if (mClosed) {
1019             return;
1020         }
1021 
1022         try {
1023             miSession.inviteParticipants(participants);
1024         } catch (RemoteException e) {
1025         }
1026     }
1027 
1028     /**
1029      * Requests the conference server to remove the specified participants from the conference.
1030      *
1031      * @param participants participant list to be removed from the conference call
1032      * @see Listener#callSessionRemoveParticipantsRequestDelivered
1033      * @see Listener#callSessionRemoveParticipantsRequestFailed
1034      */
removeParticipants(String[] participants)1035     public void removeParticipants(String[] participants) {
1036         if (mClosed) {
1037             return;
1038         }
1039 
1040         try {
1041             miSession.removeParticipants(participants);
1042         } catch (RemoteException e) {
1043         }
1044     }
1045 
1046 
1047     /**
1048      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
1049      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
1050      * and event flash to 16. Currently, event flash is not supported.
1051      *
1052      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
1053      */
sendDtmf(char c, Message result)1054     public void sendDtmf(char c, Message result) {
1055         if (mClosed) {
1056             return;
1057         }
1058 
1059         try {
1060             miSession.sendDtmf(c, result);
1061         } catch (RemoteException e) {
1062         }
1063     }
1064 
1065     /**
1066      * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
1067      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
1068      * and event flash to 16. Currently, event flash is not supported.
1069      *
1070      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
1071      */
startDtmf(char c)1072     public void startDtmf(char c) {
1073         if (mClosed) {
1074             return;
1075         }
1076 
1077         try {
1078             miSession.startDtmf(c);
1079         } catch (RemoteException e) {
1080         }
1081     }
1082 
1083     /**
1084      * Stops a DTMF code.
1085      */
stopDtmf()1086     public void stopDtmf() {
1087         if (mClosed) {
1088             return;
1089         }
1090 
1091         try {
1092             miSession.stopDtmf();
1093         } catch (RemoteException e) {
1094         }
1095     }
1096 
1097     /**
1098      * Sends an USSD message.
1099      *
1100      * @param ussdMessage USSD message to send
1101      */
sendUssd(String ussdMessage)1102     public void sendUssd(String ussdMessage) {
1103         if (mClosed) {
1104             return;
1105         }
1106 
1107         try {
1108             miSession.sendUssd(ussdMessage);
1109         } catch (RemoteException e) {
1110         }
1111     }
1112 
1113     /**
1114      * Determines if the session is multiparty.
1115      *
1116      * @return {@code True} if the session is multiparty.
1117      */
isMultiparty()1118     public boolean isMultiparty() {
1119         if (mClosed) {
1120             return false;
1121         }
1122 
1123         try {
1124             return miSession.isMultiparty();
1125         } catch (RemoteException e) {
1126             return false;
1127         }
1128     }
1129 
1130     /**
1131      * Sends Rtt Message
1132      *
1133      * @param rttMessage rtt text to be sent
1134      */
sendRttMessage(String rttMessage)1135     public void sendRttMessage(String rttMessage) {
1136         if (mClosed) {
1137             return;
1138         }
1139 
1140         try {
1141             miSession.sendRttMessage(rttMessage);
1142         } catch (RemoteException e) {
1143         }
1144     }
1145 
1146     /**
1147      * Sends RTT Upgrade or downgrade request
1148      *
1149      * @param to Profile with the RTT flag set to the desired value
1150      */
sendRttModifyRequest(ImsCallProfile to)1151     public void sendRttModifyRequest(ImsCallProfile to) {
1152         if (mClosed) {
1153             return;
1154         }
1155 
1156         try {
1157             miSession.sendRttModifyRequest(to);
1158         } catch (RemoteException e) {
1159         }
1160     }
1161 
1162     /**
1163      * Sends RTT Upgrade response
1164      *
1165      * @param response : response for upgrade
1166      */
sendRttModifyResponse(boolean response)1167     public void sendRttModifyResponse(boolean response) {
1168         if (mClosed) {
1169             return;
1170         }
1171 
1172         try {
1173             miSession.sendRttModifyResponse(response);
1174         } catch (RemoteException e) {
1175         }
1176     }
1177 
1178     /**
1179      * Requests that {@code rtpHeaderExtensions} are sent as a header extension with the next
1180      * RTP packet sent by the IMS stack.
1181      * <p>
1182      * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol)
1183      * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method.
1184      * See RFC8285 for more information.
1185      * <p>
1186      * By specification, the RTP header extension is an unacknowledged transmission and there is no
1187      * guarantee that the header extension will be delivered by the network to the other end of the
1188      * call.
1189      * @param rtpHeaderExtensions The header extensions to be included in the next RTP header.
1190      */
sendRtpHeaderExtensions(@onNull Set<RtpHeaderExtension> rtpHeaderExtensions)1191     public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
1192         if (mClosed) {
1193             return;
1194         }
1195 
1196         try {
1197             miSession.sendRtpHeaderExtensions(
1198                     new ArrayList<RtpHeaderExtension>(rtpHeaderExtensions));
1199         } catch (RemoteException e) {
1200         }
1201     }
1202 
1203     /**
1204      * A listener type for receiving notification on IMS call session events.
1205      * When an event is generated for an {@link IImsCallSession},
1206      * the application is notified by having one of the methods called on
1207      * the {@link IImsCallSessionListener}.
1208      */
1209     private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
1210         /**
1211          * Notifies the result of the basic session operation (setup / terminate).
1212          */
1213         @Override
callSessionInitiating(ImsCallProfile profile)1214         public void callSessionInitiating(ImsCallProfile profile) {
1215             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1216                 if (mListener != null) {
1217                     mListener.callSessionInitiating(ImsCallSession.this, profile);
1218                 }
1219             }, mListenerExecutor);
1220         }
1221 
1222         @Override
callSessionProgressing(ImsStreamMediaProfile profile)1223         public void callSessionProgressing(ImsStreamMediaProfile profile) {
1224             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1225                 if (mListener != null) {
1226                     mListener.callSessionProgressing(ImsCallSession.this, profile);
1227                 }
1228             }, mListenerExecutor);
1229         }
1230 
1231         @Override
callSessionInitiated(ImsCallProfile profile)1232         public void callSessionInitiated(ImsCallProfile profile) {
1233             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1234                 if (mListener != null) {
1235                     mListener.callSessionStarted(ImsCallSession.this, profile);
1236                 }
1237             }, mListenerExecutor);
1238         }
1239 
1240         @Override
callSessionInitiatingFailed(ImsReasonInfo reasonInfo)1241         public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
1242             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1243                 if (mListener != null) {
1244                     mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
1245                 }
1246             }, mListenerExecutor);
1247         }
1248 
1249         @Override
callSessionInitiatedFailed(ImsReasonInfo reasonInfo)1250         public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
1251             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1252                 if (mListener != null) {
1253                     mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
1254                 }
1255             }, mListenerExecutor);
1256         }
1257 
1258         @Override
callSessionTerminated(ImsReasonInfo reasonInfo)1259         public void callSessionTerminated(ImsReasonInfo reasonInfo) {
1260             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1261                 if (mListener != null) {
1262                     mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
1263                 }
1264             }, mListenerExecutor);
1265         }
1266 
1267         /**
1268          * Notifies the result of the call hold/resume operation.
1269          */
1270         @Override
callSessionHeld(ImsCallProfile profile)1271         public void callSessionHeld(ImsCallProfile profile) {
1272             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1273                 if (mListener != null) {
1274                     mListener.callSessionHeld(ImsCallSession.this, profile);
1275                 }
1276             }, mListenerExecutor);
1277         }
1278 
1279         @Override
callSessionHoldFailed(ImsReasonInfo reasonInfo)1280         public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
1281             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1282                 if (mListener != null) {
1283                     mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
1284                 }
1285             }, mListenerExecutor);
1286         }
1287 
1288         @Override
callSessionHoldReceived(ImsCallProfile profile)1289         public void callSessionHoldReceived(ImsCallProfile profile) {
1290             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1291                 if (mListener != null) {
1292                     mListener.callSessionHoldReceived(ImsCallSession.this, profile);
1293                 }
1294             }, mListenerExecutor);
1295         }
1296 
1297         @Override
callSessionResumed(ImsCallProfile profile)1298         public void callSessionResumed(ImsCallProfile profile) {
1299             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1300                 if (mListener != null) {
1301                     mListener.callSessionResumed(ImsCallSession.this, profile);
1302                 }
1303             }, mListenerExecutor);
1304         }
1305 
1306         @Override
callSessionResumeFailed(ImsReasonInfo reasonInfo)1307         public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
1308             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1309                 if (mListener != null) {
1310                     mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
1311                 }
1312             }, mListenerExecutor);
1313         }
1314 
1315         @Override
callSessionResumeReceived(ImsCallProfile profile)1316         public void callSessionResumeReceived(ImsCallProfile profile) {
1317             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1318                 if (mListener != null) {
1319                     mListener.callSessionResumeReceived(ImsCallSession.this, profile);
1320                 }
1321             }, mListenerExecutor);
1322         }
1323 
1324         /**
1325          * Notifies the start of a call merge operation.
1326          *
1327          * @param newSession The merged call session.
1328          * @param profile The call profile.
1329          */
1330         @Override
callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)1331         public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
1332             // This callback can be used for future use to add additional
1333             // functionality that may be needed between conference start and complete
1334             Log.d(TAG, "callSessionMergeStarted");
1335         }
1336 
1337         /**
1338          * Notifies the successful completion of a call merge operation.
1339          *
1340          * @param newSession The call session.
1341          */
1342         @Override
callSessionMergeComplete(IImsCallSession newSession)1343         public void callSessionMergeComplete(IImsCallSession newSession) {
1344             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1345                 if (mListener != null) {
1346                     if (newSession != null) {
1347                         // New session created after conference
1348                         mListener.callSessionMergeComplete(new ImsCallSession(newSession));
1349                     } else {
1350                         // Session already exists. Hence no need to pass
1351                         mListener.callSessionMergeComplete(null);
1352                     }
1353                 }
1354             }, mListenerExecutor);
1355         }
1356 
1357         /**
1358          * Notifies of a failure to perform a call merge operation.
1359          *
1360          * @param reasonInfo The merge failure reason.
1361          */
1362         @Override
callSessionMergeFailed(ImsReasonInfo reasonInfo)1363         public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
1364             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1365                 if (mListener != null) {
1366                     mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
1367                 }
1368             }, mListenerExecutor);
1369         }
1370 
1371         /**
1372          * Notifies the result of call upgrade / downgrade or any other call updates.
1373          */
1374         @Override
callSessionUpdated(ImsCallProfile profile)1375         public void callSessionUpdated(ImsCallProfile profile) {
1376             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1377                 if (mListener != null) {
1378                     mListener.callSessionUpdated(ImsCallSession.this, profile);
1379                 }
1380             }, mListenerExecutor);
1381         }
1382 
1383         @Override
callSessionUpdateFailed(ImsReasonInfo reasonInfo)1384         public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
1385             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1386                 if (mListener != null) {
1387                     mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
1388                 }
1389             }, mListenerExecutor);
1390         }
1391 
1392         @Override
callSessionUpdateReceived(ImsCallProfile profile)1393         public void callSessionUpdateReceived(ImsCallProfile profile) {
1394             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1395                 if (mListener != null) {
1396                     mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
1397                 }
1398             }, mListenerExecutor);
1399         }
1400 
1401         /**
1402          * Notifies the result of conference extension.
1403          */
1404         @Override
callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile)1405         public void callSessionConferenceExtended(IImsCallSession newSession,
1406                 ImsCallProfile profile) {
1407             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1408                 if (mListener != null) {
1409                     mListener.callSessionConferenceExtended(ImsCallSession.this,
1410                             new ImsCallSession(newSession), profile);
1411                 }
1412             }, mListenerExecutor);
1413         }
1414 
1415         @Override
callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo)1416         public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
1417             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1418                 if (mListener != null) {
1419                     mListener.callSessionConferenceExtendFailed(
1420                             ImsCallSession.this, reasonInfo);
1421                 }
1422             }, mListenerExecutor);
1423         }
1424 
1425         @Override
callSessionConferenceExtendReceived(IImsCallSession newSession, ImsCallProfile profile)1426         public void callSessionConferenceExtendReceived(IImsCallSession newSession,
1427                 ImsCallProfile profile) {
1428             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1429                 if (mListener != null) {
1430                     mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
1431                             new ImsCallSession(newSession), profile);
1432                 }
1433             }, mListenerExecutor);
1434         }
1435 
1436         /**
1437          * Notifies the result of the participant invitation / removal to/from
1438          * the conference session.
1439          */
1440         @Override
callSessionInviteParticipantsRequestDelivered()1441         public void callSessionInviteParticipantsRequestDelivered() {
1442             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1443                 if (mListener != null) {
1444                     mListener.callSessionInviteParticipantsRequestDelivered(
1445                             ImsCallSession.this);
1446                 }
1447             }, mListenerExecutor);
1448         }
1449 
1450         @Override
callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)1451         public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
1452             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1453                 if (mListener != null) {
1454                     mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
1455                             reasonInfo);
1456                 }
1457             }, mListenerExecutor);
1458         }
1459 
1460         @Override
callSessionRemoveParticipantsRequestDelivered()1461         public void callSessionRemoveParticipantsRequestDelivered() {
1462             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1463                 if (mListener != null) {
1464                     mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
1465                 }
1466             }, mListenerExecutor);
1467         }
1468 
1469         @Override
callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)1470         public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
1471             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1472                 if (mListener != null) {
1473                     mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
1474                             reasonInfo);
1475                 }
1476             }, mListenerExecutor);
1477         }
1478 
1479         /**
1480          * Notifies the changes of the conference info. in the conference session.
1481          */
1482         @Override
callSessionConferenceStateUpdated(ImsConferenceState state)1483         public void callSessionConferenceStateUpdated(ImsConferenceState state) {
1484             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1485                 if (mListener != null) {
1486                     mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
1487                 }
1488             }, mListenerExecutor);
1489         }
1490 
1491         /**
1492          * Notifies the incoming USSD message.
1493          */
1494         @Override
callSessionUssdMessageReceived(int mode, String ussdMessage)1495         public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
1496             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1497                 if (mListener != null) {
1498                     mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
1499                             ussdMessage);
1500                 }
1501             }, mListenerExecutor);
1502         }
1503 
1504         /**
1505          * Notifies of a case where a {@link ImsCallSession} may
1506          * potentially handover from one radio technology to another.
1507          * @param srcNetworkType The source network type; one of the network type constants defined
1508          *                       in {@link android.telephony.TelephonyManager}.  For example
1509          *                      {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
1510          * @param targetNetworkType The target radio access technology; one of the network type
1511          *                          constants defined in {@link android.telephony.TelephonyManager}.
1512          *                          For example
1513          *                          {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
1514          */
1515         @Override
callSessionMayHandover(int srcNetworkType, int targetNetworkType)1516         public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
1517             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1518                 if (mListener != null) {
1519                     mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
1520                             targetNetworkType);
1521                 }
1522             }, mListenerExecutor);
1523         }
1524 
1525         /**
1526          * Notifies of handover information for this call
1527          */
1528         @Override
callSessionHandover(int srcNetworkType, int targetNetworkType, ImsReasonInfo reasonInfo)1529         public void callSessionHandover(int srcNetworkType, int targetNetworkType,
1530                 ImsReasonInfo reasonInfo) {
1531             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1532                 if (mListener != null) {
1533                     mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
1534                             targetNetworkType, reasonInfo);
1535                 }
1536             }, mListenerExecutor);
1537         }
1538 
1539         /**
1540          * Notifies of handover failure info for this call
1541          */
1542         @Override
callSessionHandoverFailed(int srcNetworkType, int targetNetworkType, ImsReasonInfo reasonInfo)1543         public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
1544                 ImsReasonInfo reasonInfo) {
1545             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1546                 if (mListener != null) {
1547                     mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
1548                             targetNetworkType, reasonInfo);
1549                 }
1550             }, mListenerExecutor);
1551         }
1552 
1553         /**
1554          * Notifies the TTY mode received from remote party.
1555          */
1556         @Override
callSessionTtyModeReceived(int mode)1557         public void callSessionTtyModeReceived(int mode) {
1558             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1559                 if (mListener != null) {
1560                     mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
1561                 }
1562             }, mListenerExecutor);
1563         }
1564 
1565         /**
1566          * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
1567          *
1568          * @param isMultiParty {@code true} if the session became multiparty, {@code false}
1569          *      otherwise.
1570          */
callSessionMultipartyStateChanged(boolean isMultiParty)1571         public void callSessionMultipartyStateChanged(boolean isMultiParty) {
1572             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1573                 if (mListener != null) {
1574                     mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
1575                             isMultiParty);
1576                 }
1577             }, mListenerExecutor);
1578         }
1579 
1580         @Override
callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo )1581         public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
1582             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1583                 if (mListener != null) {
1584                     mListener.callSessionSuppServiceReceived(ImsCallSession.this,
1585                             suppServiceInfo);
1586                 }
1587             }, mListenerExecutor);
1588         }
1589 
1590         /**
1591          * Received RTT modify request from remote party
1592          */
1593         @Override
callSessionRttModifyRequestReceived(ImsCallProfile callProfile)1594         public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
1595             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1596                 if (mListener != null) {
1597                     mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
1598                             callProfile);
1599                 }
1600             }, mListenerExecutor);
1601         }
1602 
1603         /**
1604          * Received response for RTT modify request
1605          */
1606         @Override
callSessionRttModifyResponseReceived(int status)1607         public void callSessionRttModifyResponseReceived(int status) {
1608             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1609                 if (mListener != null) {
1610                     mListener.callSessionRttModifyResponseReceived(status);
1611                 }
1612             }, mListenerExecutor);
1613         }
1614 
1615         /**
1616          * RTT Message received
1617          */
1618         @Override
callSessionRttMessageReceived(String rttMessage)1619         public void callSessionRttMessageReceived(String rttMessage) {
1620             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1621                 if (mListener != null) {
1622                     mListener.callSessionRttMessageReceived(rttMessage);
1623                 }
1624             }, mListenerExecutor);
1625         }
1626 
1627         /**
1628          * While in call, there has been a change in RTT audio indicator.
1629          */
1630         @Override
callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)1631         public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
1632             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1633                 if (mListener != null) {
1634                     mListener.callSessionRttAudioIndicatorChanged(profile);
1635                 }
1636             }, mListenerExecutor);
1637         }
1638 
1639         @Override
callSessionTransferred()1640         public void callSessionTransferred() {
1641             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1642                 if (mListener != null) {
1643                     mListener.callSessionTransferred(ImsCallSession.this);
1644                 }
1645             }, mListenerExecutor);
1646         }
1647 
1648         @Override
callSessionTransferFailed(@ullable ImsReasonInfo reasonInfo)1649         public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
1650             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1651                 if (mListener != null) {
1652                     mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
1653                 }
1654             }, mListenerExecutor);
1655         }
1656 
1657         /**
1658          * DTMF digit received.
1659          * @param dtmf The DTMF digit.
1660          */
1661         @Override
callSessionDtmfReceived(char dtmf)1662         public void callSessionDtmfReceived(char dtmf) {
1663             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1664                 if (mListener != null) {
1665                     mListener.callSessionDtmfReceived(dtmf);
1666                 }
1667             }, mListenerExecutor);
1668         }
1669 
1670         /**
1671          * Call quality updated
1672          */
1673         @Override
callQualityChanged(CallQuality callQuality)1674         public void callQualityChanged(CallQuality callQuality) {
1675             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1676                 if (mListener != null) {
1677                     mListener.callQualityChanged(callQuality);
1678                 }
1679             }, mListenerExecutor);
1680         }
1681 
1682         /**
1683          * RTP header extension data received.
1684          * @param extensions The header extension data.
1685          */
1686         @Override
callSessionRtpHeaderExtensionsReceived( @onNull List<RtpHeaderExtension> extensions)1687         public void callSessionRtpHeaderExtensionsReceived(
1688                 @NonNull List<RtpHeaderExtension> extensions) {
1689             TelephonyUtils.runWithCleanCallingIdentity(()-> {
1690                 if (mListener != null) {
1691                     mListener.callSessionRtpHeaderExtensionsReceived(
1692                             new ArraySet<RtpHeaderExtension>(extensions));
1693                 }
1694             }, mListenerExecutor);
1695         }
1696     }
1697 
1698     /**
1699      * Provides a string representation of the {@link ImsCallSession}.  Primarily intended for
1700      * use in log statements.
1701      *
1702      * @return String representation of session.
1703      */
1704     @Override
toString()1705     public String toString() {
1706         StringBuilder sb = new StringBuilder();
1707         sb.append("[ImsCallSession objId:");
1708         sb.append(System.identityHashCode(this));
1709         sb.append(" state:");
1710         sb.append(State.toString(getState()));
1711         sb.append(" callId:");
1712         sb.append(getCallId());
1713         sb.append("]");
1714         return sb.toString();
1715     }
1716 }
1717