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