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