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