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