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