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