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