ImsPhone.java revision 888c60a7fe1c8f3612ab8e8ec5f2f7ca8f24e766
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.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25
26import android.telephony.PhoneNumberUtils;
27import android.telephony.ServiceState;
28import android.telephony.Rlog;
29import android.text.TextUtils;
30
31import com.android.ims.ImsCallForwardInfo;
32import com.android.ims.ImsException;
33import com.android.ims.ImsReasonInfo;
34import com.android.ims.ImsSsInfo;
35import com.android.ims.ImsUtInterface;
36
37import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
38import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
39import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
40import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
41import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
42import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
43import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
44import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
45
46import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
47import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
48import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
49import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
50import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
51import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
52import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
53import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
54import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
55import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
56import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
57import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
58
59import com.android.internal.telephony.Call;
60import com.android.internal.telephony.CallForwardInfo;
61import com.android.internal.telephony.CallStateException;
62import com.android.internal.telephony.CallTracker;
63import com.android.internal.telephony.CommandException;
64import com.android.internal.telephony.CommandsInterface;
65import com.android.internal.telephony.Connection;
66import com.android.internal.telephony.Phone;
67import com.android.internal.telephony.PhoneBase;
68import com.android.internal.telephony.PhoneConstants;
69import com.android.internal.telephony.PhoneNotifier;
70import com.android.internal.telephony.Subscription;
71import com.android.internal.telephony.uicc.IccRecords;
72
73import java.util.ArrayList;
74import java.util.List;
75
76/**
77 * {@hide}
78 */
79public class ImsPhone extends ImsPhoneBase {
80    private static final String LOG_TAG = "ImsPhone";
81    private static final boolean DBG = true;
82    private static final boolean VDBG = false; // STOPSHIP if true
83
84    protected static final int EVENT_SET_CALL_BARRING_DONE          = EVENT_LAST + 1;
85    protected static final int EVENT_GET_CALL_BARRING_DONE          = EVENT_LAST + 2;
86    protected static final int EVENT_SET_CALL_WAITING_DONE          = EVENT_LAST + 3;
87    protected static final int EVENT_GET_CALL_WAITING_DONE          = EVENT_LAST + 4;
88
89    public static final String CS_FALLBACK = "cs_fallback";
90
91    // Instance Variables
92    PhoneBase mDefaultPhone;
93    ImsPhoneCallTracker mCT;
94    ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
95
96    Registrant mPostDialHandler;
97    ServiceState mSS = new ServiceState();
98
99    // To redial silently through GSM or CDMA when dialing through IMS fails
100    private String mLastDialString;
101
102    private final RegistrantList mSilentRedialRegistrants
103            = new RegistrantList();
104
105    // Create Cf (Call forward) so that dialling number &
106    // mIsCfu (true if reason is call forward unconditional)
107    // mOnComplete (Message object passed by client) can be packed &
108    // given as a single Cf object as user data to UtInterface.
109    private static class Cf {
110        final String mSetCfNumber;
111        final Message mOnComplete;
112        final boolean mIsCfu;
113
114        Cf(String cfNumber, boolean isCfu, Message onComplete) {
115            mSetCfNumber = cfNumber;
116            mIsCfu = isCfu;
117            mOnComplete = onComplete;
118        }
119    }
120
121    // Constructors
122
123    ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
124        super("ImsPhone", context, notifier);
125
126        mDefaultPhone = (PhoneBase) defaultPhone;
127        mCT = new ImsPhoneCallTracker(this);
128        mSS.setStateOff();
129
130        mPhoneId = mDefaultPhone.getPhoneId();
131    }
132
133    @Override
134    public void dispose() {
135        Rlog.d(LOG_TAG, "dispose");
136        // Nothing to dispose in PhoneBase
137        //super.dispose();
138        mPendingMMIs.clear();
139        mCT.dispose();
140
141        //Force all referenced classes to unregister their former registered events
142    }
143
144    @Override
145    public void removeReferences() {
146        Rlog.d(LOG_TAG, "removeReferences");
147        super.removeReferences();
148
149        mCT = null;
150        mSS = null;
151    }
152
153    @Override
154    public ServiceState
155    getServiceState() {
156        return mSS;
157    }
158
159    /* package */ void setServiceState(int state) {
160        mSS.setState(state);
161    }
162
163    @Override
164    public CallTracker getCallTracker() {
165        return mCT;
166    }
167
168    @Override
169    public List<? extends ImsPhoneMmiCode>
170    getPendingMmiCodes() {
171        return mPendingMMIs;
172    }
173
174
175    @Override
176    public void
177    acceptCall() throws CallStateException {
178        mCT.acceptCall();
179    }
180
181    @Override
182    public void
183    rejectCall() throws CallStateException {
184        mCT.rejectCall();
185    }
186
187    @Override
188    public void
189    switchHoldingAndActive() throws CallStateException {
190        mCT.switchWaitingOrHoldingAndActive();
191    }
192
193    @Override
194    public boolean canConference() {
195        return mCT.canConference();
196    }
197
198    public boolean canDial() {
199        return mCT.canDial();
200    }
201
202    @Override
203    public void conference() {
204        mCT.conference();
205    }
206
207    @Override
208    public void clearDisconnected() {
209        mCT.clearDisconnected();
210    }
211
212    @Override
213    public boolean canTransfer() {
214        return mCT.canTransfer();
215    }
216
217    @Override
218    public void explicitCallTransfer() {
219        mCT.explicitCallTransfer();
220    }
221
222    @Override
223    public ImsPhoneCall
224    getForegroundCall() {
225        return mCT.mForegroundCall;
226    }
227
228    @Override
229    public ImsPhoneCall
230    getBackgroundCall() {
231        return mCT.mBackgroundCall;
232    }
233
234    @Override
235    public ImsPhoneCall
236    getRingingCall() {
237        return mCT.mRingingCall;
238    }
239
240    private boolean handleCallDeflectionIncallSupplementaryService(
241            String dialString) {
242        if (dialString.length() > 1) {
243            return false;
244        }
245
246        if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
247            if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall");
248            try {
249                mCT.rejectCall();
250            } catch (CallStateException e) {
251                if (DBG) Rlog.d(LOG_TAG, "reject failed", e);
252                notifySuppServiceFailed(Phone.SuppService.REJECT);
253            }
254        } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) {
255            if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground");
256            try {
257                mCT.hangup(getBackgroundCall());
258            } catch (CallStateException e) {
259                if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
260            }
261        }
262
263        return true;
264    }
265
266
267    private boolean handleCallWaitingIncallSupplementaryService(
268            String dialString) {
269        int len = dialString.length();
270
271        if (len > 2) {
272            return false;
273        }
274
275        ImsPhoneCall call = getForegroundCall();
276
277        try {
278            if (len > 1) {
279                if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND");
280                notifySuppServiceFailed(Phone.SuppService.HANGUP);
281            } else {
282                if (call.getState() != ImsPhoneCall.State.IDLE) {
283                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground");
284                    mCT.hangup(call);
285                } else {
286                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive");
287                    mCT.switchWaitingOrHoldingAndActive();
288                }
289            }
290        } catch (CallStateException e) {
291            if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
292            notifySuppServiceFailed(Phone.SuppService.HANGUP);
293        }
294
295        return true;
296    }
297
298    private boolean handleCallHoldIncallSupplementaryService(String dialString) {
299        int len = dialString.length();
300
301        if (len > 2) {
302            return false;
303        }
304
305        ImsPhoneCall call = getForegroundCall();
306
307        if (len > 1) {
308            if (DBG) Rlog.d(LOG_TAG, "separate not supported");
309            notifySuppServiceFailed(Phone.SuppService.SEPARATE);
310        } else {
311            try {
312                if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
313                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call");
314                    mCT.acceptCall();
315                } else {
316                    if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive");
317                    mCT.switchWaitingOrHoldingAndActive();
318                }
319            } catch (CallStateException e) {
320                if (DBG) Rlog.d(LOG_TAG, "switch failed", e);
321                notifySuppServiceFailed(Phone.SuppService.SWITCH);
322            }
323        }
324
325        return true;
326    }
327
328    private boolean handleMultipartyIncallSupplementaryService(
329            String dialString) {
330        if (dialString.length() > 1) {
331            return false;
332        }
333
334        if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls");
335        conference();
336        return true;
337    }
338
339    private boolean handleEctIncallSupplementaryService(String dialString) {
340
341        int len = dialString.length();
342
343        if (len != 1) {
344            return false;
345        }
346
347        if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer");
348        notifySuppServiceFailed(Phone.SuppService.TRANSFER);
349        return true;
350    }
351
352    private boolean handleCcbsIncallSupplementaryService(String dialString) {
353        if (dialString.length() > 1) {
354            return false;
355        }
356
357        Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!");
358        // Treat it as an "unknown" service.
359        notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
360        return true;
361    }
362
363    @Override
364    public boolean handleInCallMmiCommands(String dialString) {
365        if (!isInCall()) {
366            return false;
367        }
368
369        if (TextUtils.isEmpty(dialString)) {
370            return false;
371        }
372
373        boolean result = false;
374        char ch = dialString.charAt(0);
375        switch (ch) {
376            case '0':
377                result = handleCallDeflectionIncallSupplementaryService(
378                        dialString);
379                break;
380            case '1':
381                result = handleCallWaitingIncallSupplementaryService(
382                        dialString);
383                break;
384            case '2':
385                result = handleCallHoldIncallSupplementaryService(dialString);
386                break;
387            case '3':
388                result = handleMultipartyIncallSupplementaryService(dialString);
389                break;
390            case '4':
391                result = handleEctIncallSupplementaryService(dialString);
392                break;
393            case '5':
394                result = handleCcbsIncallSupplementaryService(dialString);
395                break;
396            default:
397                break;
398        }
399
400        return result;
401    }
402
403    boolean isInCall() {
404        ImsPhoneCall.State foregroundCallState = getForegroundCall().getState();
405        ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState();
406        ImsPhoneCall.State ringingCallState = getRingingCall().getState();
407
408       return (foregroundCallState.isAlive() ||
409               backgroundCallState.isAlive() ||
410               ringingCallState.isAlive());
411    }
412
413    @Override
414    public Connection
415    dial(String dialString) throws CallStateException {
416        // Need to make sure dialString gets parsed properly
417        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
418
419        // handle in-call MMI first if applicable
420        if (handleInCallMmiCommands(newDialString)) {
421            return null;
422        }
423
424        if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
425            return mCT.dial(dialString);
426        }
427
428        // Only look at the Network portion for mmi
429        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
430        ImsPhoneMmiCode mmi =
431                ImsPhoneMmiCode.newFromDialString(networkPortion, this);
432        if (DBG) Rlog.d(LOG_TAG,
433                "dialing w/ mmi '" + mmi + "'...");
434
435        if (mmi == null) {
436            return mCT.dial(dialString);
437        } else if (mmi.isTemporaryModeCLIR()) {
438            return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode());
439        } else if (!mmi.isSupportedOverImsPhone()) {
440            // If the mmi is not supported by IMS service,
441            // try to initiate dialing with default phone
442            throw new CallStateException(CS_FALLBACK);
443        } else {
444            mPendingMMIs.add(mmi);
445            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
446            mmi.processCode();
447
448            return null;
449        }
450    }
451
452
453    @Override
454    public void
455    sendDtmf(char c) {
456        if (!PhoneNumberUtils.is12Key(c)) {
457            Rlog.e(LOG_TAG,
458                    "sendDtmf called with invalid character '" + c + "'");
459        } else {
460            if (mCT.mState ==  PhoneConstants.State.OFFHOOK) {
461                mCT.sendDtmf(c);
462            }
463        }
464    }
465
466    @Override
467    public void
468    startDtmf(char c) {
469        if (!PhoneNumberUtils.is12Key(c)) {
470            Rlog.e(LOG_TAG,
471                    "startDtmf called with invalid character '" + c + "'");
472        } else {
473            sendDtmf(c);
474        }
475    }
476
477    @Override
478    public void
479    stopDtmf() {
480        // no op
481    }
482
483    @Override
484    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
485        mPostDialHandler = new Registrant(h, what, obj);
486    }
487
488    /*package*/ void notifyIncomingRing() {
489        if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing");
490        AsyncResult ar = new AsyncResult(null, null, null);
491        sendMessage(obtainMessage(EVENT_CALL_RING, ar));
492    }
493
494    @Override
495    public void setMute(boolean muted) {
496        mCT.setMute(muted);
497    }
498
499    @Override
500    public boolean getMute() {
501        return mCT.getMute();
502    }
503
504    @Override
505    public PhoneConstants.State getState() {
506        return mCT.mState;
507    }
508
509    private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
510        switch (commandInterfaceCFReason) {
511        case CF_REASON_UNCONDITIONAL:
512        case CF_REASON_BUSY:
513        case CF_REASON_NO_REPLY:
514        case CF_REASON_NOT_REACHABLE:
515        case CF_REASON_ALL:
516        case CF_REASON_ALL_CONDITIONAL:
517            return true;
518        default:
519            return false;
520        }
521    }
522
523    private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
524        switch (commandInterfaceCFAction) {
525        case CF_ACTION_DISABLE:
526        case CF_ACTION_ENABLE:
527        case CF_ACTION_REGISTRATION:
528        case CF_ACTION_ERASURE:
529            return true;
530        default:
531            return false;
532        }
533    }
534
535    private  boolean isCfEnable(int action) {
536        return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
537    }
538
539    private int getConditionFromCFReason(int reason) {
540        switch(reason) {
541            case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
542            case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
543            case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
544            case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
545            default:
546                break;
547        }
548
549        return ImsUtInterface.INVALID;
550    }
551
552    private int getCFReasonFromCondition(int condition) {
553        switch(condition) {
554            case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
555            case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
556            case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
557            case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
558            default:
559                break;
560        }
561
562        return CF_REASON_NOT_REACHABLE;
563    }
564
565    private int getActionFromCFAction(int action) {
566        switch(action) {
567            case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
568            case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
569            case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
570            case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
571            default:
572                break;
573        }
574
575        return ImsUtInterface.INVALID;
576    }
577
578    @Override
579    public void getCallForwardingOption(int commandInterfaceCFReason,
580            Message onComplete) {
581        if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
582        if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
583            if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
584            Message resp;
585            resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
586
587            try {
588                ImsUtInterface ut = mCT.getUtInterface();
589                ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason),null,resp);
590            } catch (ImsException e) {
591                sendErrorResponse(onComplete, e);
592            }
593        } else if (onComplete != null) {
594            sendErrorResponse(onComplete);
595        }
596    }
597
598    @Override
599    public void setCallForwardingOption(int commandInterfaceCFAction,
600            int commandInterfaceCFReason,
601            String dialingNumber,
602            int timerSeconds,
603            Message onComplete) {
604        if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
605                + ", reason=" + commandInterfaceCFReason);
606        if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
607                (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
608            Message resp;
609            Cf cf = new Cf(dialingNumber,
610                    (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
611                    onComplete);
612            resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
613                    isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
614
615            try {
616                ImsUtInterface ut = mCT.getUtInterface();
617                ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
618                        getConditionFromCFReason(commandInterfaceCFReason),
619                        dialingNumber,
620                        timerSeconds,
621                        onComplete);
622             } catch (ImsException e) {
623                sendErrorResponse(onComplete, e);
624             }
625        } else if (onComplete != null) {
626            sendErrorResponse(onComplete);
627        }
628    }
629
630    @Override
631    public void getCallWaiting(Message onComplete) {
632        if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
633        Message resp;
634        resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
635
636        try {
637            ImsUtInterface ut = mCT.getUtInterface();
638            ut.queryCallWaiting(resp);
639        } catch (ImsException e) {
640            sendErrorResponse(onComplete, e);
641        }
642    }
643
644    @Override
645    public void setCallWaiting(boolean enable, Message onComplete) {
646        if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
647        Message resp;
648        resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
649
650        try {
651            ImsUtInterface ut = mCT.getUtInterface();
652            ut.updateCallWaiting(enable, resp);
653        } catch (ImsException e) {
654            sendErrorResponse(onComplete, e);
655        }
656    }
657
658    private int getCBTypeFromFacility(String facility) {
659        if (CB_FACILITY_BAOC.equals(facility)) {
660            return ImsUtInterface.CB_BAOC;
661        } else if (CB_FACILITY_BAOIC.equals(facility)) {
662            return ImsUtInterface.CB_BOIC;
663        } else if (CB_FACILITY_BAOICxH.equals(facility)) {
664            return ImsUtInterface.CB_BOIC_EXHC;
665        } else if (CB_FACILITY_BAIC.equals(facility)) {
666            return ImsUtInterface.CB_BAIC;
667        } else if (CB_FACILITY_BAICr.equals(facility)) {
668            return ImsUtInterface.CB_BIC_WR;
669        } else if (CB_FACILITY_BA_ALL.equals(facility)) {
670            return 0;
671        } else if (CB_FACILITY_BA_MO.equals(facility)) {
672            return 0;
673        } else if (CB_FACILITY_BA_MT.equals(facility)) {
674            return 0;
675        }
676
677        return 0;
678    }
679
680    /* package */
681    void getCallBarring(String facility, Message onComplete) {
682        if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
683        Message resp;
684        resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
685
686        try {
687            ImsUtInterface ut = mCT.getUtInterface();
688            ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
689        } catch (ImsException e) {
690            sendErrorResponse(onComplete, e);
691        }
692    }
693
694    /* package */
695    void setCallBarring(String facility, boolean lockState, String password, Message onComplete) {
696        if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
697                + ", lockState=" + lockState);
698        Message resp;
699        resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
700
701        try {
702            ImsUtInterface ut = mCT.getUtInterface();
703            // password is not required with Ut interface
704            ut.updateCallBarring(getCBTypeFromFacility(facility), lockState, resp);
705        } catch (ImsException e) {
706            sendErrorResponse(onComplete, e);
707        }
708    }
709
710    @Override
711    public void sendUssdResponse(String ussdMessge) {
712        Rlog.d(LOG_TAG, "sendUssdResponse");
713        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
714        mPendingMMIs.add(mmi);
715        mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
716        mmi.sendUssd(ussdMessge);
717    }
718
719    /* package */
720    void sendUSSD (String ussdString, Message response) {
721        mCT.sendUSSD(ussdString, response);
722    }
723
724    /* package */
725    void cancelUSSD() {
726        mCT.cancelUSSD();
727    }
728
729    /* package */
730    void sendErrorResponse(Message onComplete) {
731        Rlog.d(LOG_TAG, "sendErrorResponse");
732        if (onComplete != null) {
733            AsyncResult.forMessage(onComplete, null,
734                    new CommandException(CommandException.Error.GENERIC_FAILURE));
735            onComplete.sendToTarget();
736        }
737    }
738
739    /* package */
740    void sendErrorResponse(Message onComplete, Throwable e) {
741        Rlog.d(LOG_TAG, "sendErrorResponse");
742        if (onComplete != null) {
743            AsyncResult.forMessage(onComplete, null, getCommandException(e));
744            onComplete.sendToTarget();
745        }
746    }
747
748    /* package */
749    void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) {
750        Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode());
751        if (onComplete != null) {
752            AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode()));
753            onComplete.sendToTarget();
754        }
755    }
756
757    /* package */
758    CommandException getCommandException(int code) {
759        Rlog.d(LOG_TAG, "getCommandException code=" + code);
760        CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
761
762        switch(code) {
763            case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
764                error = CommandException.Error.REQUEST_NOT_SUPPORTED;
765                break;
766            case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
767                error = CommandException.Error.PASSWORD_INCORRECT;
768                break;
769            default:
770                break;
771        }
772
773        return new CommandException(error);
774    }
775
776    /* package */
777    CommandException getCommandException(Throwable e) {
778        CommandException ex = null;
779
780        if (e instanceof ImsException) {
781            ex = getCommandException(((ImsException)e).getCode());
782        } else {
783            Rlog.d(LOG_TAG, "getCommandException generic failure");
784            ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
785        }
786        return ex;
787    }
788
789    private void
790    onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
791        Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
792        mMmiCompleteRegistrants.notifyRegistrants(
793            new AsyncResult(null, mmi, null));
794    }
795
796    /* package */
797    void onIncomingUSSD (int ussdMode, String ussdMessage) {
798        if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
799
800        boolean isUssdError;
801        boolean isUssdRequest;
802
803        isUssdRequest
804            = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
805
806        isUssdError
807            = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
808                && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
809
810        ImsPhoneMmiCode found = null;
811        for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
812            if(mPendingMMIs.get(i).isPendingUSSD()) {
813                found = mPendingMMIs.get(i);
814                break;
815            }
816        }
817
818        if (found != null) {
819            // Complete pending USSD
820            if (isUssdError) {
821                found.onUssdFinishedError();
822            } else {
823                found.onUssdFinished(ussdMessage, isUssdRequest);
824            }
825        } else { // pending USSD not found
826            // The network may initiate its own USSD request
827
828            // ignore everything that isnt a Notify or a Request
829            // also, discard if there is no message to present
830            if (!isUssdError && ussdMessage != null) {
831                ImsPhoneMmiCode mmi;
832                mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
833                        isUssdRequest,
834                        ImsPhone.this);
835                onNetworkInitiatedUssd(mmi);
836            }
837        }
838    }
839
840    /**
841     * Removes the given MMI from the pending list and notifies
842     * registrants that it is complete.
843     * @param mmi MMI that is done
844     */
845    /*package*/ void
846    onMMIDone(ImsPhoneMmiCode mmi) {
847        /* Only notify complete if it's on the pending list.
848         * Otherwise, it's already been handled (eg, previously canceled).
849         * The exception is cancellation of an incoming USSD-REQUEST, which is
850         * not on the list.
851         */
852        if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
853            mMmiCompleteRegistrants.notifyRegistrants(
854                    new AsyncResult(null, mmi, null));
855        }
856    }
857
858    public ImsPhoneConnection getHandoverConnection() {
859        // handover for single foreground call
860        ImsPhoneConnection conn = getForegroundCall().getHandoverConnection();
861
862        // handover for single background call
863        if (conn == null) {
864            conn = getBackgroundCall().getHandoverConnection();
865        }
866
867        // handover for single ringing call
868        if (conn == null) {
869            conn = getRingingCall().getHandoverConnection();
870        }
871
872        return conn;
873    }
874
875    public void notifySrvccState(Call.SrvccState state) {
876        mCT.notifySrvccState(state);
877    }
878
879    /* package */ void
880    initiateSilentRedial() {
881        String result = mLastDialString;
882        AsyncResult ar = new AsyncResult(null, result, null);
883        if (ar != null) {
884            mSilentRedialRegistrants.notifyRegistrants(ar);
885        }
886    }
887
888    public void registerForSilentRedial(Handler h, int what, Object obj) {
889        mSilentRedialRegistrants.addUnique(h, what, obj);
890    }
891
892    public void unregisterForSilentRedial(Handler h) {
893        mSilentRedialRegistrants.remove(h);
894    }
895
896    @Override
897    public long getSubId() {
898        return mDefaultPhone.getSubId();
899    }
900
901    @Override
902    public int getPhoneId() {
903        return mDefaultPhone.getPhoneId();
904    }
905
906    @Override
907    public Subscription getSubscriptionInfo() {
908        return mDefaultPhone.getSubscriptionInfo();
909    }
910
911    private IccRecords getIccRecords() {
912        return mDefaultPhone.mIccRecords.get();
913    }
914
915    private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
916        CallForwardInfo cfInfo = new CallForwardInfo();
917        cfInfo.status = info.mStatus;
918        cfInfo.reason = getCFReasonFromCondition(info.mCondition);
919        cfInfo.serviceClass = SERVICE_CLASS_VOICE;
920        cfInfo.toa = info.mToA;
921        cfInfo.number = info.mNumber;
922        cfInfo.timeSeconds = info.mTimeSeconds;
923        return cfInfo;
924    }
925
926    private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
927        CallForwardInfo[] cfInfos = null;
928
929        if (infos != null && infos.length != 0) {
930            cfInfos = new CallForwardInfo[infos.length];
931        }
932
933        IccRecords r = getIccRecords();
934        if (infos == null || infos.length == 0) {
935            if (r != null) {
936                // Assume the default is not active
937                // Set unconditional CFF in SIM to false
938                r.setVoiceCallForwardingFlag(1, false, null);
939            }
940        } else {
941            for (int i = 0, s = infos.length; i < s; i++) {
942                if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
943                    if (r != null) {
944                        r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1),
945                            infos[i].mNumber);
946                    }
947                }
948                cfInfos[i] = getCallForwardInfo(infos[i]);
949            }
950        }
951
952        return cfInfos;
953    }
954
955    private int[] handleCbQueryResult(ImsSsInfo[] infos) {
956        int[] cbInfos = new int[1];
957        cbInfos[0] = SERVICE_CLASS_NONE;
958
959        if (infos[0].mStatus == 1) {
960            cbInfos[0] = SERVICE_CLASS_VOICE;
961        }
962
963        return cbInfos;
964    }
965
966    private int[] handleCwQueryResult(ImsSsInfo[] infos) {
967        int[] cwInfos = new int[2];
968        cwInfos[0] = 0;
969
970        if (infos[0].mStatus == 1) {
971            cwInfos[0] = 1;
972            cwInfos[1] = SERVICE_CLASS_VOICE;
973        }
974
975        return cwInfos;
976    }
977
978    private void
979    sendResponse(Message onComplete, Object result, Throwable e) {
980        if (onComplete != null) {
981            CommandException ex = null;
982            if (e != null) {
983                ex = getCommandException(e);
984            }
985            AsyncResult.forMessage(onComplete, result, ex);
986            onComplete.sendToTarget();
987        }
988    }
989
990    @Override
991    public void handleMessage (Message msg) {
992        AsyncResult ar = (AsyncResult) msg.obj;
993        Message onComplete;
994
995        if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
996        switch (msg.what) {
997            case EVENT_SET_CALL_FORWARD_DONE:
998                IccRecords r = getIccRecords();
999                Cf cf = (Cf) ar.userObj;
1000                if (cf.mIsCfu && ar.exception == null && r != null) {
1001                    r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber);
1002                }
1003                sendResponse(cf.mOnComplete, null, ar.exception);
1004                break;
1005
1006            case EVENT_GET_CALL_FORWARD_DONE:
1007                CallForwardInfo[] cfInfos = null;
1008                if (ar.exception == null) {
1009                    cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1010                }
1011                sendResponse((Message) ar.userObj, cfInfos, ar.exception);
1012                break;
1013
1014             case EVENT_GET_CALL_BARRING_DONE:
1015             case EVENT_GET_CALL_WAITING_DONE:
1016                int[] ssInfos = null;
1017                if (ar.exception == null) {
1018                    if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1019                        ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1020                    } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1021                        ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1022                    }
1023                }
1024                sendResponse((Message) ar.userObj, ssInfos, ar.exception);
1025                break;
1026
1027             case EVENT_SET_CALL_BARRING_DONE:
1028             case EVENT_SET_CALL_WAITING_DONE:
1029                sendResponse((Message) ar.userObj, null, ar.exception);
1030                break;
1031
1032             default:
1033                 super.handleMessage(msg);
1034                 break;
1035        }
1036    }
1037}
1038