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