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