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