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