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