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