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