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