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 
Cf(String cfNumber, boolean isCfu, Message onComplete)165         Cf(String cfNumber, boolean isCfu, Message onComplete) {
166             mSetCfNumber = cfNumber;
167             mIsCfu = isCfu;
168             mOnComplete = onComplete;
169         }
170     }
171 
172     // Constructors
173 
ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone)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 
updateParentPhone(PhoneBase parentPhone)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
dispose()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
removeReferences()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
getServiceState()247     getServiceState() {
248         return mSS;
249     }
250 
setServiceState(int state)251     /* package */ void setServiceState(int state) {
252         mSS.setState(state);
253         updateDataServiceState();
254     }
255 
256     @Override
getCallTracker()257     public CallTracker getCallTracker() {
258         return mCT;
259     }
260 
261     @Override
262     public List<? extends ImsPhoneMmiCode>
getPendingMmiCodes()263     getPendingMmiCodes() {
264         return mPendingMMIs;
265     }
266 
267 
268     @Override
269     public void
acceptCall(int videoState)270     acceptCall(int videoState) throws CallStateException {
271         mCT.acceptCall(videoState);
272     }
273 
274     @Override
275     public void
rejectCall()276     rejectCall() throws CallStateException {
277         mCT.rejectCall();
278     }
279 
280     @Override
281     public void
switchHoldingAndActive()282     switchHoldingAndActive() throws CallStateException {
283         mCT.switchWaitingOrHoldingAndActive();
284     }
285 
286     @Override
canConference()287     public boolean canConference() {
288         return mCT.canConference();
289     }
290 
canDial()291     public boolean canDial() {
292         return mCT.canDial();
293     }
294 
295     @Override
conference()296     public void conference() {
297         mCT.conference();
298     }
299 
300     @Override
clearDisconnected()301     public void clearDisconnected() {
302         mCT.clearDisconnected();
303     }
304 
305     @Override
canTransfer()306     public boolean canTransfer() {
307         return mCT.canTransfer();
308     }
309 
310     @Override
explicitCallTransfer()311     public void explicitCallTransfer() {
312         mCT.explicitCallTransfer();
313     }
314 
315     @Override
316     public ImsPhoneCall
getForegroundCall()317     getForegroundCall() {
318         return mCT.mForegroundCall;
319     }
320 
321     @Override
322     public ImsPhoneCall
getBackgroundCall()323     getBackgroundCall() {
324         return mCT.mBackgroundCall;
325     }
326 
327     @Override
328     public ImsPhoneCall
getRingingCall()329     getRingingCall() {
330         return mCT.mRingingCall;
331     }
332 
handleCallDeflectionIncallSupplementaryService( String dialString)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 
handleCallWaitingIncallSupplementaryService( String dialString)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 
handleCallHoldIncallSupplementaryService(String dialString)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 
handleMultipartyIncallSupplementaryService( String dialString)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 
handleEctIncallSupplementaryService(String dialString)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 
handleCcbsIncallSupplementaryService(String dialString)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
handleInCallMmiCommands(String dialString)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 
isInCall()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 
notifyNewRingingConnection(Connection c)506     void notifyNewRingingConnection(Connection c) {
507         mDefaultPhone.notifyNewRingingConnectionP(c);
508     }
509 
checkWfcWifiOnlyModeBeforeDial(ImsPhone imsPhone, Context context)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 
notifyForVideoCapabilityChanged(boolean isVideoCapable)526     public void notifyForVideoCapabilityChanged(boolean isVideoCapable) {
527         mIsVideoCapable = isVideoCapable;
528         mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable);
529     }
530 
531     @Override
532     public Connection
dial(String dialString, int videoState)533     dial(String dialString, int videoState) throws CallStateException {
534         return dialInternal(dialString, videoState, null);
535     }
536 
537     @Override
538     public Connection
dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)539     dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
540             throws CallStateException {
541         // ignore UUSInfo
542         return dialInternal (dialString, videoState, intentExtras);
543     }
544 
dialInternal(String dialString, int videoState, Bundle intentExtras)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
sendDtmf(char c)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
startDtmf(char c)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
stopDtmf()609     stopDtmf() {
610         mCT.stopDtmf();
611     }
612 
613     @Override
setOnPostDialCharacter(Handler h, int what, Object obj)614     public void setOnPostDialCharacter(Handler h, int what, Object obj) {
615         mPostDialHandler = new Registrant(h, what, obj);
616     }
617 
notifyIncomingRing()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
setMute(boolean muted)625     public void setMute(boolean muted) {
626         mCT.setMute(muted);
627     }
628 
629     @Override
setUiTTYMode(int uiTtyMode, Message onComplete)630     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
631         mCT.setUiTTYMode(uiTtyMode, onComplete);
632     }
633 
634     @Override
getMute()635     public boolean getMute() {
636         return mCT.getMute();
637     }
638 
639     @Override
getState()640     public PhoneConstants.State getState() {
641         return mCT.mState;
642     }
643 
isValidCommandInterfaceCFReason(int commandInterfaceCFReason)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 
isValidCommandInterfaceCFAction(int commandInterfaceCFAction)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 
isCfEnable(int action)670     private  boolean isCfEnable(int action) {
671         return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
672     }
673 
getConditionFromCFReason(int reason)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 
getCFReasonFromCondition(int condition)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 
getActionFromCFAction(int action)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
getCallForwardingOption(int commandInterfaceCFReason, Message onComplete)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
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete)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 
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)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
getCallWaiting(Message onComplete)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
setCallWaiting(boolean enable, Message onComplete)795     public void setCallWaiting(boolean enable, Message onComplete) {
796         setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
797     }
798 
setCallWaiting(boolean enable, int serviceClass, Message onComplete)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 
getCBTypeFromFacility(String facility)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 */
getCallBarring(String facility, Message onComplete)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 */
setCallBarring(String facility, boolean lockState, String password, Message onComplete)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
sendUssdResponse(String ussdMessge)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 */
sendUSSD(String ussdString, Message response)874     void sendUSSD (String ussdString, Message response) {
875         mCT.sendUSSD(ussdString, response);
876     }
877 
878     /* package */
cancelUSSD()879     void cancelUSSD() {
880         mCT.cancelUSSD();
881     }
882 
883     /* package */
sendErrorResponse(Message onComplete)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 */
sendErrorResponse(Message onComplete, Throwable e)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 */
sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo)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 */
getCommandException(int code)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 */
getCommandException(Throwable e)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
onNetworkInitiatedUssd(ImsPhoneMmiCode mmi)946     onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
947         Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
948         mMmiCompleteRegistrants.notifyRegistrants(
949             new AsyncResult(null, mmi, null));
950     }
951 
952     /* package */
onIncomingUSSD(int ussdMode, String ussdMessage)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
onMMIDone(ImsPhoneMmiCode mmi)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 
getHandoverConnection()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 
notifySrvccState(Call.SrvccState state)1029     public void notifySrvccState(Call.SrvccState state) {
1030         mCT.notifySrvccState(state);
1031     }
1032 
1033     /* package */ void
initiateSilentRedial()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 
registerForSilentRedial(Handler h, int what, Object obj)1042     public void registerForSilentRedial(Handler h, int what, Object obj) {
1043         mSilentRedialRegistrants.addUnique(h, what, obj);
1044     }
1045 
unregisterForSilentRedial(Handler h)1046     public void unregisterForSilentRedial(Handler h) {
1047         mSilentRedialRegistrants.remove(h);
1048     }
1049 
1050     @Override
getSubId()1051     public int getSubId() {
1052         return mDefaultPhone.getSubId();
1053     }
1054 
1055     @Override
getPhoneId()1056     public int getPhoneId() {
1057         return mDefaultPhone.getPhoneId();
1058     }
1059 
getIccRecords()1060     private IccRecords getIccRecords() {
1061         return mDefaultPhone.mIccRecords.get();
1062     }
1063 
getCallForwardInfo(ImsCallForwardInfo info)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 
handleCfQueryResult(ImsCallForwardInfo[] infos)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 
handleCbQueryResult(ImsSsInfo[] infos)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 
handleCwQueryResult(ImsSsInfo[] infos)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
sendResponse(Message onComplete, Object result, Throwable e)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 
updateDataServiceState()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
handleMessage(Message msg)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 
isInEmergencyCall()1220     public boolean isInEmergencyCall() {
1221         return mCT.isInEmergencyCall();
1222     }
1223 
isInEcm()1224     public boolean isInEcm() {
1225         return mIsPhoneInEcmState;
1226     }
1227 
sendEmergencyCallbackModeChange()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
exitEmergencyCallbackMode()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 
handleEnterEmergencyCallbackMode()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 
handleExitEmergencyCallbackMode()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      */
handleTimerInEmergencyCallbackMode(int action)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 
setOnEcbModeExitResponse(Handler h, int what, Object obj)1325     public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
1326         mEcmExitRespRegistrant = new Registrant(h, what, obj);
1327     }
1328 
unsetOnEcbModeExitResponse(Handler h)1329     public void unsetOnEcbModeExitResponse(Handler h) {
1330         mEcmExitRespRegistrant.clear();
1331     }
1332 
onFeatureCapabilityChanged()1333     public void onFeatureCapabilityChanged() {
1334         mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
1335     }
1336 
isVolteEnabled()1337     public boolean isVolteEnabled() {
1338         return mCT.isVolteEnabled();
1339     }
1340 
isVowifiEnabled()1341     public boolean isVowifiEnabled() {
1342         return mCT.isVowifiEnabled();
1343     }
1344 
isVideoCallEnabled()1345     public boolean isVideoCallEnabled() {
1346         return mCT.isVideoCallEnabled();
1347     }
1348 
getDefaultPhone()1349     public Phone getDefaultPhone() {
1350         return mDefaultPhone;
1351     }
1352 
isImsRegistered()1353     public boolean isImsRegistered() {
1354         return mImsRegistered;
1355     }
1356 
setImsRegistered(boolean value)1357     public void setImsRegistered(boolean value) {
1358         mImsRegistered = value;
1359     }
1360 
callEndCleanupHandOverCallIfAny()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      */
processDisconnectReason(ImsReasonInfo imsReasonInfo)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