Home | History | Annotate | Download | only in imsphone
      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 
     17 package com.android.internal.telephony.imsphone;
     18 
     19 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
     20 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
     21 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
     22 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
     23 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
     24 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
     25 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
     26 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
     27 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
     28 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
     29 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
     30 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
     31 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
     32 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
     33 import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
     34 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
     35 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
     36 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
     37 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
     38 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
     39 
     40 import android.app.Activity;
     41 import android.app.ActivityManager;
     42 import android.app.Notification;
     43 import android.app.NotificationManager;
     44 import android.app.PendingIntent;
     45 import android.content.BroadcastReceiver;
     46 import android.content.Context;
     47 import android.content.Intent;
     48 import android.net.NetworkStats;
     49 import android.net.Uri;
     50 import android.os.AsyncResult;
     51 import android.os.Bundle;
     52 import android.os.Handler;
     53 import android.os.Message;
     54 import android.os.PersistableBundle;
     55 import android.os.PowerManager;
     56 import android.os.PowerManager.WakeLock;
     57 import android.os.Registrant;
     58 import android.os.RegistrantList;
     59 import android.os.ResultReceiver;
     60 import android.os.SystemProperties;
     61 import android.os.UserHandle;
     62 import android.telephony.CarrierConfigManager;
     63 import android.telephony.PhoneNumberUtils;
     64 import android.telephony.Rlog;
     65 import android.telephony.ServiceState;
     66 import android.telephony.SubscriptionManager;
     67 import android.telephony.TelephonyManager;
     68 import android.telephony.UssdResponse;
     69 import android.telephony.ims.ImsCallForwardInfo;
     70 import android.telephony.ims.ImsCallProfile;
     71 import android.telephony.ims.ImsReasonInfo;
     72 import android.telephony.ims.ImsSsInfo;
     73 import android.text.TextUtils;
     74 
     75 import com.android.ims.ImsEcbm;
     76 import com.android.ims.ImsEcbmStateListener;
     77 import com.android.ims.ImsException;
     78 import com.android.ims.ImsManager;
     79 import com.android.ims.ImsUtInterface;
     80 import com.android.internal.annotations.VisibleForTesting;
     81 import com.android.internal.telephony.Call;
     82 import com.android.internal.telephony.CallForwardInfo;
     83 import com.android.internal.telephony.CallStateException;
     84 import com.android.internal.telephony.CallTracker;
     85 import com.android.internal.telephony.CommandException;
     86 import com.android.internal.telephony.CommandsInterface;
     87 import com.android.internal.telephony.Connection;
     88 import com.android.internal.telephony.GsmCdmaPhone;
     89 import com.android.internal.telephony.MmiCode;
     90 import com.android.internal.telephony.Phone;
     91 import com.android.internal.telephony.PhoneConstants;
     92 import com.android.internal.telephony.PhoneNotifier;
     93 import com.android.internal.telephony.TelephonyComponentFactory;
     94 import com.android.internal.telephony.TelephonyIntents;
     95 import com.android.internal.telephony.TelephonyProperties;
     96 import com.android.internal.telephony.gsm.GsmMmiCode;
     97 import com.android.internal.telephony.gsm.SuppServiceNotification;
     98 import com.android.internal.telephony.uicc.IccRecords;
     99 import com.android.internal.telephony.util.NotificationChannelController;
    100 
    101 import java.io.FileDescriptor;
    102 import java.io.PrintWriter;
    103 import java.util.ArrayList;
    104 import java.util.List;
    105 
    106 /**
    107  * {@hide}
    108  */
    109 public 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(mmi.getDialingNumber(), 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.getStatus();
   1284         cfInfo.reason = getCFReasonFromCondition(info.getCondition());
   1285         cfInfo.serviceClass = SERVICE_CLASS_VOICE;
   1286         cfInfo.toa = info.getToA();
   1287         cfInfo.number = info.getNumber();
   1288         cfInfo.timeSeconds = info.getTimeSeconds();
   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].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
   1313                     if (r != null) {
   1314                         setVoiceCallForwardingFlag(r, 1, (infos[i].getStatus() == 1),
   1315                                 infos[i].getNumber());
   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].getStatus() == 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].getStatus() == 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