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