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