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