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