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