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