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