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