Home | History | Annotate | Download | only in ims
      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;
     18 
     19 import com.android.internal.R;
     20 
     21 import java.util.ArrayList;
     22 import java.util.HashMap;
     23 import java.util.Iterator;
     24 import java.util.Map.Entry;
     25 import java.util.Set;
     26 
     27 import android.content.Context;
     28 import android.os.Bundle;
     29 import android.os.Message;
     30 import android.telephony.Rlog;
     31 
     32 import com.android.ims.internal.CallGroup;
     33 import com.android.ims.internal.CallGroupManager;
     34 import com.android.ims.internal.ICall;
     35 import com.android.ims.internal.ImsCallSession;
     36 import com.android.ims.internal.ImsStreamMediaSession;
     37 
     38 /**
     39  * Handles an IMS voice / video call over LTE. You can instantiate this class with
     40  * {@link ImsManager}.
     41  *
     42  * @hide
     43  */
     44 public class ImsCall implements ICall {
     45     public static final int CALL_STATE_ACTIVE_TO_HOLD = 1;
     46     public static final int CALL_STATE_HOLD_TO_ACTIVE = 2;
     47 
     48     // Mode of USSD message
     49     public static final int USSD_MODE_NOTIFY = 0;
     50     public static final int USSD_MODE_REQUEST = 1;
     51 
     52     private static final String TAG = "ImsCall";
     53     private static final boolean DBG = true;
     54 
     55     /**
     56      * Listener for events relating to an IMS call, such as when a call is being
     57      * recieved ("on ringing") or a call is outgoing ("on calling").
     58      * <p>Many of these events are also received by {@link ImsCallSession.Listener}.</p>
     59      */
     60     public static class Listener {
     61         /**
     62          * Called when a request is sent out to initiate a new call
     63          * and 1xx response is received from the network.
     64          * The default implementation calls {@link #onCallStateChanged}.
     65          *
     66          * @param call the call object that carries out the IMS call
     67          */
     68         public void onCallProgressing(ImsCall call) {
     69             onCallStateChanged(call);
     70         }
     71 
     72         /**
     73          * Called when the call is established.
     74          * The default implementation calls {@link #onCallStateChanged}.
     75          *
     76          * @param call the call object that carries out the IMS call
     77          */
     78         public void onCallStarted(ImsCall call) {
     79             onCallStateChanged(call);
     80         }
     81 
     82         /**
     83          * Called when the call setup is failed.
     84          * The default implementation calls {@link #onCallError}.
     85          *
     86          * @param call the call object that carries out the IMS call
     87          * @param reasonInfo detailed reason of the call setup failure
     88          */
     89         public void onCallStartFailed(ImsCall call, ImsReasonInfo reasonInfo) {
     90             onCallError(call, reasonInfo);
     91         }
     92 
     93         /**
     94          * Called when the call is terminated.
     95          * The default implementation calls {@link #onCallStateChanged}.
     96          *
     97          * @param call the call object that carries out the IMS call
     98          * @param reasonInfo detailed reason of the call termination
     99          */
    100         public void onCallTerminated(ImsCall call, ImsReasonInfo reasonInfo) {
    101             // Store the call termination reason
    102 
    103             onCallStateChanged(call);
    104         }
    105 
    106         /**
    107          * Called when the call is in hold.
    108          * The default implementation calls {@link #onCallStateChanged}.
    109          *
    110          * @param call the call object that carries out the IMS call
    111          */
    112         public void onCallHeld(ImsCall call) {
    113             onCallStateChanged(call);
    114         }
    115 
    116         /**
    117          * Called when the call hold is failed.
    118          * The default implementation calls {@link #onCallError}.
    119          *
    120          * @param call the call object that carries out the IMS call
    121          * @param reasonInfo detailed reason of the call hold failure
    122          */
    123         public void onCallHoldFailed(ImsCall call, ImsReasonInfo reasonInfo) {
    124             onCallError(call, reasonInfo);
    125         }
    126 
    127         /**
    128          * Called when the call hold is received from the remote user.
    129          * The default implementation calls {@link #onCallStateChanged}.
    130          *
    131          * @param call the call object that carries out the IMS call
    132          */
    133         public void onCallHoldReceived(ImsCall call) {
    134             onCallStateChanged(call);
    135         }
    136 
    137         /**
    138          * Called when the call is in call.
    139          * The default implementation calls {@link #onCallStateChanged}.
    140          *
    141          * @param call the call object that carries out the IMS call
    142          */
    143         public void onCallResumed(ImsCall call) {
    144             onCallStateChanged(call);
    145         }
    146 
    147         /**
    148          * Called when the call resume is failed.
    149          * The default implementation calls {@link #onCallError}.
    150          *
    151          * @param call the call object that carries out the IMS call
    152          * @param reasonInfo detailed reason of the call resume failure
    153          */
    154         public void onCallResumeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
    155             onCallError(call, reasonInfo);
    156         }
    157 
    158         /**
    159          * Called when the call resume is received from the remote user.
    160          * The default implementation calls {@link #onCallStateChanged}.
    161          *
    162          * @param call the call object that carries out the IMS call
    163          */
    164         public void onCallResumeReceived(ImsCall call) {
    165             onCallStateChanged(call);
    166         }
    167 
    168         /**
    169          * Called when the call is in call.
    170          * The default implementation calls {@link #onCallStateChanged}.
    171          *
    172          * @param call the call object that carries out the IMS call
    173          * @param newCall the call object that is merged with an active & hold call
    174          */
    175         public void onCallMerged(ImsCall call, ImsCall newCall) {
    176             onCallStateChanged(call, newCall);
    177         }
    178 
    179         /**
    180          * Called when the call merge is failed.
    181          * The default implementation calls {@link #onCallError}.
    182          *
    183          * @param call the call object that carries out the IMS call
    184          * @param reasonInfo detailed reason of the call merge failure
    185          */
    186         public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
    187             onCallError(call, reasonInfo);
    188         }
    189 
    190         /**
    191          * Called when the call is updated (except for hold/unhold).
    192          * The default implementation calls {@link #onCallStateChanged}.
    193          *
    194          * @param call the call object that carries out the IMS call
    195          */
    196         public void onCallUpdated(ImsCall call) {
    197             onCallStateChanged(call);
    198         }
    199 
    200         /**
    201          * Called when the call update is failed.
    202          * The default implementation calls {@link #onCallError}.
    203          *
    204          * @param call the call object that carries out the IMS call
    205          * @param reasonInfo detailed reason of the call update failure
    206          */
    207         public void onCallUpdateFailed(ImsCall call, ImsReasonInfo reasonInfo) {
    208             onCallError(call, reasonInfo);
    209         }
    210 
    211         /**
    212          * Called when the call update is received from the remote user.
    213          *
    214          * @param call the call object that carries out the IMS call
    215          */
    216         public void onCallUpdateReceived(ImsCall call) {
    217             // no-op
    218         }
    219 
    220         /**
    221          * Called when the call is extended to the conference call.
    222          * The default implementation calls {@link #onCallStateChanged}.
    223          *
    224          * @param call the call object that carries out the IMS call
    225          * @param newCall the call object that is extended to the conference from the active call
    226          */
    227         public void onCallConferenceExtended(ImsCall call, ImsCall newCall) {
    228             onCallStateChanged(call, newCall);
    229         }
    230 
    231         /**
    232          * Called when the conference extension is failed.
    233          * The default implementation calls {@link #onCallError}.
    234          *
    235          * @param call the call object that carries out the IMS call
    236          * @param reasonInfo detailed reason of the conference extension failure
    237          */
    238         public void onCallConferenceExtendFailed(ImsCall call,
    239                 ImsReasonInfo reasonInfo) {
    240             onCallError(call, reasonInfo);
    241         }
    242 
    243         /**
    244          * Called when the conference extension is received from the remote user.
    245          *
    246          * @param call the call object that carries out the IMS call
    247          * @param newCall the call object that is extended to the conference from the active call
    248          */
    249         public void onCallConferenceExtendReceived(ImsCall call, ImsCall newCall) {
    250             onCallStateChanged(call, newCall);
    251         }
    252 
    253         /**
    254          * Called when the invitation request of the participants is delivered to
    255          * the conference server.
    256          *
    257          * @param call the call object that carries out the IMS call
    258          */
    259         public void onCallInviteParticipantsRequestDelivered(ImsCall call) {
    260             // no-op
    261         }
    262 
    263         /**
    264          * Called when the invitation request of the participants is failed.
    265          *
    266          * @param call the call object that carries out the IMS call
    267          * @param reasonInfo detailed reason of the conference invitation failure
    268          */
    269         public void onCallInviteParticipantsRequestFailed(ImsCall call,
    270                 ImsReasonInfo reasonInfo) {
    271             // no-op
    272         }
    273 
    274         /**
    275          * Called when the removal request of the participants is delivered to
    276          * the conference server.
    277          *
    278          * @param call the call object that carries out the IMS call
    279          */
    280         public void onCallRemoveParticipantsRequestDelivered(ImsCall call) {
    281             // no-op
    282         }
    283 
    284         /**
    285          * Called when the removal request of the participants is failed.
    286          *
    287          * @param call the call object that carries out the IMS call
    288          * @param reasonInfo detailed reason of the conference removal failure
    289          */
    290         public void onCallRemoveParticipantsRequestFailed(ImsCall call,
    291                 ImsReasonInfo reasonInfo) {
    292             // no-op
    293         }
    294 
    295         /**
    296          * Called when the conference state is updated.
    297          *
    298          * @param call the call object that carries out the IMS call
    299          * @param state state of the participant who is participated in the conference call
    300          */
    301         public void onCallConferenceStateUpdated(ImsCall call, ImsConferenceState state) {
    302             // no-op
    303         }
    304 
    305         /**
    306          * Called when the USSD message is received from the network.
    307          *
    308          * @param mode mode of the USSD message (REQUEST / NOTIFY)
    309          * @param ussdMessage USSD message
    310          */
    311         public void onCallUssdMessageReceived(ImsCall call,
    312                 int mode, String ussdMessage) {
    313             // no-op
    314         }
    315 
    316         /**
    317          * Called when an error occurs. The default implementation is no op.
    318          * overridden. The default implementation is no op. Error events are
    319          * not re-directed to this callback and are handled in {@link #onCallError}.
    320          *
    321          * @param call the call object that carries out the IMS call
    322          * @param reasonInfo detailed reason of this error
    323          * @see ImsReasonInfo
    324          */
    325         public void onCallError(ImsCall call, ImsReasonInfo reasonInfo) {
    326             // no-op
    327         }
    328 
    329         /**
    330          * Called when an event occurs and the corresponding callback is not
    331          * overridden. The default implementation is no op. Error events are
    332          * not re-directed to this callback and are handled in {@link #onCallError}.
    333          *
    334          * @param call the call object that carries out the IMS call
    335          */
    336         public void onCallStateChanged(ImsCall call) {
    337             // no-op
    338         }
    339 
    340         /**
    341          * Called when an event occurs and the corresponding callback is not
    342          * overridden. The default implementation is no op. Error events are
    343          * not re-directed to this callback and are handled in {@link #onCallError}.
    344          *
    345          * @param call the call object that carries out the IMS call
    346          * @param newCall the call object that will be replaced by the previous call
    347          */
    348         public void onCallStateChanged(ImsCall call, ImsCall newCall) {
    349             // no-op
    350         }
    351 
    352         /**
    353          * Called when the call moves the hold state to the conversation state.
    354          * For example, when merging the active & hold call, the state of all the hold call
    355          * will be changed from hold state to conversation state.
    356          * This callback method can be invoked even though the application does not trigger
    357          * any operations.
    358          *
    359          * @param call the call object that carries out the IMS call
    360          * @param state the detailed state of call state changes;
    361          *      Refer to CALL_STATE_* in {@link ImsCall}
    362          */
    363         public void onCallStateChanged(ImsCall call, int state) {
    364             // no-op
    365         }
    366     }
    367 
    368 
    369 
    370     // List of update operation for IMS call control
    371     private static final int UPDATE_NONE = 0;
    372     private static final int UPDATE_HOLD = 1;
    373     private static final int UPDATE_HOLD_MERGE = 2;
    374     private static final int UPDATE_RESUME = 3;
    375     private static final int UPDATE_MERGE = 4;
    376     private static final int UPDATE_EXTEND_TO_CONFERENCE = 5;
    377     private static final int UPDATE_UNSPECIFIED = 6;
    378 
    379     // For synchronization of private variables
    380     private Object mLockObj = new Object();
    381     private Context mContext;
    382 
    383     // true if the call is established & in the conversation state
    384     private boolean mInCall = false;
    385     // true if the call is on hold
    386     // If it is triggered by the local, mute the call. Otherwise, play local hold tone
    387     // or network generated media.
    388     private boolean mHold = false;
    389     // true if the call is on mute
    390     private boolean mMute = false;
    391     // It contains the exclusive call update request. Refer to UPDATE_*.
    392     private int mUpdateRequest = UPDATE_NONE;
    393 
    394     private ImsCall.Listener mListener = null;
    395     // It is for managing the multiple calls
    396     // when the multiparty call is extended to the conference.
    397     private CallGroup mCallGroup = null;
    398 
    399     // Wrapper call session to interworking the IMS service (server).
    400     private ImsCallSession mSession = null;
    401     // Call profile of the current session.
    402     // It can be changed at anytime when the call is updated.
    403     private ImsCallProfile mCallProfile = null;
    404     // Call profile to be updated after the application's action (accept/reject)
    405     // to the call update. After the application's action (accept/reject) is done,
    406     // it will be set to null.
    407     private ImsCallProfile mProposedCallProfile = null;
    408     private ImsReasonInfo mLastReasonInfo = null;
    409 
    410     // Media session to control media (audio/video) operations for an IMS call
    411     private ImsStreamMediaSession mMediaSession = null;
    412 
    413     /**
    414      * Create an IMS call object.
    415      *
    416      * @param context the context for accessing system services
    417      * @param profile the call profile to make/take a call
    418      */
    419     public ImsCall(Context context, ImsCallProfile profile) {
    420         mContext = context;
    421         mCallProfile = profile;
    422     }
    423 
    424     /**
    425      * Closes this object. This object is not usable after being closed.
    426      */
    427     @Override
    428     public void close() {
    429         synchronized(mLockObj) {
    430             destroyCallGroup();
    431 
    432             if (mSession != null) {
    433                 mSession.close();
    434                 mSession = null;
    435             }
    436 
    437             mCallProfile = null;
    438             mProposedCallProfile = null;
    439             mLastReasonInfo = null;
    440             mMediaSession = null;
    441         }
    442     }
    443 
    444     /**
    445      * Checks if the call has a same remote user identity or not.
    446      *
    447      * @param userId the remote user identity
    448      * @return true if the remote user identity is equal; otherwise, false
    449      */
    450     @Override
    451     public boolean checkIfRemoteUserIsSame(String userId) {
    452         if (userId == null) {
    453             return false;
    454         }
    455 
    456         return userId.equals(mCallProfile.getCallExtra(ImsCallProfile.EXTRA_REMOTE_URI, ""));
    457     }
    458 
    459     /**
    460      * Checks if the call is equal or not.
    461      *
    462      * @param call the call to be compared
    463      * @return true if the call is equal; otherwise, false
    464      */
    465     @Override
    466     public boolean equalsTo(ICall call) {
    467         if (call == null) {
    468             return false;
    469         }
    470 
    471         if (call instanceof ImsCall) {
    472             return this.equals((ImsCall)call);
    473         }
    474 
    475         return false;
    476     }
    477 
    478     /**
    479      * Gets the negotiated (local & remote) call profile.
    480      *
    481      * @return a {@link ImsCallProfile} object that has the negotiated call profile
    482      */
    483     public ImsCallProfile getCallProfile() {
    484         synchronized(mLockObj) {
    485             return mCallProfile;
    486         }
    487     }
    488 
    489     /**
    490      * Gets the local call profile (local capabilities).
    491      *
    492      * @return a {@link ImsCallProfile} object that has the local call profile
    493      */
    494     public ImsCallProfile getLocalCallProfile() throws ImsException {
    495         synchronized(mLockObj) {
    496             if (mSession == null) {
    497                 throw new ImsException("No call session",
    498                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
    499             }
    500 
    501             try {
    502                 return mSession.getLocalCallProfile();
    503             } catch (Throwable t) {
    504                 loge("getLocalCallProfile :: ", t);
    505                 throw new ImsException("getLocalCallProfile()", t, 0);
    506             }
    507         }
    508     }
    509 
    510     /**
    511      * Gets the call profile proposed by the local/remote user.
    512      *
    513      * @return a {@link ImsCallProfile} object that has the proposed call profile
    514      */
    515     public ImsCallProfile getProposedCallProfile() {
    516         synchronized(mLockObj) {
    517             if (!isInCall()) {
    518                 return null;
    519             }
    520 
    521             return mProposedCallProfile;
    522         }
    523     }
    524 
    525     /**
    526      * Gets the state of the {@link ImsCallSession} that carries this call.
    527      * The value returned must be one of the states in {@link ImsCallSession#State}.
    528      *
    529      * @return the session state
    530      */
    531     public int getState() {
    532         synchronized(mLockObj) {
    533             if (mSession == null) {
    534                 return ImsCallSession.State.IDLE;
    535             }
    536 
    537             return mSession.getState();
    538         }
    539     }
    540 
    541     /**
    542      * Gets the {@link ImsCallSession} that carries this call.
    543      *
    544      * @return the session object that carries this call
    545      * @hide
    546      */
    547     public ImsCallSession getCallSession() {
    548         synchronized(mLockObj) {
    549             return mSession;
    550         }
    551     }
    552 
    553     /**
    554      * Gets the {@link ImsStreamMediaSession} that handles the media operation of this call.
    555      * Almost interface APIs are for the VT (Video Telephony).
    556      *
    557      * @return the media session object that handles the media operation of this call
    558      * @hide
    559      */
    560     public ImsStreamMediaSession getMediaSession() {
    561         synchronized(mLockObj) {
    562             return mMediaSession;
    563         }
    564     }
    565 
    566     /**
    567      * Gets the specified property of this call.
    568      *
    569      * @param name key to get the extra call information defined in {@link ImsCallProfile}
    570      * @return the extra call information as string
    571      */
    572     public String getCallExtra(String name) throws ImsException {
    573         // Lookup the cache
    574 
    575         synchronized(mLockObj) {
    576             // If not found, try to get the property from the remote
    577             if (mSession == null) {
    578                 throw new ImsException("No call session",
    579                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
    580             }
    581 
    582             try {
    583                 return mSession.getProperty(name);
    584             } catch (Throwable t) {
    585                 loge("getCallExtra :: ", t);
    586                 throw new ImsException("getCallExtra()", t, 0);
    587             }
    588         }
    589     }
    590 
    591     /**
    592      * Gets the last reason information when the call is not established, cancelled or terminated.
    593      *
    594      * @return the last reason information
    595      */
    596     public ImsReasonInfo getLastReasonInfo() {
    597         synchronized(mLockObj) {
    598             return mLastReasonInfo;
    599         }
    600     }
    601 
    602     /**
    603      * Checks if the call has a pending update operation.
    604      *
    605      * @return true if the call has a pending update operation
    606      */
    607     public boolean hasPendingUpdate() {
    608         synchronized(mLockObj) {
    609             return (mUpdateRequest != UPDATE_NONE);
    610         }
    611     }
    612 
    613     /**
    614      * Checks if the call is established.
    615      *
    616      * @return true if the call is established
    617      */
    618     public boolean isInCall() {
    619         synchronized(mLockObj) {
    620             return mInCall;
    621         }
    622     }
    623 
    624     /**
    625      * Checks if the call is muted.
    626      *
    627      * @return true if the call is muted
    628      */
    629     public boolean isMuted() {
    630         synchronized(mLockObj) {
    631             return mMute;
    632         }
    633     }
    634 
    635     /**
    636      * Checks if the call is on hold.
    637      *
    638      * @return true if the call is on hold
    639      */
    640     public boolean isOnHold() {
    641         synchronized(mLockObj) {
    642             return mHold;
    643         }
    644     }
    645 
    646     /**
    647      * Sets the listener to listen to the IMS call events.
    648      * The method calls {@link #setListener setListener(listener, false)}.
    649      *
    650      * @param listener to listen to the IMS call events of this object; null to remove listener
    651      * @see #setListener(Listener, boolean)
    652      */
    653     public void setListener(ImsCall.Listener listener) {
    654         setListener(listener, false);
    655     }
    656 
    657     /**
    658      * Sets the listener to listen to the IMS call events.
    659      * A {@link ImsCall} can only hold one listener at a time. Subsequent calls
    660      * to this method override the previous listener.
    661      *
    662      * @param listener to listen to the IMS call events of this object; null to remove listener
    663      * @param callbackImmediately set to true if the caller wants to be called
    664      *        back immediately on the current state
    665      */
    666     public void setListener(ImsCall.Listener listener, boolean callbackImmediately) {
    667         boolean inCall;
    668         boolean onHold;
    669         int state;
    670         ImsReasonInfo lastReasonInfo;
    671 
    672         synchronized(mLockObj) {
    673             mListener = listener;
    674 
    675             if ((listener == null) || !callbackImmediately) {
    676                 return;
    677             }
    678 
    679             inCall = mInCall;
    680             onHold = mHold;
    681             state = getState();
    682             lastReasonInfo = mLastReasonInfo;
    683         }
    684 
    685         try {
    686             if (lastReasonInfo != null) {
    687                 listener.onCallError(this, lastReasonInfo);
    688             } else if (inCall) {
    689                 if (onHold) {
    690                     listener.onCallHeld(this);
    691                 } else {
    692                     listener.onCallStarted(this);
    693                 }
    694             } else {
    695                 switch (state) {
    696                     case ImsCallSession.State.ESTABLISHING:
    697                         listener.onCallProgressing(this);
    698                         break;
    699                     case ImsCallSession.State.TERMINATED:
    700                         listener.onCallTerminated(this, lastReasonInfo);
    701                         break;
    702                     default:
    703                         // Ignore it. There is no action in the other state.
    704                         break;
    705                 }
    706             }
    707         } catch (Throwable t) {
    708             loge("setListener()", t);
    709         }
    710     }
    711 
    712     /**
    713      * Mutes or unmutes the mic for the active call.
    714      *
    715      * @param muted true if the call is muted, false otherwise
    716      */
    717     public void setMute(boolean muted) throws ImsException {
    718         synchronized(mLockObj) {
    719             if (mMute != muted) {
    720                 mMute = muted;
    721 
    722                 try {
    723                     mSession.setMute(muted);
    724                 } catch (Throwable t) {
    725                     loge("setMute :: ", t);
    726                     throwImsException(t, 0);
    727                 }
    728             }
    729         }
    730     }
    731 
    732      /**
    733       * Attaches an incoming call to this call object.
    734       *
    735       * @param session the session that receives the incoming call
    736       * @throws ImsException if the IMS service fails to attach this object to the session
    737       */
    738      public void attachSession(ImsCallSession session) throws ImsException {
    739          if (DBG) {
    740              log("attachSession :: session=" + session);
    741          }
    742 
    743          synchronized(mLockObj) {
    744              mSession = session;
    745 
    746              try {
    747                  mSession.setListener(createCallSessionListener());
    748              } catch (Throwable t) {
    749                  loge("attachSession :: ", t);
    750                  throwImsException(t, 0);
    751              }
    752          }
    753      }
    754 
    755     /**
    756      * Initiates an IMS call with the call profile which is provided
    757      * when creating a {@link ImsCall}.
    758      *
    759      * @param session the {@link ImsCallSession} for carrying out the call
    760      * @param callee callee information to initiate an IMS call
    761      * @throws ImsException if the IMS service fails to initiate the call
    762      */
    763     public void start(ImsCallSession session, String callee)
    764             throws ImsException {
    765         if (DBG) {
    766             log("start(1) :: session=" + session + ", callee=" + callee);
    767         }
    768 
    769         synchronized(mLockObj) {
    770             mSession = session;
    771 
    772             try {
    773                 session.setListener(createCallSessionListener());
    774                 session.start(callee, mCallProfile);
    775             } catch (Throwable t) {
    776                 loge("start(1) :: ", t);
    777                 throw new ImsException("start(1)", t, 0);
    778             }
    779         }
    780     }
    781 
    782     /**
    783      * Initiates an IMS conferenca call with the call profile which is provided
    784      * when creating a {@link ImsCall}.
    785      *
    786      * @param session the {@link ImsCallSession} for carrying out the call
    787      * @param participants participant list to initiate an IMS conference call
    788      * @throws ImsException if the IMS service fails to initiate the call
    789      */
    790     public void start(ImsCallSession session, String[] participants)
    791             throws ImsException {
    792         if (DBG) {
    793             log("start(n) :: session=" + session + ", callee=" + participants);
    794         }
    795 
    796         synchronized(mLockObj) {
    797             mSession = session;
    798 
    799             try {
    800                 session.setListener(createCallSessionListener());
    801                 session.start(participants, mCallProfile);
    802             } catch (Throwable t) {
    803                 loge("start(n) :: ", t);
    804                 throw new ImsException("start(n)", t, 0);
    805             }
    806         }
    807     }
    808 
    809     /**
    810      * Accepts a call.
    811      *
    812      * @see Listener#onCallStarted
    813      *
    814      * @param callType The call type the user agreed to for accepting the call.
    815      * @throws ImsException if the IMS service fails to accept the call
    816      */
    817     public void accept(int callType) throws ImsException {
    818         if (DBG) {
    819             log("accept :: session=" + mSession);
    820         }
    821 
    822         accept(callType, new ImsStreamMediaProfile());
    823     }
    824 
    825     /**
    826      * Accepts a call.
    827      *
    828      * @param callType call type to be answered in {@link ImsCallProfile}
    829      * @param profile a media profile to be answered (audio/audio & video, direction, ...)
    830      * @see Listener#onCallStarted
    831      * @throws ImsException if the IMS service fails to accept the call
    832      */
    833     public void accept(int callType, ImsStreamMediaProfile profile) throws ImsException {
    834         if (DBG) {
    835             log("accept :: session=" + mSession
    836                     + ", callType=" + callType + ", profile=" + profile);
    837         }
    838 
    839         synchronized(mLockObj) {
    840             if (mSession == null) {
    841                 throw new ImsException("No call to answer",
    842                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
    843             }
    844 
    845             try {
    846                 mSession.accept(callType, profile);
    847             } catch (Throwable t) {
    848                 loge("accept :: ", t);
    849                 throw new ImsException("accept()", t, 0);
    850             }
    851 
    852             if (mInCall && (mProposedCallProfile != null)) {
    853                 if (DBG) {
    854                     log("accept :: call profile will be updated");
    855                 }
    856 
    857                 mCallProfile = mProposedCallProfile;
    858                 mProposedCallProfile = null;
    859             }
    860 
    861             // Other call update received
    862             if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
    863                 mUpdateRequest = UPDATE_NONE;
    864             }
    865         }
    866     }
    867 
    868     /**
    869      * Rejects a call.
    870      *
    871      * @param reason reason code to reject an incoming call
    872      * @see Listener#onCallStartFailed
    873      * @throws ImsException if the IMS service fails to accept the call
    874      */
    875     public void reject(int reason) throws ImsException {
    876         if (DBG) {
    877             log("reject :: session=" + mSession + ", reason=" + reason);
    878         }
    879 
    880         synchronized(mLockObj) {
    881             if (mSession != null) {
    882                 mSession.reject(reason);
    883             }
    884 
    885             if (mInCall && (mProposedCallProfile != null)) {
    886                 if (DBG) {
    887                     log("reject :: call profile is not updated; destroy it...");
    888                 }
    889 
    890                 mProposedCallProfile = null;
    891             }
    892 
    893             // Other call update received
    894             if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
    895                 mUpdateRequest = UPDATE_NONE;
    896             }
    897         }
    898     }
    899 
    900     /**
    901      * Terminates an IMS call.
    902      *
    903      * @param reason reason code to terminate a call
    904      * @throws ImsException if the IMS service fails to terminate the call
    905      */
    906     public void terminate(int reason) throws ImsException {
    907         if (DBG) {
    908             log("terminate :: session=" + mSession + ", reason=" + reason);
    909         }
    910 
    911         synchronized(mLockObj) {
    912             mHold = false;
    913             mInCall = false;
    914             CallGroup callGroup = getCallGroup();
    915 
    916             if (mSession != null) {
    917                 if (callGroup != null && !callGroup.isOwner(ImsCall.this)) {
    918                     log("terminate owner of the call group");
    919                     ImsCall owner = (ImsCall) callGroup.getOwner();
    920                     if (owner != null) {
    921                         owner.terminate(reason);
    922                         return;
    923                     }
    924                 }
    925                 mSession.terminate(reason);
    926             }
    927         }
    928     }
    929 
    930 
    931     /**
    932      * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is called.
    933      *
    934      * @see Listener#onCallHeld, Listener#onCallHoldFailed
    935      * @throws ImsException if the IMS service fails to hold the call
    936      */
    937     public void hold() throws ImsException {
    938         if (DBG) {
    939             log("hold :: session=" + mSession);
    940         }
    941 
    942         // perform operation on owner before doing any local checks: local
    943         // call may not have its status updated
    944         synchronized (mLockObj) {
    945             CallGroup callGroup = mCallGroup;
    946             if (callGroup != null && !callGroup.isOwner(ImsCall.this)) {
    947                 log("hold owner of the call group");
    948                 ImsCall owner = (ImsCall) callGroup.getOwner();
    949                 if (owner != null) {
    950                     owner.hold();
    951                     return;
    952                 }
    953             }
    954         }
    955 
    956         if (isOnHold()) {
    957             if (DBG) {
    958                 log("hold :: call is already on hold");
    959             }
    960             return;
    961         }
    962 
    963         synchronized(mLockObj) {
    964             if (mUpdateRequest != UPDATE_NONE) {
    965                 loge("hold :: update is in progress; request=" + mUpdateRequest);
    966                 throw new ImsException("Call update is in progress",
    967                         ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
    968             }
    969 
    970             if (mSession == null) {
    971                 loge("hold :: ");
    972                 throw new ImsException("No call session",
    973                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
    974             }
    975 
    976             mSession.hold(createHoldMediaProfile());
    977             // FIXME: update the state on the callback?
    978             mHold = true;
    979             mUpdateRequest = UPDATE_HOLD;
    980         }
    981     }
    982 
    983     /**
    984      * Continues a call that's on hold. When succeeds, {@link Listener#onCallResumed} is called.
    985      *
    986      * @see Listener#onCallResumed, Listener#onCallResumeFailed
    987      * @throws ImsException if the IMS service fails to resume the call
    988      */
    989     public void resume() throws ImsException {
    990         if (DBG) {
    991             log("resume :: session=" + mSession);
    992         }
    993 
    994         // perform operation on owner before doing any local checks: local
    995         // call may not have its status updated
    996         synchronized (mLockObj) {
    997             CallGroup callGroup = mCallGroup;
    998             if (callGroup != null && !callGroup.isOwner(ImsCall.this)) {
    999                 log("resume owner of the call group");
   1000                 ImsCall owner = (ImsCall) callGroup.getOwner();
   1001                 if (owner != null) {
   1002                     owner.resume();
   1003                     return;
   1004                 }
   1005             }
   1006         }
   1007 
   1008         if (!isOnHold()) {
   1009             if (DBG) {
   1010                 log("resume :: call is in conversation");
   1011             }
   1012             return;
   1013         }
   1014 
   1015         synchronized(mLockObj) {
   1016             if (mUpdateRequest != UPDATE_NONE) {
   1017                 loge("resume :: update is in progress; request=" + mUpdateRequest);
   1018                 throw new ImsException("Call update is in progress",
   1019                         ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1020             }
   1021 
   1022             if (mSession == null) {
   1023                 loge("resume :: ");
   1024                 throw new ImsException("No call session",
   1025                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1026             }
   1027 
   1028             mSession.resume(createResumeMediaProfile());
   1029             // FIXME: update the state on the callback?
   1030             mHold = false;
   1031             mUpdateRequest = UPDATE_RESUME;
   1032         }
   1033     }
   1034 
   1035     /**
   1036      * Merges the active & hold call.
   1037      *
   1038      * @see Listener#onCallMerged, Listener#onCallMergeFailed
   1039      * @throws ImsException if the IMS service fails to merge the call
   1040      */
   1041     public void merge() throws ImsException {
   1042         if (DBG) {
   1043             log("merge :: session=" + mSession);
   1044         }
   1045 
   1046         synchronized(mLockObj) {
   1047             if (mUpdateRequest != UPDATE_NONE) {
   1048                 loge("merge :: update is in progress; request=" + mUpdateRequest);
   1049                 throw new ImsException("Call update is in progress",
   1050                         ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1051             }
   1052 
   1053             if (mSession == null) {
   1054                 loge("merge :: ");
   1055                 throw new ImsException("No call session",
   1056                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1057             }
   1058 
   1059             // if skipHoldBeforeMerge = true, IMS service implementation will
   1060             // merge without explicitly holding the call.
   1061             if (mHold || (mContext.getResources().getBoolean(
   1062                     com.android.internal.R.bool.skipHoldBeforeMerge))) {
   1063                 mSession.merge();
   1064                 mUpdateRequest = UPDATE_MERGE;
   1065             } else {
   1066                 mSession.hold(createHoldMediaProfile());
   1067                 // FIXME: ?
   1068                 mHold = true;
   1069                 mUpdateRequest = UPDATE_HOLD_MERGE;
   1070             }
   1071         }
   1072     }
   1073 
   1074     /**
   1075      * Merges the active & hold call.
   1076      *
   1077      * @param bgCall the background (holding) call
   1078      * @see Listener#onCallMerged, Listener#onCallMergeFailed
   1079      * @throws ImsException if the IMS service fails to merge the call
   1080      */
   1081     public void merge(ImsCall bgCall) throws ImsException {
   1082         if (DBG) {
   1083             log("merge(1) :: session=" + mSession);
   1084         }
   1085 
   1086         if (bgCall == null) {
   1087             throw new ImsException("No background call",
   1088                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
   1089         }
   1090 
   1091         synchronized(mLockObj) {
   1092             createCallGroup(bgCall);
   1093         }
   1094 
   1095         merge();
   1096     }
   1097 
   1098     /**
   1099      * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
   1100      */
   1101     public void update(int callType, ImsStreamMediaProfile mediaProfile) throws ImsException {
   1102         if (DBG) {
   1103             log("update :: session=" + mSession);
   1104         }
   1105 
   1106         if (isOnHold()) {
   1107             if (DBG) {
   1108                 log("update :: call is on hold");
   1109             }
   1110             throw new ImsException("Not in a call to update call",
   1111                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1112         }
   1113 
   1114         synchronized(mLockObj) {
   1115             if (mUpdateRequest != UPDATE_NONE) {
   1116                 if (DBG) {
   1117                     log("update :: update is in progress; request=" + mUpdateRequest);
   1118                 }
   1119                 throw new ImsException("Call update is in progress",
   1120                         ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1121             }
   1122 
   1123             if (mSession == null) {
   1124                 loge("update :: ");
   1125                 throw new ImsException("No call session",
   1126                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1127             }
   1128 
   1129             mSession.update(callType, mediaProfile);
   1130             mUpdateRequest = UPDATE_UNSPECIFIED;
   1131         }
   1132     }
   1133 
   1134     /**
   1135      * Extends this call (1-to-1 call) to the conference call
   1136      * inviting the specified participants to.
   1137      *
   1138      */
   1139     public void extendToConference(String[] participants) throws ImsException {
   1140         if (DBG) {
   1141             log("extendToConference :: session=" + mSession);
   1142         }
   1143 
   1144         if (isOnHold()) {
   1145             if (DBG) {
   1146                 log("extendToConference :: call is on hold");
   1147             }
   1148             throw new ImsException("Not in a call to extend a call to conference",
   1149                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1150         }
   1151 
   1152         synchronized(mLockObj) {
   1153             if (mUpdateRequest != UPDATE_NONE) {
   1154                 if (DBG) {
   1155                     log("extendToConference :: update is in progress; request=" + mUpdateRequest);
   1156                 }
   1157                 throw new ImsException("Call update is in progress",
   1158                         ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
   1159             }
   1160 
   1161             if (mSession == null) {
   1162                 loge("extendToConference :: ");
   1163                 throw new ImsException("No call session",
   1164                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1165             }
   1166 
   1167             mSession.extendToConference(participants);
   1168             mUpdateRequest = UPDATE_EXTEND_TO_CONFERENCE;
   1169         }
   1170     }
   1171 
   1172     /**
   1173      * Requests the conference server to invite an additional participants to the conference.
   1174      *
   1175      */
   1176     public void inviteParticipants(String[] participants) throws ImsException {
   1177         if (DBG) {
   1178             log("inviteParticipants :: session=" + mSession);
   1179         }
   1180 
   1181         synchronized(mLockObj) {
   1182             if (mSession == null) {
   1183                 loge("inviteParticipants :: ");
   1184                 throw new ImsException("No call session",
   1185                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1186             }
   1187 
   1188             mSession.inviteParticipants(participants);
   1189         }
   1190     }
   1191 
   1192     /**
   1193      * Requests the conference server to remove the specified participants from the conference.
   1194      *
   1195      */
   1196     public void removeParticipants(String[] participants) throws ImsException {
   1197         if (DBG) {
   1198             log("removeParticipants :: session=" + mSession);
   1199         }
   1200 
   1201         synchronized(mLockObj) {
   1202             if (mSession == null) {
   1203                 loge("removeParticipants :: ");
   1204                 throw new ImsException("No call session",
   1205                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1206             }
   1207 
   1208             mSession.removeParticipants(participants);
   1209         }
   1210     }
   1211 
   1212 
   1213     /**
   1214      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
   1215      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
   1216      * and event flash to 16. Currently, event flash is not supported.
   1217      *
   1218      * @param char that represents the DTMF digit to send.
   1219      */
   1220     public void sendDtmf(char c) {
   1221         sendDtmf(c, null);
   1222     }
   1223 
   1224     /**
   1225      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
   1226      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
   1227      * and event flash to 16. Currently, event flash is not supported.
   1228      *
   1229      * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
   1230      * @param result the result message to send when done.
   1231      */
   1232     public void sendDtmf(char c, Message result) {
   1233         if (DBG) {
   1234             log("sendDtmf :: session=" + mSession + ", code=" + c);
   1235         }
   1236 
   1237         synchronized(mLockObj) {
   1238             if (mSession != null) {
   1239                 mSession.sendDtmf(c);
   1240             }
   1241         }
   1242 
   1243         if (result != null) {
   1244             result.sendToTarget();
   1245         }
   1246     }
   1247 
   1248     /**
   1249      * Sends an USSD message.
   1250      *
   1251      * @param ussdMessage USSD message to send
   1252      */
   1253     public void sendUssd(String ussdMessage) throws ImsException {
   1254         if (DBG) {
   1255             log("sendUssd :: session=" + mSession + ", ussdMessage=" + ussdMessage);
   1256         }
   1257 
   1258         synchronized(mLockObj) {
   1259             if (mSession == null) {
   1260                 loge("sendUssd :: ");
   1261                 throw new ImsException("No call session",
   1262                         ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
   1263             }
   1264 
   1265             mSession.sendUssd(ussdMessage);
   1266         }
   1267     }
   1268 
   1269     private void clear(ImsReasonInfo lastReasonInfo) {
   1270         mInCall = false;
   1271         mHold = false;
   1272         mUpdateRequest = UPDATE_NONE;
   1273         mLastReasonInfo = lastReasonInfo;
   1274         destroyCallGroup();
   1275     }
   1276 
   1277     private void createCallGroup(ImsCall neutralReferrer) {
   1278         CallGroup referrerCallGroup = neutralReferrer.getCallGroup();
   1279 
   1280         if (mCallGroup == null) {
   1281             if (referrerCallGroup == null) {
   1282                 mCallGroup = CallGroupManager.getInstance().createCallGroup(new ImsCallGroup());
   1283             } else {
   1284                 mCallGroup = referrerCallGroup;
   1285             }
   1286 
   1287             if (mCallGroup != null) {
   1288                 mCallGroup.setNeutralReferrer(neutralReferrer);
   1289             }
   1290         } else {
   1291             mCallGroup.setNeutralReferrer(neutralReferrer);
   1292 
   1293             if ((referrerCallGroup != null)
   1294                     && (mCallGroup != referrerCallGroup)) {
   1295                 loge("fatal :: call group is mismatched; call is corrupted...");
   1296             }
   1297         }
   1298     }
   1299 
   1300     private void updateCallGroup(ImsCall owner) {
   1301         if (mCallGroup == null) {
   1302             return;
   1303         }
   1304 
   1305         ImsCall neutralReferrer = (ImsCall)mCallGroup.getNeutralReferrer();
   1306 
   1307         if (owner == null) {
   1308             // Maintain the call group if the current call has been merged in the past.
   1309             if (!mCallGroup.hasReferrer()) {
   1310                 CallGroupManager.getInstance().destroyCallGroup(mCallGroup);
   1311                 mCallGroup = null;
   1312             }
   1313         } else {
   1314             mCallGroup.addReferrer(this);
   1315 
   1316             if (neutralReferrer != null) {
   1317                 if (neutralReferrer.getCallGroup() == null) {
   1318                     neutralReferrer.setCallGroup(mCallGroup);
   1319                     mCallGroup.addReferrer(neutralReferrer);
   1320                 }
   1321 
   1322                 neutralReferrer.enforceConversationMode();
   1323             }
   1324 
   1325             // Close the existing owner call if present
   1326             ImsCall exOwner = (ImsCall)mCallGroup.getOwner();
   1327 
   1328             mCallGroup.setOwner(owner);
   1329 
   1330             if (exOwner != null) {
   1331                 exOwner.close();
   1332             }
   1333         }
   1334     }
   1335 
   1336     private void destroyCallGroup() {
   1337         if (mCallGroup == null) {
   1338             return;
   1339         }
   1340 
   1341         mCallGroup.removeReferrer(this);
   1342 
   1343         if (!mCallGroup.hasReferrer()) {
   1344             CallGroupManager.getInstance().destroyCallGroup(mCallGroup);
   1345         }
   1346 
   1347         mCallGroup = null;
   1348     }
   1349 
   1350     private CallGroup getCallGroup() {
   1351         synchronized(mLockObj) {
   1352             return mCallGroup;
   1353         }
   1354     }
   1355 
   1356     private void setCallGroup(CallGroup callGroup) {
   1357         synchronized(mLockObj) {
   1358             mCallGroup = callGroup;
   1359         }
   1360     }
   1361 
   1362     /**
   1363      * Creates an IMS call session listener.
   1364      */
   1365     private ImsCallSession.Listener createCallSessionListener() {
   1366         return new ImsCallSessionListenerProxy();
   1367     }
   1368 
   1369     private ImsCall createNewCall(ImsCallSession session, ImsCallProfile profile) {
   1370         ImsCall call = new ImsCall(mContext, profile);
   1371 
   1372         try {
   1373             call.attachSession(session);
   1374         } catch (ImsException e) {
   1375             if (call != null) {
   1376                 call.close();
   1377                 call = null;
   1378             }
   1379         }
   1380 
   1381         // Do additional operations...
   1382 
   1383         return call;
   1384     }
   1385 
   1386     private ImsStreamMediaProfile createHoldMediaProfile() {
   1387         ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
   1388 
   1389         if (mCallProfile == null) {
   1390             return mediaProfile;
   1391         }
   1392 
   1393         mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
   1394         mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
   1395         mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND;
   1396 
   1397         if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
   1398             mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND;
   1399         }
   1400 
   1401         return mediaProfile;
   1402     }
   1403 
   1404     private ImsStreamMediaProfile createResumeMediaProfile() {
   1405         ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
   1406 
   1407         if (mCallProfile == null) {
   1408             return mediaProfile;
   1409         }
   1410 
   1411         mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
   1412         mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
   1413         mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
   1414 
   1415         if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
   1416             mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
   1417         }
   1418 
   1419         return mediaProfile;
   1420     }
   1421 
   1422     private void enforceConversationMode() {
   1423         if (mInCall) {
   1424             mHold = false;
   1425             mUpdateRequest = UPDATE_NONE;
   1426         }
   1427     }
   1428 
   1429     private void mergeInternal() {
   1430         if (DBG) {
   1431             log("mergeInternal :: session=" + mSession);
   1432         }
   1433 
   1434         mSession.merge();
   1435         mUpdateRequest = UPDATE_MERGE;
   1436     }
   1437 
   1438     private void notifyCallStateChanged() {
   1439         int state = 0;
   1440 
   1441         if (mInCall && (mUpdateRequest == UPDATE_HOLD_MERGE)) {
   1442             state = CALL_STATE_ACTIVE_TO_HOLD;
   1443             mHold = true;
   1444         } else if (mInCall && ((mUpdateRequest == UPDATE_MERGE)
   1445                 || (mUpdateRequest == UPDATE_EXTEND_TO_CONFERENCE))) {
   1446             state = CALL_STATE_HOLD_TO_ACTIVE;
   1447             mHold = false;
   1448             mMute = false;
   1449         }
   1450 
   1451         if (state != 0) {
   1452             if (mListener != null) {
   1453                 try {
   1454                     mListener.onCallStateChanged(ImsCall.this, state);
   1455                 } catch (Throwable t) {
   1456                     loge("notifyCallStateChanged :: ", t);
   1457                 }
   1458             }
   1459         }
   1460     }
   1461 
   1462     private void notifyConferenceSessionTerminated(ImsReasonInfo reasonInfo) {
   1463         ImsCall.Listener listener;
   1464 
   1465         if (mCallGroup.isOwner(ImsCall.this)) {
   1466             log("Group Owner! Size of referrers list = " + mCallGroup.getReferrers().size());
   1467             while (mCallGroup.hasReferrer()) {
   1468                 ImsCall call = (ImsCall) mCallGroup.getReferrers().get(0);
   1469                 log("onCallTerminated to be called for the call:: " + call);
   1470 
   1471                 if (call == null) {
   1472                     continue;
   1473                 }
   1474 
   1475                 listener = call.mListener;
   1476                 call.clear(reasonInfo);
   1477 
   1478                 if (listener != null) {
   1479                     try {
   1480                         listener.onCallTerminated(call, reasonInfo);
   1481                     } catch (Throwable t) {
   1482                         loge("notifyConferenceSessionTerminated :: ", t);
   1483                     }
   1484                 }
   1485             }
   1486         } else if (!mCallGroup.isReferrer(ImsCall.this)) {
   1487             return;
   1488         }
   1489 
   1490         listener = mListener;
   1491         clear(reasonInfo);
   1492 
   1493         if (listener != null) {
   1494             try {
   1495                 listener.onCallTerminated(this, reasonInfo);
   1496             } catch (Throwable t) {
   1497                 loge("notifyConferenceSessionTerminated :: ", t);
   1498             }
   1499         }
   1500     }
   1501 
   1502     private void notifyConferenceStateUpdatedThroughGroupOwner(int update) {
   1503         ImsCall.Listener listener;
   1504 
   1505         if (mCallGroup.isOwner(ImsCall.this)) {
   1506             log("Group Owner! Size of referrers list = " + mCallGroup.getReferrers().size());
   1507             for (ICall icall : mCallGroup.getReferrers()) {
   1508                 ImsCall call = (ImsCall) icall;
   1509                 log("notifyConferenceStateUpdatedThroughGroupOwner to be called for the call:: " + call);
   1510 
   1511                 if (call == null) {
   1512                     continue;
   1513                 }
   1514 
   1515                 listener = call.mListener;
   1516 
   1517                 if (listener != null) {
   1518                     try {
   1519                         switch (update) {
   1520                             case UPDATE_HOLD:
   1521                                 listener.onCallHeld(call);
   1522                                 break;
   1523                             case UPDATE_RESUME:
   1524                                 listener.onCallResumed(call);
   1525                                 break;
   1526                             default:
   1527                                 loge("notifyConferenceStateUpdatedThroughGroupOwner :: not handled update "
   1528                                         + update);
   1529                         }
   1530                     } catch (Throwable t) {
   1531                         loge("notifyConferenceStateUpdatedThroughGroupOwner :: ", t);
   1532                     }
   1533                 }
   1534             }
   1535         }
   1536     }
   1537 
   1538     private void notifyConferenceStateUpdated(ImsConferenceState state) {
   1539         Set<Entry<String, Bundle>> paticipants = state.mParticipants.entrySet();
   1540 
   1541         if (paticipants == null) {
   1542             return;
   1543         }
   1544 
   1545         Iterator<Entry<String, Bundle>> iterator = paticipants.iterator();
   1546 
   1547         while (iterator.hasNext()) {
   1548             Entry<String, Bundle> entry = iterator.next();
   1549 
   1550             String key = entry.getKey();
   1551             Bundle confInfo = entry.getValue();
   1552             String status = confInfo.getString(ImsConferenceState.STATUS);
   1553             String user = confInfo.getString(ImsConferenceState.USER);
   1554             String endpoint = confInfo.getString(ImsConferenceState.ENDPOINT);
   1555 
   1556             if (DBG) {
   1557                 log("notifyConferenceStateUpdated :: key=" + key +
   1558                         ", status=" + status +
   1559                         ", user=" + user +
   1560                         ", endpoint=" + endpoint);
   1561             }
   1562 
   1563             if ((mCallGroup != null) && (!mCallGroup.isOwner(ImsCall.this))) {
   1564                 continue;
   1565             }
   1566 
   1567             ImsCall referrer = (ImsCall)mCallGroup.getReferrer(endpoint);
   1568 
   1569             if (referrer == null) {
   1570                 continue;
   1571             }
   1572 
   1573             if (referrer.mListener == null) {
   1574                 continue;
   1575             }
   1576 
   1577             try {
   1578                 if (status.equals(ImsConferenceState.STATUS_ALERTING)) {
   1579                     referrer.mListener.onCallProgressing(referrer);
   1580                 }
   1581                 else if (status.equals(ImsConferenceState.STATUS_CONNECT_FAIL)) {
   1582                     referrer.mListener.onCallStartFailed(referrer, new ImsReasonInfo());
   1583                 }
   1584                 else if (status.equals(ImsConferenceState.STATUS_ON_HOLD)) {
   1585                     referrer.mListener.onCallHoldReceived(referrer);
   1586                 }
   1587                 else if (status.equals(ImsConferenceState.STATUS_CONNECTED)) {
   1588                     referrer.mListener.onCallStarted(referrer);
   1589                 }
   1590                 else if (status.equals(ImsConferenceState.STATUS_DISCONNECTED)) {
   1591                     referrer.clear(new ImsReasonInfo());
   1592                     referrer.mListener.onCallTerminated(referrer, referrer.mLastReasonInfo);
   1593                 }
   1594             } catch (Throwable t) {
   1595                 loge("notifyConferenceStateUpdated :: ", t);
   1596             }
   1597         }
   1598     }
   1599 
   1600     private void notifyError(int reason, int statusCode, String message) {
   1601     }
   1602 
   1603     private void throwImsException(Throwable t, int code) throws ImsException {
   1604         if (t instanceof ImsException) {
   1605             throw (ImsException) t;
   1606         } else {
   1607             throw new ImsException(String.valueOf(code), t, code);
   1608         }
   1609     }
   1610 
   1611     private void log(String s) {
   1612         Rlog.d(TAG, s);
   1613     }
   1614 
   1615     private void loge(String s) {
   1616         Rlog.e(TAG, s);
   1617     }
   1618 
   1619     private void loge(String s, Throwable t) {
   1620         Rlog.e(TAG, s, t);
   1621     }
   1622 
   1623     private class ImsCallSessionListenerProxy extends ImsCallSession.Listener {
   1624         @Override
   1625         public void callSessionProgressing(ImsCallSession session,
   1626                 ImsStreamMediaProfile profile) {
   1627             if (DBG) {
   1628                 log("callSessionProgressing :: session=" + session + ", profile=" + profile);
   1629             }
   1630 
   1631             ImsCall.Listener listener;
   1632 
   1633             synchronized(ImsCall.this) {
   1634                 listener = mListener;
   1635                 mCallProfile.mMediaProfile.copyFrom(profile);
   1636             }
   1637 
   1638             if (listener != null) {
   1639                 try {
   1640                     listener.onCallProgressing(ImsCall.this);
   1641                 } catch (Throwable t) {
   1642                     loge("callSessionProgressing :: ", t);
   1643                 }
   1644             }
   1645         }
   1646 
   1647         @Override
   1648         public void callSessionStarted(ImsCallSession session,
   1649                 ImsCallProfile profile) {
   1650             if (DBG) {
   1651                 log("callSessionStarted :: session=" + session + ", profile=" + profile);
   1652             }
   1653 
   1654             ImsCall.Listener listener;
   1655 
   1656             synchronized(ImsCall.this) {
   1657                 listener = mListener;
   1658                 mCallProfile = profile;
   1659             }
   1660 
   1661             if (listener != null) {
   1662                 try {
   1663                     listener.onCallStarted(ImsCall.this);
   1664                 } catch (Throwable t) {
   1665                     loge("callSessionStarted :: ", t);
   1666                 }
   1667             }
   1668         }
   1669 
   1670         @Override
   1671         public void callSessionStartFailed(ImsCallSession session,
   1672                 ImsReasonInfo reasonInfo) {
   1673             if (DBG) {
   1674                 log("callSessionStartFailed :: session=" + session +
   1675                         ", reasonInfo=" + reasonInfo);
   1676             }
   1677 
   1678             ImsCall.Listener listener;
   1679 
   1680             synchronized(ImsCall.this) {
   1681                 listener = mListener;
   1682                 mLastReasonInfo = reasonInfo;
   1683             }
   1684 
   1685             if (listener != null) {
   1686                 try {
   1687                     listener.onCallStartFailed(ImsCall.this, reasonInfo);
   1688                 } catch (Throwable t) {
   1689                     loge("callSessionStarted :: ", t);
   1690                 }
   1691             }
   1692         }
   1693 
   1694         @Override
   1695         public void callSessionTerminated(ImsCallSession session,
   1696                 ImsReasonInfo reasonInfo) {
   1697             if (DBG) {
   1698                 log("callSessionTerminated :: session=" + session +
   1699                         ", reasonInfo=" + reasonInfo);
   1700             }
   1701 
   1702             ImsCall.Listener listener = null;
   1703 
   1704             synchronized(ImsCall.this) {
   1705                 if (mCallGroup != null) {
   1706                     notifyConferenceSessionTerminated(reasonInfo);
   1707                 } else {
   1708                     listener = mListener;
   1709                     clear(reasonInfo);
   1710                 }
   1711             }
   1712 
   1713             if (listener != null) {
   1714                 try {
   1715                     listener.onCallTerminated(ImsCall.this, reasonInfo);
   1716                 } catch (Throwable t) {
   1717                     loge("callSessionTerminated :: ", t);
   1718                 }
   1719             }
   1720         }
   1721 
   1722         @Override
   1723         public void callSessionHeld(ImsCallSession session,
   1724                 ImsCallProfile profile) {
   1725             if (DBG) {
   1726                 log("callSessionHeld :: session=" + session + ", profile=" + profile);
   1727             }
   1728 
   1729             ImsCall.Listener listener;
   1730 
   1731             synchronized(ImsCall.this) {
   1732                 mCallProfile = profile;
   1733 
   1734                 if (mUpdateRequest == UPDATE_HOLD_MERGE) {
   1735                     mergeInternal();
   1736                     return;
   1737                 }
   1738 
   1739                 mUpdateRequest = UPDATE_NONE;
   1740                 listener = mListener;
   1741             }
   1742 
   1743             if (listener != null) {
   1744                 try {
   1745                     listener.onCallHeld(ImsCall.this);
   1746                 } catch (Throwable t) {
   1747                     loge("callSessionHeld :: ", t);
   1748                 }
   1749             }
   1750 
   1751             if (mCallGroup != null) {
   1752                 notifyConferenceStateUpdatedThroughGroupOwner(UPDATE_HOLD);
   1753             }
   1754         }
   1755 
   1756         @Override
   1757         public void callSessionHoldFailed(ImsCallSession session,
   1758                 ImsReasonInfo reasonInfo) {
   1759             if (DBG) {
   1760                 log("callSessionHoldFailed :: session=" + session +
   1761                         ", reasonInfo=" + reasonInfo);
   1762             }
   1763 
   1764             boolean isHoldForMerge = false;
   1765             ImsCall.Listener listener;
   1766 
   1767             synchronized(ImsCall.this) {
   1768                 if (mUpdateRequest == UPDATE_HOLD_MERGE) {
   1769                     isHoldForMerge = true;
   1770                 }
   1771 
   1772                 mUpdateRequest = UPDATE_NONE;
   1773                 listener = mListener;
   1774             }
   1775 
   1776             if (isHoldForMerge) {
   1777                 callSessionMergeFailed(session, reasonInfo);
   1778                 return;
   1779             }
   1780 
   1781             if (listener != null) {
   1782                 try {
   1783                     listener.onCallHoldFailed(ImsCall.this, reasonInfo);
   1784                 } catch (Throwable t) {
   1785                     loge("callSessionHoldFailed :: ", t);
   1786                 }
   1787             }
   1788         }
   1789 
   1790         @Override
   1791         public void callSessionHoldReceived(ImsCallSession session,
   1792                 ImsCallProfile profile) {
   1793             if (DBG) {
   1794                 log("callSessionHoldReceived :: session=" + session + ", profile=" + profile);
   1795             }
   1796 
   1797             ImsCall.Listener listener;
   1798 
   1799             synchronized(ImsCall.this) {
   1800                 listener = mListener;
   1801                 mCallProfile = profile;
   1802             }
   1803 
   1804             if (listener != null) {
   1805                 try {
   1806                     listener.onCallHoldReceived(ImsCall.this);
   1807                 } catch (Throwable t) {
   1808                     loge("callSessionHoldReceived :: ", t);
   1809                 }
   1810             }
   1811         }
   1812 
   1813         @Override
   1814         public void callSessionResumed(ImsCallSession session,
   1815                 ImsCallProfile profile) {
   1816             if (DBG) {
   1817                 log("callSessionResumed :: session=" + session + ", profile=" + profile);
   1818             }
   1819 
   1820             ImsCall.Listener listener;
   1821 
   1822             synchronized(ImsCall.this) {
   1823                 listener = mListener;
   1824                 mCallProfile = profile;
   1825                 mUpdateRequest = UPDATE_NONE;
   1826             }
   1827 
   1828             if (listener != null) {
   1829                 try {
   1830                     listener.onCallResumed(ImsCall.this);
   1831                 } catch (Throwable t) {
   1832                     loge("callSessionResumed :: ", t);
   1833                 }
   1834             }
   1835 
   1836             if (mCallGroup != null) {
   1837                 notifyConferenceStateUpdatedThroughGroupOwner(UPDATE_RESUME);
   1838             }
   1839         }
   1840 
   1841         @Override
   1842         public void callSessionResumeFailed(ImsCallSession session,
   1843                 ImsReasonInfo reasonInfo) {
   1844             if (DBG) {
   1845                 log("callSessionResumeFailed :: session=" + session +
   1846                         ", reasonInfo=" + reasonInfo);
   1847             }
   1848 
   1849             ImsCall.Listener listener;
   1850 
   1851             synchronized(ImsCall.this) {
   1852                 listener = mListener;
   1853                 mUpdateRequest = UPDATE_NONE;
   1854             }
   1855 
   1856             if (listener != null) {
   1857                 try {
   1858                     listener.onCallResumeFailed(ImsCall.this, reasonInfo);
   1859                 } catch (Throwable t) {
   1860                     loge("callSessionResumeFailed :: ", t);
   1861                 }
   1862             }
   1863         }
   1864 
   1865         @Override
   1866         public void callSessionResumeReceived(ImsCallSession session,
   1867                 ImsCallProfile profile) {
   1868             if (DBG) {
   1869                 log("callSessionResumeReceived :: session=" + session +
   1870                         ", profile=" + profile);
   1871             }
   1872 
   1873             ImsCall.Listener listener;
   1874 
   1875             synchronized(ImsCall.this) {
   1876                 listener = mListener;
   1877                 mCallProfile = profile;
   1878             }
   1879 
   1880             if (listener != null) {
   1881                 try {
   1882                     listener.onCallResumeReceived(ImsCall.this);
   1883                 } catch (Throwable t) {
   1884                     loge("callSessionResumeReceived :: ", t);
   1885                 }
   1886             }
   1887         }
   1888 
   1889         @Override
   1890         public void callSessionMerged(ImsCallSession session,
   1891                 ImsCallSession newSession, ImsCallProfile profile) {
   1892             if (DBG) {
   1893                 log("callSessionMerged :: session=" + session
   1894                         + ", newSession=" + newSession + ", profile=" + profile);
   1895             }
   1896 
   1897             ImsCall newCall = createNewCall(newSession, profile);
   1898 
   1899             if (newCall == null) {
   1900                 callSessionMergeFailed(session, new ImsReasonInfo());
   1901                 return;
   1902             }
   1903 
   1904             ImsCall.Listener listener;
   1905 
   1906             synchronized(ImsCall.this) {
   1907                 listener = mListener;
   1908                 updateCallGroup(newCall);
   1909                 newCall.setListener(mListener);
   1910                 newCall.setCallGroup(mCallGroup);
   1911                 mUpdateRequest = UPDATE_NONE;
   1912             }
   1913 
   1914             if (listener != null) {
   1915                 try {
   1916                     listener.onCallMerged(ImsCall.this, newCall);
   1917                 } catch (Throwable t) {
   1918                     loge("callSessionMerged :: ", t);
   1919                 }
   1920             }
   1921         }
   1922 
   1923         @Override
   1924         public void callSessionMergeFailed(ImsCallSession session,
   1925                 ImsReasonInfo reasonInfo) {
   1926             if (DBG) {
   1927                 log("callSessionMergeFailed :: session=" + session +
   1928                         ", reasonInfo=" + reasonInfo);
   1929             }
   1930 
   1931             ImsCall.Listener listener;
   1932 
   1933             synchronized(ImsCall.this) {
   1934                 listener = mListener;
   1935                 updateCallGroup(null);
   1936                 mUpdateRequest = UPDATE_NONE;
   1937             }
   1938 
   1939             if (listener != null) {
   1940                 try {
   1941                     listener.onCallMergeFailed(ImsCall.this, reasonInfo);
   1942                 } catch (Throwable t) {
   1943                     loge("callSessionMergeFailed :: ", t);
   1944                 }
   1945             }
   1946         }
   1947 
   1948         @Override
   1949         public void callSessionUpdated(ImsCallSession session,
   1950                 ImsCallProfile profile) {
   1951             if (DBG) {
   1952                 log("callSessionUpdated :: session=" + session + ", profile=" + profile);
   1953             }
   1954 
   1955             ImsCall.Listener listener;
   1956 
   1957             synchronized(ImsCall.this) {
   1958                 listener = mListener;
   1959                 mCallProfile = profile;
   1960                 mUpdateRequest = UPDATE_NONE;
   1961             }
   1962 
   1963             if (listener != null) {
   1964                 try {
   1965                     listener.onCallUpdated(ImsCall.this);
   1966                 } catch (Throwable t) {
   1967                     loge("callSessionUpdated :: ", t);
   1968                 }
   1969             }
   1970         }
   1971 
   1972         @Override
   1973         public void callSessionUpdateFailed(ImsCallSession session,
   1974                 ImsReasonInfo reasonInfo) {
   1975             if (DBG) {
   1976                 log("callSessionUpdateFailed :: session=" + session +
   1977                         ", reasonInfo=" + reasonInfo);
   1978             }
   1979 
   1980             ImsCall.Listener listener;
   1981 
   1982             synchronized(ImsCall.this) {
   1983                 listener = mListener;
   1984                 mUpdateRequest = UPDATE_NONE;
   1985             }
   1986 
   1987             if (listener != null) {
   1988                 try {
   1989                     listener.onCallUpdateFailed(ImsCall.this, reasonInfo);
   1990                 } catch (Throwable t) {
   1991                     loge("callSessionUpdateFailed :: ", t);
   1992                 }
   1993             }
   1994         }
   1995 
   1996         @Override
   1997         public void callSessionUpdateReceived(ImsCallSession session,
   1998                 ImsCallProfile profile) {
   1999             if (DBG) {
   2000                 log("callSessionUpdateReceived :: session=" + session +
   2001                         ", profile=" + profile);
   2002             }
   2003 
   2004             ImsCall.Listener listener;
   2005 
   2006             synchronized(ImsCall.this) {
   2007                 listener = mListener;
   2008                 mProposedCallProfile = profile;
   2009                 mUpdateRequest = UPDATE_UNSPECIFIED;
   2010             }
   2011 
   2012             if (listener != null) {
   2013                 try {
   2014                     listener.onCallUpdateReceived(ImsCall.this);
   2015                 } catch (Throwable t) {
   2016                     loge("callSessionUpdateReceived :: ", t);
   2017                 }
   2018             }
   2019         }
   2020 
   2021         @Override
   2022         public void callSessionConferenceExtended(ImsCallSession session,
   2023                 ImsCallSession newSession, ImsCallProfile profile) {
   2024             if (DBG) {
   2025                 log("callSessionConferenceExtended :: session=" + session
   2026                         + ", newSession=" + newSession + ", profile=" + profile);
   2027             }
   2028 
   2029             ImsCall newCall = createNewCall(newSession, profile);
   2030 
   2031             if (newCall == null) {
   2032                 callSessionConferenceExtendFailed(session, new ImsReasonInfo());
   2033                 return;
   2034             }
   2035 
   2036             ImsCall.Listener listener;
   2037 
   2038             synchronized(ImsCall.this) {
   2039                 listener = mListener;
   2040                 mUpdateRequest = UPDATE_NONE;
   2041             }
   2042 
   2043             if (listener != null) {
   2044                 try {
   2045                     listener.onCallConferenceExtended(ImsCall.this, newCall);
   2046                 } catch (Throwable t) {
   2047                     loge("callSessionConferenceExtended :: ", t);
   2048                 }
   2049             }
   2050         }
   2051 
   2052         @Override
   2053         public void callSessionConferenceExtendFailed(ImsCallSession session,
   2054                 ImsReasonInfo reasonInfo) {
   2055             if (DBG) {
   2056                 log("callSessionConferenceExtendFailed :: session=" + session +
   2057                         ", reasonInfo=" + reasonInfo);
   2058             }
   2059 
   2060             ImsCall.Listener listener;
   2061 
   2062             synchronized(ImsCall.this) {
   2063                 listener = mListener;
   2064                 mUpdateRequest = UPDATE_NONE;
   2065             }
   2066 
   2067             if (listener != null) {
   2068                 try {
   2069                     listener.onCallConferenceExtendFailed(ImsCall.this, reasonInfo);
   2070                 } catch (Throwable t) {
   2071                     loge("callSessionConferenceExtendFailed :: ", t);
   2072                 }
   2073             }
   2074         }
   2075 
   2076         @Override
   2077         public void callSessionConferenceExtendReceived(ImsCallSession session,
   2078                 ImsCallSession newSession, ImsCallProfile profile) {
   2079             if (DBG) {
   2080                 log("callSessionConferenceExtendReceived :: session=" + session
   2081                         + ", newSession=" + newSession + ", profile=" + profile);
   2082             }
   2083 
   2084             ImsCall newCall = createNewCall(newSession, profile);
   2085 
   2086             if (newCall == null) {
   2087                 // Should all the calls be terminated...???
   2088                 return;
   2089             }
   2090 
   2091             ImsCall.Listener listener;
   2092 
   2093             synchronized(ImsCall.this) {
   2094                 listener = mListener;
   2095             }
   2096 
   2097             if (listener != null) {
   2098                 try {
   2099                     listener.onCallConferenceExtendReceived(ImsCall.this, newCall);
   2100                 } catch (Throwable t) {
   2101                     loge("callSessionConferenceExtendReceived :: ", t);
   2102                 }
   2103             }
   2104         }
   2105 
   2106         @Override
   2107         public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
   2108             if (DBG) {
   2109                 log("callSessionInviteParticipantsRequestDelivered :: session=" + session);
   2110             }
   2111 
   2112             ImsCall.Listener listener;
   2113 
   2114             synchronized(ImsCall.this) {
   2115                 listener = mListener;
   2116             }
   2117 
   2118             if (listener != null) {
   2119                 try {
   2120                     listener.onCallInviteParticipantsRequestDelivered(ImsCall.this);
   2121                 } catch (Throwable t) {
   2122                     loge("callSessionInviteParticipantsRequestDelivered :: ", t);
   2123                 }
   2124             }
   2125         }
   2126 
   2127         @Override
   2128         public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
   2129                 ImsReasonInfo reasonInfo) {
   2130             if (DBG) {
   2131                 log("callSessionInviteParticipantsRequestFailed :: session=" + session
   2132                         + ", reasonInfo=" + reasonInfo);
   2133             }
   2134 
   2135             ImsCall.Listener listener;
   2136 
   2137             synchronized(ImsCall.this) {
   2138                 listener = mListener;
   2139             }
   2140 
   2141             if (listener != null) {
   2142                 try {
   2143                     listener.onCallInviteParticipantsRequestFailed(ImsCall.this, reasonInfo);
   2144                 } catch (Throwable t) {
   2145                     loge("callSessionInviteParticipantsRequestFailed :: ", t);
   2146                 }
   2147             }
   2148         }
   2149 
   2150         @Override
   2151         public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
   2152             if (DBG) {
   2153                 log("callSessionRemoveParticipantsRequestDelivered :: session=" + session);
   2154             }
   2155 
   2156             ImsCall.Listener listener;
   2157 
   2158             synchronized(ImsCall.this) {
   2159                 listener = mListener;
   2160             }
   2161 
   2162             if (listener != null) {
   2163                 try {
   2164                     listener.onCallRemoveParticipantsRequestDelivered(ImsCall.this);
   2165                 } catch (Throwable t) {
   2166                     loge("callSessionRemoveParticipantsRequestDelivered :: ", t);
   2167                 }
   2168             }
   2169         }
   2170 
   2171         @Override
   2172         public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
   2173                 ImsReasonInfo reasonInfo) {
   2174             if (DBG) {
   2175                 log("callSessionRemoveParticipantsRequestFailed :: session=" + session
   2176                         + ", reasonInfo=" + reasonInfo);
   2177             }
   2178 
   2179             ImsCall.Listener listener;
   2180 
   2181             synchronized(ImsCall.this) {
   2182                 listener = mListener;
   2183             }
   2184 
   2185             if (listener != null) {
   2186                 try {
   2187                     listener.onCallRemoveParticipantsRequestFailed(ImsCall.this, reasonInfo);
   2188                 } catch (Throwable t) {
   2189                     loge("callSessionRemoveParticipantsRequestFailed :: ", t);
   2190                 }
   2191             }
   2192         }
   2193 
   2194         @Override
   2195         public void callSessionConferenceStateUpdated(ImsCallSession session,
   2196                 ImsConferenceState state) {
   2197             if (DBG) {
   2198                 log("callSessionConferenceStateUpdated :: session=" + session
   2199                         + ", state=" + state);
   2200             }
   2201 
   2202             ImsCall.Listener listener;
   2203 
   2204             synchronized(ImsCall.this) {
   2205                 notifyConferenceStateUpdated(state);
   2206                 listener = mListener;
   2207             }
   2208 
   2209             if (listener != null) {
   2210                 try {
   2211                     listener.onCallConferenceStateUpdated(ImsCall.this, state);
   2212                 } catch (Throwable t) {
   2213                     loge("callSessionConferenceStateUpdated :: ", t);
   2214                 }
   2215             }
   2216         }
   2217 
   2218         @Override
   2219         public void callSessionUssdMessageReceived(ImsCallSession session,
   2220                 int mode, String ussdMessage) {
   2221             if (DBG) {
   2222                 log("callSessionUssdMessageReceived :: session=" + session
   2223                         + ", mode=" + mode + ", ussdMessage=" + ussdMessage);
   2224             }
   2225 
   2226             ImsCall.Listener listener;
   2227 
   2228             synchronized(ImsCall.this) {
   2229                 listener = mListener;
   2230             }
   2231 
   2232             if (listener != null) {
   2233                 try {
   2234                     listener.onCallUssdMessageReceived(ImsCall.this, mode, ussdMessage);
   2235                 } catch (Throwable t) {
   2236                     loge("callSessionUssdMessageReceived :: ", t);
   2237                 }
   2238             }
   2239         }
   2240     }
   2241 }
   2242