ImsPhone.java revision b7aff911e1106eb2eb16fd2bafae0ed8f4d4d4f7
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)) {
527            Rlog.e(LOG_TAG,
528                    "startDtmf called with invalid character '" + c + "'");
529        } else {
530            sendDtmf(c);
531        }
532    }
533
534    @Override
535    public void
536    stopDtmf() {
537        // no op
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            default:
608                break;
609        }
610
611        return ImsUtInterface.INVALID;
612    }
613
614    private int getCFReasonFromCondition(int condition) {
615        switch(condition) {
616            case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
617            case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
618            case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
619            case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
620            default:
621                break;
622        }
623
624        return CF_REASON_NOT_REACHABLE;
625    }
626
627    private int getActionFromCFAction(int action) {
628        switch(action) {
629            case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
630            case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
631            case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
632            case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
633            default:
634                break;
635        }
636
637        return ImsUtInterface.INVALID;
638    }
639
640    @Override
641    public void getCallForwardingOption(int commandInterfaceCFReason,
642            Message onComplete) {
643        if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
644        if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
645            if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
646            Message resp;
647            resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
648
649            try {
650                ImsUtInterface ut = mCT.getUtInterface();
651                ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason),null,resp);
652            } catch (ImsException e) {
653                sendErrorResponse(onComplete, e);
654            }
655        } else if (onComplete != null) {
656            sendErrorResponse(onComplete);
657        }
658    }
659
660    @Override
661    public void setCallForwardingOption(int commandInterfaceCFAction,
662            int commandInterfaceCFReason,
663            String dialingNumber,
664            int timerSeconds,
665            Message onComplete) {
666        if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
667                + ", reason=" + commandInterfaceCFReason);
668        if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
669                (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
670            Message resp;
671            Cf cf = new Cf(dialingNumber,
672                    (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
673                    onComplete);
674            resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
675                    isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
676
677            try {
678                ImsUtInterface ut = mCT.getUtInterface();
679                ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
680                        getConditionFromCFReason(commandInterfaceCFReason),
681                        dialingNumber,
682                        timerSeconds,
683                        onComplete);
684             } catch (ImsException e) {
685                sendErrorResponse(onComplete, e);
686             }
687        } else if (onComplete != null) {
688            sendErrorResponse(onComplete);
689        }
690    }
691
692    @Override
693    public void getCallWaiting(Message onComplete) {
694        if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
695        Message resp;
696        resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
697
698        try {
699            ImsUtInterface ut = mCT.getUtInterface();
700            ut.queryCallWaiting(resp);
701        } catch (ImsException e) {
702            sendErrorResponse(onComplete, e);
703        }
704    }
705
706    @Override
707    public void setCallWaiting(boolean enable, Message onComplete) {
708        if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
709        Message resp;
710        resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
711
712        try {
713            ImsUtInterface ut = mCT.getUtInterface();
714            ut.updateCallWaiting(enable, resp);
715        } catch (ImsException e) {
716            sendErrorResponse(onComplete, e);
717        }
718    }
719
720    private int getCBTypeFromFacility(String facility) {
721        if (CB_FACILITY_BAOC.equals(facility)) {
722            return ImsUtInterface.CB_BAOC;
723        } else if (CB_FACILITY_BAOIC.equals(facility)) {
724            return ImsUtInterface.CB_BOIC;
725        } else if (CB_FACILITY_BAOICxH.equals(facility)) {
726            return ImsUtInterface.CB_BOIC_EXHC;
727        } else if (CB_FACILITY_BAIC.equals(facility)) {
728            return ImsUtInterface.CB_BAIC;
729        } else if (CB_FACILITY_BAICr.equals(facility)) {
730            return ImsUtInterface.CB_BIC_WR;
731        } else if (CB_FACILITY_BA_ALL.equals(facility)) {
732            return ImsUtInterface.CB_BA_ALL;
733        } else if (CB_FACILITY_BA_MO.equals(facility)) {
734            return ImsUtInterface.CB_BA_MO;
735        } else if (CB_FACILITY_BA_MT.equals(facility)) {
736            return ImsUtInterface.CB_BA_MT;
737        }
738
739        return 0;
740    }
741
742    /* package */
743    void getCallBarring(String facility, Message onComplete) {
744        if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
745        Message resp;
746        resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
747
748        try {
749            ImsUtInterface ut = mCT.getUtInterface();
750            ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
751        } catch (ImsException e) {
752            sendErrorResponse(onComplete, e);
753        }
754    }
755
756    /* package */
757    void setCallBarring(String facility, boolean lockState, String password, Message onComplete) {
758        if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
759                + ", lockState=" + lockState);
760        Message resp;
761        resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
762
763        try {
764            ImsUtInterface ut = mCT.getUtInterface();
765            // password is not required with Ut interface
766            ut.updateCallBarring(getCBTypeFromFacility(facility), lockState, resp, null);
767        } catch (ImsException e) {
768            sendErrorResponse(onComplete, e);
769        }
770    }
771
772    @Override
773    public void sendUssdResponse(String ussdMessge) {
774        Rlog.d(LOG_TAG, "sendUssdResponse");
775        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
776        mPendingMMIs.add(mmi);
777        mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
778        mmi.sendUssd(ussdMessge);
779    }
780
781    /* package */
782    void sendUSSD (String ussdString, Message response) {
783        mCT.sendUSSD(ussdString, response);
784    }
785
786    /* package */
787    void cancelUSSD() {
788        mCT.cancelUSSD();
789    }
790
791    /* package */
792    void sendErrorResponse(Message onComplete) {
793        Rlog.d(LOG_TAG, "sendErrorResponse");
794        if (onComplete != null) {
795            AsyncResult.forMessage(onComplete, null,
796                    new CommandException(CommandException.Error.GENERIC_FAILURE));
797            onComplete.sendToTarget();
798        }
799    }
800
801    /* package */
802    void sendErrorResponse(Message onComplete, Throwable e) {
803        Rlog.d(LOG_TAG, "sendErrorResponse");
804        if (onComplete != null) {
805            AsyncResult.forMessage(onComplete, null, getCommandException(e));
806            onComplete.sendToTarget();
807        }
808    }
809
810    /* package */
811    void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) {
812        Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode());
813        if (onComplete != null) {
814            AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode()));
815            onComplete.sendToTarget();
816        }
817    }
818
819    /* package */
820    CommandException getCommandException(int code) {
821        Rlog.d(LOG_TAG, "getCommandException code=" + code);
822        CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
823
824        switch(code) {
825            case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
826                error = CommandException.Error.REQUEST_NOT_SUPPORTED;
827                break;
828            case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
829                error = CommandException.Error.PASSWORD_INCORRECT;
830                break;
831            default:
832                break;
833        }
834
835        return new CommandException(error);
836    }
837
838    /* package */
839    CommandException getCommandException(Throwable e) {
840        CommandException ex = null;
841
842        if (e instanceof ImsException) {
843            ex = getCommandException(((ImsException)e).getCode());
844        } else {
845            Rlog.d(LOG_TAG, "getCommandException generic failure");
846            ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
847        }
848        return ex;
849    }
850
851    private void
852    onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
853        Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
854        mMmiCompleteRegistrants.notifyRegistrants(
855            new AsyncResult(null, mmi, null));
856    }
857
858    /* package */
859    void onIncomingUSSD (int ussdMode, String ussdMessage) {
860        if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
861
862        boolean isUssdError;
863        boolean isUssdRequest;
864
865        isUssdRequest
866            = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
867
868        isUssdError
869            = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
870                && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
871
872        ImsPhoneMmiCode found = null;
873        for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
874            if(mPendingMMIs.get(i).isPendingUSSD()) {
875                found = mPendingMMIs.get(i);
876                break;
877            }
878        }
879
880        if (found != null) {
881            // Complete pending USSD
882            if (isUssdError) {
883                found.onUssdFinishedError();
884            } else {
885                found.onUssdFinished(ussdMessage, isUssdRequest);
886            }
887        } else { // pending USSD not found
888            // The network may initiate its own USSD request
889
890            // ignore everything that isnt a Notify or a Request
891            // also, discard if there is no message to present
892            if (!isUssdError && ussdMessage != null) {
893                ImsPhoneMmiCode mmi;
894                mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
895                        isUssdRequest,
896                        ImsPhone.this);
897                onNetworkInitiatedUssd(mmi);
898            }
899        }
900    }
901
902    /**
903     * Removes the given MMI from the pending list and notifies
904     * registrants that it is complete.
905     * @param mmi MMI that is done
906     */
907    /*package*/ void
908    onMMIDone(ImsPhoneMmiCode mmi) {
909        /* Only notify complete if it's on the pending list.
910         * Otherwise, it's already been handled (eg, previously canceled).
911         * The exception is cancellation of an incoming USSD-REQUEST, which is
912         * not on the list.
913         */
914        if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
915            mMmiCompleteRegistrants.notifyRegistrants(
916                    new AsyncResult(null, mmi, null));
917        }
918    }
919
920    public ImsPhoneConnection getHandoverConnection() {
921        // handover for single foreground call
922        ImsPhoneConnection conn = getForegroundCall().getHandoverConnection();
923
924        // handover for single background call
925        if (conn == null) {
926            conn = getBackgroundCall().getHandoverConnection();
927        }
928
929        // handover for single ringing call
930        if (conn == null) {
931            conn = getRingingCall().getHandoverConnection();
932        }
933
934        return conn;
935    }
936
937    public void notifySrvccState(Call.SrvccState state) {
938        mCT.notifySrvccState(state);
939    }
940
941    /* package */ void
942    initiateSilentRedial() {
943        String result = mLastDialString;
944        AsyncResult ar = new AsyncResult(null, result, null);
945        if (ar != null) {
946            mSilentRedialRegistrants.notifyRegistrants(ar);
947        }
948    }
949
950    public void registerForSilentRedial(Handler h, int what, Object obj) {
951        mSilentRedialRegistrants.addUnique(h, what, obj);
952    }
953
954    public void unregisterForSilentRedial(Handler h) {
955        mSilentRedialRegistrants.remove(h);
956    }
957
958    @Override
959    public long getSubId() {
960        return mDefaultPhone.getSubId();
961    }
962
963    @Override
964    public int getPhoneId() {
965        return mDefaultPhone.getPhoneId();
966    }
967
968    private IccRecords getIccRecords() {
969        return mDefaultPhone.mIccRecords.get();
970    }
971
972    private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
973        CallForwardInfo cfInfo = new CallForwardInfo();
974        cfInfo.status = info.mStatus;
975        cfInfo.reason = getCFReasonFromCondition(info.mCondition);
976        cfInfo.serviceClass = SERVICE_CLASS_VOICE;
977        cfInfo.toa = info.mToA;
978        cfInfo.number = info.mNumber;
979        cfInfo.timeSeconds = info.mTimeSeconds;
980        return cfInfo;
981    }
982
983    private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
984        CallForwardInfo[] cfInfos = null;
985
986        if (infos != null && infos.length != 0) {
987            cfInfos = new CallForwardInfo[infos.length];
988        }
989
990        IccRecords r = getIccRecords();
991        if (infos == null || infos.length == 0) {
992            if (r != null) {
993                // Assume the default is not active
994                // Set unconditional CFF in SIM to false
995                r.setVoiceCallForwardingFlag(1, false, null);
996            }
997        } else {
998            for (int i = 0, s = infos.length; i < s; i++) {
999                if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
1000                    if (r != null) {
1001                        r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1),
1002                            infos[i].mNumber);
1003                    }
1004                }
1005                cfInfos[i] = getCallForwardInfo(infos[i]);
1006            }
1007        }
1008
1009        return cfInfos;
1010    }
1011
1012    private int[] handleCbQueryResult(ImsSsInfo[] infos) {
1013        int[] cbInfos = new int[1];
1014        cbInfos[0] = SERVICE_CLASS_NONE;
1015
1016        if (infos[0].mStatus == 1) {
1017            cbInfos[0] = SERVICE_CLASS_VOICE;
1018        }
1019
1020        return cbInfos;
1021    }
1022
1023    private int[] handleCwQueryResult(ImsSsInfo[] infos) {
1024        int[] cwInfos = new int[2];
1025        cwInfos[0] = 0;
1026
1027        if (infos[0].mStatus == 1) {
1028            cwInfos[0] = 1;
1029            cwInfos[1] = SERVICE_CLASS_VOICE;
1030        }
1031
1032        return cwInfos;
1033    }
1034
1035    private void
1036    sendResponse(Message onComplete, Object result, Throwable e) {
1037        if (onComplete != null) {
1038            CommandException ex = null;
1039            if (e != null) {
1040                ex = getCommandException(e);
1041            }
1042            AsyncResult.forMessage(onComplete, result, ex);
1043            onComplete.sendToTarget();
1044        }
1045    }
1046
1047    @Override
1048    public void handleMessage (Message msg) {
1049        AsyncResult ar = (AsyncResult) msg.obj;
1050        Message onComplete;
1051
1052        if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
1053        switch (msg.what) {
1054            case EVENT_SET_CALL_FORWARD_DONE:
1055                IccRecords r = getIccRecords();
1056                Cf cf = (Cf) ar.userObj;
1057                if (cf.mIsCfu && ar.exception == null && r != null) {
1058                    r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber);
1059                }
1060                sendResponse(cf.mOnComplete, null, ar.exception);
1061                break;
1062
1063            case EVENT_GET_CALL_FORWARD_DONE:
1064                CallForwardInfo[] cfInfos = null;
1065                if (ar.exception == null) {
1066                    cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1067                }
1068                sendResponse((Message) ar.userObj, cfInfos, ar.exception);
1069                break;
1070
1071             case EVENT_GET_CALL_BARRING_DONE:
1072             case EVENT_GET_CALL_WAITING_DONE:
1073                int[] ssInfos = null;
1074                if (ar.exception == null) {
1075                    if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1076                        ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1077                    } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1078                        ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1079                    }
1080                }
1081                sendResponse((Message) ar.userObj, ssInfos, ar.exception);
1082                break;
1083
1084             case EVENT_SET_CALL_BARRING_DONE:
1085             case EVENT_SET_CALL_WAITING_DONE:
1086                sendResponse((Message) ar.userObj, null, ar.exception);
1087                break;
1088
1089             default:
1090                 super.handleMessage(msg);
1091                 break;
1092        }
1093    }
1094
1095    /**
1096     * Listen to the IMS ECBM state change
1097     */
1098    ImsEcbmStateListener mImsEcbmStateListener =
1099            new ImsEcbmStateListener() {
1100                @Override
1101                public void onECBMEntered() {
1102                    if (DBG) Rlog.d(LOG_TAG, "onECBMEntered");
1103                    handleEnterEmergencyCallbackMode();
1104                }
1105
1106                @Override
1107                public void onECBMExited() {
1108                    if (DBG) Rlog.d(LOG_TAG, "onECBMExited");
1109                    handleExitEmergencyCallbackMode();
1110                }
1111            };
1112
1113    public boolean isInEmergencyCall() {
1114        return mCT.isInEmergencyCall();
1115    }
1116
1117    public boolean isInEcm() {
1118        return mIsPhoneInEcmState;
1119    }
1120
1121    void sendEmergencyCallbackModeChange() {
1122        // Send an Intent
1123        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1124        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
1125        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
1126        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
1127        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange");
1128    }
1129
1130    @Override
1131    public void exitEmergencyCallbackMode() {
1132        if (mWakeLock.isHeld()) {
1133            mWakeLock.release();
1134        }
1135        if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()");
1136
1137        // Send a message which will invoke handleExitEmergencyCallbackMode
1138        ImsEcbm ecbm;
1139        try {
1140            ecbm = mCT.getEcbmInterface();
1141            ecbm.exitEmergencyCallbackMode();
1142        } catch (ImsException e) {
1143            e.printStackTrace();
1144        }
1145    }
1146
1147    private void handleEnterEmergencyCallbackMode() {
1148        if (DBG) {
1149            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
1150                    + mIsPhoneInEcmState);
1151        }
1152        // if phone is not in Ecm mode, and it's changed to Ecm mode
1153        if (mIsPhoneInEcmState == false) {
1154            mIsPhoneInEcmState = true;
1155            // notify change
1156            sendEmergencyCallbackModeChange();
1157            setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
1158
1159            // Post this runnable so we will automatically exit
1160            // if no one invokes exitEmergencyCallbackMode() directly.
1161            long delayInMillis = SystemProperties.getLong(
1162                    TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1163            postDelayed(mExitEcmRunnable, delayInMillis);
1164            // We don't want to go to sleep while in Ecm
1165            mWakeLock.acquire();
1166        }
1167    }
1168
1169    private void handleExitEmergencyCallbackMode() {
1170        if (DBG) {
1171            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = "
1172                    + mIsPhoneInEcmState);
1173        }
1174        // Remove pending exit Ecm runnable, if any
1175        removeCallbacks(mExitEcmRunnable);
1176
1177        if (mEcmExitRespRegistrant != null) {
1178            mEcmExitRespRegistrant.notifyResult(Boolean.TRUE);
1179        }
1180            if (mIsPhoneInEcmState) {
1181                mIsPhoneInEcmState = false;
1182                setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
1183            }
1184            // send an Intent
1185            sendEmergencyCallbackModeChange();
1186    }
1187
1188    /**
1189     * Handle to cancel or restart Ecm timer in emergency call back mode if action is
1190     * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart
1191     * Ecm timer and notify apps the timer is restarted.
1192     */
1193    void handleTimerInEmergencyCallbackMode(int action) {
1194        switch (action) {
1195            case CANCEL_ECM_TIMER:
1196                removeCallbacks(mExitEcmRunnable);
1197                if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
1198                    ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE);
1199                } else { // Should be CDMA - also go here by default
1200                    ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE);
1201                }
1202                break;
1203            case RESTART_ECM_TIMER:
1204                long delayInMillis = SystemProperties.getLong(
1205                        TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1206                postDelayed(mExitEcmRunnable, delayInMillis);
1207                if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
1208                    ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
1209                } else { // Should be CDMA - also go here by default
1210                    ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
1211                }
1212                break;
1213            default:
1214                Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
1215        }
1216    }
1217
1218    public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
1219        mEcmExitRespRegistrant = new Registrant(h, what, obj);
1220    }
1221
1222    public void unsetOnEcbModeExitResponse(Handler h) {
1223        mEcmExitRespRegistrant.clear();
1224    }
1225
1226    public boolean isVolteEnabled() {
1227        return mCT.isVolteEnabled();
1228    }
1229
1230    public boolean isVtEnabled() {
1231        return mCT.isVtEnabled();
1232    }
1233}
1234