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