ImsPhone.java revision 6d05f561549a66b597a5119665ccc3bf8a962d16
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(int videoState) 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, int videoState) throws CallStateException {
416        return dialInternal(dialString, videoState);
417    }
418
419    protected Connection dialInternal(String dialString, int videoState)
420            throws CallStateException {
421        // Need to make sure dialString gets parsed properly
422        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
423
424        // handle in-call MMI first if applicable
425        if (handleInCallMmiCommands(newDialString)) {
426            return null;
427        }
428
429        if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
430            return mCT.dial(dialString, videoState);
431        }
432
433        // Only look at the Network portion for mmi
434        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
435        ImsPhoneMmiCode mmi =
436                ImsPhoneMmiCode.newFromDialString(networkPortion, this);
437        if (DBG) Rlog.d(LOG_TAG,
438                "dialing w/ mmi '" + mmi + "'...");
439
440        if (mmi == null) {
441            return mCT.dial(dialString, videoState);
442        } else if (mmi.isTemporaryModeCLIR()) {
443            return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState);
444        } else if (!mmi.isSupportedOverImsPhone()) {
445            // If the mmi is not supported by IMS service,
446            // try to initiate dialing with default phone
447            throw new CallStateException(CS_FALLBACK);
448        } else {
449            mPendingMMIs.add(mmi);
450            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
451            mmi.processCode();
452
453            return null;
454        }
455    }
456
457    @Override
458    public void
459    sendDtmf(char c) {
460        if (!PhoneNumberUtils.is12Key(c)) {
461            Rlog.e(LOG_TAG,
462                    "sendDtmf called with invalid character '" + c + "'");
463        } else {
464            if (mCT.mState ==  PhoneConstants.State.OFFHOOK) {
465                mCT.sendDtmf(c);
466            }
467        }
468    }
469
470    @Override
471    public void
472    startDtmf(char c) {
473        if (!PhoneNumberUtils.is12Key(c)) {
474            Rlog.e(LOG_TAG,
475                    "startDtmf called with invalid character '" + c + "'");
476        } else {
477            sendDtmf(c);
478        }
479    }
480
481    @Override
482    public void
483    stopDtmf() {
484        // no op
485    }
486
487    @Override
488    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
489        mPostDialHandler = new Registrant(h, what, obj);
490    }
491
492    /*package*/ void notifyIncomingRing() {
493        if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing");
494        AsyncResult ar = new AsyncResult(null, null, null);
495        sendMessage(obtainMessage(EVENT_CALL_RING, ar));
496    }
497
498    @Override
499    public void setMute(boolean muted) {
500        mCT.setMute(muted);
501    }
502
503    @Override
504    public boolean getMute() {
505        return mCT.getMute();
506    }
507
508    @Override
509    public PhoneConstants.State getState() {
510        return mCT.mState;
511    }
512
513    private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
514        switch (commandInterfaceCFReason) {
515        case CF_REASON_UNCONDITIONAL:
516        case CF_REASON_BUSY:
517        case CF_REASON_NO_REPLY:
518        case CF_REASON_NOT_REACHABLE:
519        case CF_REASON_ALL:
520        case CF_REASON_ALL_CONDITIONAL:
521            return true;
522        default:
523            return false;
524        }
525    }
526
527    private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
528        switch (commandInterfaceCFAction) {
529        case CF_ACTION_DISABLE:
530        case CF_ACTION_ENABLE:
531        case CF_ACTION_REGISTRATION:
532        case CF_ACTION_ERASURE:
533            return true;
534        default:
535            return false;
536        }
537    }
538
539    private  boolean isCfEnable(int action) {
540        return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
541    }
542
543    private int getConditionFromCFReason(int reason) {
544        switch(reason) {
545            case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
546            case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
547            case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
548            case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
549            default:
550                break;
551        }
552
553        return ImsUtInterface.INVALID;
554    }
555
556    private int getCFReasonFromCondition(int condition) {
557        switch(condition) {
558            case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
559            case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
560            case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
561            case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
562            default:
563                break;
564        }
565
566        return CF_REASON_NOT_REACHABLE;
567    }
568
569    private int getActionFromCFAction(int action) {
570        switch(action) {
571            case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
572            case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
573            case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
574            case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
575            default:
576                break;
577        }
578
579        return ImsUtInterface.INVALID;
580    }
581
582    @Override
583    public void getCallForwardingOption(int commandInterfaceCFReason,
584            Message onComplete) {
585        if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
586        if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
587            if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
588            Message resp;
589            resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
590
591            try {
592                ImsUtInterface ut = mCT.getUtInterface();
593                ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason),null,resp);
594            } catch (ImsException e) {
595                sendErrorResponse(onComplete, e);
596            }
597        } else if (onComplete != null) {
598            sendErrorResponse(onComplete);
599        }
600    }
601
602    @Override
603    public void setCallForwardingOption(int commandInterfaceCFAction,
604            int commandInterfaceCFReason,
605            String dialingNumber,
606            int timerSeconds,
607            Message onComplete) {
608        if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
609                + ", reason=" + commandInterfaceCFReason);
610        if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
611                (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
612            Message resp;
613            Cf cf = new Cf(dialingNumber,
614                    (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
615                    onComplete);
616            resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
617                    isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
618
619            try {
620                ImsUtInterface ut = mCT.getUtInterface();
621                ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
622                        getConditionFromCFReason(commandInterfaceCFReason),
623                        dialingNumber,
624                        timerSeconds,
625                        onComplete);
626             } catch (ImsException e) {
627                sendErrorResponse(onComplete, e);
628             }
629        } else if (onComplete != null) {
630            sendErrorResponse(onComplete);
631        }
632    }
633
634    @Override
635    public void getCallWaiting(Message onComplete) {
636        if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
637        Message resp;
638        resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
639
640        try {
641            ImsUtInterface ut = mCT.getUtInterface();
642            ut.queryCallWaiting(resp);
643        } catch (ImsException e) {
644            sendErrorResponse(onComplete, e);
645        }
646    }
647
648    @Override
649    public void setCallWaiting(boolean enable, Message onComplete) {
650        if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
651        Message resp;
652        resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
653
654        try {
655            ImsUtInterface ut = mCT.getUtInterface();
656            ut.updateCallWaiting(enable, resp);
657        } catch (ImsException e) {
658            sendErrorResponse(onComplete, e);
659        }
660    }
661
662    private int getCBTypeFromFacility(String facility) {
663        if (CB_FACILITY_BAOC.equals(facility)) {
664            return ImsUtInterface.CB_BAOC;
665        } else if (CB_FACILITY_BAOIC.equals(facility)) {
666            return ImsUtInterface.CB_BOIC;
667        } else if (CB_FACILITY_BAOICxH.equals(facility)) {
668            return ImsUtInterface.CB_BOIC_EXHC;
669        } else if (CB_FACILITY_BAIC.equals(facility)) {
670            return ImsUtInterface.CB_BAIC;
671        } else if (CB_FACILITY_BAICr.equals(facility)) {
672            return ImsUtInterface.CB_BIC_WR;
673        } else if (CB_FACILITY_BA_ALL.equals(facility)) {
674            return 0;
675        } else if (CB_FACILITY_BA_MO.equals(facility)) {
676            return 0;
677        } else if (CB_FACILITY_BA_MT.equals(facility)) {
678            return 0;
679        }
680
681        return 0;
682    }
683
684    /* package */
685    void getCallBarring(String facility, Message onComplete) {
686        if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
687        Message resp;
688        resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
689
690        try {
691            ImsUtInterface ut = mCT.getUtInterface();
692            ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
693        } catch (ImsException e) {
694            sendErrorResponse(onComplete, e);
695        }
696    }
697
698    /* package */
699    void setCallBarring(String facility, boolean lockState, String password, Message onComplete) {
700        if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
701                + ", lockState=" + lockState);
702        Message resp;
703        resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
704
705        try {
706            ImsUtInterface ut = mCT.getUtInterface();
707            // password is not required with Ut interface
708            ut.updateCallBarring(getCBTypeFromFacility(facility), lockState, resp);
709        } catch (ImsException e) {
710            sendErrorResponse(onComplete, e);
711        }
712    }
713
714    @Override
715    public void sendUssdResponse(String ussdMessge) {
716        Rlog.d(LOG_TAG, "sendUssdResponse");
717        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
718        mPendingMMIs.add(mmi);
719        mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
720        mmi.sendUssd(ussdMessge);
721    }
722
723    /* package */
724    void sendUSSD (String ussdString, Message response) {
725        mCT.sendUSSD(ussdString, response);
726    }
727
728    /* package */
729    void cancelUSSD() {
730        mCT.cancelUSSD();
731    }
732
733    /* package */
734    void sendErrorResponse(Message onComplete) {
735        Rlog.d(LOG_TAG, "sendErrorResponse");
736        if (onComplete != null) {
737            AsyncResult.forMessage(onComplete, null,
738                    new CommandException(CommandException.Error.GENERIC_FAILURE));
739            onComplete.sendToTarget();
740        }
741    }
742
743    /* package */
744    void sendErrorResponse(Message onComplete, Throwable e) {
745        Rlog.d(LOG_TAG, "sendErrorResponse");
746        if (onComplete != null) {
747            AsyncResult.forMessage(onComplete, null, getCommandException(e));
748            onComplete.sendToTarget();
749        }
750    }
751
752    /* package */
753    void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) {
754        Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode());
755        if (onComplete != null) {
756            AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode()));
757            onComplete.sendToTarget();
758        }
759    }
760
761    /* package */
762    CommandException getCommandException(int code) {
763        Rlog.d(LOG_TAG, "getCommandException code=" + code);
764        CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
765
766        switch(code) {
767            case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
768                error = CommandException.Error.REQUEST_NOT_SUPPORTED;
769                break;
770            case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
771                error = CommandException.Error.PASSWORD_INCORRECT;
772                break;
773            default:
774                break;
775        }
776
777        return new CommandException(error);
778    }
779
780    /* package */
781    CommandException getCommandException(Throwable e) {
782        CommandException ex = null;
783
784        if (e instanceof ImsException) {
785            ex = getCommandException(((ImsException)e).getCode());
786        } else {
787            Rlog.d(LOG_TAG, "getCommandException generic failure");
788            ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
789        }
790        return ex;
791    }
792
793    private void
794    onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
795        Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
796        mMmiCompleteRegistrants.notifyRegistrants(
797            new AsyncResult(null, mmi, null));
798    }
799
800    /* package */
801    void onIncomingUSSD (int ussdMode, String ussdMessage) {
802        if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
803
804        boolean isUssdError;
805        boolean isUssdRequest;
806
807        isUssdRequest
808            = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
809
810        isUssdError
811            = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
812                && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
813
814        ImsPhoneMmiCode found = null;
815        for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
816            if(mPendingMMIs.get(i).isPendingUSSD()) {
817                found = mPendingMMIs.get(i);
818                break;
819            }
820        }
821
822        if (found != null) {
823            // Complete pending USSD
824            if (isUssdError) {
825                found.onUssdFinishedError();
826            } else {
827                found.onUssdFinished(ussdMessage, isUssdRequest);
828            }
829        } else { // pending USSD not found
830            // The network may initiate its own USSD request
831
832            // ignore everything that isnt a Notify or a Request
833            // also, discard if there is no message to present
834            if (!isUssdError && ussdMessage != null) {
835                ImsPhoneMmiCode mmi;
836                mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
837                        isUssdRequest,
838                        ImsPhone.this);
839                onNetworkInitiatedUssd(mmi);
840            }
841        }
842    }
843
844    /**
845     * Removes the given MMI from the pending list and notifies
846     * registrants that it is complete.
847     * @param mmi MMI that is done
848     */
849    /*package*/ void
850    onMMIDone(ImsPhoneMmiCode mmi) {
851        /* Only notify complete if it's on the pending list.
852         * Otherwise, it's already been handled (eg, previously canceled).
853         * The exception is cancellation of an incoming USSD-REQUEST, which is
854         * not on the list.
855         */
856        if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
857            mMmiCompleteRegistrants.notifyRegistrants(
858                    new AsyncResult(null, mmi, null));
859        }
860    }
861
862    public ImsPhoneConnection getHandoverConnection() {
863        // handover for single foreground call
864        ImsPhoneConnection conn = getForegroundCall().getHandoverConnection();
865
866        // handover for single background call
867        if (conn == null) {
868            conn = getBackgroundCall().getHandoverConnection();
869        }
870
871        // handover for single ringing call
872        if (conn == null) {
873            conn = getRingingCall().getHandoverConnection();
874        }
875
876        return conn;
877    }
878
879    public void notifySrvccState(Call.SrvccState state) {
880        mCT.notifySrvccState(state);
881    }
882
883    /* package */ void
884    initiateSilentRedial() {
885        String result = mLastDialString;
886        AsyncResult ar = new AsyncResult(null, result, null);
887        if (ar != null) {
888            mSilentRedialRegistrants.notifyRegistrants(ar);
889        }
890    }
891
892    public void registerForSilentRedial(Handler h, int what, Object obj) {
893        mSilentRedialRegistrants.addUnique(h, what, obj);
894    }
895
896    public void unregisterForSilentRedial(Handler h) {
897        mSilentRedialRegistrants.remove(h);
898    }
899
900    @Override
901    public long getSubId() {
902        return mDefaultPhone.getSubId();
903    }
904
905    @Override
906    public int getPhoneId() {
907        return mDefaultPhone.getPhoneId();
908    }
909
910    @Override
911    public Subscription getSubscriptionInfo() {
912        return mDefaultPhone.getSubscriptionInfo();
913    }
914
915    private IccRecords getIccRecords() {
916        return mDefaultPhone.mIccRecords.get();
917    }
918
919    private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
920        CallForwardInfo cfInfo = new CallForwardInfo();
921        cfInfo.status = info.mStatus;
922        cfInfo.reason = getCFReasonFromCondition(info.mCondition);
923        cfInfo.serviceClass = SERVICE_CLASS_VOICE;
924        cfInfo.toa = info.mToA;
925        cfInfo.number = info.mNumber;
926        cfInfo.timeSeconds = info.mTimeSeconds;
927        return cfInfo;
928    }
929
930    private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
931        CallForwardInfo[] cfInfos = null;
932
933        if (infos != null && infos.length != 0) {
934            cfInfos = new CallForwardInfo[infos.length];
935        }
936
937        IccRecords r = getIccRecords();
938        if (infos == null || infos.length == 0) {
939            if (r != null) {
940                // Assume the default is not active
941                // Set unconditional CFF in SIM to false
942                r.setVoiceCallForwardingFlag(1, false, null);
943            }
944        } else {
945            for (int i = 0, s = infos.length; i < s; i++) {
946                if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
947                    if (r != null) {
948                        r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1),
949                            infos[i].mNumber);
950                    }
951                }
952                cfInfos[i] = getCallForwardInfo(infos[i]);
953            }
954        }
955
956        return cfInfos;
957    }
958
959    private int[] handleCbQueryResult(ImsSsInfo[] infos) {
960        int[] cbInfos = new int[1];
961        cbInfos[0] = SERVICE_CLASS_NONE;
962
963        if (infos[0].mStatus == 1) {
964            cbInfos[0] = SERVICE_CLASS_VOICE;
965        }
966
967        return cbInfos;
968    }
969
970    private int[] handleCwQueryResult(ImsSsInfo[] infos) {
971        int[] cwInfos = new int[2];
972        cwInfos[0] = 0;
973
974        if (infos[0].mStatus == 1) {
975            cwInfos[0] = 1;
976            cwInfos[1] = SERVICE_CLASS_VOICE;
977        }
978
979        return cwInfos;
980    }
981
982    private void
983    sendResponse(Message onComplete, Object result, Throwable e) {
984        if (onComplete != null) {
985            CommandException ex = null;
986            if (e != null) {
987                ex = getCommandException(e);
988            }
989            AsyncResult.forMessage(onComplete, result, ex);
990            onComplete.sendToTarget();
991        }
992    }
993
994    @Override
995    public void handleMessage (Message msg) {
996        AsyncResult ar = (AsyncResult) msg.obj;
997        Message onComplete;
998
999        if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
1000        switch (msg.what) {
1001            case EVENT_SET_CALL_FORWARD_DONE:
1002                IccRecords r = getIccRecords();
1003                Cf cf = (Cf) ar.userObj;
1004                if (cf.mIsCfu && ar.exception == null && r != null) {
1005                    r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber);
1006                }
1007                sendResponse(cf.mOnComplete, null, ar.exception);
1008                break;
1009
1010            case EVENT_GET_CALL_FORWARD_DONE:
1011                CallForwardInfo[] cfInfos = null;
1012                if (ar.exception == null) {
1013                    cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1014                }
1015                sendResponse((Message) ar.userObj, cfInfos, ar.exception);
1016                break;
1017
1018             case EVENT_GET_CALL_BARRING_DONE:
1019             case EVENT_GET_CALL_WAITING_DONE:
1020                int[] ssInfos = null;
1021                if (ar.exception == null) {
1022                    if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1023                        ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1024                    } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1025                        ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1026                    }
1027                }
1028                sendResponse((Message) ar.userObj, ssInfos, ar.exception);
1029                break;
1030
1031             case EVENT_SET_CALL_BARRING_DONE:
1032             case EVENT_SET_CALL_WAITING_DONE:
1033                sendResponse((Message) ar.userObj, null, ar.exception);
1034                break;
1035
1036             default:
1037                 super.handleMessage(msg);
1038                 break;
1039        }
1040    }
1041}
1042