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