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