ImsPhone.java revision c9ccc7158bc3a3b73944fed68e6f3fc390a08eec
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 static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
20import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
21import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
22import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
23import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
24import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
25import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
26import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
27import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
28import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
29import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
30import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
31import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
32import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
33import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
34import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
35import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
36import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
37import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
38import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
39
40import android.app.Activity;
41import android.app.ActivityManager;
42import android.app.Notification;
43import android.app.NotificationManager;
44import android.app.PendingIntent;
45import android.content.BroadcastReceiver;
46import android.content.Context;
47import android.content.Intent;
48import android.net.NetworkStats;
49import android.net.Uri;
50import android.os.AsyncResult;
51import android.os.Bundle;
52import android.os.Handler;
53import android.os.Message;
54import android.os.PersistableBundle;
55import android.os.PowerManager;
56import android.os.PowerManager.WakeLock;
57import android.os.Registrant;
58import android.os.RegistrantList;
59import android.os.ResultReceiver;
60import android.os.SystemProperties;
61import android.os.UserHandle;
62import android.telecom.VideoProfile;
63import android.telephony.CarrierConfigManager;
64import android.telephony.PhoneNumberUtils;
65import android.telephony.Rlog;
66import android.telephony.ServiceState;
67import android.telephony.SubscriptionManager;
68import android.telephony.TelephonyManager;
69import android.telephony.UssdResponse;
70import android.text.TextUtils;
71
72import com.android.ims.ImsCallForwardInfo;
73import com.android.ims.ImsCallProfile;
74import com.android.ims.ImsEcbm;
75import com.android.ims.ImsEcbmStateListener;
76import com.android.ims.ImsException;
77import com.android.ims.ImsManager;
78import com.android.ims.ImsReasonInfo;
79import com.android.ims.ImsSsInfo;
80import com.android.ims.ImsUtInterface;
81import com.android.internal.annotations.VisibleForTesting;
82import com.android.internal.telephony.Call;
83import com.android.internal.telephony.CallForwardInfo;
84import com.android.internal.telephony.CallStateException;
85import com.android.internal.telephony.CallTracker;
86import com.android.internal.telephony.CommandException;
87import com.android.internal.telephony.CommandsInterface;
88import com.android.internal.telephony.Connection;
89import com.android.internal.telephony.GsmCdmaPhone;
90import com.android.internal.telephony.MmiCode;
91import com.android.internal.telephony.Phone;
92import com.android.internal.telephony.PhoneConstants;
93import com.android.internal.telephony.PhoneNotifier;
94import com.android.internal.telephony.TelephonyComponentFactory;
95import com.android.internal.telephony.TelephonyIntents;
96import com.android.internal.telephony.TelephonyProperties;
97import com.android.internal.telephony.UUSInfo;
98import com.android.internal.telephony.gsm.SuppServiceNotification;
99import com.android.internal.telephony.uicc.IccRecords;
100import com.android.internal.telephony.util.NotificationChannelController;
101
102import java.io.FileDescriptor;
103import java.io.PrintWriter;
104import java.util.ArrayDeque;
105import java.util.ArrayList;
106import java.util.Deque;
107import java.util.List;
108
109/**
110 * {@hide}
111 */
112public class ImsPhone extends ImsPhoneBase {
113    private static final String LOG_TAG = "ImsPhone";
114    private static final boolean DBG = true;
115    private static final boolean VDBG = false; // STOPSHIP if true
116
117    private static final int EVENT_SET_CALL_BARRING_DONE             = EVENT_LAST + 1;
118    private static final int EVENT_GET_CALL_BARRING_DONE             = EVENT_LAST + 2;
119    private static final int EVENT_SET_CALL_WAITING_DONE             = EVENT_LAST + 3;
120    private static final int EVENT_GET_CALL_WAITING_DONE             = EVENT_LAST + 4;
121    private static final int EVENT_SET_CLIR_DONE                     = EVENT_LAST + 5;
122    private static final int EVENT_GET_CLIR_DONE                     = EVENT_LAST + 6;
123    private static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED  = EVENT_LAST + 7;
124    private static final int EVENT_SERVICE_STATE_CHANGED             = EVENT_LAST + 8;
125    private static final int EVENT_VOICE_CALL_ENDED                  = EVENT_LAST + 9;
126
127    static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
128    static final int CANCEL_ECM_TIMER  = 1; // cancel Ecm timer
129
130    // Default Emergency Callback Mode exit timer
131    private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
132
133    // Instance Variables
134    Phone mDefaultPhone;
135    ImsPhoneCallTracker mCT;
136    ImsExternalCallTracker mExternalCallTracker;
137    private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
138    private ServiceState mSS = new ServiceState();
139
140    // To redial silently through GSM or CDMA when dialing through IMS fails
141    private String mLastDialString;
142
143    private WakeLock mWakeLock;
144
145    // mEcmExitRespRegistrant is informed after the phone has been exited the emergency
146    // callback mode keep track of if phone is in emergency callback mode
147    private Registrant mEcmExitRespRegistrant;
148
149    private final RegistrantList mSilentRedialRegistrants = new RegistrantList();
150
151    private boolean mImsRegistered = false;
152
153    private boolean mRoaming = false;
154
155    // List of Registrants to send supplementary service notifications to.
156    private RegistrantList mSsnRegistrants = new RegistrantList();
157
158    // A runnable which is used to automatically exit from Ecm after a period of time.
159    private Runnable mExitEcmRunnable = new Runnable() {
160        @Override
161        public void run() {
162            exitEmergencyCallbackMode();
163        }
164    };
165
166    private Uri[] mCurrentSubscriberUris;
167
168    protected void setCurrentSubscriberUris(Uri[] currentSubscriberUris) {
169        this.mCurrentSubscriberUris = currentSubscriberUris;
170    }
171
172    @Override
173    public Uri[] getCurrentSubscriberUris() {
174        return mCurrentSubscriberUris;
175    }
176
177    // Create Cf (Call forward) so that dialling number &
178    // mIsCfu (true if reason is call forward unconditional)
179    // mOnComplete (Message object passed by client) can be packed &
180    // given as a single Cf object as user data to UtInterface.
181    private static class Cf {
182        final String mSetCfNumber;
183        final Message mOnComplete;
184        final boolean mIsCfu;
185
186        Cf(String cfNumber, boolean isCfu, Message onComplete) {
187            mSetCfNumber = cfNumber;
188            mIsCfu = isCfu;
189            mOnComplete = onComplete;
190        }
191    }
192
193    // Constructors
194    public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
195        this(context, notifier, defaultPhone, false);
196    }
197
198    @VisibleForTesting
199    public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone,
200                    boolean unitTestMode) {
201        super("ImsPhone", context, notifier, unitTestMode);
202
203        mDefaultPhone = defaultPhone;
204        // The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the
205        // ImsPhoneCallTracker uses a thread to spool up the ImsManager.  Part of this involves
206        // setting the multiendpoint listener on the external call tracker.  So we need to ensure
207        // the external call tracker is available first to avoid potential timing issues.
208        mExternalCallTracker =
209                TelephonyComponentFactory.getInstance().makeImsExternalCallTracker(this);
210        mCT = TelephonyComponentFactory.getInstance().makeImsPhoneCallTracker(this);
211        mCT.registerPhoneStateListener(mExternalCallTracker);
212        mExternalCallTracker.setCallPuller(mCT);
213
214        mSS.setStateOff();
215
216        mPhoneId = mDefaultPhone.getPhoneId();
217
218        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
219        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
220        mWakeLock.setReferenceCounted(false);
221
222        if (mDefaultPhone.getServiceStateTracker() != null) {
223            mDefaultPhone.getServiceStateTracker()
224                    .registerForDataRegStateOrRatChanged(this,
225                            EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
226        }
227        // Sets the Voice reg state to STATE_OUT_OF_SERVICE and also queries the data service
228        // state. We don't ever need the voice reg state to be anything other than in or out of
229        // service.
230        setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
231
232        mDefaultPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
233        // Force initial roaming state update later, on EVENT_CARRIER_CONFIG_CHANGED.
234        // Settings provider or CarrierConfig may not be loaded now.
235    }
236
237    //todo: get rid of this function. It is not needed since parentPhone obj never changes
238    @Override
239    public void dispose() {
240        Rlog.d(LOG_TAG, "dispose");
241        // Nothing to dispose in Phone
242        //super.dispose();
243        mPendingMMIs.clear();
244        mExternalCallTracker.tearDown();
245        mCT.unregisterPhoneStateListener(mExternalCallTracker);
246        mCT.unregisterForVoiceCallEnded(this);
247        mCT.dispose();
248
249        //Force all referenced classes to unregister their former registered events
250        if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
251            mDefaultPhone.getServiceStateTracker().
252                    unregisterForDataRegStateOrRatChanged(this);
253            mDefaultPhone.unregisterForServiceStateChanged(this);
254        }
255    }
256
257    @Override
258    public ServiceState
259    getServiceState() {
260        return mSS;
261    }
262
263    /* package */ void setServiceState(int state) {
264        mSS.setVoiceRegState(state);
265        updateDataServiceState();
266    }
267
268    @Override
269    public CallTracker getCallTracker() {
270        return mCT;
271    }
272
273    public ImsExternalCallTracker getExternalCallTracker() {
274        return mExternalCallTracker;
275    }
276
277    @Override
278    public List<? extends ImsPhoneMmiCode>
279    getPendingMmiCodes() {
280        return mPendingMMIs;
281    }
282
283    @Override
284    public void
285    acceptCall(int videoState) throws CallStateException {
286        mCT.acceptCall(videoState);
287    }
288
289    @Override
290    public void
291    rejectCall() throws CallStateException {
292        mCT.rejectCall();
293    }
294
295    @Override
296    public void
297    switchHoldingAndActive() throws CallStateException {
298        mCT.switchWaitingOrHoldingAndActive();
299    }
300
301    @Override
302    public boolean canConference() {
303        return mCT.canConference();
304    }
305
306    public boolean canDial() {
307        return mCT.canDial();
308    }
309
310    @Override
311    public void conference() {
312        mCT.conference();
313    }
314
315    @Override
316    public void clearDisconnected() {
317        mCT.clearDisconnected();
318    }
319
320    @Override
321    public boolean canTransfer() {
322        return mCT.canTransfer();
323    }
324
325    @Override
326    public void explicitCallTransfer() {
327        mCT.explicitCallTransfer();
328    }
329
330    @Override
331    public ImsPhoneCall
332    getForegroundCall() {
333        return mCT.mForegroundCall;
334    }
335
336    @Override
337    public ImsPhoneCall
338    getBackgroundCall() {
339        return mCT.mBackgroundCall;
340    }
341
342    @Override
343    public ImsPhoneCall
344    getRingingCall() {
345        return mCT.mRingingCall;
346    }
347
348    @Override
349    public boolean isImsAvailable() {
350        return mCT.isImsServiceReady();
351    }
352
353    private boolean handleCallDeflectionIncallSupplementaryService(
354            String dialString) {
355        if (dialString.length() > 1) {
356            return false;
357        }
358
359        if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
360            if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall");
361            try {
362                mCT.rejectCall();
363            } catch (CallStateException e) {
364                if (DBG) Rlog.d(LOG_TAG, "reject failed", e);
365                notifySuppServiceFailed(Phone.SuppService.REJECT);
366            }
367        } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) {
368            if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground");
369            try {
370                mCT.hangup(getBackgroundCall());
371            } catch (CallStateException e) {
372                if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
373            }
374        }
375
376        return true;
377    }
378
379    private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
380                                   ResultReceiver wrappedCallback) {
381        UssdResponse response = new UssdResponse(ussdRequest, message);
382        Bundle returnData = new Bundle();
383        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
384        wrappedCallback.send(returnCode, returnData);
385
386    }
387
388    @Override
389    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
390            throws CallStateException {
391        if (mPendingMMIs.size() > 0) {
392            // There are MMI codes in progress; fail attempt now.
393            Rlog.i(LOG_TAG, "handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
394            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
395                    wrappedCallback );
396            return true;
397        }
398        try {
399            dialInternal(ussdRequest, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
400        } catch (CallStateException cse) {
401            if (CS_FALLBACK.equals(cse.getMessage())) {
402                throw cse;
403            } else {
404                Rlog.w(LOG_TAG, "Could not execute USSD " + cse);
405                sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
406                        wrappedCallback);
407            }
408        } catch (Exception e) {
409            Rlog.w(LOG_TAG, "Could not execute USSD " + e);
410            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
411                    wrappedCallback);
412            return false;
413        }
414        return true;
415    }
416
417    private boolean handleCallWaitingIncallSupplementaryService(
418            String dialString) {
419        int len = dialString.length();
420
421        if (len > 2) {
422            return false;
423        }
424
425        ImsPhoneCall call = getForegroundCall();
426
427        try {
428            if (len > 1) {
429                if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND");
430                notifySuppServiceFailed(Phone.SuppService.HANGUP);
431            } else {
432                if (call.getState() != ImsPhoneCall.State.IDLE) {
433                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground");
434                    mCT.hangup(call);
435                } else {
436                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive");
437                    mCT.switchWaitingOrHoldingAndActive();
438                }
439            }
440        } catch (CallStateException e) {
441            if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
442            notifySuppServiceFailed(Phone.SuppService.HANGUP);
443        }
444
445        return true;
446    }
447
448    private boolean handleCallHoldIncallSupplementaryService(String dialString) {
449        int len = dialString.length();
450
451        if (len > 2) {
452            return false;
453        }
454
455        if (len > 1) {
456            if (DBG) Rlog.d(LOG_TAG, "separate not supported");
457            notifySuppServiceFailed(Phone.SuppService.SEPARATE);
458        } else {
459            try {
460                if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
461                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call");
462                    mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
463                } else {
464                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive");
465                    mCT.switchWaitingOrHoldingAndActive();
466                }
467            } catch (CallStateException e) {
468                if (DBG) Rlog.d(LOG_TAG, "switch failed", e);
469                notifySuppServiceFailed(Phone.SuppService.SWITCH);
470            }
471        }
472
473        return true;
474    }
475
476    private boolean handleMultipartyIncallSupplementaryService(
477            String dialString) {
478        if (dialString.length() > 1) {
479            return false;
480        }
481
482        if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls");
483        conference();
484        return true;
485    }
486
487    private boolean handleEctIncallSupplementaryService(String dialString) {
488
489        int len = dialString.length();
490
491        if (len != 1) {
492            return false;
493        }
494
495        if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer");
496        notifySuppServiceFailed(Phone.SuppService.TRANSFER);
497        return true;
498    }
499
500    private boolean handleCcbsIncallSupplementaryService(String dialString) {
501        if (dialString.length() > 1) {
502            return false;
503        }
504
505        Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!");
506        // Treat it as an "unknown" service.
507        notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
508        return true;
509    }
510
511    public void notifySuppSvcNotification(SuppServiceNotification suppSvc) {
512        Rlog.d(LOG_TAG, "notifySuppSvcNotification: suppSvc = " + suppSvc);
513
514        AsyncResult ar = new AsyncResult(null, suppSvc, null);
515        mSsnRegistrants.notifyRegistrants(ar);
516    }
517
518    @Override
519    public boolean handleInCallMmiCommands(String dialString) {
520        if (!isInCall()) {
521            return false;
522        }
523
524        if (TextUtils.isEmpty(dialString)) {
525            return false;
526        }
527
528        boolean result = false;
529        char ch = dialString.charAt(0);
530        switch (ch) {
531            case '0':
532                result = handleCallDeflectionIncallSupplementaryService(
533                        dialString);
534                break;
535            case '1':
536                result = handleCallWaitingIncallSupplementaryService(
537                        dialString);
538                break;
539            case '2':
540                result = handleCallHoldIncallSupplementaryService(dialString);
541                break;
542            case '3':
543                result = handleMultipartyIncallSupplementaryService(dialString);
544                break;
545            case '4':
546                result = handleEctIncallSupplementaryService(dialString);
547                break;
548            case '5':
549                result = handleCcbsIncallSupplementaryService(dialString);
550                break;
551            default:
552                break;
553        }
554
555        return result;
556    }
557
558    boolean isInCall() {
559        ImsPhoneCall.State foregroundCallState = getForegroundCall().getState();
560        ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState();
561        ImsPhoneCall.State ringingCallState = getRingingCall().getState();
562
563       return (foregroundCallState.isAlive() ||
564               backgroundCallState.isAlive() ||
565               ringingCallState.isAlive());
566    }
567
568    @Override
569    public boolean isInEcm() {
570        return mDefaultPhone.isInEcm();
571    }
572
573    @Override
574    public void setIsInEcm(boolean isInEcm){
575        mDefaultPhone.setIsInEcm(isInEcm);
576    }
577
578    public void notifyNewRingingConnection(Connection c) {
579        mDefaultPhone.notifyNewRingingConnectionP(c);
580    }
581
582    void notifyUnknownConnection(Connection c) {
583        mDefaultPhone.notifyUnknownConnectionP(c);
584    }
585
586    @Override
587    public void notifyForVideoCapabilityChanged(boolean isVideoCapable) {
588        mIsVideoCapable = isVideoCapable;
589        mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable);
590    }
591
592    @Override
593    public Connection
594    dial(String dialString, int videoState) throws CallStateException {
595        return dialInternal(dialString, videoState, null, null);
596    }
597
598    @Override
599    public Connection
600    dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
601            throws CallStateException {
602        // ignore UUSInfo
603        return dialInternal (dialString, videoState, intentExtras, null);
604    }
605
606    protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
607            throws CallStateException {
608        return dialInternal(dialString, videoState, intentExtras, null);
609    }
610
611    private Connection dialInternal(String dialString, int videoState,
612                                    Bundle intentExtras, ResultReceiver wrappedCallback)
613            throws CallStateException {
614        // Need to make sure dialString gets parsed properly
615        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
616
617        // handle in-call MMI first if applicable
618        if (handleInCallMmiCommands(newDialString)) {
619            return null;
620        }
621
622        if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
623            return mCT.dial(dialString, videoState, intentExtras);
624        }
625
626        // Only look at the Network portion for mmi
627        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
628        ImsPhoneMmiCode mmi =
629                ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
630        if (DBG) Rlog.d(LOG_TAG,
631                "dialInternal: dialing w/ mmi '" + mmi + "'...");
632
633        if (mmi == null) {
634            return mCT.dial(dialString, videoState, intentExtras);
635        } else if (mmi.isTemporaryModeCLIR()) {
636            return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState, intentExtras);
637        } else if (!mmi.isSupportedOverImsPhone()) {
638            // If the mmi is not supported by IMS service,
639            // try to initiate dialing with default phone
640            // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
641            // causes it to return true even though the "processCode" method ultimately throws the
642            // exception.
643            Rlog.i(LOG_TAG, "dialInternal: USSD not supported by IMS; fallback to CS.");
644            throw new CallStateException(CS_FALLBACK);
645        } else {
646            mPendingMMIs.add(mmi);
647            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
648
649            try {
650                mmi.processCode();
651            } catch (CallStateException cse) {
652                if (CS_FALLBACK.equals(cse.getMessage())) {
653                    Rlog.i(LOG_TAG, "dialInternal: fallback to GSM required.");
654                    // Make sure we remove from the list of pending MMIs since it will handover to
655                    // GSM.
656                    mPendingMMIs.remove(mmi);
657                    throw cse;
658                }
659            }
660
661            return null;
662        }
663    }
664
665    @Override
666    public void
667    sendDtmf(char c) {
668        if (!PhoneNumberUtils.is12Key(c)) {
669            Rlog.e(LOG_TAG,
670                    "sendDtmf called with invalid character '" + c + "'");
671        } else {
672            if (mCT.getState() ==  PhoneConstants.State.OFFHOOK) {
673                mCT.sendDtmf(c, null);
674            }
675        }
676    }
677
678    @Override
679    public void
680    startDtmf(char c) {
681        if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) {
682            Rlog.e(LOG_TAG,
683                    "startDtmf called with invalid character '" + c + "'");
684        } else {
685            mCT.startDtmf(c);
686        }
687    }
688
689    @Override
690    public void
691    stopDtmf() {
692        mCT.stopDtmf();
693    }
694
695    public void notifyIncomingRing() {
696        if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing");
697        AsyncResult ar = new AsyncResult(null, null, null);
698        sendMessage(obtainMessage(EVENT_CALL_RING, ar));
699    }
700
701    @Override
702    public void setMute(boolean muted) {
703        mCT.setMute(muted);
704    }
705
706    @Override
707    public void setTTYMode(int ttyMode, Message onComplete) {
708        mCT.setTtyMode(ttyMode);
709    }
710
711    @Override
712    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
713        mCT.setUiTTYMode(uiTtyMode, onComplete);
714    }
715
716    @Override
717    public boolean getMute() {
718        return mCT.getMute();
719    }
720
721    @Override
722    public PhoneConstants.State getState() {
723        return mCT.getState();
724    }
725
726    private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
727        switch (commandInterfaceCFReason) {
728        case CF_REASON_UNCONDITIONAL:
729        case CF_REASON_BUSY:
730        case CF_REASON_NO_REPLY:
731        case CF_REASON_NOT_REACHABLE:
732        case CF_REASON_ALL:
733        case CF_REASON_ALL_CONDITIONAL:
734            return true;
735        default:
736            return false;
737        }
738    }
739
740    private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
741        switch (commandInterfaceCFAction) {
742        case CF_ACTION_DISABLE:
743        case CF_ACTION_ENABLE:
744        case CF_ACTION_REGISTRATION:
745        case CF_ACTION_ERASURE:
746            return true;
747        default:
748            return false;
749        }
750    }
751
752    private  boolean isCfEnable(int action) {
753        return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
754    }
755
756    private int getConditionFromCFReason(int reason) {
757        switch(reason) {
758            case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
759            case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
760            case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
761            case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
762            case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL;
763            case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL;
764            default:
765                break;
766        }
767
768        return ImsUtInterface.INVALID;
769    }
770
771    private int getCFReasonFromCondition(int condition) {
772        switch(condition) {
773            case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
774            case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
775            case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
776            case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
777            case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL;
778            case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL;
779            default:
780                break;
781        }
782
783        return CF_REASON_NOT_REACHABLE;
784    }
785
786    private int getActionFromCFAction(int action) {
787        switch(action) {
788            case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
789            case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
790            case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
791            case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
792            default:
793                break;
794        }
795
796        return ImsUtInterface.INVALID;
797    }
798
799    @Override
800    public void getOutgoingCallerIdDisplay(Message onComplete) {
801        if (DBG) Rlog.d(LOG_TAG, "getCLIR");
802        Message resp;
803        resp = obtainMessage(EVENT_GET_CLIR_DONE, onComplete);
804
805        try {
806            ImsUtInterface ut = mCT.getUtInterface();
807            ut.queryCLIR(resp);
808        } catch (ImsException e) {
809            sendErrorResponse(onComplete, e);
810        }
811    }
812
813    @Override
814    public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) {
815        if (DBG) Rlog.d(LOG_TAG, "setCLIR action= " + clirMode);
816        Message resp;
817        // Packing CLIR value in the message. This will be required for
818        // SharedPreference caching, if the message comes back as part of
819        // a success response.
820        resp = obtainMessage(EVENT_SET_CLIR_DONE, clirMode, 0, onComplete);
821        try {
822            ImsUtInterface ut = mCT.getUtInterface();
823            ut.updateCLIR(clirMode, resp);
824        } catch (ImsException e) {
825            sendErrorResponse(onComplete, e);
826        }
827    }
828
829    @Override
830    public void getCallForwardingOption(int commandInterfaceCFReason,
831            Message onComplete) {
832        if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
833        if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
834            if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
835            Message resp;
836            resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
837
838            try {
839                ImsUtInterface ut = mCT.getUtInterface();
840                ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason), null, resp);
841            } catch (ImsException e) {
842                sendErrorResponse(onComplete, e);
843            }
844        } else if (onComplete != null) {
845            sendErrorResponse(onComplete);
846        }
847    }
848
849    @Override
850    public void setCallForwardingOption(int commandInterfaceCFAction,
851            int commandInterfaceCFReason,
852            String dialingNumber,
853            int timerSeconds,
854            Message onComplete) {
855        setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber,
856                CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
857    }
858
859    public void setCallForwardingOption(int commandInterfaceCFAction,
860            int commandInterfaceCFReason,
861            String dialingNumber,
862            int serviceClass,
863            int timerSeconds,
864            Message onComplete) {
865        if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
866                + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
867        if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
868                (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
869            Message resp;
870            Cf cf = new Cf(dialingNumber,
871                    (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
872                    onComplete);
873            resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
874                    isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
875
876            try {
877                ImsUtInterface ut = mCT.getUtInterface();
878                ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
879                        getConditionFromCFReason(commandInterfaceCFReason),
880                        dialingNumber,
881                        serviceClass,
882                        timerSeconds,
883                        resp);
884            } catch (ImsException e) {
885                sendErrorResponse(onComplete, e);
886            }
887        } else if (onComplete != null) {
888            sendErrorResponse(onComplete);
889        }
890    }
891
892    @Override
893    public void getCallWaiting(Message onComplete) {
894        if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
895        Message resp;
896        resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
897
898        try {
899            ImsUtInterface ut = mCT.getUtInterface();
900            ut.queryCallWaiting(resp);
901        } catch (ImsException e) {
902            sendErrorResponse(onComplete, e);
903        }
904    }
905
906    @Override
907    public void setCallWaiting(boolean enable, Message onComplete) {
908        setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
909    }
910
911    public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
912        if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
913        Message resp;
914        resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
915
916        try {
917            ImsUtInterface ut = mCT.getUtInterface();
918            ut.updateCallWaiting(enable, serviceClass, resp);
919        } catch (ImsException e) {
920            sendErrorResponse(onComplete, e);
921        }
922    }
923
924    private int getCBTypeFromFacility(String facility) {
925        if (CB_FACILITY_BAOC.equals(facility)) {
926            return ImsUtInterface.CB_BAOC;
927        } else if (CB_FACILITY_BAOIC.equals(facility)) {
928            return ImsUtInterface.CB_BOIC;
929        } else if (CB_FACILITY_BAOICxH.equals(facility)) {
930            return ImsUtInterface.CB_BOIC_EXHC;
931        } else if (CB_FACILITY_BAIC.equals(facility)) {
932            return ImsUtInterface.CB_BAIC;
933        } else if (CB_FACILITY_BAICr.equals(facility)) {
934            return ImsUtInterface.CB_BIC_WR;
935        } else if (CB_FACILITY_BA_ALL.equals(facility)) {
936            return ImsUtInterface.CB_BA_ALL;
937        } else if (CB_FACILITY_BA_MO.equals(facility)) {
938            return ImsUtInterface.CB_BA_MO;
939        } else if (CB_FACILITY_BA_MT.equals(facility)) {
940            return ImsUtInterface.CB_BA_MT;
941        }
942
943        return 0;
944    }
945
946    public void getCallBarring(String facility, Message onComplete) {
947        if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
948        Message resp;
949        resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
950
951        try {
952            ImsUtInterface ut = mCT.getUtInterface();
953            ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
954        } catch (ImsException e) {
955            sendErrorResponse(onComplete, e);
956        }
957    }
958
959    public void setCallBarring(String facility, boolean lockState, String password, Message
960            onComplete) {
961        if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
962                + ", lockState=" + lockState);
963        Message resp;
964        resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
965
966        int action;
967        if (lockState) {
968            action = CommandsInterface.CF_ACTION_ENABLE;
969        }
970        else {
971            action = CommandsInterface.CF_ACTION_DISABLE;
972        }
973
974        try {
975            ImsUtInterface ut = mCT.getUtInterface();
976            // password is not required with Ut interface
977            ut.updateCallBarring(getCBTypeFromFacility(facility), action, resp, null);
978        } catch (ImsException e) {
979            sendErrorResponse(onComplete, e);
980        }
981    }
982
983    @Override
984    public void sendUssdResponse(String ussdMessge) {
985        Rlog.d(LOG_TAG, "sendUssdResponse");
986        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
987        mPendingMMIs.add(mmi);
988        mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
989        mmi.sendUssd(ussdMessge);
990    }
991
992    public void sendUSSD(String ussdString, Message response) {
993        mCT.sendUSSD(ussdString, response);
994    }
995
996    @Override
997    public void cancelUSSD() {
998        mCT.cancelUSSD();
999    }
1000
1001    private void sendErrorResponse(Message onComplete) {
1002        Rlog.d(LOG_TAG, "sendErrorResponse");
1003        if (onComplete != null) {
1004            AsyncResult.forMessage(onComplete, null,
1005                    new CommandException(CommandException.Error.GENERIC_FAILURE));
1006            onComplete.sendToTarget();
1007        }
1008    }
1009
1010    @VisibleForTesting
1011    public void sendErrorResponse(Message onComplete, Throwable e) {
1012        Rlog.d(LOG_TAG, "sendErrorResponse");
1013        if (onComplete != null) {
1014            AsyncResult.forMessage(onComplete, null, getCommandException(e));
1015            onComplete.sendToTarget();
1016        }
1017    }
1018
1019    private CommandException getCommandException(int code, String errorString) {
1020        Rlog.d(LOG_TAG, "getCommandException code= " + code
1021                + ", errorString= " + errorString);
1022        CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
1023
1024        switch(code) {
1025            case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
1026                error = CommandException.Error.REQUEST_NOT_SUPPORTED;
1027                break;
1028            case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
1029                error = CommandException.Error.PASSWORD_INCORRECT;
1030                break;
1031            case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
1032                error = CommandException.Error.RADIO_NOT_AVAILABLE;
1033            default:
1034                break;
1035        }
1036
1037        return new CommandException(error, errorString);
1038    }
1039
1040    private CommandException getCommandException(Throwable e) {
1041        CommandException ex = null;
1042
1043        if (e instanceof ImsException) {
1044            ex = getCommandException(((ImsException)e).getCode(), e.getMessage());
1045        } else {
1046            Rlog.d(LOG_TAG, "getCommandException generic failure");
1047            ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
1048        }
1049        return ex;
1050    }
1051
1052    private void
1053    onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
1054        Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
1055        mMmiCompleteRegistrants.notifyRegistrants(
1056            new AsyncResult(null, mmi, null));
1057    }
1058
1059    /* package */
1060    void onIncomingUSSD(int ussdMode, String ussdMessage) {
1061        if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
1062
1063        boolean isUssdError;
1064        boolean isUssdRequest;
1065
1066        isUssdRequest
1067            = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
1068
1069        isUssdError
1070            = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
1071                && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
1072
1073        ImsPhoneMmiCode found = null;
1074        for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
1075            if(mPendingMMIs.get(i).isPendingUSSD()) {
1076                found = mPendingMMIs.get(i);
1077                break;
1078            }
1079        }
1080
1081        if (found != null) {
1082            // Complete pending USSD
1083            if (isUssdError) {
1084                found.onUssdFinishedError();
1085            } else {
1086                found.onUssdFinished(ussdMessage, isUssdRequest);
1087            }
1088        } else if (!isUssdError && ussdMessage != null) {
1089                // pending USSD not found
1090                // The network may initiate its own USSD request
1091
1092                // ignore everything that isnt a Notify or a Request
1093                // also, discard if there is no message to present
1094                ImsPhoneMmiCode mmi;
1095                mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
1096                        isUssdRequest,
1097                        this);
1098                onNetworkInitiatedUssd(mmi);
1099        }
1100    }
1101
1102    /**
1103     * Removes the given MMI from the pending list and notifies
1104     * registrants that it is complete.
1105     * @param mmi MMI that is done
1106     */
1107    public void onMMIDone(ImsPhoneMmiCode mmi) {
1108        /* Only notify complete if it's on the pending list.
1109         * Otherwise, it's already been handled (eg, previously canceled).
1110         * The exception is cancellation of an incoming USSD-REQUEST, which is
1111         * not on the list.
1112         */
1113        Rlog.d(LOG_TAG, "onMMIDone: mmi=" + mmi);
1114        if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
1115            ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
1116            if (receiverCallback != null) {
1117                int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
1118                        TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
1119                sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
1120                        receiverCallback );
1121            } else {
1122                Rlog.v(LOG_TAG, "onMMIDone: notifyRegistrants");
1123                mMmiCompleteRegistrants.notifyRegistrants(
1124                    new AsyncResult(null, mmi, null));
1125            }
1126        }
1127    }
1128
1129    @Override
1130    public ArrayList<Connection> getHandoverConnection() {
1131        ArrayList<Connection> connList = new ArrayList<Connection>();
1132        // Add all foreground call connections
1133        connList.addAll(getForegroundCall().mConnections);
1134        // Add all background call connections
1135        connList.addAll(getBackgroundCall().mConnections);
1136        // Add all background call connections
1137        connList.addAll(getRingingCall().mConnections);
1138        if (connList.size() > 0) {
1139            return connList;
1140        } else {
1141            return null;
1142        }
1143    }
1144
1145    @Override
1146    public void notifySrvccState(Call.SrvccState state) {
1147        mCT.notifySrvccState(state);
1148    }
1149
1150    /* package */ void
1151    initiateSilentRedial() {
1152        String result = mLastDialString;
1153        AsyncResult ar = new AsyncResult(null, result, null);
1154        if (ar != null) {
1155            mSilentRedialRegistrants.notifyRegistrants(ar);
1156        }
1157    }
1158
1159    @Override
1160    public void registerForSilentRedial(Handler h, int what, Object obj) {
1161        mSilentRedialRegistrants.addUnique(h, what, obj);
1162    }
1163
1164    @Override
1165    public void unregisterForSilentRedial(Handler h) {
1166        mSilentRedialRegistrants.remove(h);
1167    }
1168
1169    @Override
1170    public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
1171        mSsnRegistrants.addUnique(h, what, obj);
1172    }
1173
1174    @Override
1175    public void unregisterForSuppServiceNotification(Handler h) {
1176        mSsnRegistrants.remove(h);
1177    }
1178
1179    @Override
1180    public int getSubId() {
1181        return mDefaultPhone.getSubId();
1182    }
1183
1184    @Override
1185    public int getPhoneId() {
1186        return mDefaultPhone.getPhoneId();
1187    }
1188
1189    private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
1190        CallForwardInfo cfInfo = new CallForwardInfo();
1191        cfInfo.status = info.mStatus;
1192        cfInfo.reason = getCFReasonFromCondition(info.mCondition);
1193        cfInfo.serviceClass = SERVICE_CLASS_VOICE;
1194        cfInfo.toa = info.mToA;
1195        cfInfo.number = info.mNumber;
1196        cfInfo.timeSeconds = info.mTimeSeconds;
1197        return cfInfo;
1198    }
1199
1200    private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
1201        CallForwardInfo[] cfInfos = null;
1202
1203        if (infos != null && infos.length != 0) {
1204            cfInfos = new CallForwardInfo[infos.length];
1205        }
1206
1207        IccRecords r = mDefaultPhone.getIccRecords();
1208        if (infos == null || infos.length == 0) {
1209            if (r != null) {
1210                // Assume the default is not active
1211                // Set unconditional CFF in SIM to false
1212                setVoiceCallForwardingFlag(r, 1, false, null);
1213            }
1214        } else {
1215            for (int i = 0, s = infos.length; i < s; i++) {
1216                if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
1217                    if (r != null) {
1218                        setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1),
1219                            infos[i].mNumber);
1220                    }
1221                }
1222                cfInfos[i] = getCallForwardInfo(infos[i]);
1223            }
1224        }
1225
1226        return cfInfos;
1227    }
1228
1229    private int[] handleCbQueryResult(ImsSsInfo[] infos) {
1230        int[] cbInfos = new int[1];
1231        cbInfos[0] = SERVICE_CLASS_NONE;
1232
1233        if (infos[0].mStatus == 1) {
1234            cbInfos[0] = SERVICE_CLASS_VOICE;
1235        }
1236
1237        return cbInfos;
1238    }
1239
1240    private int[] handleCwQueryResult(ImsSsInfo[] infos) {
1241        int[] cwInfos = new int[2];
1242        cwInfos[0] = 0;
1243
1244        if (infos[0].mStatus == 1) {
1245            cwInfos[0] = 1;
1246            cwInfos[1] = SERVICE_CLASS_VOICE;
1247        }
1248
1249        return cwInfos;
1250    }
1251
1252    private void
1253    sendResponse(Message onComplete, Object result, Throwable e) {
1254        if (onComplete != null) {
1255            CommandException ex = null;
1256            if (e != null) {
1257                ex = getCommandException(e);
1258            }
1259            AsyncResult.forMessage(onComplete, result, ex);
1260            onComplete.sendToTarget();
1261        }
1262    }
1263
1264    private void updateDataServiceState() {
1265        if (mSS != null && mDefaultPhone.getServiceStateTracker() != null
1266                && mDefaultPhone.getServiceStateTracker().mSS != null) {
1267            ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
1268            mSS.setDataRegState(ss.getDataRegState());
1269            mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology());
1270            Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
1271        }
1272    }
1273
1274    @Override
1275    public void handleMessage(Message msg) {
1276        AsyncResult ar = (AsyncResult) msg.obj;
1277
1278        if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
1279        switch (msg.what) {
1280            case EVENT_SET_CALL_FORWARD_DONE:
1281                IccRecords r = mDefaultPhone.getIccRecords();
1282                Cf cf = (Cf) ar.userObj;
1283                if (cf.mIsCfu && ar.exception == null && r != null) {
1284                    setVoiceCallForwardingFlag(r, 1, msg.arg1 == 1, cf.mSetCfNumber);
1285                }
1286                sendResponse(cf.mOnComplete, null, ar.exception);
1287                break;
1288
1289            case EVENT_GET_CALL_FORWARD_DONE:
1290                CallForwardInfo[] cfInfos = null;
1291                if (ar.exception == null) {
1292                    cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1293                }
1294                sendResponse((Message) ar.userObj, cfInfos, ar.exception);
1295                break;
1296
1297            case EVENT_GET_CALL_BARRING_DONE:
1298            case EVENT_GET_CALL_WAITING_DONE:
1299                int[] ssInfos = null;
1300                if (ar.exception == null) {
1301                    if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1302                        ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1303                    } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1304                        ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1305                    }
1306                }
1307                sendResponse((Message) ar.userObj, ssInfos, ar.exception);
1308                break;
1309
1310            case EVENT_GET_CLIR_DONE:
1311                Bundle ssInfo = (Bundle) ar.result;
1312                int[] clirInfo = null;
1313                if (ssInfo != null) {
1314                    clirInfo = ssInfo.getIntArray(ImsPhoneMmiCode.UT_BUNDLE_KEY_CLIR);
1315                }
1316                sendResponse((Message) ar.userObj, clirInfo, ar.exception);
1317                break;
1318
1319            case EVENT_SET_CLIR_DONE:
1320                if (ar.exception == null) {
1321                    saveClirSetting(msg.arg1);
1322                }
1323                 // (Intentional fallthrough)
1324            case EVENT_SET_CALL_BARRING_DONE:
1325            case EVENT_SET_CALL_WAITING_DONE:
1326                sendResponse((Message) ar.userObj, null, ar.exception);
1327                break;
1328
1329            case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
1330                if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
1331                updateDataServiceState();
1332                break;
1333
1334            case EVENT_SERVICE_STATE_CHANGED:
1335                if (VDBG) Rlog.d(LOG_TAG, "EVENT_SERVICE_STATE_CHANGED");
1336                ar = (AsyncResult) msg.obj;
1337                ServiceState newServiceState = (ServiceState) ar.result;
1338                // only update if roaming status changed
1339                if (mRoaming != newServiceState.getRoaming()) {
1340                    if (DBG) Rlog.d(LOG_TAG, "Roaming state changed");
1341                    updateRoamingState(newServiceState.getRoaming());
1342                }
1343                break;
1344            case EVENT_VOICE_CALL_ENDED:
1345                if (DBG) Rlog.d(LOG_TAG, "Voice call ended. Handle pending updateRoamingState.");
1346                mCT.unregisterForVoiceCallEnded(this);
1347                // only update if roaming status changed
1348                boolean newRoaming = getCurrentRoaming();
1349                if (mRoaming != newRoaming) {
1350                    updateRoamingState(newRoaming);
1351                }
1352                break;
1353
1354            default:
1355                super.handleMessage(msg);
1356                break;
1357        }
1358    }
1359
1360    /**
1361     * Listen to the IMS ECBM state change
1362     */
1363    private ImsEcbmStateListener mImsEcbmStateListener =
1364            new ImsEcbmStateListener() {
1365                @Override
1366                public void onECBMEntered() {
1367                    if (DBG) Rlog.d(LOG_TAG, "onECBMEntered");
1368                    handleEnterEmergencyCallbackMode();
1369                }
1370
1371                @Override
1372                public void onECBMExited() {
1373                    if (DBG) Rlog.d(LOG_TAG, "onECBMExited");
1374                    handleExitEmergencyCallbackMode();
1375                }
1376            };
1377
1378    @VisibleForTesting
1379    public ImsEcbmStateListener getImsEcbmStateListener() {
1380        return mImsEcbmStateListener;
1381    }
1382
1383    @Override
1384    public boolean isInEmergencyCall() {
1385        return mCT.isInEmergencyCall();
1386    }
1387
1388    private void sendEmergencyCallbackModeChange() {
1389        // Send an Intent
1390        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1391        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
1392        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
1393        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
1394        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
1395    }
1396
1397    @Override
1398    public void exitEmergencyCallbackMode() {
1399        if (mWakeLock.isHeld()) {
1400            mWakeLock.release();
1401        }
1402        if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()");
1403
1404        // Send a message which will invoke handleExitEmergencyCallbackMode
1405        ImsEcbm ecbm;
1406        try {
1407            ecbm = mCT.getEcbmInterface();
1408            ecbm.exitEmergencyCallbackMode();
1409        } catch (ImsException e) {
1410            e.printStackTrace();
1411        }
1412    }
1413
1414    private void handleEnterEmergencyCallbackMode() {
1415        if (DBG) {
1416            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
1417                    + isInEcm());
1418        }
1419        // if phone is not in Ecm mode, and it's changed to Ecm mode
1420        if (!isInEcm()) {
1421            setIsInEcm(true);
1422            // notify change
1423            sendEmergencyCallbackModeChange();
1424
1425            // Post this runnable so we will automatically exit
1426            // if no one invokes exitEmergencyCallbackMode() directly.
1427            long delayInMillis = SystemProperties.getLong(
1428                    TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1429            postDelayed(mExitEcmRunnable, delayInMillis);
1430            // We don't want to go to sleep while in Ecm
1431            mWakeLock.acquire();
1432        }
1433    }
1434
1435    private void handleExitEmergencyCallbackMode() {
1436        if (DBG) {
1437            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = "
1438                    + isInEcm());
1439        }
1440
1441        if (isInEcm()) {
1442            setIsInEcm(false);
1443        }
1444
1445        // Remove pending exit Ecm runnable, if any
1446        removeCallbacks(mExitEcmRunnable);
1447
1448        if (mEcmExitRespRegistrant != null) {
1449            mEcmExitRespRegistrant.notifyResult(Boolean.TRUE);
1450        }
1451
1452        // release wakeLock
1453        if (mWakeLock.isHeld()) {
1454            mWakeLock.release();
1455        }
1456
1457        // send an Intent
1458        sendEmergencyCallbackModeChange();
1459    }
1460
1461    /**
1462     * Handle to cancel or restart Ecm timer in emergency call back mode if action is
1463     * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart
1464     * Ecm timer and notify apps the timer is restarted.
1465     */
1466    void handleTimerInEmergencyCallbackMode(int action) {
1467        switch (action) {
1468            case CANCEL_ECM_TIMER:
1469                removeCallbacks(mExitEcmRunnable);
1470                ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE);
1471                break;
1472            case RESTART_ECM_TIMER:
1473                long delayInMillis = SystemProperties.getLong(
1474                        TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1475                postDelayed(mExitEcmRunnable, delayInMillis);
1476                ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
1477                break;
1478            default:
1479                Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
1480        }
1481    }
1482
1483    @Override
1484    public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
1485        mEcmExitRespRegistrant = new Registrant(h, what, obj);
1486    }
1487
1488    @Override
1489    public void unsetOnEcbModeExitResponse(Handler h) {
1490        mEcmExitRespRegistrant.clear();
1491    }
1492
1493    public void onFeatureCapabilityChanged() {
1494        mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
1495    }
1496
1497    @Override
1498    public boolean isVolteEnabled() {
1499        return mCT.isVolteEnabled();
1500    }
1501
1502    @Override
1503    public boolean isWifiCallingEnabled() {
1504        return mCT.isVowifiEnabled();
1505    }
1506
1507    @Override
1508    public boolean isVideoEnabled() {
1509        return mCT.isVideoCallEnabled();
1510    }
1511
1512    @Override
1513    public Phone getDefaultPhone() {
1514        return mDefaultPhone;
1515    }
1516
1517    @Override
1518    public boolean isImsRegistered() {
1519        return mImsRegistered;
1520    }
1521
1522    public void setImsRegistered(boolean value) {
1523        mImsRegistered = value;
1524    }
1525
1526    @Override
1527    public void callEndCleanupHandOverCallIfAny() {
1528        mCT.callEndCleanupHandOverCallIfAny();
1529    }
1530
1531    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
1532        @Override
1533        public void onReceive(Context context, Intent intent) {
1534            // Add notification only if alert was not shown by WfcSettings
1535            if (getResultCode() == Activity.RESULT_OK) {
1536                // Default result code (as passed to sendOrderedBroadcast)
1537                // means that intent was not received by WfcSettings.
1538
1539                CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE);
1540                CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE);
1541                CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE);
1542
1543                Intent resultIntent = new Intent(Intent.ACTION_MAIN);
1544                resultIntent.setClassName("com.android.settings",
1545                        "com.android.settings.Settings$WifiCallingSettingsActivity");
1546                resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true);
1547                resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
1548                resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
1549                PendingIntent resultPendingIntent =
1550                        PendingIntent.getActivity(
1551                                mContext,
1552                                0,
1553                                resultIntent,
1554                                PendingIntent.FLAG_UPDATE_CURRENT
1555                        );
1556
1557                final Notification notification = new Notification.Builder(mContext)
1558                                .setSmallIcon(android.R.drawable.stat_sys_warning)
1559                                .setContentTitle(title)
1560                                .setContentText(messageNotification)
1561                                .setAutoCancel(true)
1562                                .setContentIntent(resultPendingIntent)
1563                                .setStyle(new Notification.BigTextStyle()
1564                                .bigText(messageNotification))
1565                                .setChannelId(NotificationChannelController.CHANNEL_ID_WFC)
1566                                .build();
1567                final String notificationTag = "wifi_calling";
1568                final int notificationId = 1;
1569
1570                NotificationManager notificationManager =
1571                        (NotificationManager) mContext.getSystemService(
1572                                Context.NOTIFICATION_SERVICE);
1573                notificationManager.notify(notificationTag, notificationId,
1574                        notification);
1575            }
1576        }
1577    };
1578
1579    /**
1580     * Show notification in case of some error codes.
1581     */
1582    public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
1583        if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
1584                && imsReasonInfo.mExtraMessage != null) {
1585            // Suppress WFC Registration notifications if WFC is not enabled by the user.
1586            if (ImsManager.isWfcEnabledByUser(mContext)) {
1587                processWfcDisconnectForNotification(imsReasonInfo);
1588            }
1589        }
1590    }
1591
1592    // Processes an IMS disconnect cause for possible WFC registration errors and optionally
1593    // disable WFC.
1594    private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) {
1595        CarrierConfigManager configManager =
1596                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
1597        if (configManager == null) {
1598            Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
1599            return;
1600        }
1601        PersistableBundle pb = configManager.getConfigForSubId(getSubId());
1602        if (pb == null) {
1603            Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
1604            return;
1605        }
1606        final String[] wfcOperatorErrorCodes =
1607                pb.getStringArray(
1608                        CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
1609        if (wfcOperatorErrorCodes == null) {
1610            // no operator-specific error codes
1611            return;
1612        }
1613
1614        final String[] wfcOperatorErrorAlertMessages =
1615                mContext.getResources().getStringArray(
1616                        com.android.internal.R.array.wfcOperatorErrorAlertMessages);
1617        final String[] wfcOperatorErrorNotificationMessages =
1618                mContext.getResources().getStringArray(
1619                        com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
1620
1621        for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
1622            String[] codes = wfcOperatorErrorCodes[i].split("\\|");
1623            if (codes.length != 2) {
1624                Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
1625                continue;
1626            }
1627
1628            // Match error code.
1629            if (!imsReasonInfo.mExtraMessage.startsWith(
1630                    codes[0])) {
1631                continue;
1632            }
1633            // If there is no delimiter at the end of error code string
1634            // then we need to verify that we are not matching partial code.
1635            // EXAMPLE: "REG9" must not match "REG99".
1636            // NOTE: Error code must not be empty.
1637            int codeStringLength = codes[0].length();
1638            char lastChar = codes[0].charAt(codeStringLength - 1);
1639            if (Character.isLetterOrDigit(lastChar)) {
1640                if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
1641                    char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
1642                    if (Character.isLetterOrDigit(nextChar)) {
1643                        continue;
1644                    }
1645                }
1646            }
1647
1648            final CharSequence title = mContext.getText(
1649                    com.android.internal.R.string.wfcRegErrorTitle);
1650
1651            int idx = Integer.parseInt(codes[1]);
1652            if (idx < 0
1653                    || idx >= wfcOperatorErrorAlertMessages.length
1654                    || idx >= wfcOperatorErrorNotificationMessages.length) {
1655                Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
1656                continue;
1657            }
1658            CharSequence messageAlert = imsReasonInfo.mExtraMessage;
1659            CharSequence messageNotification = imsReasonInfo.mExtraMessage;
1660            if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
1661                messageAlert = wfcOperatorErrorAlertMessages[idx];
1662            }
1663            if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
1664                messageNotification = wfcOperatorErrorNotificationMessages[idx];
1665            }
1666
1667            // UX requirement is to disable WFC in case of "permanent" registration failures.
1668            ImsManager.setWfcSetting(mContext, false);
1669
1670            // If WfcSettings are active then alert will be shown
1671            // otherwise notification will be added.
1672            Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
1673            intent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
1674            intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
1675            intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
1676            mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
1677                    null, Activity.RESULT_OK, null, null);
1678
1679            // We can only match a single error code
1680            // so should break the loop after a successful match.
1681            break;
1682        }
1683    }
1684
1685    @Override
1686    public boolean isUtEnabled() {
1687        return mCT.isUtEnabled();
1688    }
1689
1690    @Override
1691    public void sendEmergencyCallStateChange(boolean callActive) {
1692        mDefaultPhone.sendEmergencyCallStateChange(callActive);
1693    }
1694
1695    @Override
1696    public void setBroadcastEmergencyCallStateChanges(boolean broadcast) {
1697        mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast);
1698    }
1699
1700    @VisibleForTesting
1701    public PowerManager.WakeLock getWakeLock() {
1702        return mWakeLock;
1703    }
1704
1705    @Override
1706    public NetworkStats getVtDataUsage(boolean perUidStats) {
1707        return mCT.getVtDataUsage(perUidStats);
1708    }
1709
1710    private void updateRoamingState(boolean newRoaming) {
1711        if (mCT.getState() == PhoneConstants.State.IDLE) {
1712            if (DBG) Rlog.d(LOG_TAG, "updateRoamingState now: " + newRoaming);
1713            mRoaming = newRoaming;
1714            ImsManager.setWfcMode(mContext,
1715                    ImsManager.getWfcMode(mContext, newRoaming), newRoaming);
1716        } else {
1717            if (DBG) Rlog.d(LOG_TAG, "updateRoamingState postponed: " + newRoaming);
1718            mCT.registerForVoiceCallEnded(this,
1719                    EVENT_VOICE_CALL_ENDED, null);
1720        }
1721    }
1722
1723    private boolean getCurrentRoaming() {
1724        TelephonyManager tm = (TelephonyManager) mContext
1725                .getSystemService(Context.TELEPHONY_SERVICE);
1726        return tm.isNetworkRoaming();
1727    }
1728
1729    @Override
1730    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1731        pw.println("ImsPhone extends:");
1732        super.dump(fd, pw, args);
1733        pw.flush();
1734
1735        pw.println("ImsPhone:");
1736        pw.println("  mDefaultPhone = " + mDefaultPhone);
1737        pw.println("  mPendingMMIs = " + mPendingMMIs);
1738        pw.println("  mPostDialHandler = " + mPostDialHandler);
1739        pw.println("  mSS = " + mSS);
1740        pw.println("  mWakeLock = " + mWakeLock);
1741        pw.println("  mIsPhoneInEcmState = " + isInEcm());
1742        pw.println("  mEcmExitRespRegistrant = " + mEcmExitRespRegistrant);
1743        pw.println("  mSilentRedialRegistrants = " + mSilentRedialRegistrants);
1744        pw.println("  mImsRegistered = " + mImsRegistered);
1745        pw.println("  mRoaming = " + mRoaming);
1746        pw.println("  mSsnRegistrants = " + mSsnRegistrants);
1747        pw.flush();
1748    }
1749}
1750