ImsPhoneCallTracker.java revision d913f668eaaa68716beccc4084417dfc56b7be89
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.internal.telephony.imsphone;
18
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.SharedPreferences;
25import android.content.pm.PackageManager;
26import android.net.ConnectivityManager;
27import android.net.NetworkInfo;
28import android.net.NetworkStats;
29import android.net.Uri;
30import android.os.AsyncResult;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.PersistableBundle;
35import android.os.Registrant;
36import android.os.RegistrantList;
37import android.os.RemoteException;
38import android.os.SystemClock;
39import android.os.SystemProperties;
40import android.preference.PreferenceManager;
41import android.provider.Settings;
42import android.telecom.ConferenceParticipant;
43import android.telecom.TelecomManager;
44import android.telecom.VideoProfile;
45import android.telephony.CarrierConfigManager;
46import android.telephony.DisconnectCause;
47import android.telephony.PhoneNumberUtils;
48import android.telephony.PreciseDisconnectCause;
49import android.telephony.Rlog;
50import android.telephony.ServiceState;
51import android.telephony.SubscriptionManager;
52import android.telephony.TelephonyManager;
53import android.telephony.ims.ImsServiceProxy;
54import android.telephony.ims.feature.ImsFeature;
55import android.text.TextUtils;
56import android.util.ArrayMap;
57import android.util.Log;
58import android.util.Pair;
59import android.util.SparseIntArray;
60
61import com.android.ims.ImsCall;
62import com.android.ims.ImsCallProfile;
63import com.android.ims.ImsConfig;
64import com.android.ims.ImsConfigListener;
65import com.android.ims.ImsConnectionStateListener;
66import com.android.ims.ImsEcbm;
67import com.android.ims.ImsException;
68import com.android.ims.ImsManager;
69import com.android.ims.ImsMultiEndpoint;
70import com.android.ims.ImsReasonInfo;
71import com.android.ims.ImsServiceClass;
72import com.android.ims.ImsSuppServiceNotification;
73import com.android.ims.ImsUtInterface;
74import com.android.ims.internal.IImsVideoCallProvider;
75import com.android.ims.internal.ImsVideoCallProviderWrapper;
76import com.android.ims.internal.VideoPauseTracker;
77import com.android.internal.annotations.VisibleForTesting;
78import com.android.internal.os.SomeArgs;
79import com.android.internal.telephony.Call;
80import com.android.internal.telephony.CallStateException;
81import com.android.internal.telephony.CallTracker;
82import com.android.internal.telephony.CommandException;
83import com.android.internal.telephony.CommandsInterface;
84import com.android.internal.telephony.Connection;
85import com.android.internal.telephony.Phone;
86import com.android.internal.telephony.PhoneConstants;
87import com.android.internal.telephony.TelephonyProperties;
88import com.android.internal.telephony.dataconnection.DataEnabledSettings;
89import com.android.internal.telephony.gsm.SuppServiceNotification;
90import com.android.internal.telephony.metrics.TelephonyMetrics;
91import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
92import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
93import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
94import com.android.server.net.NetworkStatsService;
95
96import java.io.FileDescriptor;
97import java.io.PrintWriter;
98import java.util.ArrayList;
99import java.util.HashMap;
100import java.util.List;
101import java.util.Map;
102import java.util.concurrent.atomic.AtomicInteger;
103import java.util.regex.Pattern;
104
105/**
106 * {@hide}
107 */
108public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
109    static final String LOG_TAG = "ImsPhoneCallTracker";
110    static final String VERBOSE_STATE_TAG = "IPCTState";
111
112    public interface PhoneStateListener {
113        void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
114    }
115
116    private static final boolean DBG = true;
117
118    // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
119    // calls.  This is helpful for debugging.  It is also possible to enable this at runtime by
120    // setting the IPCTState log tag to VERBOSE.
121    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
122    private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING ||
123            Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE);
124
125    //Indices map to ImsConfig.FeatureConstants
126    private boolean[] mImsFeatureEnabled = {false, false, false, false, false, false};
127    private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi",
128            "UTLTE", "UTWiFi"};
129
130    private TelephonyMetrics mMetrics;
131
132    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
133        @Override
134        public void onReceive(Context context, Intent intent) {
135            if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
136                if (DBG) log("onReceive : incoming call intent");
137
138                if (mImsManager == null) return;
139
140                if (mServiceId < 0) return;
141
142                try {
143                    // Network initiated USSD will be treated by mImsUssdListener
144                    boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
145                    if (isUssd) {
146                        if (DBG) log("onReceive : USSD");
147                        mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
148                        if (mUssdSession != null) {
149                            mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
150                        }
151                        return;
152                    }
153
154                    boolean isUnknown = intent.getBooleanExtra(ImsManager.EXTRA_IS_UNKNOWN_CALL,
155                            false);
156                    if (DBG) {
157                        log("onReceive : isUnknown = " + isUnknown +
158                                " fg = " + mForegroundCall.getState() +
159                                " bg = " + mBackgroundCall.getState());
160                    }
161
162                    // Normal MT/Unknown call
163                    ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
164                    ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
165                            ImsPhoneCallTracker.this,
166                            (isUnknown? mForegroundCall: mRingingCall), isUnknown);
167
168                    // If there is an active call.
169                    if (mForegroundCall.hasConnections()) {
170                        ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
171                        if (activeCall != null && imsCall != null) {
172                            // activeCall could be null if the foreground call is in a disconnected
173                            // state.  If either of the calls is null there is no need to check if
174                            // one will be disconnected on answer.
175                            boolean answeringWillDisconnect =
176                                    shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
177                            conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
178                        }
179                    }
180                    conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
181                    addConnection(conn);
182
183                    setVideoCallProvider(conn, imsCall);
184
185                    TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
186                            imsCall.getSession());
187
188                    if (isUnknown) {
189                        mPhone.notifyUnknownConnection(conn);
190                    } else {
191                        if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
192                                (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
193                            conn.update(imsCall, ImsPhoneCall.State.WAITING);
194                        }
195
196                        mPhone.notifyNewRingingConnection(conn);
197                        mPhone.notifyIncomingRing();
198                    }
199
200                    updatePhoneState();
201                    mPhone.notifyPreciseCallStateChanged();
202                } catch (ImsException e) {
203                    loge("onReceive : exception " + e);
204                } catch (RemoteException e) {
205                }
206            } else if (intent.getAction().equals(
207                    CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
208                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
209                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
210                if (subId == mPhone.getSubId()) {
211                    cacheCarrierConfiguration(subId);
212                    log("onReceive : Updating mAllowEmergencyVideoCalls = " +
213                            mAllowEmergencyVideoCalls);
214                }
215            } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
216                mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
217                        TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
218            }
219        }
220    };
221
222    //***** Constants
223
224    static final int MAX_CONNECTIONS = 7;
225    static final int MAX_CONNECTIONS_PER_CALL = 5;
226
227    private static final int EVENT_HANGUP_PENDINGMO = 18;
228    private static final int EVENT_RESUME_BACKGROUND = 19;
229    private static final int EVENT_DIAL_PENDINGMO = 20;
230    private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21;
231    private static final int EVENT_VT_DATA_USAGE_UPDATE = 22;
232    private static final int EVENT_DATA_ENABLED_CHANGED = 23;
233    private static final int EVENT_GET_IMS_SERVICE = 24;
234    private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
235    private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
236
237    private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
238
239    // Initial condition for ims connection retry.
240    private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
241    // Ceiling bitshift amount for service query timeout, calculated as:
242    // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
243    // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
244    private static final int CEILING_SERVICE_RETRY_COUNT = 6;
245
246    private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms
247
248    //***** Instance Variables
249    private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>();
250    private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
251    private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
252
253    public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING);
254    public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this,
255            ImsPhoneCall.CONTEXT_FOREGROUND);
256    public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this,
257            ImsPhoneCall.CONTEXT_BACKGROUND);
258    public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER);
259
260    // Hold aggregated video call data usage for each video call since boot.
261    // The ImsCall's call id is the key of the map.
262    private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>();
263
264    private volatile NetworkStats mVtDataUsageSnapshot = null;
265    private volatile NetworkStats mVtDataUsageUidSnapshot = null;
266
267    private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL);
268
269    private ImsPhoneConnection mPendingMO;
270    private int mClirMode = CommandsInterface.CLIR_DEFAULT;
271    private Object mSyncHold = new Object();
272
273    private ImsCall mUssdSession = null;
274    private Message mPendingUssd = null;
275
276    ImsPhone mPhone;
277
278    private boolean mDesiredMute = false;    // false = mute off
279    private boolean mOnHoldToneStarted = false;
280    private int mOnHoldToneId = -1;
281
282    private PhoneConstants.State mState = PhoneConstants.State.IDLE;
283
284    private int mImsServiceRetryCount;
285    private ImsManager mImsManager;
286    private int mServiceId = -1;
287
288    private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
289
290    private boolean mIsInEmergencyCall = false;
291    private boolean mIsDataEnabled = false;
292
293    private int pendingCallClirMode;
294    private int mPendingCallVideoState;
295    private Bundle mPendingIntentExtras;
296    private boolean pendingCallInEcm = false;
297    private boolean mSwitchingFgAndBgCalls = false;
298    private ImsCall mCallExpectedToResume = null;
299    private boolean mAllowEmergencyVideoCalls = false;
300    private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
301
302    /**
303     * Listeners to changes in the phone state.  Intended for use by other interested IMS components
304     * without the need to register a full blown {@link android.telephony.PhoneStateListener}.
305     */
306    private List<PhoneStateListener> mPhoneStateListeners = new ArrayList<>();
307
308    /**
309     * Carrier configuration option which determines if video calls which have been downgraded to an
310     * audio call should be treated as if they are still video calls.
311     */
312    private boolean mTreatDowngradedVideoCallsAsVideoCalls = false;
313
314    /**
315     * Carrier configuration option which determines if an ongoing video call over wifi should be
316     * dropped when an audio call is answered.
317     */
318    private boolean mDropVideoCallWhenAnsweringAudioCall = false;
319
320    /**
321     * Carrier configuration option which determines whether adding a call during a video call
322     * should be allowed.
323     */
324    private boolean mAllowAddCallDuringVideoCall = true;
325
326    /**
327     * Carrier configuration option which determines whether to notify the connection if a handover
328     * to wifi fails.
329     */
330    private boolean mNotifyVtHandoverToWifiFail = false;
331
332    /**
333     * Carrier configuration option which determines whether the carrier supports downgrading a
334     * TX/RX/TX-RX video call directly to an audio-only call.
335     */
336    private boolean mSupportDowngradeVtToAudio = false;
337
338    /**
339     * Stores the mapping of {@code ImsReasonInfo#CODE_*} to {@code PreciseDisconnectCause#*}
340     */
341    private static final SparseIntArray PRECISE_CAUSE_MAP = new SparseIntArray();
342    static {
343        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT,
344                PreciseDisconnectCause.LOCAL_ILLEGAL_ARGUMENT);
345        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE,
346                PreciseDisconnectCause.LOCAL_ILLEGAL_STATE);
347        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR,
348                PreciseDisconnectCause.LOCAL_INTERNAL_ERROR);
349        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN,
350                PreciseDisconnectCause.LOCAL_IMS_SERVICE_DOWN);
351        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL,
352                PreciseDisconnectCause.LOCAL_NO_PENDING_CALL);
353        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
354                PreciseDisconnectCause.NORMAL);
355        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF,
356                PreciseDisconnectCause.LOCAL_POWER_OFF);
357        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY,
358                PreciseDisconnectCause.LOCAL_LOW_BATTERY);
359        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE,
360                PreciseDisconnectCause.LOCAL_NETWORK_NO_SERVICE);
361        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
362                PreciseDisconnectCause.LOCAL_NETWORK_NO_LTE_COVERAGE);
363        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING,
364                PreciseDisconnectCause.LOCAL_NETWORK_ROAMING);
365        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED,
366                PreciseDisconnectCause.LOCAL_NETWORK_IP_CHANGED);
367        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE,
368                PreciseDisconnectCause.LOCAL_SERVICE_UNAVAILABLE);
369        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
370                PreciseDisconnectCause.LOCAL_NOT_REGISTERED);
371        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED,
372                PreciseDisconnectCause.LOCAL_MAX_CALL_EXCEEDED);
373        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE,
374                PreciseDisconnectCause.LOCAL_CALL_DECLINE);
375        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
376                PreciseDisconnectCause.LOCAL_CALL_VCC_ON_PROGRESSING);
377        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
378                PreciseDisconnectCause.LOCAL_CALL_RESOURCE_RESERVATION_FAILED);
379        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
380                PreciseDisconnectCause.LOCAL_CALL_CS_RETRY_REQUIRED);
381        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
382                PreciseDisconnectCause.LOCAL_CALL_VOLTE_RETRY_REQUIRED);
383        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED,
384                PreciseDisconnectCause.LOCAL_CALL_TERMINATED);
385        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
386                PreciseDisconnectCause.LOCAL_HO_NOT_FEASIBLE);
387        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING,
388                PreciseDisconnectCause.TIMEOUT_1XX_WAITING);
389        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER,
390                PreciseDisconnectCause.TIMEOUT_NO_ANSWER);
391        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
392                PreciseDisconnectCause.TIMEOUT_NO_ANSWER_CALL_UPDATE);
393        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_FDN_BLOCKED,
394                PreciseDisconnectCause.FDN_BLOCKED);
395        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REDIRECTED,
396                PreciseDisconnectCause.SIP_REDIRECTED);
397        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_REQUEST,
398                PreciseDisconnectCause.SIP_BAD_REQUEST);
399        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_FORBIDDEN,
400                PreciseDisconnectCause.SIP_FORBIDDEN);
401        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_FOUND,
402                PreciseDisconnectCause.SIP_NOT_FOUND);
403        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED,
404                PreciseDisconnectCause.SIP_NOT_SUPPORTED);
405        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT,
406                PreciseDisconnectCause.SIP_REQUEST_TIMEOUT);
407        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE,
408                PreciseDisconnectCause.SIP_TEMPRARILY_UNAVAILABLE);
409        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_ADDRESS,
410                PreciseDisconnectCause.SIP_BAD_ADDRESS);
411        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BUSY,
412                PreciseDisconnectCause.SIP_BUSY);
413        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED,
414                PreciseDisconnectCause.SIP_REQUEST_CANCELLED);
415        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE,
416                PreciseDisconnectCause.SIP_NOT_ACCEPTABLE);
417        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_REACHABLE,
418                PreciseDisconnectCause.SIP_NOT_REACHABLE);
419        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR,
420                PreciseDisconnectCause.SIP_CLIENT_ERROR);
421        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR,
422                PreciseDisconnectCause.SIP_SERVER_INTERNAL_ERROR);
423        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE,
424                PreciseDisconnectCause.SIP_SERVICE_UNAVAILABLE);
425        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT,
426                PreciseDisconnectCause.SIP_SERVER_TIMEOUT);
427        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_ERROR,
428                PreciseDisconnectCause.SIP_SERVER_ERROR);
429        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_USER_REJECTED,
430                PreciseDisconnectCause.SIP_USER_REJECTED);
431        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR,
432                PreciseDisconnectCause.SIP_GLOBAL_ERROR);
433        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE,
434                PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE);
435        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE,
436                PreciseDisconnectCause.EMERGENCY_PERM_FAILURE);
437        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_INIT_FAILED,
438                PreciseDisconnectCause.MEDIA_INIT_FAILED);
439        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NO_DATA,
440                PreciseDisconnectCause.MEDIA_NO_DATA);
441        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE,
442                PreciseDisconnectCause.MEDIA_NOT_ACCEPTABLE);
443        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED,
444                PreciseDisconnectCause.MEDIA_UNSPECIFIED);
445        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED,
446                PreciseDisconnectCause.USER_TERMINATED);
447        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_NOANSWER,
448                PreciseDisconnectCause.USER_NOANSWER);
449        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_IGNORE,
450                PreciseDisconnectCause.USER_IGNORE);
451        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_DECLINE,
452                PreciseDisconnectCause.USER_DECLINE);
453        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOW_BATTERY,
454                PreciseDisconnectCause.LOW_BATTERY);
455        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID,
456                PreciseDisconnectCause.BLACKLISTED_CALL_ID);
457        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE,
458                PreciseDisconnectCause.USER_TERMINATED_BY_REMOTE);
459        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NOT_SUPPORTED,
460                PreciseDisconnectCause.UT_NOT_SUPPORTED);
461        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE,
462                PreciseDisconnectCause.UT_SERVICE_UNAVAILABLE);
463        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED,
464                PreciseDisconnectCause.UT_OPERATION_NOT_ALLOWED);
465        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NETWORK_ERROR,
466                PreciseDisconnectCause.UT_NETWORK_ERROR);
467        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH,
468                PreciseDisconnectCause.UT_CB_PASSWORD_MISMATCH);
469        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED,
470                PreciseDisconnectCause.ECBM_NOT_SUPPORTED);
471        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED,
472                PreciseDisconnectCause.MULTIENDPOINT_NOT_SUPPORTED);
473        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
474                PreciseDisconnectCause.CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE);
475        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
476                PreciseDisconnectCause.ANSWERED_ELSEWHERE);
477        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC,
478                PreciseDisconnectCause.CALL_PULL_OUT_OF_SYNC);
479        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL,
480                PreciseDisconnectCause.CALL_PULLED);
481        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_FAILED,
482                PreciseDisconnectCause.SUPP_SVC_FAILED);
483        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED,
484                PreciseDisconnectCause.SUPP_SVC_CANCELLED);
485        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION,
486                PreciseDisconnectCause.SUPP_SVC_REINVITE_COLLISION);
487        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE,
488                PreciseDisconnectCause.IWLAN_DPD_FAILURE);
489        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
490                PreciseDisconnectCause.EPDG_TUNNEL_ESTABLISH_FAILURE);
491        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE,
492                PreciseDisconnectCause.EPDG_TUNNEL_REKEY_FAILURE);
493        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION,
494                PreciseDisconnectCause.EPDG_TUNNEL_LOST_CONNECTION);
495        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
496                PreciseDisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED);
497        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE,
498                PreciseDisconnectCause.REMOTE_CALL_DECLINE);
499        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_LIMIT_REACHED,
500                PreciseDisconnectCause.DATA_LIMIT_REACHED);
501        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_DISABLED,
502                PreciseDisconnectCause.DATA_DISABLED);
503        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_WIFI_LOST,
504                PreciseDisconnectCause.WIFI_LOST);
505        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_OFF,
506                PreciseDisconnectCause.RADIO_OFF);
507        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NO_VALID_SIM,
508                PreciseDisconnectCause.NO_VALID_SIM);
509        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR,
510                PreciseDisconnectCause.RADIO_INTERNAL_ERROR);
511        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT,
512                PreciseDisconnectCause.NETWORK_RESP_TIMEOUT);
513        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_REJECT,
514                PreciseDisconnectCause.NETWORK_REJECT);
515        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE,
516                PreciseDisconnectCause.RADIO_ACCESS_FAILURE);
517        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_FAILURE,
518                PreciseDisconnectCause.RADIO_LINK_FAILURE);
519        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_LOST,
520                PreciseDisconnectCause.RADIO_LINK_LOST);
521        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE,
522                PreciseDisconnectCause.RADIO_UPLINK_FAILURE);
523        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE,
524                PreciseDisconnectCause.RADIO_SETUP_FAILURE);
525        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL,
526                PreciseDisconnectCause.RADIO_RELEASE_NORMAL);
527        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL,
528                PreciseDisconnectCause.RADIO_RELEASE_ABNORMAL);
529        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED,
530                PreciseDisconnectCause.ACCESS_CLASS_BLOCKED);
531        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH,
532                PreciseDisconnectCause.NETWORK_DETACH);
533        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1,
534                PreciseDisconnectCause.OEM_CAUSE_1);
535        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2,
536                PreciseDisconnectCause.OEM_CAUSE_2);
537        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_3,
538                PreciseDisconnectCause.OEM_CAUSE_3);
539        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_4,
540                PreciseDisconnectCause.OEM_CAUSE_4);
541        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_5,
542                PreciseDisconnectCause.OEM_CAUSE_5);
543        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_6,
544                PreciseDisconnectCause.OEM_CAUSE_6);
545        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_7,
546                PreciseDisconnectCause.OEM_CAUSE_7);
547        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_8,
548                PreciseDisconnectCause.OEM_CAUSE_8);
549        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_9,
550                PreciseDisconnectCause.OEM_CAUSE_9);
551        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_10,
552                PreciseDisconnectCause.OEM_CAUSE_10);
553        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_11,
554                PreciseDisconnectCause.OEM_CAUSE_11);
555        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_12,
556                PreciseDisconnectCause.OEM_CAUSE_12);
557        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_13,
558                PreciseDisconnectCause.OEM_CAUSE_13);
559        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_14,
560                PreciseDisconnectCause.OEM_CAUSE_14);
561        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_15,
562                PreciseDisconnectCause.OEM_CAUSE_15);
563    }
564
565    /**
566     * Carrier configuration option which determines whether the carrier wants to inform the user
567     * when a video call is handed over from WIFI to LTE.
568     * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL} for more
569     * information.
570     */
571    private boolean mNotifyHandoverVideoFromWifiToLTE = false;
572
573    /**
574     * Carrier configuration option which determines whether the carrier supports the
575     * {@link VideoProfile#STATE_PAUSED} signalling.
576     * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
577     */
578    private boolean mSupportPauseVideo = false;
579
580    /**
581     * Carrier configuration option which defines a mapping from pairs of
582     * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new
583     * {@code ImsReasonInfo#CODE_*} value.
584     *
585     * See {@link CarrierConfigManager#KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY}.
586     */
587    private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>();
588
589
590    /**
591     * TODO: Remove this code; it is a workaround.
592     * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(Context, int, boolean)} to
593     * be called when an ongoing video call is disconnected.  In some cases, where video pause is
594     * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
595     * has been disabled we will pause the video rather than disconnecting the call.  When this
596     * happens we need to prevent the IMS service config from being updated, as this will cause VT
597     * to be disabled mid-call, resulting in an inability to un-pause the video.
598     */
599    private boolean mShouldUpdateImsConfigOnDisconnect = false;
600
601    // Callback fires when ImsManager MMTel Feature changes state
602    private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> {
603        try {
604            int status = mImsManager.getImsServiceStatus();
605            log("Status Changed: " + status);
606            switch(status) {
607                case ImsFeature.STATE_READY: {
608                    startListeningForCalls();
609                    break;
610                }
611                case ImsFeature.STATE_INITIALIZING:
612                    // fall through
613                case ImsFeature.STATE_NOT_AVAILABLE: {
614                    stopListeningForCalls();
615                    break;
616                }
617                default: {
618                    Log.w(LOG_TAG, "Unexpected State!");
619                }
620            }
621        } catch (ImsException e) {
622            // Could not get the ImsService, retry!
623            retryGetImsService();
624        }
625    };
626
627    @VisibleForTesting
628    public interface IRetryTimeout {
629        int get();
630    }
631
632    /**
633     * Default implementation of interface that calculates the ImsService retry timeout.
634     * Override-able for testing.
635     */
636    @VisibleForTesting
637    public IRetryTimeout mRetryTimeout = () -> {
638        int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
639        if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
640            mImsServiceRetryCount++;
641        }
642        return timeout;
643    };
644
645    //***** Events
646
647
648    //***** Constructors
649
650    public ImsPhoneCallTracker(ImsPhone phone) {
651        this.mPhone = phone;
652
653        mMetrics = TelephonyMetrics.getInstance();
654
655        IntentFilter intentfilter = new IntentFilter();
656        intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
657        intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
658        intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
659        mPhone.getContext().registerReceiver(mReceiver, intentfilter);
660        cacheCarrierConfiguration(mPhone.getSubId());
661
662        mPhone.getDefaultPhone().registerForDataEnabledChanged(
663                this, EVENT_DATA_ENABLED_CHANGED, null);
664
665        mImsServiceRetryCount = 0;
666
667        final TelecomManager telecomManager =
668                (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
669        mDefaultDialerUid.set(
670                getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
671
672        long currentTime = SystemClock.elapsedRealtime();
673        mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
674        mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
675
676        // Send a message to connect to the Ims Service and open a connection through
677        // getImsService().
678        sendEmptyMessage(EVENT_GET_IMS_SERVICE);
679    }
680
681    private int getPackageUid(Context context, String pkg) {
682        if (pkg == null) {
683            return NetworkStats.UID_ALL;
684        }
685
686        int uid = NetworkStats.UID_ALL;
687        try {
688            uid = context.getPackageManager().getPackageUid(pkg, 0);
689        } catch (PackageManager.NameNotFoundException e) {
690            loge("Cannot find package uid. pkg = " + pkg);
691        }
692        return uid;
693    }
694
695    private PendingIntent createIncomingCallPendingIntent() {
696        Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
697        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
698        return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
699                PendingIntent.FLAG_UPDATE_CURRENT);
700    }
701
702    private void getImsService() throws ImsException {
703        if (DBG) log("getImsService");
704        mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
705        // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
706        // this method will throw an ImsException.
707        mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
708        // Wait for ImsService.STATE_READY to start listening for calls.
709        // Call the callback right away for compatibility with older devices that do not use states.
710        mNotifyStatusChangedCallback.notifyStatusChanged();
711    }
712
713    private void startListeningForCalls() throws ImsException {
714        mImsServiceRetryCount = 0;
715        mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
716                createIncomingCallPendingIntent(),
717                mImsConnectionStateListener);
718
719        mImsManager.setImsConfigListener(mImsConfigListener);
720
721        // Get the ECBM interface and set IMSPhone's listener object for notifications
722        getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener());
723        if (mPhone.isInEcm()) {
724            // Call exit ECBM which will invoke onECBMExited
725            mPhone.exitEmergencyCallbackMode();
726        }
727        int mPreferredTtyMode = Settings.Secure.getInt(
728                mPhone.getContext().getContentResolver(),
729                Settings.Secure.PREFERRED_TTY_MODE,
730                Phone.TTY_MODE_OFF);
731        mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null);
732
733        ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface();
734        if (multiEndpoint != null) {
735            multiEndpoint.setExternalCallStateListener(
736                    mPhone.getExternalCallTracker().getExternalCallStateListener());
737        }
738    }
739
740    private void stopListeningForCalls() {
741        try {
742            // Only close on valid session.
743            if (mImsManager != null && mServiceId > 0) {
744                mImsManager.close(mServiceId);
745                mServiceId = -1;
746            }
747        } catch (ImsException e) {
748            // If the binder is unavailable, then the ImsService doesn't need to close.
749        }
750    }
751
752    public void dispose() {
753        if (DBG) log("dispose");
754        mRingingCall.dispose();
755        mBackgroundCall.dispose();
756        mForegroundCall.dispose();
757        mHandoverCall.dispose();
758
759        clearDisconnected();
760        mPhone.getContext().unregisterReceiver(mReceiver);
761        mPhone.getDefaultPhone().unregisterForDataEnabledChanged(this);
762        removeMessages(EVENT_GET_IMS_SERVICE);
763    }
764
765    @Override
766    protected void finalize() {
767        log("ImsPhoneCallTracker finalized");
768    }
769
770    //***** Instance Methods
771
772    //***** Public Methods
773    @Override
774    public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
775        Registrant r = new Registrant(h, what, obj);
776        mVoiceCallStartedRegistrants.add(r);
777    }
778
779    @Override
780    public void unregisterForVoiceCallStarted(Handler h) {
781        mVoiceCallStartedRegistrants.remove(h);
782    }
783
784    @Override
785    public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
786        Registrant r = new Registrant(h, what, obj);
787        mVoiceCallEndedRegistrants.add(r);
788    }
789
790    @Override
791    public void unregisterForVoiceCallEnded(Handler h) {
792        mVoiceCallEndedRegistrants.remove(h);
793    }
794
795    public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
796            CallStateException {
797        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
798        int oirMode = sp.getInt(Phone.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
799        return dial(dialString, oirMode, videoState, intentExtras);
800    }
801
802    /**
803     * oirMode is one of the CLIR_ constants
804     */
805    synchronized Connection
806    dial(String dialString, int clirMode, int videoState, Bundle intentExtras)
807            throws CallStateException {
808        boolean isPhoneInEcmMode = isPhoneInEcbMode();
809        boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);
810
811        if (DBG) log("dial clirMode=" + clirMode);
812
813        // note that this triggers call state changed notif
814        clearDisconnected();
815
816        if (mImsManager == null) {
817            throw new CallStateException("service not available");
818        }
819
820        if (!canDial()) {
821            throw new CallStateException("cannot dial in current state");
822        }
823
824        if (isPhoneInEcmMode && isEmergencyNumber) {
825            handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER);
826        }
827
828        // If the call is to an emergency number and the carrier does not support video emergency
829        // calls, dial as an audio-only call.
830        if (isEmergencyNumber && VideoProfile.isVideo(videoState) &&
831                !mAllowEmergencyVideoCalls) {
832            loge("dial: carrier does not support video emergency calls; downgrade to audio-only");
833            videoState = VideoProfile.STATE_AUDIO_ONLY;
834        }
835
836        boolean holdBeforeDial = false;
837
838        // The new call must be assigned to the foreground call.
839        // That call must be idle, so place anything that's
840        // there on hold
841        if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
842            if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
843                //we should have failed in !canDial() above before we get here
844                throw new CallStateException("cannot dial in current state");
845            }
846            // foreground call is empty for the newly dialed connection
847            holdBeforeDial = true;
848            // Cache the video state for pending MO call.
849            mPendingCallVideoState = videoState;
850            mPendingIntentExtras = intentExtras;
851            switchWaitingOrHoldingAndActive();
852        }
853
854        ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
855        ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE;
856
857        mClirMode = clirMode;
858
859        synchronized (mSyncHold) {
860            if (holdBeforeDial) {
861                fgState = mForegroundCall.getState();
862                bgState = mBackgroundCall.getState();
863
864                //holding foreground call failed
865                if (fgState == ImsPhoneCall.State.ACTIVE) {
866                    throw new CallStateException("cannot dial in current state");
867                }
868
869                //holding foreground call succeeded
870                if (bgState == ImsPhoneCall.State.HOLDING) {
871                    holdBeforeDial = false;
872                }
873            }
874
875            mPendingMO = new ImsPhoneConnection(mPhone,
876                    checkForTestEmergencyNumber(dialString), this, mForegroundCall,
877                    isEmergencyNumber);
878            mPendingMO.setVideoState(videoState);
879        }
880        addConnection(mPendingMO);
881
882        if (!holdBeforeDial) {
883            if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
884                dialInternal(mPendingMO, clirMode, videoState, intentExtras);
885            } else {
886                try {
887                    getEcbmInterface().exitEmergencyCallbackMode();
888                } catch (ImsException e) {
889                    e.printStackTrace();
890                    throw new CallStateException("service not available");
891                }
892                mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
893                pendingCallClirMode = clirMode;
894                mPendingCallVideoState = videoState;
895                pendingCallInEcm = true;
896            }
897        }
898
899        updatePhoneState();
900        mPhone.notifyPreciseCallStateChanged();
901
902        return mPendingMO;
903    }
904
905    boolean isImsServiceReady() {
906        if (mImsManager == null) {
907            return false;
908        }
909
910        return mImsManager.isServiceAvailable();
911    }
912
913    /**
914     * Caches frequently used carrier configuration items locally.
915     *
916     * @param subId The sub id.
917     */
918    private void cacheCarrierConfiguration(int subId) {
919        CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
920                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
921        if (carrierConfigManager == null) {
922            loge("cacheCarrierConfiguration: No carrier config service found.");
923            return;
924        }
925
926        PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
927        if (carrierConfig == null) {
928            loge("cacheCarrierConfiguration: Empty carrier config.");
929            return;
930        }
931
932        mAllowEmergencyVideoCalls =
933                carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
934        mTreatDowngradedVideoCallsAsVideoCalls =
935                carrierConfig.getBoolean(
936                        CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL);
937        mDropVideoCallWhenAnsweringAudioCall =
938                carrierConfig.getBoolean(
939                        CarrierConfigManager.KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL);
940        mAllowAddCallDuringVideoCall =
941                carrierConfig.getBoolean(
942                        CarrierConfigManager.KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL);
943        mNotifyVtHandoverToWifiFail = carrierConfig.getBoolean(
944                CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
945        mSupportDowngradeVtToAudio = carrierConfig.getBoolean(
946                CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
947        mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
948                CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
949        mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
950                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
951        mSupportPauseVideo = carrierConfig.getBoolean(
952                CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
953
954        String[] mappings = carrierConfig
955                .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
956        if (mappings != null && mappings.length > 0) {
957            for (String mapping : mappings) {
958                String[] values = mapping.split(Pattern.quote("|"));
959                if (values.length != 3) {
960                    continue;
961                }
962
963                try {
964                    Integer fromCode;
965                    if (values[0].equals("*")) {
966                        fromCode = null;
967                    } else {
968                        fromCode = Integer.parseInt(values[0]);
969                    }
970                    String message = values[1];
971                    int toCode = Integer.parseInt(values[2]);
972
973                    addReasonCodeRemapping(fromCode, message, toCode);
974                    log("Loaded ImsReasonInfo mapping : fromCode = " +
975                            fromCode == null ? "any" : fromCode + " ; message = " +
976                            message + " ; toCode = " + toCode);
977                } catch (NumberFormatException nfe) {
978                    loge("Invalid ImsReasonInfo mapping found: " + mapping);
979                }
980            }
981        } else {
982            log("No carrier ImsReasonInfo mappings defined.");
983        }
984    }
985
986    private void handleEcmTimer(int action) {
987        mPhone.handleTimerInEmergencyCallbackMode(action);
988        switch (action) {
989            case ImsPhone.CANCEL_ECM_TIMER:
990                break;
991            case ImsPhone.RESTART_ECM_TIMER:
992                break;
993            default:
994                log("handleEcmTimer, unsupported action " + action);
995        }
996    }
997
998    private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState,
999            Bundle intentExtras) {
1000
1001        if (conn == null) {
1002            return;
1003        }
1004
1005        if (conn.getAddress()== null || conn.getAddress().length() == 0
1006                || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
1007            // Phone number is invalid
1008            conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER);
1009            sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
1010            return;
1011        }
1012
1013        // Always unmute when initiating a new call
1014        setMute(false);
1015        int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ?
1016                ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
1017        int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
1018        //TODO(vt): Is this sufficient?  At what point do we know the video state of the call?
1019        conn.setVideoState(videoState);
1020
1021        try {
1022            String[] callees = new String[] { conn.getAddress() };
1023            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
1024                    serviceType, callType);
1025            profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
1026
1027            // Translate call subject intent-extra from Telecom-specific extra key to the
1028            // ImsCallProfile key.
1029            if (intentExtras != null) {
1030                if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) {
1031                    intentExtras.putString(ImsCallProfile.EXTRA_DISPLAY_TEXT,
1032                            cleanseInstantLetteringMessage(intentExtras.getString(
1033                                    android.telecom.TelecomManager.EXTRA_CALL_SUBJECT))
1034                    );
1035                }
1036
1037                if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) {
1038                    profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL,
1039                            intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL));
1040                    int dialogId = intentExtras.getInt(
1041                            ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID);
1042                    conn.setIsPulledCall(true);
1043                    conn.setPulledDialogId(dialogId);
1044                }
1045
1046                // Pack the OEM-specific call extras.
1047                profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras);
1048
1049                // NOTE: Extras to be sent over the network are packed into the
1050                // intentExtras individually, with uniquely defined keys.
1051                // These key-value pairs are processed by IMS Service before
1052                // being sent to the lower layers/to the network.
1053            }
1054
1055            ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,
1056                    callees, mImsCallListener);
1057            conn.setImsCall(imsCall);
1058
1059            mMetrics.writeOnImsCallStart(mPhone.getPhoneId(),
1060                    imsCall.getSession());
1061
1062            setVideoCallProvider(conn, imsCall);
1063            conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
1064        } catch (ImsException e) {
1065            loge("dialInternal : " + e);
1066            conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
1067            sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
1068            retryGetImsService();
1069        } catch (RemoteException e) {
1070        }
1071    }
1072
1073    /**
1074     * Accepts a call with the specified video state.  The video state is the video state that the
1075     * user has agreed upon in the InCall UI.
1076     *
1077     * @param videoState The video State
1078     * @throws CallStateException
1079     */
1080    public void acceptCall (int videoState) throws CallStateException {
1081        if (DBG) log("acceptCall");
1082
1083        if (mForegroundCall.getState().isAlive()
1084                && mBackgroundCall.getState().isAlive()) {
1085            throw new CallStateException("cannot accept call");
1086        }
1087
1088        if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
1089                && mForegroundCall.getState().isAlive()) {
1090            setMute(false);
1091
1092            boolean answeringWillDisconnect = false;
1093            ImsCall activeCall = mForegroundCall.getImsCall();
1094            ImsCall ringingCall = mRingingCall.getImsCall();
1095            if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) {
1096                answeringWillDisconnect =
1097                        shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall);
1098            }
1099
1100            // Cache video state for pending MT call.
1101            mPendingCallVideoState = videoState;
1102
1103            if (answeringWillDisconnect) {
1104                // We need to disconnect the foreground call before answering the background call.
1105                mForegroundCall.hangup();
1106                try {
1107                    ringingCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
1108                } catch (ImsException e) {
1109                    throw new CallStateException("cannot accept call");
1110                }
1111            } else {
1112                switchWaitingOrHoldingAndActive();
1113            }
1114        } else if (mRingingCall.getState().isRinging()) {
1115            if (DBG) log("acceptCall: incoming...");
1116            // Always unmute when answering a new call
1117            setMute(false);
1118            try {
1119                ImsCall imsCall = mRingingCall.getImsCall();
1120                if (imsCall != null) {
1121                    imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
1122                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1123                            ImsCommand.IMS_CMD_ACCEPT);
1124                } else {
1125                    throw new CallStateException("no valid ims call");
1126                }
1127            } catch (ImsException e) {
1128                throw new CallStateException("cannot accept call");
1129            }
1130        } else {
1131            throw new CallStateException("phone not ringing");
1132        }
1133    }
1134
1135    public void rejectCall () throws CallStateException {
1136        if (DBG) log("rejectCall");
1137
1138        if (mRingingCall.getState().isRinging()) {
1139            hangup(mRingingCall);
1140        } else {
1141            throw new CallStateException("phone not ringing");
1142        }
1143    }
1144
1145
1146    private void switchAfterConferenceSuccess() {
1147        if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() +
1148                ", bg = " + mBackgroundCall.getState());
1149
1150        if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
1151            log("switchAfterConferenceSuccess");
1152            mForegroundCall.switchWith(mBackgroundCall);
1153        }
1154    }
1155
1156    public void switchWaitingOrHoldingAndActive() throws CallStateException {
1157        if (DBG) log("switchWaitingOrHoldingAndActive");
1158
1159        if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) {
1160            throw new CallStateException("cannot be in the incoming state");
1161        }
1162
1163        if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
1164            ImsCall imsCall = mForegroundCall.getImsCall();
1165            if (imsCall == null) {
1166                throw new CallStateException("no ims call");
1167            }
1168
1169            // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls.
1170            // If hold or resume later fails, we will swap them back.
1171            boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive() &&
1172                    mRingingCall != null &&
1173                    mRingingCall.getState() == ImsPhoneCall.State.WAITING;
1174
1175            mSwitchingFgAndBgCalls = true;
1176            if (switchingWithWaitingCall) {
1177                mCallExpectedToResume = mRingingCall.getImsCall();
1178            } else {
1179                mCallExpectedToResume = mBackgroundCall.getImsCall();
1180            }
1181            mForegroundCall.switchWith(mBackgroundCall);
1182
1183            // Hold the foreground call; once the foreground call is held, the background call will
1184            // be resumed.
1185            try {
1186                imsCall.hold();
1187                mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1188                        ImsCommand.IMS_CMD_HOLD);
1189
1190                // If there is no background call to resume, then don't expect there to be a switch.
1191                if (mCallExpectedToResume == null) {
1192                    log("mCallExpectedToResume is null");
1193                    mSwitchingFgAndBgCalls = false;
1194                }
1195            } catch (ImsException e) {
1196                mForegroundCall.switchWith(mBackgroundCall);
1197                throw new CallStateException(e.getMessage());
1198            }
1199        } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
1200            resumeWaitingOrHolding();
1201        }
1202    }
1203
1204    public void
1205    conference() {
1206        ImsCall fgImsCall = mForegroundCall.getImsCall();
1207        if (fgImsCall == null) {
1208            log("conference no foreground ims call");
1209            return;
1210        }
1211
1212        ImsCall bgImsCall = mBackgroundCall.getImsCall();
1213        if (bgImsCall == null) {
1214            log("conference no background ims call");
1215            return;
1216        }
1217
1218        if (fgImsCall.isCallSessionMergePending()) {
1219            log("conference: skip; foreground call already in process of merging.");
1220            return;
1221        }
1222
1223        if (bgImsCall.isCallSessionMergePending()) {
1224            log("conference: skip; background call already in process of merging.");
1225            return;
1226        }
1227
1228        // Keep track of the connect time of the earliest call so that it can be set on the
1229        // {@code ImsConference} when it is created.
1230        long foregroundConnectTime = mForegroundCall.getEarliestConnectTime();
1231        long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime();
1232        long conferenceConnectTime;
1233        if (foregroundConnectTime > 0 && backgroundConnectTime > 0) {
1234            conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
1235                    mBackgroundCall.getEarliestConnectTime());
1236            log("conference - using connect time = " + conferenceConnectTime);
1237        } else if (foregroundConnectTime > 0) {
1238            log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime);
1239            conferenceConnectTime = foregroundConnectTime;
1240        } else {
1241            log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime);
1242            conferenceConnectTime = backgroundConnectTime;
1243        }
1244
1245        String foregroundId = "";
1246        ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
1247        if (foregroundConnection != null) {
1248            foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
1249            foregroundConnection.handleMergeStart();
1250            foregroundId = foregroundConnection.getTelecomCallId();
1251        }
1252        String backgroundId = "";
1253        ImsPhoneConnection backgroundConnection = findConnection(bgImsCall);
1254        if (backgroundConnection != null) {
1255            backgroundConnection.handleMergeStart();
1256            backgroundId = backgroundConnection.getTelecomCallId();
1257        }
1258        log("conference: fgCallId=" + foregroundId + ", bgCallId=" + backgroundId);
1259
1260        try {
1261            fgImsCall.merge(bgImsCall);
1262        } catch (ImsException e) {
1263            log("conference " + e.getMessage());
1264        }
1265    }
1266
1267    public void
1268    explicitCallTransfer() {
1269        //TODO : implement
1270    }
1271
1272    public void
1273    clearDisconnected() {
1274        if (DBG) log("clearDisconnected");
1275
1276        internalClearDisconnected();
1277
1278        updatePhoneState();
1279        mPhone.notifyPreciseCallStateChanged();
1280    }
1281
1282    public boolean
1283    canConference() {
1284        return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
1285            && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING
1286            && !mBackgroundCall.isFull()
1287            && !mForegroundCall.isFull();
1288    }
1289
1290    public boolean canDial() {
1291        boolean ret;
1292        String disableCall = SystemProperties.get(
1293                TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
1294
1295        ret = mPendingMO == null
1296                && !mRingingCall.isRinging()
1297                && !disableCall.equals("true")
1298                && (!mForegroundCall.getState().isAlive()
1299                        || !mBackgroundCall.getState().isAlive());
1300
1301        return ret;
1302    }
1303
1304    public boolean
1305    canTransfer() {
1306        return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
1307            && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING;
1308    }
1309
1310    //***** Private Instance Methods
1311
1312    private void
1313    internalClearDisconnected() {
1314        mRingingCall.clearDisconnected();
1315        mForegroundCall.clearDisconnected();
1316        mBackgroundCall.clearDisconnected();
1317        mHandoverCall.clearDisconnected();
1318    }
1319
1320    private void
1321    updatePhoneState() {
1322        PhoneConstants.State oldState = mState;
1323
1324        boolean isPendingMOIdle = mPendingMO == null || !mPendingMO.getState().isAlive();
1325
1326        if (mRingingCall.isRinging()) {
1327            mState = PhoneConstants.State.RINGING;
1328        } else if (!isPendingMOIdle || !mForegroundCall.isIdle() || !mBackgroundCall.isIdle()) {
1329            // There is a non-idle call, so we're off the hook.
1330            mState = PhoneConstants.State.OFFHOOK;
1331        } else {
1332            mState = PhoneConstants.State.IDLE;
1333        }
1334
1335        if (mState == PhoneConstants.State.IDLE && oldState != mState) {
1336            mVoiceCallEndedRegistrants.notifyRegistrants(
1337                    new AsyncResult(null, null, null));
1338        } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
1339            mVoiceCallStartedRegistrants.notifyRegistrants (
1340                    new AsyncResult(null, null, null));
1341        }
1342
1343        if (DBG) {
1344            log("updatePhoneState pendingMo = " + (mPendingMO == null ? "null"
1345                    : mPendingMO.getState()) + ", fg= " + mForegroundCall.getState() + "("
1346                    + mForegroundCall.getConnections().size() + "), bg= " + mBackgroundCall
1347                    .getState() + "(" + mBackgroundCall.getConnections().size() + ")");
1348            log("updatePhoneState oldState=" + oldState + ", newState=" + mState);
1349        }
1350
1351        if (mState != oldState) {
1352            mPhone.notifyPhoneStateChanged();
1353            mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
1354            notifyPhoneStateChanged(oldState, mState);
1355        }
1356    }
1357
1358    private void
1359    handleRadioNotAvailable() {
1360        // handlePollCalls will clear out its
1361        // call list when it gets the CommandException
1362        // error result from this
1363        pollCallsWhenSafe();
1364    }
1365
1366    private void
1367    dumpState() {
1368        List l;
1369
1370        log("Phone State:" + mState);
1371
1372        log("Ringing call: " + mRingingCall.toString());
1373
1374        l = mRingingCall.getConnections();
1375        for (int i = 0, s = l.size(); i < s; i++) {
1376            log(l.get(i).toString());
1377        }
1378
1379        log("Foreground call: " + mForegroundCall.toString());
1380
1381        l = mForegroundCall.getConnections();
1382        for (int i = 0, s = l.size(); i < s; i++) {
1383            log(l.get(i).toString());
1384        }
1385
1386        log("Background call: " + mBackgroundCall.toString());
1387
1388        l = mBackgroundCall.getConnections();
1389        for (int i = 0, s = l.size(); i < s; i++) {
1390            log(l.get(i).toString());
1391        }
1392
1393    }
1394
1395    //***** Called from ImsPhone
1396
1397    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
1398        if (mImsManager == null) {
1399            mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException());
1400            return;
1401        }
1402
1403        try {
1404            mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete);
1405        } catch (ImsException e) {
1406            loge("setTTYMode : " + e);
1407            mPhone.sendErrorResponse(onComplete, e);
1408            retryGetImsService();
1409        }
1410    }
1411
1412    public void setMute(boolean mute) {
1413        mDesiredMute = mute;
1414        mForegroundCall.setMute(mute);
1415    }
1416
1417    public boolean getMute() {
1418        return mDesiredMute;
1419    }
1420
1421    public void sendDtmf(char c, Message result) {
1422        if (DBG) log("sendDtmf");
1423
1424        ImsCall imscall = mForegroundCall.getImsCall();
1425        if (imscall != null) {
1426            imscall.sendDtmf(c, result);
1427        }
1428    }
1429
1430    public void
1431    startDtmf(char c) {
1432        if (DBG) log("startDtmf");
1433
1434        ImsCall imscall = mForegroundCall.getImsCall();
1435        if (imscall != null) {
1436            imscall.startDtmf(c);
1437        } else {
1438            loge("startDtmf : no foreground call");
1439        }
1440    }
1441
1442    public void
1443    stopDtmf() {
1444        if (DBG) log("stopDtmf");
1445
1446        ImsCall imscall = mForegroundCall.getImsCall();
1447        if (imscall != null) {
1448            imscall.stopDtmf();
1449        } else {
1450            loge("stopDtmf : no foreground call");
1451        }
1452    }
1453
1454    //***** Called from ImsPhoneConnection
1455
1456    public void hangup (ImsPhoneConnection conn) throws CallStateException {
1457        if (DBG) log("hangup connection");
1458
1459        if (conn.getOwner() != this) {
1460            throw new CallStateException ("ImsPhoneConnection " + conn
1461                    + "does not belong to ImsPhoneCallTracker " + this);
1462        }
1463
1464        hangup(conn.getCall());
1465    }
1466
1467    //***** Called from ImsPhoneCall
1468
1469    public void hangup (ImsPhoneCall call) throws CallStateException {
1470        if (DBG) log("hangup call");
1471
1472        if (call.getConnections().size() == 0) {
1473            throw new CallStateException("no connections");
1474        }
1475
1476        ImsCall imsCall = call.getImsCall();
1477        boolean rejectCall = false;
1478
1479        if (call == mRingingCall) {
1480            if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming");
1481            rejectCall = true;
1482        } else if (call == mForegroundCall) {
1483            if (call.isDialingOrAlerting()) {
1484                if (Phone.DEBUG_PHONE) {
1485                    log("(foregnd) hangup dialing or alerting...");
1486                }
1487            } else {
1488                if (Phone.DEBUG_PHONE) {
1489                    log("(foregnd) hangup foreground");
1490                }
1491                //held call will be resumed by onCallTerminated
1492            }
1493        } else if (call == mBackgroundCall) {
1494            if (Phone.DEBUG_PHONE) {
1495                log("(backgnd) hangup waiting or background");
1496            }
1497        } else {
1498            throw new CallStateException ("ImsPhoneCall " + call +
1499                    "does not belong to ImsPhoneCallTracker " + this);
1500        }
1501
1502        call.onHangupLocal();
1503
1504        try {
1505            if (imsCall != null) {
1506                if (rejectCall) {
1507                    imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
1508                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1509                            ImsCommand.IMS_CMD_REJECT);
1510                } else {
1511                    imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
1512                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1513                            ImsCommand.IMS_CMD_TERMINATE);
1514                }
1515            } else if (mPendingMO != null && call == mForegroundCall) {
1516                // is holding a foreground call
1517                mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
1518                mPendingMO.onDisconnect();
1519                removeConnection(mPendingMO);
1520                mPendingMO = null;
1521                updatePhoneState();
1522                removeMessages(EVENT_DIAL_PENDINGMO);
1523            }
1524        } catch (ImsException e) {
1525            throw new CallStateException(e.getMessage());
1526        }
1527
1528        mPhone.notifyPreciseCallStateChanged();
1529    }
1530
1531    void callEndCleanupHandOverCallIfAny() {
1532        if (mHandoverCall.mConnections.size() > 0) {
1533            if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections="
1534                    + mHandoverCall.mConnections);
1535            mHandoverCall.mConnections.clear();
1536            mConnections.clear();
1537            mState = PhoneConstants.State.IDLE;
1538        }
1539    }
1540
1541    /* package */
1542    void resumeWaitingOrHolding() throws CallStateException {
1543        if (DBG) log("resumeWaitingOrHolding");
1544
1545        try {
1546            if (mForegroundCall.getState().isAlive()) {
1547                //resume foreground call after holding background call
1548                //they were switched before holding
1549                ImsCall imsCall = mForegroundCall.getImsCall();
1550                if (imsCall != null) {
1551                    imsCall.resume();
1552                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1553                            ImsCommand.IMS_CMD_RESUME);
1554                }
1555            } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
1556                //accept waiting call after holding background call
1557                ImsCall imsCall = mRingingCall.getImsCall();
1558                if (imsCall != null) {
1559                    imsCall.accept(
1560                        ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState));
1561                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1562                            ImsCommand.IMS_CMD_ACCEPT);
1563                }
1564            } else {
1565                //Just resume background call.
1566                //To distinguish resuming call with swapping calls
1567                //we do not switch calls.here
1568                //ImsPhoneConnection.update will chnage the parent when completed
1569                ImsCall imsCall = mBackgroundCall.getImsCall();
1570                if (imsCall != null) {
1571                    imsCall.resume();
1572                    mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1573                            ImsCommand.IMS_CMD_RESUME);
1574                }
1575            }
1576        } catch (ImsException e) {
1577            throw new CallStateException(e.getMessage());
1578        }
1579    }
1580
1581    public void sendUSSD (String ussdString, Message response) {
1582        if (DBG) log("sendUSSD");
1583
1584        try {
1585            if (mUssdSession != null) {
1586                mUssdSession.sendUssd(ussdString);
1587                AsyncResult.forMessage(response, null, null);
1588                response.sendToTarget();
1589                return;
1590            }
1591
1592            if (mImsManager == null) {
1593                mPhone.sendErrorResponse(response, getImsManagerIsNullException());
1594                return;
1595            }
1596
1597            String[] callees = new String[] { ussdString };
1598            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
1599                    ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
1600            profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
1601                    ImsCallProfile.DIALSTRING_USSD);
1602
1603            mUssdSession = mImsManager.makeCall(mServiceId, profile,
1604                    callees, mImsUssdListener);
1605        } catch (ImsException e) {
1606            loge("sendUSSD : " + e);
1607            mPhone.sendErrorResponse(response, e);
1608            retryGetImsService();
1609        }
1610    }
1611
1612    public void cancelUSSD() {
1613        if (mUssdSession == null) return;
1614
1615        try {
1616            mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
1617        } catch (ImsException e) {
1618        }
1619
1620    }
1621
1622    private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
1623        for (ImsPhoneConnection conn : mConnections) {
1624            if (conn.getImsCall() == imsCall) {
1625                return conn;
1626            }
1627        }
1628        return null;
1629    }
1630
1631    private synchronized void removeConnection(ImsPhoneConnection conn) {
1632        mConnections.remove(conn);
1633        // If not emergency call is remaining, notify emergency call registrants
1634        if (mIsInEmergencyCall) {
1635            boolean isEmergencyCallInList = false;
1636            // if no emergency calls pending, set this to false
1637            for (ImsPhoneConnection imsPhoneConnection : mConnections) {
1638                if (imsPhoneConnection != null && imsPhoneConnection.isEmergency() == true) {
1639                    isEmergencyCallInList = true;
1640                    break;
1641                }
1642            }
1643
1644            if (!isEmergencyCallInList) {
1645                mIsInEmergencyCall = false;
1646                mPhone.sendEmergencyCallStateChange(false);
1647            }
1648        }
1649    }
1650
1651    private synchronized void addConnection(ImsPhoneConnection conn) {
1652        mConnections.add(conn);
1653        if (conn.isEmergency()) {
1654            mIsInEmergencyCall = true;
1655            mPhone.sendEmergencyCallStateChange(true);
1656        }
1657    }
1658
1659    private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
1660        if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);
1661        // This method is called on onCallUpdate() where there is not necessarily a call state
1662        // change. In these situations, we'll ignore the state related updates and only process
1663        // the change in media capabilities (as expected).  The default is to not ignore state
1664        // changes so we do not change existing behavior.
1665        processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
1666    }
1667
1668    private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
1669            boolean ignoreState) {
1670        if (DBG) {
1671            log("processCallStateChange state=" + state + " cause=" + cause
1672                    + " ignoreState=" + ignoreState);
1673        }
1674
1675        if (imsCall == null) return;
1676
1677        boolean changed = false;
1678        ImsPhoneConnection conn = findConnection(imsCall);
1679
1680        if (conn == null) {
1681            // TODO : what should be done?
1682            return;
1683        }
1684
1685        // processCallStateChange is triggered for onCallUpdated as well.
1686        // onCallUpdated should not modify the state of the call
1687        // It should modify only other capabilities of call through updateMediaCapabilities
1688        // State updates will be triggered through individual callbacks
1689        // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update
1690        conn.updateMediaCapabilities(imsCall);
1691        if (ignoreState) {
1692            conn.updateAddressDisplay(imsCall);
1693            conn.updateExtras(imsCall);
1694
1695            maybeSetVideoCallProvider(conn, imsCall);
1696            return;
1697        }
1698
1699        changed = conn.update(imsCall, state);
1700        if (state == ImsPhoneCall.State.DISCONNECTED) {
1701            changed = conn.onDisconnect(cause) || changed;
1702            //detach the disconnected connections
1703            conn.getCall().detach(conn);
1704            removeConnection(conn);
1705        }
1706
1707        if (changed) {
1708            if (conn.getCall() == mHandoverCall) return;
1709            updatePhoneState();
1710            mPhone.notifyPreciseCallStateChanged();
1711        }
1712    }
1713
1714    private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) {
1715        android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider();
1716        if (connVideoProvider != null || imsCall.getCallSession().getVideoCallProvider() == null) {
1717            return;
1718        }
1719
1720        try {
1721            setVideoCallProvider(conn, imsCall);
1722        } catch (RemoteException e) {
1723            loge("maybeSetVideoCallProvider: exception " + e);
1724        }
1725    }
1726
1727    /**
1728     * Adds a reason code remapping, for test purposes.
1729     *
1730     * @param fromCode The from code, or {@code null} if all.
1731     * @param message The message to map.
1732     * @param toCode The code to remap to.
1733     */
1734    @VisibleForTesting
1735    public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) {
1736        mImsReasonCodeMap.put(new Pair<>(fromCode, message), toCode);
1737    }
1738
1739    /**
1740     * Returns the {@link ImsReasonInfo#getCode()}, potentially remapping to a new value based on
1741     * the {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()}.
1742     *
1743     * See {@link #mImsReasonCodeMap}.
1744     *
1745     * @param reasonInfo The {@link ImsReasonInfo}.
1746     * @return The remapped code.
1747     */
1748    @VisibleForTesting
1749    public int maybeRemapReasonCode(ImsReasonInfo reasonInfo) {
1750        int code = reasonInfo.getCode();
1751
1752        Pair<Integer, String> toCheck = new Pair<>(code, reasonInfo.getExtraMessage());
1753        Pair<Integer, String> wildcardToCheck = new Pair<>(null, reasonInfo.getExtraMessage());
1754        if (mImsReasonCodeMap.containsKey(toCheck)) {
1755            int toCode = mImsReasonCodeMap.get(toCheck);
1756
1757            log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
1758                    + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
1759            return toCode;
1760        } else if (mImsReasonCodeMap.containsKey(wildcardToCheck)) {
1761            // Handle the case where a wildcard is specified for the fromCode; in this case we will
1762            // match without caring about the fromCode.
1763            int toCode = mImsReasonCodeMap.get(wildcardToCheck);
1764
1765            log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() +
1766                    " ; message = " + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
1767            return toCode;
1768        }
1769        return code;
1770    }
1771
1772    private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
1773        int cause = DisconnectCause.ERROR_UNSPECIFIED;
1774
1775        //int type = reasonInfo.getReasonType();
1776        int code = maybeRemapReasonCode(reasonInfo);
1777        switch (code) {
1778            case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
1779            case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
1780                return DisconnectCause.NUMBER_UNREACHABLE;
1781
1782            case ImsReasonInfo.CODE_SIP_BUSY:
1783                return DisconnectCause.BUSY;
1784
1785            case ImsReasonInfo.CODE_USER_TERMINATED:
1786                return DisconnectCause.LOCAL;
1787
1788            case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE:
1789                return DisconnectCause.IMS_MERGED_SUCCESSFULLY;
1790
1791            case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE:
1792            case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE:
1793                // If the call has been declined locally (on this device), or on remotely (on
1794                // another device using multiendpoint functionality), mark it as rejected.
1795                return DisconnectCause.INCOMING_REJECTED;
1796
1797            case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE:
1798                return DisconnectCause.NORMAL;
1799
1800            case ImsReasonInfo.CODE_SIP_FORBIDDEN:
1801                return DisconnectCause.SERVER_ERROR;
1802
1803            case ImsReasonInfo.CODE_SIP_REDIRECTED:
1804            case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
1805            case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE:
1806            case ImsReasonInfo.CODE_SIP_USER_REJECTED:
1807            case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR:
1808                return DisconnectCause.SERVER_ERROR;
1809
1810            case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE:
1811            case ImsReasonInfo.CODE_SIP_NOT_FOUND:
1812            case ImsReasonInfo.CODE_SIP_SERVER_ERROR:
1813                return DisconnectCause.SERVER_UNREACHABLE;
1814
1815            case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING:
1816            case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED:
1817            case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN:
1818            case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE:
1819            case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED:
1820            case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE:
1821            case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE:
1822            case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING:
1823                return DisconnectCause.OUT_OF_SERVICE;
1824
1825            case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT:
1826            case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING:
1827            case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER:
1828            case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
1829                return DisconnectCause.TIMED_OUT;
1830
1831            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
1832            case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
1833                return DisconnectCause.POWER_OFF;
1834
1835            case ImsReasonInfo.CODE_FDN_BLOCKED:
1836                return DisconnectCause.FDN_BLOCKED;
1837
1838            case ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED:
1839                return DisconnectCause.IMEI_NOT_ACCEPTED;
1840
1841            case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE:
1842                return DisconnectCause.ANSWERED_ELSEWHERE;
1843
1844            case ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL:
1845                return DisconnectCause.CALL_PULLED;
1846
1847            case ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED:
1848                return DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED;
1849
1850            case ImsReasonInfo.CODE_DATA_DISABLED:
1851                return DisconnectCause.DATA_DISABLED;
1852
1853            case ImsReasonInfo.CODE_DATA_LIMIT_REACHED:
1854                return DisconnectCause.DATA_LIMIT_REACHED;
1855
1856            case ImsReasonInfo.CODE_WIFI_LOST:
1857                return DisconnectCause.WIFI_LOST;
1858
1859            case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED:
1860                return DisconnectCause.IMS_ACCESS_BLOCKED;
1861            default:
1862        }
1863
1864        return cause;
1865    }
1866
1867    private int getPreciseDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
1868        return PRECISE_CAUSE_MAP.get(maybeRemapReasonCode(reasonInfo),
1869                PreciseDisconnectCause.ERROR_UNSPECIFIED);
1870    }
1871
1872    /**
1873     * @return true if the phone is in Emergency Callback mode, otherwise false
1874     */
1875    private boolean isPhoneInEcbMode() {
1876        return mPhone.isInEcm();
1877    }
1878
1879    /**
1880     * Before dialing pending MO request, check for the Emergency Callback mode.
1881     * If device is in Emergency callback mode, then exit the mode before dialing pending MO.
1882     */
1883    private void dialPendingMO() {
1884        boolean isPhoneInEcmMode = isPhoneInEcbMode();
1885        boolean isEmergencyNumber = mPendingMO.isEmergency();
1886        if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
1887            sendEmptyMessage(EVENT_DIAL_PENDINGMO);
1888        } else {
1889            sendEmptyMessage(EVENT_EXIT_ECBM_BEFORE_PENDINGMO);
1890        }
1891    }
1892
1893    /**
1894     * Listen to the IMS call state change
1895     */
1896    private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
1897        @Override
1898        public void onCallProgressing(ImsCall imsCall) {
1899            if (DBG) log("onCallProgressing");
1900
1901            mPendingMO = null;
1902            processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING,
1903                    DisconnectCause.NOT_DISCONNECTED);
1904            mMetrics.writeOnImsCallProgressing(mPhone.getPhoneId(), imsCall.getCallSession());
1905        }
1906
1907        @Override
1908        public void onCallStarted(ImsCall imsCall) {
1909            if (DBG) log("onCallStarted");
1910
1911            if (mSwitchingFgAndBgCalls) {
1912                // If we put a call on hold to answer an incoming call, we should reset the
1913                // variables that keep track of the switch here.
1914                if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
1915                    if (DBG) log("onCallStarted: starting a call as a result of a switch.");
1916                    mSwitchingFgAndBgCalls = false;
1917                    mCallExpectedToResume = null;
1918                }
1919            }
1920
1921            mPendingMO = null;
1922            processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
1923                    DisconnectCause.NOT_DISCONNECTED);
1924
1925            if (mNotifyVtHandoverToWifiFail &&
1926                    !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) {
1927                // Schedule check to see if handover succeeded.
1928                sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
1929                        HANDOVER_TO_WIFI_TIMEOUT_MS);
1930            }
1931
1932            mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession());
1933        }
1934
1935        @Override
1936        public void onCallUpdated(ImsCall imsCall) {
1937            if (DBG) log("onCallUpdated");
1938            if (imsCall == null) {
1939                return;
1940            }
1941            ImsPhoneConnection conn = findConnection(imsCall);
1942            if (conn != null) {
1943                processCallStateChange(imsCall, conn.getCall().mState,
1944                        DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
1945                mMetrics.writeImsCallState(mPhone.getPhoneId(),
1946                        imsCall.getCallSession(), conn.getCall().mState);
1947            }
1948        }
1949
1950        /**
1951         * onCallStartFailed will be invoked when:
1952         * case 1) Dialing fails
1953         * case 2) Ringing call is disconnected by local or remote user
1954         */
1955        @Override
1956        public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1957            if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
1958
1959            if (mSwitchingFgAndBgCalls) {
1960                // If we put a call on hold to answer an incoming call, we should reset the
1961                // variables that keep track of the switch here.
1962                if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
1963                    if (DBG) log("onCallStarted: starting a call as a result of a switch.");
1964                    mSwitchingFgAndBgCalls = false;
1965                    mCallExpectedToResume = null;
1966                }
1967            }
1968
1969            if (mPendingMO != null) {
1970                // To initiate dialing circuit-switched call
1971                if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
1972                        && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
1973                        && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
1974                    mForegroundCall.detach(mPendingMO);
1975                    removeConnection(mPendingMO);
1976                    mPendingMO.finalize();
1977                    mPendingMO = null;
1978                    mPhone.initiateSilentRedial();
1979                    return;
1980                } else {
1981                    mPendingMO = null;
1982                    int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
1983                    ImsPhoneConnection conn = findConnection(imsCall);
1984
1985                    if(conn != null) {
1986                        conn.setPreciseDisconnectCause(
1987                                getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
1988                    }
1989
1990                    processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
1991                }
1992                mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
1993                        reasonInfo);
1994            }
1995        }
1996
1997        @Override
1998        public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1999            if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
2000
2001            int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
2002            ImsPhoneConnection conn = findConnection(imsCall);
2003            if (DBG) log("cause = " + cause + " conn = " + conn);
2004
2005            if (conn != null) {
2006                android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider();
2007                if (videoProvider instanceof ImsVideoCallProviderWrapper) {
2008                    ImsVideoCallProviderWrapper wrapper = (ImsVideoCallProviderWrapper)
2009                            videoProvider;
2010
2011                    wrapper.removeImsVideoProviderCallback(conn);
2012                }
2013            }
2014            if (mOnHoldToneId == System.identityHashCode(conn)) {
2015                if (conn != null && mOnHoldToneStarted) {
2016                    mPhone.stopOnHoldTone(conn);
2017                }
2018                mOnHoldToneStarted = false;
2019                mOnHoldToneId = -1;
2020            }
2021            if (conn != null) {
2022                if (conn.isPulledCall() && (
2023                        reasonInfo.getCode() == ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC ||
2024                        reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE ||
2025                        reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_FORBIDDEN) &&
2026                        mPhone != null && mPhone.getExternalCallTracker() != null) {
2027
2028                    log("Call pull failed.");
2029                    // Call was being pulled, but the call pull has failed -- inform the associated
2030                    // TelephonyConnection that the pull failed, and provide it with the original
2031                    // external connection which was pulled so that it can be swapped back.
2032                    conn.onCallPullFailed(mPhone.getExternalCallTracker()
2033                            .getConnectionById(conn.getPulledDialogId()));
2034                    // Do not mark as disconnected; the call will just change from being a regular
2035                    // call to being an external call again.
2036                    cause = DisconnectCause.NOT_DISCONNECTED;
2037
2038                } else if (conn.isIncoming() && conn.getConnectTime() == 0
2039                        && cause != DisconnectCause.ANSWERED_ELSEWHERE) {
2040                    // Missed
2041                    if (cause == DisconnectCause.NORMAL) {
2042                        cause = DisconnectCause.INCOMING_MISSED;
2043                    } else {
2044                        cause = DisconnectCause.INCOMING_REJECTED;
2045                    }
2046                    if (DBG) log("Incoming connection of 0 connect time detected - translated " +
2047                            "cause = " + cause);
2048                }
2049            }
2050
2051            if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) {
2052                // Call was terminated while it is merged instead of a remote disconnect.
2053                cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY;
2054            }
2055
2056            mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
2057                    reasonInfo);
2058
2059            if(conn != null) {
2060                conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
2061            }
2062
2063            processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
2064            if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) {
2065                if (mRingingCall.getState().isRinging()) {
2066                    // Drop pending MO. We should address incoming call first
2067                    mPendingMO = null;
2068                } else if (mPendingMO != null) {
2069                    sendEmptyMessage(EVENT_DIAL_PENDINGMO);
2070                }
2071            }
2072
2073            if (mSwitchingFgAndBgCalls) {
2074                if (DBG) {
2075                    log("onCallTerminated: Call terminated in the midst of Switching " +
2076                            "Fg and Bg calls.");
2077                }
2078                // If we are the in midst of swapping FG and BG calls and the call that was
2079                // terminated was the one that we expected to resume, we need to swap the FG and
2080                // BG calls back.
2081                if (imsCall == mCallExpectedToResume) {
2082                    if (DBG) {
2083                        log("onCallTerminated: switching " + mForegroundCall + " with "
2084                                + mBackgroundCall);
2085                    }
2086                    mForegroundCall.switchWith(mBackgroundCall);
2087                }
2088                // This call terminated in the midst of a switch after the other call was held, so
2089                // resume it back to ACTIVE state since the switch failed.
2090                log("onCallTerminated: foreground call in state " + mForegroundCall.getState() +
2091                        " and ringing call in state " + (mRingingCall == null ? "null" :
2092                        mRingingCall.getState().toString()));
2093
2094                if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING ||
2095                        mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
2096                    sendEmptyMessage(EVENT_RESUME_BACKGROUND);
2097                    mSwitchingFgAndBgCalls = false;
2098                    mCallExpectedToResume = null;
2099                }
2100            }
2101
2102            if (mShouldUpdateImsConfigOnDisconnect) {
2103                // Ensure we update the IMS config when the call is disconnected; we delayed this
2104                // because a video call was paused.
2105                ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
2106                mShouldUpdateImsConfigOnDisconnect = false;
2107            }
2108        }
2109
2110        @Override
2111        public void onCallHeld(ImsCall imsCall) {
2112            if (DBG) {
2113                if (mForegroundCall.getImsCall() == imsCall) {
2114                    log("onCallHeld (fg) " + imsCall);
2115                } else if (mBackgroundCall.getImsCall() == imsCall) {
2116                    log("onCallHeld (bg) " + imsCall);
2117                }
2118            }
2119
2120            synchronized (mSyncHold) {
2121                ImsPhoneCall.State oldState = mBackgroundCall.getState();
2122                processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
2123                        DisconnectCause.NOT_DISCONNECTED);
2124
2125                // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to
2126                // processCallStateChange above may have caused the mBackgroundCall and
2127                // mForegroundCall references below to change meaning.  Watch out for this if you
2128                // are reading through this code.
2129                if (oldState == ImsPhoneCall.State.ACTIVE) {
2130                    // Note: This case comes up when we have just held a call in response to a
2131                    // switchWaitingOrHoldingAndActive.  We now need to resume the background call.
2132                    // The EVENT_RESUME_BACKGROUND causes resumeWaitingOrHolding to be called.
2133                    if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
2134                            || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
2135                            sendEmptyMessage(EVENT_RESUME_BACKGROUND);
2136                    } else {
2137                        //when multiple connections belong to background call,
2138                        //only the first callback reaches here
2139                        //otherwise the oldState is already HOLDING
2140                        if (mPendingMO != null) {
2141                            dialPendingMO();
2142                        }
2143
2144                        // In this case there will be no call resumed, so we can assume that we
2145                        // are done switching fg and bg calls now.
2146                        // This may happen if there is no BG call and we are holding a call so that
2147                        // we can dial another one.
2148                        mSwitchingFgAndBgCalls = false;
2149                    }
2150                } else if (oldState == ImsPhoneCall.State.IDLE && mSwitchingFgAndBgCalls) {
2151                    // The other call terminated in the midst of a switch before this call was held,
2152                    // so resume the foreground call back to ACTIVE state since the switch failed.
2153                    if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) {
2154                        sendEmptyMessage(EVENT_RESUME_BACKGROUND);
2155                        mSwitchingFgAndBgCalls = false;
2156                        mCallExpectedToResume = null;
2157                    }
2158                }
2159            }
2160            mMetrics.writeOnImsCallHeld(mPhone.getPhoneId(), imsCall.getCallSession());
2161        }
2162
2163        @Override
2164        public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
2165            if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode());
2166
2167            synchronized (mSyncHold) {
2168                ImsPhoneCall.State bgState = mBackgroundCall.getState();
2169                if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) {
2170                    // disconnected while processing hold
2171                    if (mPendingMO != null) {
2172                        dialPendingMO();
2173                    }
2174                } else if (bgState == ImsPhoneCall.State.ACTIVE) {
2175                    mForegroundCall.switchWith(mBackgroundCall);
2176
2177                    if (mPendingMO != null) {
2178                        mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
2179                        sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
2180                    }
2181                }
2182                mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
2183            }
2184            mMetrics.writeOnImsCallHoldFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
2185                    reasonInfo);
2186        }
2187
2188        @Override
2189        public void onCallResumed(ImsCall imsCall) {
2190            if (DBG) log("onCallResumed");
2191
2192            // If we are the in midst of swapping FG and BG calls and the call we end up resuming
2193            // is not the one we expected, we likely had a resume failure and we need to swap the
2194            // FG and BG calls back.
2195            if (mSwitchingFgAndBgCalls) {
2196                if (imsCall != mCallExpectedToResume) {
2197                    // If the call which resumed isn't as expected, we need to swap back to the
2198                    // previous configuration; the swap has failed.
2199                    if (DBG) {
2200                        log("onCallResumed : switching " + mForegroundCall + " with "
2201                                + mBackgroundCall);
2202                    }
2203                    mForegroundCall.switchWith(mBackgroundCall);
2204                } else {
2205                    // The call which resumed is the one we expected to resume, so we can clear out
2206                    // the mSwitchingFgAndBgCalls flag.
2207                    if (DBG) {
2208                        log("onCallResumed : expected call resumed.");
2209                    }
2210                }
2211                mSwitchingFgAndBgCalls = false;
2212                mCallExpectedToResume = null;
2213            }
2214            processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
2215                    DisconnectCause.NOT_DISCONNECTED);
2216            mMetrics.writeOnImsCallResumed(mPhone.getPhoneId(), imsCall.getCallSession());
2217        }
2218
2219        @Override
2220        public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
2221            if (mSwitchingFgAndBgCalls) {
2222                // If we are in the midst of swapping the FG and BG calls and
2223                // we got a resume fail, we need to swap back the FG and BG calls.
2224                // Since the FG call was held, will also try to resume the same.
2225                if (imsCall == mCallExpectedToResume) {
2226                    if (DBG) {
2227                        log("onCallResumeFailed : switching " + mForegroundCall + " with "
2228                                + mBackgroundCall);
2229                    }
2230                    mForegroundCall.switchWith(mBackgroundCall);
2231                    if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) {
2232                            sendEmptyMessage(EVENT_RESUME_BACKGROUND);
2233                    }
2234                }
2235
2236                //Call swap is done, reset the relevant variables
2237                mCallExpectedToResume = null;
2238                mSwitchingFgAndBgCalls = false;
2239            }
2240            mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME);
2241            mMetrics.writeOnImsCallResumeFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
2242                    reasonInfo);
2243        }
2244
2245        @Override
2246        public void onCallResumeReceived(ImsCall imsCall) {
2247            if (DBG) log("onCallResumeReceived");
2248            ImsPhoneConnection conn = findConnection(imsCall);
2249            if (conn != null) {
2250                if (mOnHoldToneStarted) {
2251                    mPhone.stopOnHoldTone(conn);
2252                    mOnHoldToneStarted = false;
2253                }
2254
2255                conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
2256            }
2257
2258            SuppServiceNotification supp = new SuppServiceNotification();
2259            // Type of notification: 0 = MO; 1 = MT
2260            // Refer SuppServiceNotification class documentation.
2261            supp.notificationType = 1;
2262            supp.code = SuppServiceNotification.MT_CODE_CALL_RETRIEVED;
2263            mPhone.notifySuppSvcNotification(supp);
2264            mMetrics.writeOnImsCallResumeReceived(mPhone.getPhoneId(), imsCall.getCallSession());
2265        }
2266
2267        @Override
2268        public void onCallHoldReceived(ImsCall imsCall) {
2269            if (DBG) log("onCallHoldReceived");
2270
2271            ImsPhoneConnection conn = findConnection(imsCall);
2272            if (conn != null) {
2273                if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall) &&
2274                        conn.getState() == ImsPhoneCall.State.ACTIVE) {
2275                    mPhone.startOnHoldTone(conn);
2276                    mOnHoldToneStarted = true;
2277                    mOnHoldToneId = System.identityHashCode(conn);
2278                }
2279
2280                conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
2281            }
2282
2283            SuppServiceNotification supp = new SuppServiceNotification();
2284            // Type of notification: 0 = MO; 1 = MT
2285            // Refer SuppServiceNotification class documentation.
2286            supp.notificationType = 1;
2287            supp.code = SuppServiceNotification.MT_CODE_CALL_ON_HOLD;
2288            mPhone.notifySuppSvcNotification(supp);
2289            mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession());
2290        }
2291
2292        @Override
2293        public void onCallSuppServiceReceived(ImsCall call,
2294                ImsSuppServiceNotification suppServiceInfo) {
2295            if (DBG) log("onCallSuppServiceReceived: suppServiceInfo=" + suppServiceInfo);
2296
2297            SuppServiceNotification supp = new SuppServiceNotification();
2298            supp.notificationType = suppServiceInfo.notificationType;
2299            supp.code = suppServiceInfo.code;
2300            supp.index = suppServiceInfo.index;
2301            supp.number = suppServiceInfo.number;
2302            supp.history = suppServiceInfo.history;
2303
2304            mPhone.notifySuppSvcNotification(supp);
2305        }
2306
2307        @Override
2308        public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) {
2309            if (DBG) log("onCallMerged");
2310
2311            ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall();
2312            ImsPhoneConnection peerConnection = findConnection(peerCall);
2313            ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null
2314                    : peerConnection.getCall();
2315
2316            if (swapCalls) {
2317                switchAfterConferenceSuccess();
2318            }
2319            foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE);
2320
2321            try {
2322                final ImsPhoneConnection conn = findConnection(call);
2323                log("onCallMerged: ImsPhoneConnection=" + conn);
2324                log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
2325                setVideoCallProvider(conn, call);
2326                log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
2327            } catch (Exception e) {
2328                loge("onCallMerged: exception " + e);
2329            }
2330
2331            // After merge complete, update foreground as Active
2332            // and background call as Held, if background call exists
2333            processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE,
2334                    DisconnectCause.NOT_DISCONNECTED);
2335            if (peerConnection != null) {
2336                processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING,
2337                    DisconnectCause.NOT_DISCONNECTED);
2338            }
2339
2340            // Check if the merge was requested by an existing conference call. In that
2341            // case, no further action is required.
2342            if (!call.isMergeRequestedByConf()) {
2343                log("onCallMerged :: calling onMultipartyStateChanged()");
2344                onMultipartyStateChanged(call, true);
2345            } else {
2346                log("onCallMerged :: Merge requested by existing conference.");
2347                // Reset the flag.
2348                call.resetIsMergeRequestedByConf(false);
2349            }
2350            logState();
2351        }
2352
2353        @Override
2354        public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
2355            if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo);
2356
2357            // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog
2358            // We should move this into the InCallService so that it is handled appropriately
2359            // based on the user facing UI.
2360            mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
2361
2362            // Start plumbing this even through Telecom so other components can take
2363            // appropriate action.
2364            ImsPhoneConnection conn = findConnection(call);
2365            if (conn != null) {
2366                conn.onConferenceMergeFailed();
2367                conn.handleMergeComplete();
2368            }
2369        }
2370
2371        /**
2372         * Called when the state of IMS conference participant(s) has changed.
2373         *
2374         * @param call the call object that carries out the IMS call.
2375         * @param participants the participant(s) and their new state information.
2376         */
2377        @Override
2378        public void onConferenceParticipantsStateChanged(ImsCall call,
2379                List<ConferenceParticipant> participants) {
2380            if (DBG) log("onConferenceParticipantsStateChanged");
2381
2382            ImsPhoneConnection conn = findConnection(call);
2383            if (conn != null) {
2384                conn.updateConferenceParticipants(participants);
2385            }
2386        }
2387
2388        @Override
2389        public void onCallSessionTtyModeReceived(ImsCall call, int mode) {
2390            mPhone.onTtyModeReceived(mode);
2391        }
2392
2393        @Override
2394        public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
2395            ImsReasonInfo reasonInfo) {
2396            if (DBG) {
2397                log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
2398                    targetAccessTech + ", reasonInfo=" + reasonInfo);
2399            }
2400
2401            // Only consider it a valid handover to WIFI if the source radio tech is known.
2402            boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
2403                    && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
2404                    && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
2405            if (isHandoverToWifi) {
2406                // If we handed over to wifi successfully, don't check for failure in the future.
2407                removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
2408            }
2409
2410            // Only consider it a handover from WIFI if the source and target radio tech is known.
2411            boolean isHandoverFromWifi = srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
2412                    && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
2413                    && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
2414            if (mNotifyHandoverVideoFromWifiToLTE && isHandoverFromWifi && imsCall.isVideoCall()) {
2415                log("onCallHandover :: notifying of WIFI to LTE handover.");
2416                ImsPhoneConnection conn = findConnection(imsCall);
2417                if (conn != null) {
2418                    conn.onConnectionEvent(
2419                            TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
2420                } else {
2421                    loge("onCallHandover :: failed to notify of handover; connection is null.");
2422                }
2423            }
2424
2425            mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
2426                    TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(),
2427                    srcAccessTech, targetAccessTech, reasonInfo);
2428        }
2429
2430        @Override
2431        public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
2432            ImsReasonInfo reasonInfo) {
2433            if (DBG) {
2434                log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech +
2435                    ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo);
2436            }
2437            mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
2438                    TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED,
2439                    imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo);
2440
2441            boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN &&
2442                    targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
2443            ImsPhoneConnection conn = findConnection(imsCall);
2444            if (conn != null && isHandoverToWifi) {
2445                log("onCallHandoverFailed - handover to WIFI Failed");
2446
2447                // If we know we failed to handover, don't check for failure in the future.
2448                removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
2449
2450                if (mNotifyVtHandoverToWifiFail) {
2451                    // Only notify others if carrier config indicates to do so.
2452                    conn.onHandoverToWifiFailed();
2453                }
2454            }
2455        }
2456
2457        @Override
2458        public void onRttModifyRequestReceived(ImsCall imsCall) {
2459            ImsPhoneConnection conn = findConnection(imsCall);
2460            if (conn != null) {
2461                conn.onRttModifyRequestReceived();
2462            }
2463        }
2464
2465        @Override
2466        public void onRttModifyResponseReceived(ImsCall imsCall, int status) {
2467            ImsPhoneConnection conn = findConnection(imsCall);
2468            if (conn != null) {
2469                conn.onRttModifyResponseReceived(status);
2470                if (status ==
2471                        android.telecom.Connection.RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
2472                    conn.startRttTextProcessing();
2473                }
2474            }
2475        }
2476
2477        @Override
2478        public void onRttMessageReceived(ImsCall imsCall, String message) {
2479            ImsPhoneConnection conn = findConnection(imsCall);
2480            if (conn != null) {
2481                conn.onRttMessageReceived(message);
2482            }
2483        }
2484
2485        /**
2486         * Handles a change to the multiparty state for an {@code ImsCall}.  Notifies the associated
2487         * {@link ImsPhoneConnection} of the change.
2488         *
2489         * @param imsCall The IMS call.
2490         * @param isMultiParty {@code true} if the call became multiparty, {@code false}
2491         *      otherwise.
2492         */
2493        @Override
2494        public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) {
2495            if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N"));
2496
2497            ImsPhoneConnection conn = findConnection(imsCall);
2498            if (conn != null) {
2499                conn.updateMultipartyState(isMultiParty);
2500            }
2501        }
2502    };
2503
2504    /**
2505     * Listen to the IMS call state change
2506     */
2507    private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() {
2508        @Override
2509        public void onCallStarted(ImsCall imsCall) {
2510            if (DBG) log("mImsUssdListener onCallStarted");
2511
2512            if (imsCall == mUssdSession) {
2513                if (mPendingUssd != null) {
2514                    AsyncResult.forMessage(mPendingUssd);
2515                    mPendingUssd.sendToTarget();
2516                    mPendingUssd = null;
2517                }
2518            }
2519        }
2520
2521        @Override
2522        public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
2523            if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode());
2524
2525            onCallTerminated(imsCall, reasonInfo);
2526        }
2527
2528        @Override
2529        public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
2530            if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
2531            removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
2532
2533            if (imsCall == mUssdSession) {
2534                mUssdSession = null;
2535                if (mPendingUssd != null) {
2536                    CommandException ex =
2537                            new CommandException(CommandException.Error.GENERIC_FAILURE);
2538                    AsyncResult.forMessage(mPendingUssd, null, ex);
2539                    mPendingUssd.sendToTarget();
2540                    mPendingUssd = null;
2541                }
2542            }
2543            imsCall.close();
2544        }
2545
2546        @Override
2547        public void onCallUssdMessageReceived(ImsCall call,
2548                int mode, String ussdMessage) {
2549            if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);
2550
2551            int ussdMode = -1;
2552
2553            switch(mode) {
2554                case ImsCall.USSD_MODE_REQUEST:
2555                    ussdMode = CommandsInterface.USSD_MODE_REQUEST;
2556                    break;
2557
2558                case ImsCall.USSD_MODE_NOTIFY:
2559                    ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
2560                    break;
2561            }
2562
2563            mPhone.onIncomingUSSD(ussdMode, ussdMessage);
2564        }
2565    };
2566
2567    /**
2568     * Listen to the IMS service state change
2569     *
2570     */
2571    private ImsConnectionStateListener mImsConnectionStateListener =
2572        new ImsConnectionStateListener() {
2573        @Override
2574        public void onImsConnected(int imsRadioTech) {
2575            if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
2576            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
2577            mPhone.setImsRegistered(true);
2578            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
2579                    ImsConnectionState.State.CONNECTED, null);
2580        }
2581
2582        @Override
2583        public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
2584            if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
2585            resetImsCapabilities();
2586            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2587            mPhone.setImsRegistered(false);
2588            mPhone.processDisconnectReason(imsReasonInfo);
2589            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
2590                    ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
2591        }
2592
2593        @Override
2594        public void onImsProgressing(int imsRadioTech) {
2595            if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
2596            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2597            mPhone.setImsRegistered(false);
2598            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
2599                    ImsConnectionState.State.PROGRESSING, null);
2600        }
2601
2602        @Override
2603        public void onImsResumed() {
2604            if (DBG) log("onImsResumed");
2605            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
2606            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
2607                    ImsConnectionState.State.RESUMED, null);
2608        }
2609
2610        @Override
2611        public void onImsSuspended() {
2612            if (DBG) log("onImsSuspended");
2613            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2614            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
2615                    ImsConnectionState.State.SUSPENDED, null);
2616
2617        }
2618
2619        @Override
2620        public void onFeatureCapabilityChanged(int serviceClass,
2621                int[] enabledFeatures, int[] disabledFeatures) {
2622            if (DBG) log("onFeatureCapabilityChanged");
2623            SomeArgs args = SomeArgs.obtain();
2624            args.argi1 = serviceClass;
2625            args.arg1 = enabledFeatures;
2626            args.arg2 = disabledFeatures;
2627            // Remove any pending updates; they're already stale, so no need to process them.
2628            removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
2629            obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
2630        }
2631
2632        @Override
2633        public void onVoiceMessageCountChanged(int count) {
2634            if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
2635            mPhone.mDefaultPhone.setVoiceMessageCount(count);
2636        }
2637
2638        @Override
2639        public void registrationAssociatedUriChanged(Uri[] uris) {
2640            if (DBG) log("registrationAssociatedUriChanged");
2641            mPhone.setCurrentSubscriberUris(uris);
2642        }
2643    };
2644
2645    private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() {
2646        @Override
2647        public void onGetFeatureResponse(int feature, int network, int value, int status) {}
2648
2649        @Override
2650        public void onSetFeatureResponse(int feature, int network, int value, int status) {
2651            mMetrics.writeImsSetFeatureValue(
2652                    mPhone.getPhoneId(), feature, network, value, status);
2653        }
2654
2655        @Override
2656        public void onGetVideoQuality(int status, int quality) {}
2657
2658        @Override
2659        public void onSetVideoQuality(int status) {}
2660
2661    };
2662
2663    public ImsUtInterface getUtInterface() throws ImsException {
2664        if (mImsManager == null) {
2665            throw getImsManagerIsNullException();
2666        }
2667
2668        ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration();
2669        return ut;
2670    }
2671
2672    private void transferHandoverConnections(ImsPhoneCall call) {
2673        if (call.mConnections != null) {
2674            for (Connection c : call.mConnections) {
2675                c.mPreHandoverState = call.mState;
2676                log ("Connection state before handover is " + c.getStateBeforeHandover());
2677            }
2678        }
2679        if (mHandoverCall.mConnections == null ) {
2680            mHandoverCall.mConnections = call.mConnections;
2681        } else { // Multi-call SRVCC
2682            mHandoverCall.mConnections.addAll(call.mConnections);
2683        }
2684        if (mHandoverCall.mConnections != null) {
2685            if (call.getImsCall() != null) {
2686                call.getImsCall().close();
2687            }
2688            for (Connection c : mHandoverCall.mConnections) {
2689                ((ImsPhoneConnection)c).changeParent(mHandoverCall);
2690                ((ImsPhoneConnection)c).releaseWakeLock();
2691            }
2692        }
2693        if (call.getState().isAlive()) {
2694            log ("Call is alive and state is " + call.mState);
2695            mHandoverCall.mState = call.mState;
2696        }
2697        call.mConnections.clear();
2698        call.mState = ImsPhoneCall.State.IDLE;
2699    }
2700
2701    /* package */
2702    void notifySrvccState(Call.SrvccState state) {
2703        if (DBG) log("notifySrvccState state=" + state);
2704
2705        mSrvccState = state;
2706
2707        if (mSrvccState == Call.SrvccState.COMPLETED) {
2708            transferHandoverConnections(mForegroundCall);
2709            transferHandoverConnections(mBackgroundCall);
2710            transferHandoverConnections(mRingingCall);
2711        }
2712    }
2713
2714    //****** Overridden from Handler
2715
2716    @Override
2717    public void
2718    handleMessage (Message msg) {
2719        AsyncResult ar;
2720        if (DBG) log("handleMessage what=" + msg.what);
2721
2722        switch (msg.what) {
2723            case EVENT_HANGUP_PENDINGMO:
2724                if (mPendingMO != null) {
2725                    mPendingMO.onDisconnect();
2726                    removeConnection(mPendingMO);
2727                    mPendingMO = null;
2728                }
2729                mPendingIntentExtras = null;
2730                updatePhoneState();
2731                mPhone.notifyPreciseCallStateChanged();
2732                break;
2733            case EVENT_RESUME_BACKGROUND:
2734                try {
2735                    resumeWaitingOrHolding();
2736                } catch (CallStateException e) {
2737                    if (Phone.DEBUG_PHONE) {
2738                        loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e);
2739                    }
2740                }
2741                break;
2742            case EVENT_DIAL_PENDINGMO:
2743                dialInternal(mPendingMO, mClirMode, mPendingCallVideoState, mPendingIntentExtras);
2744                mPendingIntentExtras = null;
2745                break;
2746
2747            case EVENT_EXIT_ECBM_BEFORE_PENDINGMO:
2748                if (mPendingMO != null) {
2749                    //Send ECBM exit request
2750                    try {
2751                        getEcbmInterface().exitEmergencyCallbackMode();
2752                        mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
2753                        pendingCallClirMode = mClirMode;
2754                        pendingCallInEcm = true;
2755                    } catch (ImsException e) {
2756                        e.printStackTrace();
2757                        mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
2758                        sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
2759                    }
2760                }
2761                break;
2762
2763            case EVENT_EXIT_ECM_RESPONSE_CDMA:
2764                // no matter the result, we still do the same here
2765                if (pendingCallInEcm) {
2766                    dialInternal(mPendingMO, pendingCallClirMode,
2767                            mPendingCallVideoState, mPendingIntentExtras);
2768                    mPendingIntentExtras = null;
2769                    pendingCallInEcm = false;
2770                }
2771                mPhone.unsetOnEcbModeExitResponse(this);
2772                break;
2773            case EVENT_VT_DATA_USAGE_UPDATE:
2774                ar = (AsyncResult) msg.obj;
2775                ImsCall call = (ImsCall) ar.userObj;
2776                Long usage = (long) ar.result;
2777                log("VT data usage update. usage = " + usage + ", imsCall = " + call);
2778                if (usage > 0) {
2779                    updateVtDataUsage(call, usage);
2780                }
2781                break;
2782            case EVENT_DATA_ENABLED_CHANGED:
2783                ar = (AsyncResult) msg.obj;
2784                if (ar.result instanceof Pair) {
2785                    Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
2786                    onDataEnabledChanged(p.first, p.second);
2787                }
2788                break;
2789            case EVENT_GET_IMS_SERVICE:
2790                try {
2791                    getImsService();
2792                } catch (ImsException e) {
2793                    loge("getImsService: " + e);
2794                    retryGetImsService();
2795                }
2796                break;
2797            case EVENT_CHECK_FOR_WIFI_HANDOVER:
2798                if (msg.obj instanceof ImsCall) {
2799                    ImsCall imsCall = (ImsCall) msg.obj;
2800                    if (!imsCall.isWifiCall()) {
2801                        // Call did not handover to wifi, notify of handover failure.
2802                        ImsPhoneConnection conn = findConnection(imsCall);
2803                        if (conn != null) {
2804                            conn.onHandoverToWifiFailed();
2805                        }
2806                    }
2807                }
2808                break;
2809            case EVENT_ON_FEATURE_CAPABILITY_CHANGED: {
2810                SomeArgs args = (SomeArgs) msg.obj;
2811                try {
2812                    int serviceClass = args.argi1;
2813                    int[] enabledFeatures = (int[]) args.arg1;
2814                    int[] disabledFeatures = (int[]) args.arg2;
2815                    handleFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
2816                } finally {
2817                    args.recycle();
2818                }
2819                break;
2820            }
2821        }
2822    }
2823
2824    /**
2825     * Update video call data usage
2826     *
2827     * @param call The IMS call
2828     * @param dataUsage The aggregated data usage for the call
2829     */
2830    private void updateVtDataUsage(ImsCall call, long dataUsage) {
2831        long oldUsage = 0L;
2832        if (mVtDataUsageMap.containsKey(call.uniqueId)) {
2833            oldUsage = mVtDataUsageMap.get(call.uniqueId);
2834        }
2835
2836        long delta = dataUsage - oldUsage;
2837        mVtDataUsageMap.put(call.uniqueId, dataUsage);
2838
2839        log("updateVtDataUsage: call=" + call + ", delta=" + delta);
2840
2841        long currentTime = SystemClock.elapsedRealtime();
2842        int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0;
2843
2844        // Create the snapshot of total video call data usage.
2845        NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1);
2846        vtDataUsageSnapshot.combineAllValues(mVtDataUsageSnapshot);
2847        // Since the modem only reports the total vt data usage rather than rx/tx separately,
2848        // the only thing we can do here is splitting the usage into half rx and half tx.
2849        // Uid -1 indicates this is for the overall device data usage.
2850        vtDataUsageSnapshot.combineValues(new NetworkStats.Entry(
2851                NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND,
2852                NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 0, delta / 2, 0, 0));
2853        mVtDataUsageSnapshot = vtDataUsageSnapshot;
2854
2855        // Create the snapshot of video call data usage per dialer. combineValues will create
2856        // a separate entry if uid is different from the previous snapshot.
2857        NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
2858        vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot);
2859        // Since the modem only reports the total vt data usage rather than rx/tx separately,
2860        // the only thing we can do here is splitting the usage into half rx and half tx.
2861        vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
2862                NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(),
2863                NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, 1, isRoaming, delta / 2,
2864                0, delta / 2, 0, 0));
2865        mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot;
2866    }
2867
2868    @Override
2869    protected void log(String msg) {
2870        Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
2871    }
2872
2873    protected void loge(String msg) {
2874        Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
2875    }
2876
2877    /**
2878     * Logs the current state of the ImsPhoneCallTracker.  Useful for debugging issues with
2879     * call tracking.
2880     */
2881    /* package */
2882    void logState() {
2883        if (!VERBOSE_STATE_LOGGING) {
2884            return;
2885        }
2886
2887        StringBuilder sb = new StringBuilder();
2888        sb.append("Current IMS PhoneCall State:\n");
2889        sb.append(" Foreground: ");
2890        sb.append(mForegroundCall);
2891        sb.append("\n");
2892        sb.append(" Background: ");
2893        sb.append(mBackgroundCall);
2894        sb.append("\n");
2895        sb.append(" Ringing: ");
2896        sb.append(mRingingCall);
2897        sb.append("\n");
2898        sb.append(" Handover: ");
2899        sb.append(mHandoverCall);
2900        sb.append("\n");
2901        Rlog.v(LOG_TAG, sb.toString());
2902    }
2903
2904    @Override
2905    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2906        pw.println("ImsPhoneCallTracker extends:");
2907        super.dump(fd, pw, args);
2908        pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
2909        pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
2910        pw.println(" mRingingCall=" + mRingingCall);
2911        pw.println(" mForegroundCall=" + mForegroundCall);
2912        pw.println(" mBackgroundCall=" + mBackgroundCall);
2913        pw.println(" mHandoverCall=" + mHandoverCall);
2914        pw.println(" mPendingMO=" + mPendingMO);
2915        //pw.println(" mHangupPendingMO=" + mHangupPendingMO);
2916        pw.println(" mPhone=" + mPhone);
2917        pw.println(" mDesiredMute=" + mDesiredMute);
2918        pw.println(" mState=" + mState);
2919        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
2920            pw.println(" " + mImsFeatureStrings[i] + ": "
2921                    + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled"));
2922        }
2923        pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
2924        pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
2925        pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
2926
2927        pw.flush();
2928        pw.println("++++++++++++++++++++++++++++++++");
2929
2930        try {
2931            if (mImsManager != null) {
2932                mImsManager.dump(fd, pw, args);
2933            }
2934        } catch (Exception e) {
2935            e.printStackTrace();
2936        }
2937
2938        if (mConnections != null && mConnections.size() > 0) {
2939            pw.println("mConnections:");
2940            for (int i = 0; i < mConnections.size(); i++) {
2941                pw.println("  [" + i + "]: " + mConnections.get(i));
2942            }
2943        }
2944    }
2945
2946    @Override
2947    protected void handlePollCalls(AsyncResult ar) {
2948    }
2949
2950    /* package */
2951    ImsEcbm getEcbmInterface() throws ImsException {
2952        if (mImsManager == null) {
2953            throw getImsManagerIsNullException();
2954        }
2955
2956        ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId);
2957        return ecbm;
2958    }
2959
2960    /* package */
2961    ImsMultiEndpoint getMultiEndpointInterface() throws ImsException {
2962        if (mImsManager == null) {
2963            throw getImsManagerIsNullException();
2964        }
2965
2966        try {
2967            return mImsManager.getMultiEndpointInterface(mServiceId);
2968        } catch (ImsException e) {
2969            if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) {
2970                return null;
2971            } else {
2972                throw e;
2973            }
2974
2975        }
2976    }
2977
2978    public boolean isInEmergencyCall() {
2979        return mIsInEmergencyCall;
2980    }
2981
2982    public boolean isVolteEnabled() {
2983        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE];
2984    }
2985
2986    public boolean isVowifiEnabled() {
2987        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI];
2988    }
2989
2990    public boolean isVideoCallEnabled() {
2991        return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE]
2992                || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]);
2993    }
2994
2995    @Override
2996    public PhoneConstants.State getState() {
2997        return mState;
2998    }
2999
3000    private void retryGetImsService() {
3001        // The binder connection is already up. Do not try to get it again.
3002        if (mImsManager.isServiceAvailable()) {
3003            return;
3004        }
3005        //Leave mImsManager as null, then CallStateException will be thrown when dialing
3006        mImsManager = null;
3007        // Exponential backoff during retry, limited to 32 seconds.
3008        loge("getImsService: Retrying getting ImsService...");
3009        removeMessages(EVENT_GET_IMS_SERVICE);
3010        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());
3011    }
3012
3013    private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
3014            throws RemoteException {
3015        IImsVideoCallProvider imsVideoCallProvider =
3016                imsCall.getCallSession().getVideoCallProvider();
3017        if (imsVideoCallProvider != null) {
3018            // TODO: Remove this when we can better formalize the format of session modify requests.
3019            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
3020                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
3021
3022            ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
3023                    new ImsVideoCallProviderWrapper(imsVideoCallProvider);
3024            if (useVideoPauseWorkaround) {
3025                imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround);
3026            }
3027            conn.setVideoProvider(imsVideoCallProviderWrapper);
3028            imsVideoCallProviderWrapper.registerForDataUsageUpdate
3029                    (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall);
3030            imsVideoCallProviderWrapper.addImsVideoProviderCallback(conn);
3031        }
3032    }
3033
3034    public boolean isUtEnabled() {
3035        return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE]
3036            || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI]);
3037    }
3038
3039    /**
3040     * Given a call subject, removes any characters considered by the current carrier to be
3041     * invalid, as well as escaping (using \) any characters which the carrier requires to be
3042     * escaped.
3043     *
3044     * @param callSubject The call subject.
3045     * @return The call subject with invalid characters removed and escaping applied as required.
3046     */
3047    private String cleanseInstantLetteringMessage(String callSubject) {
3048        if (TextUtils.isEmpty(callSubject)) {
3049            return callSubject;
3050        }
3051
3052        // Get the carrier config for the current sub.
3053        CarrierConfigManager configMgr = (CarrierConfigManager)
3054                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
3055        // Bail if we can't find the carrier config service.
3056        if (configMgr == null) {
3057            return callSubject;
3058        }
3059
3060        PersistableBundle carrierConfig = configMgr.getConfigForSubId(mPhone.getSubId());
3061        // Bail if no carrier config found.
3062        if (carrierConfig == null) {
3063            return callSubject;
3064        }
3065
3066        // Try to replace invalid characters
3067        String invalidCharacters = carrierConfig.getString(
3068                CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING);
3069        if (!TextUtils.isEmpty(invalidCharacters)) {
3070            callSubject = callSubject.replaceAll(invalidCharacters, "");
3071        }
3072
3073        // Try to escape characters which need to be escaped.
3074        String escapedCharacters = carrierConfig.getString(
3075                CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING);
3076        if (!TextUtils.isEmpty(escapedCharacters)) {
3077            callSubject = escapeChars(escapedCharacters, callSubject);
3078        }
3079        return callSubject;
3080    }
3081
3082    /**
3083     * Given a source string, return a string where a set of characters are escaped using the
3084     * backslash character.
3085     *
3086     * @param toEscape The characters to escape with a backslash.
3087     * @param source The source string.
3088     * @return The source string with characters escaped.
3089     */
3090    private String escapeChars(String toEscape, String source) {
3091        StringBuilder escaped = new StringBuilder();
3092        for (char c : source.toCharArray()) {
3093            if (toEscape.contains(Character.toString(c))) {
3094                escaped.append("\\");
3095            }
3096            escaped.append(c);
3097        }
3098
3099        return escaped.toString();
3100    }
3101
3102    /**
3103     * Initiates a pull of an external call.
3104     *
3105     * Initiates a pull by making a dial request with the {@link ImsCallProfile#EXTRA_IS_CALL_PULL}
3106     * extra specified.  We call {@link ImsPhone#notifyUnknownConnection(Connection)} which notifies
3107     * Telecom of the new dialed connection.  The
3108     * {@code PstnIncomingCallNotifier#maybeSwapWithUnknownConnection} logic ensures that the new
3109     * {@link ImsPhoneConnection} resulting from the dial gets swapped with the
3110     * {@link ImsExternalConnection}, which effectively makes the external call become a regular
3111     * call.  Magic!
3112     *
3113     * @param number The phone number of the call to be pulled.
3114     * @param videoState The desired video state of the pulled call.
3115     * @param dialogId The {@link ImsExternalConnection#getCallId()} dialog id associated with the
3116     *                 call which is being pulled.
3117     */
3118    @Override
3119    public void pullExternalCall(String number, int videoState, int dialogId) {
3120        Bundle extras = new Bundle();
3121        extras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, true);
3122        extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID, dialogId);
3123        try {
3124            Connection connection = dial(number, videoState, extras);
3125            mPhone.notifyUnknownConnection(connection);
3126        } catch (CallStateException e) {
3127            loge("pullExternalCall failed - " + e);
3128        }
3129    }
3130
3131    private ImsException getImsManagerIsNullException() {
3132        return new ImsException("no ims manager", ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
3133    }
3134
3135    /**
3136     * Determines if answering an incoming call will cause the active call to be disconnected.
3137     * <p>
3138     * This will be the case if
3139     * {@link CarrierConfigManager#KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL} is
3140     * {@code true} for the carrier, the active call is a video call over WIFI, and the incoming
3141     * call is an audio call.
3142     *
3143     * @param activeCall The active call.
3144     * @param incomingCall The incoming call.
3145     * @return {@code true} if answering the incoming call will cause the active call to be
3146     *      disconnected, {@code false} otherwise.
3147     */
3148    private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall,
3149            ImsCall incomingCall) {
3150
3151        if (activeCall == null || incomingCall == null) {
3152            return false;
3153        }
3154
3155        if (!mDropVideoCallWhenAnsweringAudioCall) {
3156            return false;
3157        }
3158
3159        boolean isActiveCallVideo = activeCall.isVideoCall() ||
3160                (mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall());
3161        boolean isActiveCallOnWifi = activeCall.isWifiCall();
3162        boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform(mPhone.getContext()) &&
3163                mImsManager.isWfcEnabledByUser(mPhone.getContext());
3164        boolean isIncomingCallAudio = !incomingCall.isVideoCall();
3165        log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo +
3166                " isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" +
3167                isIncomingCallAudio + " isVowifiEnabled=" + isVoWifiEnabled);
3168
3169        return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled;
3170    }
3171
3172    /**
3173     * Get aggregated video call data usage since boot.
3174     *
3175     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
3176     * @return Snapshot of video call data usage
3177     */
3178    public NetworkStats getVtDataUsage(boolean perUidStats) {
3179
3180        // If there is an ongoing VT call, request the latest VT usage from the modem. The latest
3181        // usage will return asynchronously so it won't be counted in this round, but it will be
3182        // eventually counted when next getVtDataUsage is called.
3183        if (mState != PhoneConstants.State.IDLE) {
3184            for (ImsPhoneConnection conn : mConnections) {
3185                android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider();
3186                if (videoProvider != null) {
3187                    videoProvider.onRequestConnectionDataUsage();
3188                }
3189            }
3190        }
3191
3192        return perUidStats ? mVtDataUsageUidSnapshot : mVtDataUsageSnapshot;
3193    }
3194
3195    public void registerPhoneStateListener(PhoneStateListener listener) {
3196        mPhoneStateListeners.add(listener);
3197    }
3198
3199    public void unregisterPhoneStateListener(PhoneStateListener listener) {
3200        mPhoneStateListeners.remove(listener);
3201    }
3202
3203    /**
3204     * Notifies local telephony listeners of changes to the IMS phone state.
3205     *
3206     * @param oldState The old state.
3207     * @param newState The new state.
3208     */
3209    private void notifyPhoneStateChanged(PhoneConstants.State oldState,
3210            PhoneConstants.State newState) {
3211
3212        for (PhoneStateListener listener : mPhoneStateListeners) {
3213            listener.onPhoneStateChanged(oldState, newState);
3214        }
3215    }
3216
3217    /** Modify video call to a new video state.
3218     *
3219     * @param imsCall IMS call to be modified
3220     * @param newVideoState New video state. (Refer to VideoProfile)
3221     */
3222    private void modifyVideoCall(ImsCall imsCall, int newVideoState) {
3223        ImsPhoneConnection conn = findConnection(imsCall);
3224        if (conn != null) {
3225            int oldVideoState = conn.getVideoState();
3226            if (conn.getVideoProvider() != null) {
3227                conn.getVideoProvider().onSendSessionModifyRequest(
3228                        new VideoProfile(oldVideoState), new VideoProfile(newVideoState));
3229            }
3230        }
3231    }
3232
3233    /**
3234     * Handler of data enabled changed event
3235     * @param enabled True if data is enabled, otherwise disabled.
3236     * @param reason Reason for data enabled/disabled (see {@code REASON_*} in
3237     *      {@link DataEnabledSettings}.
3238     */
3239    private void onDataEnabledChanged(boolean enabled, int reason) {
3240
3241        log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
3242
3243        ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
3244        mIsDataEnabled = enabled;
3245
3246        if (mIgnoreDataEnabledChangedForVideoCalls) {
3247            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
3248            return;
3249        }
3250
3251        if (mIgnoreDataEnabledChangedForVideoCalls) {
3252            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
3253            return;
3254        }
3255
3256        if (!enabled) {
3257            int reasonCode;
3258            if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
3259                reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
3260            } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
3261                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
3262            } else {
3263                // Unexpected code, default to data disabled.
3264                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
3265            }
3266
3267            // If data is disabled while there are ongoing VT calls which are not taking place over
3268            // wifi, then they should be disconnected to prevent the user from incurring further
3269            // data charges.
3270            for (ImsPhoneConnection conn : mConnections) {
3271                ImsCall imsCall = conn.getImsCall();
3272                if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
3273                    if (conn.hasCapabilities(
3274                            Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
3275                                    Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
3276
3277                        // If the carrier supports downgrading to voice, then we can simply issue a
3278                        // downgrade to voice instead of terminating the call.
3279                        if (reasonCode == ImsReasonInfo.CODE_DATA_DISABLED) {
3280                            conn.onConnectionEvent(TelephonyManager.EVENT_DOWNGRADE_DATA_DISABLED,
3281                                    null);
3282                        } else if (reasonCode == ImsReasonInfo.CODE_DATA_LIMIT_REACHED) {
3283                            conn.onConnectionEvent(
3284                                    TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
3285                        }
3286                        modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
3287                    } else if (mSupportPauseVideo) {
3288                        // The carrier supports video pause signalling, so pause the video.
3289                        mShouldUpdateImsConfigOnDisconnect = true;
3290                        conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
3291                    } else {
3292                        // At this point the only choice we have is to terminate the call.
3293                        try {
3294                            imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
3295                        } catch (ImsException ie) {
3296                            loge("Couldn't terminate call " + imsCall);
3297                        }
3298                    }
3299                }
3300            }
3301        } else if (mSupportPauseVideo) {
3302            // Data was re-enabled, so un-pause previously paused video calls.
3303            for (ImsPhoneConnection conn : mConnections) {
3304                // If video is paused, check to see if there are any pending pauses due to enabled
3305                // state of data changing.
3306                log("onDataEnabledChanged - resuming " + conn);
3307                if (VideoProfile.isPaused(conn.getVideoState()) &&
3308                        conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
3309                    // The data enabled state was a cause of a pending pause, so potentially
3310                    // resume the video now.
3311                    conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
3312                }
3313            }
3314            mShouldUpdateImsConfigOnDisconnect = false;
3315        }
3316
3317
3318        // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
3319        // the carrier config has loaded and will deregister IMS.
3320        if (!mShouldUpdateImsConfigOnDisconnect
3321                && reason != DataEnabledSettings.REASON_REGISTERED) {
3322            // This will call into updateVideoCallFeatureValue and eventually all clients will be
3323            // asynchronously notified that the availability of VT over LTE has changed.
3324            ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
3325        }
3326    }
3327
3328    private void resetImsCapabilities() {
3329        log("Resetting Capabilities...");
3330        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
3331            mImsFeatureEnabled[i] = false;
3332        }
3333    }
3334
3335    /**
3336     * @return {@code true} if the device is connected to a WIFI network, {@code false} otherwise.
3337     */
3338    private boolean isWifiConnected() {
3339        ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
3340                .getSystemService(Context.CONNECTIVITY_SERVICE);
3341        if (cm != null) {
3342            NetworkInfo ni = cm.getActiveNetworkInfo();
3343            if (ni != null && ni.isConnected()) {
3344                return ni.getType() == ConnectivityManager.TYPE_WIFI;
3345            }
3346        }
3347        return false;
3348    }
3349
3350    /**
3351     * @return {@code true} if downgrading of a video call to audio is supported.
3352     */
3353    public boolean isCarrierDowngradeOfVtCallSupported() {
3354        return mSupportDowngradeVtToAudio;
3355    }
3356
3357    private void handleFeatureCapabilityChanged(int serviceClass,
3358            int[] enabledFeatures, int[] disabledFeatures) {
3359        if (serviceClass == ImsServiceClass.MMTEL) {
3360            boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
3361            // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
3362            StringBuilder sb;
3363            if (DBG) {
3364                sb = new StringBuilder(120);
3365                sb.append("handleFeatureCapabilityChanged: ");
3366            }
3367            for (int  i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
3368                    i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI &&
3369                            i < enabledFeatures.length; i++) {
3370                if (enabledFeatures[i] == i) {
3371                    // If the feature is set to its own integer value it is enabled.
3372                    if (DBG) {
3373                        sb.append(mImsFeatureStrings[i]);
3374                        sb.append(":true ");
3375                    }
3376
3377                    mImsFeatureEnabled[i] = true;
3378                } else if (enabledFeatures[i]
3379                        == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
3380                    // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
3381                    if (DBG) {
3382                        sb.append(mImsFeatureStrings[i]);
3383                        sb.append(":false ");
3384                    }
3385
3386                    mImsFeatureEnabled[i] = false;
3387                } else {
3388                    // Feature has unknown state; it is not its own value or -1.
3389                    if (DBG) {
3390                        loge("handleFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i]
3391                                + "): unexpectedValue=" + enabledFeatures[i]);
3392                    }
3393                }
3394            }
3395            boolean isVideoEnabled = isVideoCallEnabled();
3396            boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
3397            if (DBG) {
3398                sb.append(" isVideoEnabledStateChanged=");
3399                sb.append(isVideoEnabledStatechanged);
3400            }
3401
3402            if (isVideoEnabledStatechanged) {
3403                log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" +
3404                        isVideoEnabled);
3405                mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
3406            }
3407
3408            if (DBG) {
3409                log(sb.toString());
3410            }
3411
3412            if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
3413                    + ", isVideoCallEnabled=" + isVideoCallEnabled()
3414                    + ", isVowifiEnabled=" + isVowifiEnabled()
3415                    + ", isUtEnabled=" + isUtEnabled());
3416
3417            mPhone.onFeatureCapabilityChanged();
3418
3419            mMetrics.writeOnImsCapabilities(
3420                    mPhone.getPhoneId(), mImsFeatureEnabled);
3421        }
3422    }
3423}
3424