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