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