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