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