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