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