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