ImsPhone.java revision 69e989aceb5660b39932049daad2299f75f07201
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony.imsphone;
18
19import android.app.ActivityManagerNative;
20import android.content.Context;
21import android.content.Intent;
22import android.os.AsyncResult;
23import android.os.Handler;
24import android.os.Message;
25import android.os.PowerManager;
26import android.os.Registrant;
27import android.os.RegistrantList;
28import android.os.PowerManager.WakeLock;
29import android.os.SystemProperties;
30import android.os.UserHandle;
31
32import android.telephony.PhoneNumberUtils;
33import android.telephony.ServiceState;
34import android.telephony.Rlog;
35import android.telephony.SubscriptionManager;
36import android.text.TextUtils;
37
38import com.android.ims.ImsCallForwardInfo;
39import com.android.ims.ImsCallProfile;
40import com.android.ims.ImsEcbm;
41import com.android.ims.ImsEcbmStateListener;
42import com.android.ims.ImsException;
43import com.android.ims.ImsReasonInfo;
44import com.android.ims.ImsSsInfo;
45import com.android.ims.ImsUtInterface;
46
47import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
48import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
49import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
50import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
51import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
52import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
53import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
54import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
55
56import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
57import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
58import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
59import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
60import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
61import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
62import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
63import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
64import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
65import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
66import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
67import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
68
69import com.android.internal.telephony.Call;
70import com.android.internal.telephony.CallForwardInfo;
71import com.android.internal.telephony.CallStateException;
72import com.android.internal.telephony.CallTracker;
73import com.android.internal.telephony.CommandException;
74import com.android.internal.telephony.CommandsInterface;
75import com.android.internal.telephony.Connection;
76import com.android.internal.telephony.Phone;
77import com.android.internal.telephony.PhoneBase;
78import com.android.internal.telephony.PhoneConstants;
79import com.android.internal.telephony.PhoneNotifier;
80import com.android.internal.telephony.TelephonyIntents;
81import com.android.internal.telephony.TelephonyProperties;
82import com.android.internal.telephony.cdma.CDMAPhone;
83import com.android.internal.telephony.gsm.GSMPhone;
84import com.android.internal.telephony.uicc.IccRecords;
85
86import java.util.ArrayList;
87import java.util.List;
88
89/**
90 * {@hide}
91 */
92public 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
148        Cf(String cfNumber, boolean isCfu, Message onComplete) {
149            mSetCfNumber = cfNumber;
150            mIsCfu = isCfu;
151            mOnComplete = onComplete;
152        }
153    }
154
155    // Constructors
156
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
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
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
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
204    getServiceState() {
205        return mSS;
206    }
207
208    /* package */ void setServiceState(int state) {
209        mSS.setState(state);
210    }
211
212    @Override
213    public CallTracker getCallTracker() {
214        return mCT;
215    }
216
217    @Override
218    public List<? extends ImsPhoneMmiCode>
219    getPendingMmiCodes() {
220        return mPendingMMIs;
221    }
222
223
224    @Override
225    public void
226    acceptCall(int videoState) throws CallStateException {
227        mCT.acceptCall(videoState);
228    }
229
230    @Override
231    public void
232    rejectCall() throws CallStateException {
233        mCT.rejectCall();
234    }
235
236    @Override
237    public void
238    switchHoldingAndActive() throws CallStateException {
239        mCT.switchWaitingOrHoldingAndActive();
240    }
241
242    @Override
243    public boolean canConference() {
244        return mCT.canConference();
245    }
246
247    public boolean canDial() {
248        return mCT.canDial();
249    }
250
251    @Override
252    public void conference() {
253        mCT.conference();
254    }
255
256    @Override
257    public void clearDisconnected() {
258        mCT.clearDisconnected();
259    }
260
261    @Override
262    public boolean canTransfer() {
263        return mCT.canTransfer();
264    }
265
266    @Override
267    public void explicitCallTransfer() {
268        mCT.explicitCallTransfer();
269    }
270
271    @Override
272    public ImsPhoneCall
273    getForegroundCall() {
274        return mCT.mForegroundCall;
275    }
276
277    @Override
278    public ImsPhoneCall
279    getBackgroundCall() {
280        return mCT.mBackgroundCall;
281    }
282
283    @Override
284    public ImsPhoneCall
285    getRingingCall() {
286        return mCT.mRingingCall;
287    }
288
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
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
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
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
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
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
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
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
462    void notifyNewRingingConnection(Connection c) {
463        mDefaultPhone.notifyNewRingingConnectionP(c);
464    }
465
466
467    @Override
468    public Connection
469    dial(String dialString, int videoState) throws CallStateException {
470        return dialInternal(dialString, videoState);
471    }
472
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
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
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
537    stopDtmf() {
538        mCT.stopDtmf();
539    }
540
541    @Override
542    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
543        mPostDialHandler = new Registrant(h, what, obj);
544    }
545
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
553    public void setMute(boolean muted) {
554        mCT.setMute(muted);
555    }
556
557    @Override
558    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
559        mCT.setUiTTYMode(uiTtyMode, onComplete);
560    }
561
562    @Override
563    public boolean getMute() {
564        return mCT.getMute();
565    }
566
567    @Override
568    public PhoneConstants.State getState() {
569        return mCT.mState;
570    }
571
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
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
598    private  boolean isCfEnable(int action) {
599        return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
600    }
601
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
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
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
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
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
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
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
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 */
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 */
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
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 */
787    void sendUSSD (String ussdString, Message response) {
788        mCT.sendUSSD(ussdString, response);
789    }
790
791    /* package */
792    void cancelUSSD() {
793        mCT.cancelUSSD();
794    }
795
796    /* package */
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 */
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 */
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 */
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 */
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
857    onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
858        Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
859        mMmiCompleteRegistrants.notifyRegistrants(
860            new AsyncResult(null, mmi, null));
861    }
862
863    /* package */
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
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
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
940    public void notifySrvccState(Call.SrvccState state) {
941        mCT.notifySrvccState(state);
942    }
943
944    /* package */ void
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
953    public void registerForSilentRedial(Handler h, int what, Object obj) {
954        mSilentRedialRegistrants.addUnique(h, what, obj);
955    }
956
957    public void unregisterForSilentRedial(Handler h) {
958        mSilentRedialRegistrants.remove(h);
959    }
960
961    @Override
962    public int getSubId() {
963        return mDefaultPhone.getSubId();
964    }
965
966    @Override
967    public int getPhoneId() {
968        return mDefaultPhone.getPhoneId();
969    }
970
971    private IccRecords getIccRecords() {
972        return mDefaultPhone.mIccRecords.get();
973    }
974
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
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
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
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
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
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
1124    public boolean isInEmergencyCall() {
1125        return mCT.isInEmergencyCall();
1126    }
1127
1128    public boolean isInEcm() {
1129        return mIsPhoneInEcmState;
1130    }
1131
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
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
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
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     */
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
1229    public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
1230        mEcmExitRespRegistrant = new Registrant(h, what, obj);
1231    }
1232
1233    public void unsetOnEcbModeExitResponse(Handler h) {
1234        mEcmExitRespRegistrant.clear();
1235    }
1236
1237    public boolean isVolteEnabled() {
1238        return mCT.isVolteEnabled();
1239    }
1240
1241    public boolean isVtEnabled() {
1242        return mCT.isVtEnabled();
1243    }
1244
1245    public Phone getDefaultPhone() {
1246        return mDefaultPhone;
1247    }
1248
1249    public boolean isImsRegistered() {
1250        return mImsRegistered;
1251    }
1252
1253    public void setImsRegistered(boolean value) {
1254        mImsRegistered = value;
1255    }
1256
1257    public void callEndCleanupHandOverCallIfAny() {
1258        mCT.callEndCleanupHandOverCallIfAny();
1259    }
1260}
1261