1/*
2 * Copyright (C) 2006 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.gsm;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.SharedPreferences;
22import android.database.SQLException;
23import android.net.Uri;
24import android.os.AsyncResult;
25import android.os.Handler;
26import android.os.Message;
27import android.os.Registrant;
28import android.os.RegistrantList;
29import android.os.SystemProperties;
30import android.preference.PreferenceManager;
31import android.provider.Telephony;
32import android.telephony.CellLocation;
33import android.telephony.PhoneNumberUtils;
34import android.telephony.ServiceState;
35import android.telephony.SignalStrength;
36import com.android.internal.telephony.CallTracker;
37import android.text.TextUtils;
38import android.util.Log;
39
40import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
41import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
42import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
43import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
44import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
45import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
46import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
47import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
48import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
49import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
50import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
51import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
52
53import com.android.internal.telephony.cat.CatService;
54import com.android.internal.telephony.Call;
55import com.android.internal.telephony.CallForwardInfo;
56import com.android.internal.telephony.CallStateException;
57import com.android.internal.telephony.CommandsInterface;
58import com.android.internal.telephony.Connection;
59import com.android.internal.telephony.IccFileHandler;
60import com.android.internal.telephony.IccPhoneBookInterfaceManager;
61import com.android.internal.telephony.IccSmsInterfaceManager;
62import com.android.internal.telephony.MmiCode;
63import com.android.internal.telephony.OperatorInfo;
64import com.android.internal.telephony.Phone;
65import com.android.internal.telephony.PhoneBase;
66import com.android.internal.telephony.PhoneNotifier;
67import com.android.internal.telephony.PhoneProxy;
68import com.android.internal.telephony.PhoneSubInfo;
69import com.android.internal.telephony.TelephonyProperties;
70import com.android.internal.telephony.UUSInfo;
71import com.android.internal.telephony.test.SimulatedRadioControl;
72import com.android.internal.telephony.IccVmNotSupportedException;
73import com.android.internal.telephony.ServiceStateTracker;
74
75import java.io.IOException;
76import java.net.InetSocketAddress;
77import java.net.ServerSocket;
78import java.net.Socket;
79import java.util.ArrayList;
80import java.util.List;
81
82/**
83 * {@hide}
84 */
85public class GSMPhone extends PhoneBase {
86    // NOTE that LOG_TAG here is "GSM", which means that log messages
87    // from this file will go into the radio log rather than the main
88    // log.  (Use "adb logcat -b radio" to see them.)
89    static final String LOG_TAG = "GSM";
90    private static final boolean LOCAL_DEBUG = true;
91
92    // Key used to read/write current ciphering state
93    public static final String CIPHERING_KEY = "ciphering_key";
94    // Key used to read/write voice mail number
95    public static final String VM_NUMBER = "vm_number_key";
96    // Key used to read/write the SIM IMSI used for storing the voice mail
97    public static final String VM_SIM_IMSI = "vm_sim_imsi_key";
98
99    // Instance Variables
100    GsmCallTracker mCT;
101    GsmServiceStateTracker mSST;
102    CatService mStkService;
103    ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>();
104    SimPhoneBookInterfaceManager mSimPhoneBookIntManager;
105    SimSmsInterfaceManager mSimSmsIntManager;
106    PhoneSubInfo mSubInfo;
107
108
109    Registrant mPostDialHandler;
110
111    /** List of Registrants to receive Supplementary Service Notifications. */
112    RegistrantList mSsnRegistrants = new RegistrantList();
113
114    Thread debugPortThread;
115    ServerSocket debugSocket;
116
117    private String mImei;
118    private String mImeiSv;
119    private String mVmNumber;
120
121
122    // Constructors
123
124    public
125    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) {
126        this(context,ci,notifier, false);
127    }
128
129    public
130    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
131        super(notifier, context, ci, unitTestMode);
132
133        if (ci instanceof SimulatedRadioControl) {
134            mSimulatedRadioControl = (SimulatedRadioControl) ci;
135        }
136
137        mCM.setPhoneType(Phone.PHONE_TYPE_GSM);
138        mCT = new GsmCallTracker(this);
139        mSST = new GsmServiceStateTracker (this);
140        mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
141        mIccFileHandler = new SIMFileHandler(this);
142        mIccRecords = new SIMRecords(this);
143        mDataConnectionTracker = new GsmDataConnectionTracker (this);
144        mIccCard = new SimCard(this);
145        if (!unitTestMode) {
146            mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
147            mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
148            mSubInfo = new PhoneSubInfo(this);
149        }
150        mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard);
151
152        mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
153        mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
154        mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
155        mCM.registerForOn(this, EVENT_RADIO_ON, null);
156        mCM.setOnUSSD(this, EVENT_USSD, null);
157        mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
158        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
159
160        if (false) {
161            try {
162                //debugSocket = new LocalServerSocket("com.android.internal.telephony.debug");
163                debugSocket = new ServerSocket();
164                debugSocket.setReuseAddress(true);
165                debugSocket.bind (new InetSocketAddress("127.0.0.1", 6666));
166
167                debugPortThread
168                    = new Thread(
169                        new Runnable() {
170                            public void run() {
171                                for(;;) {
172                                    try {
173                                        Socket sock;
174                                        sock = debugSocket.accept();
175                                        Log.i(LOG_TAG, "New connection; resetting radio");
176                                        mCM.resetRadio(null);
177                                        sock.close();
178                                    } catch (IOException ex) {
179                                        Log.w(LOG_TAG,
180                                            "Exception accepting socket", ex);
181                                    }
182                                }
183                            }
184                        },
185                        "GSMPhone debug");
186
187                debugPortThread.start();
188
189            } catch (IOException ex) {
190                Log.w(LOG_TAG, "Failure to open com.android.internal.telephony.debug socket", ex);
191            }
192        }
193
194        //Change the system property
195        SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
196                new Integer(Phone.PHONE_TYPE_GSM).toString());
197    }
198
199    @Override
200    public void dispose() {
201        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
202            super.dispose();
203
204            //Unregister from all former registered events
205            mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
206            mIccRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
207            mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
208            mCM.unregisterForOn(this); //EVENT_RADIO_ON
209            mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
210            mCM.unSetOnUSSD(this);
211            mCM.unSetOnSuppServiceNotification(this);
212
213            mPendingMMIs.clear();
214
215            //Force all referenced classes to unregister their former registered events
216            mStkService.dispose();
217            mCT.dispose();
218            mDataConnectionTracker.dispose();
219            mSST.dispose();
220            mIccFileHandler.dispose(); // instance of SimFileHandler
221            mIccRecords.dispose();
222            mIccCard.dispose();
223            mSimPhoneBookIntManager.dispose();
224            mSimSmsIntManager.dispose();
225            mSubInfo.dispose();
226        }
227    }
228
229    @Override
230    public void removeReferences() {
231        Log.d(LOG_TAG, "removeReferences");
232        super.removeReferences();
233        mSimulatedRadioControl = null;
234        mStkService = null;
235        mSimPhoneBookIntManager = null;
236        mSimSmsIntManager = null;
237        mSMS = null;
238        mSubInfo = null;
239        mIccRecords = null;
240        mIccFileHandler = null;
241        mIccCard = null;
242        mDataConnectionTracker = null;
243        mCT = null;
244        mSST = null;
245    }
246
247    protected void finalize() {
248        if(LOCAL_DEBUG) Log.d(LOG_TAG, "GSMPhone finalized");
249    }
250
251
252    public ServiceState
253    getServiceState() {
254        return mSST.ss;
255    }
256
257    public CellLocation getCellLocation() {
258        return mSST.cellLoc;
259    }
260
261    public Phone.State getState() {
262        return mCT.state;
263    }
264
265    public String getPhoneName() {
266        return "GSM";
267    }
268
269    public int getPhoneType() {
270        return Phone.PHONE_TYPE_GSM;
271    }
272
273    public SignalStrength getSignalStrength() {
274        return mSST.mSignalStrength;
275    }
276
277    public CallTracker getCallTracker() {
278        return mCT;
279    }
280
281    public ServiceStateTracker getServiceStateTracker() {
282        return mSST;
283    }
284
285    public List<? extends MmiCode>
286    getPendingMmiCodes() {
287        return mPendingMMIs;
288    }
289
290    public DataState getDataConnectionState(String apnType) {
291        DataState ret = DataState.DISCONNECTED;
292
293        if (mSST == null) {
294            // Radio Technology Change is ongoning, dispose() and removeReferences() have
295            // already been called
296
297            ret = DataState.DISCONNECTED;
298        } else if (mSST.getCurrentGprsState()
299                != ServiceState.STATE_IN_SERVICE) {
300            // If we're out of service, open TCP sockets may still work
301            // but no data will flow
302            ret = DataState.DISCONNECTED;
303        } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false ||
304                mDataConnectionTracker.isApnTypeActive(apnType) == false) {
305            //TODO: isApnTypeActive() is just checking whether ApnContext holds
306            //      Dataconnection or not. Checking each ApnState below should
307            //      provide the same state. Calling isApnTypeActive() can be removed.
308            ret = DataState.DISCONNECTED;
309        } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
310            switch (mDataConnectionTracker.getState(apnType)) {
311                case FAILED:
312                case IDLE:
313                    ret = DataState.DISCONNECTED;
314                break;
315
316                case CONNECTED:
317                case DISCONNECTING:
318                    if ( mCT.state != Phone.State.IDLE
319                            && !mSST.isConcurrentVoiceAndDataAllowed()) {
320                        ret = DataState.SUSPENDED;
321                    } else {
322                        ret = DataState.CONNECTED;
323                    }
324                break;
325
326                case INITING:
327                case CONNECTING:
328                case SCANNING:
329                    ret = DataState.CONNECTING;
330                break;
331            }
332        }
333
334        return ret;
335    }
336
337    public DataActivityState getDataActivityState() {
338        DataActivityState ret = DataActivityState.NONE;
339
340        if (mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE) {
341            switch (mDataConnectionTracker.getActivity()) {
342                case DATAIN:
343                    ret = DataActivityState.DATAIN;
344                break;
345
346                case DATAOUT:
347                    ret = DataActivityState.DATAOUT;
348                break;
349
350                case DATAINANDOUT:
351                    ret = DataActivityState.DATAINANDOUT;
352                break;
353            }
354        }
355
356        return ret;
357    }
358
359    /**
360     * Notify any interested party of a Phone state change {@link Phone.State}
361     */
362    /*package*/ void notifyPhoneStateChanged() {
363        mNotifier.notifyPhoneState(this);
364    }
365
366    /**
367     * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
368     * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
369     */
370    /*package*/ void notifyPreciseCallStateChanged() {
371        /* we'd love it if this was package-scoped*/
372        super.notifyPreciseCallStateChangedP();
373    }
374
375    /*package*/ void
376    notifyNewRingingConnection(Connection c) {
377        /* we'd love it if this was package-scoped*/
378        super.notifyNewRingingConnectionP(c);
379    }
380
381    /*package*/ void
382    notifyDisconnect(Connection cn) {
383        mDisconnectRegistrants.notifyResult(cn);
384    }
385
386    void notifyUnknownConnection() {
387        mUnknownConnectionRegistrants.notifyResult(this);
388    }
389
390    void notifySuppServiceFailed(SuppService code) {
391        mSuppServiceFailedRegistrants.notifyResult(code);
392    }
393
394    /*package*/ void
395    notifyServiceStateChanged(ServiceState ss) {
396        super.notifyServiceStateChangedP(ss);
397    }
398
399    /*package*/
400    void notifyLocationChanged() {
401        mNotifier.notifyCellLocation(this);
402    }
403
404    /*package*/ void
405    notifySignalStrength() {
406        mNotifier.notifySignalStrength(this);
407    }
408
409    public void
410    notifyCallForwardingIndicator() {
411        mNotifier.notifyCallForwardingChanged(this);
412    }
413
414    // override for allowing access from other classes of this package
415    /**
416     * {@inheritDoc}
417     */
418    public final void
419    setSystemProperty(String property, String value) {
420        super.setSystemProperty(property, value);
421    }
422
423    public void registerForSuppServiceNotification(
424            Handler h, int what, Object obj) {
425        mSsnRegistrants.addUnique(h, what, obj);
426        if (mSsnRegistrants.size() == 1) mCM.setSuppServiceNotifications(true, null);
427    }
428
429    public void unregisterForSuppServiceNotification(Handler h) {
430        mSsnRegistrants.remove(h);
431        if (mSsnRegistrants.size() == 0) mCM.setSuppServiceNotifications(false, null);
432    }
433
434    public void
435    acceptCall() throws CallStateException {
436        mCT.acceptCall();
437    }
438
439    public void
440    rejectCall() throws CallStateException {
441        mCT.rejectCall();
442    }
443
444    public void
445    switchHoldingAndActive() throws CallStateException {
446        mCT.switchWaitingOrHoldingAndActive();
447    }
448
449    public boolean canConference() {
450        return mCT.canConference();
451    }
452
453    public boolean canDial() {
454        return mCT.canDial();
455    }
456
457    public void conference() throws CallStateException {
458        mCT.conference();
459    }
460
461    public void clearDisconnected() {
462        mCT.clearDisconnected();
463    }
464
465    public boolean canTransfer() {
466        return mCT.canTransfer();
467    }
468
469    public void explicitCallTransfer() throws CallStateException {
470        mCT.explicitCallTransfer();
471    }
472
473    public GsmCall
474    getForegroundCall() {
475        return mCT.foregroundCall;
476    }
477
478    public GsmCall
479    getBackgroundCall() {
480        return mCT.backgroundCall;
481    }
482
483    public GsmCall
484    getRingingCall() {
485        return mCT.ringingCall;
486    }
487
488    private boolean handleCallDeflectionIncallSupplementaryService(
489            String dialString) throws CallStateException {
490        if (dialString.length() > 1) {
491            return false;
492        }
493
494        if (getRingingCall().getState() != GsmCall.State.IDLE) {
495            if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 0: rejectCall");
496            try {
497                mCT.rejectCall();
498            } catch (CallStateException e) {
499                if (LOCAL_DEBUG) Log.d(LOG_TAG,
500                    "reject failed", e);
501                notifySuppServiceFailed(Phone.SuppService.REJECT);
502            }
503        } else if (getBackgroundCall().getState() != GsmCall.State.IDLE) {
504            if (LOCAL_DEBUG) Log.d(LOG_TAG,
505                    "MmiCode 0: hangupWaitingOrBackground");
506            mCT.hangupWaitingOrBackground();
507        }
508
509        return true;
510    }
511
512    private boolean handleCallWaitingIncallSupplementaryService(
513            String dialString) throws CallStateException {
514        int len = dialString.length();
515
516        if (len > 2) {
517            return false;
518        }
519
520        GsmCall call = (GsmCall) getForegroundCall();
521
522        try {
523            if (len > 1) {
524                char ch = dialString.charAt(1);
525                int callIndex = ch - '0';
526
527                if (callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) {
528                    if (LOCAL_DEBUG) Log.d(LOG_TAG,
529                            "MmiCode 1: hangupConnectionByIndex " +
530                            callIndex);
531                    mCT.hangupConnectionByIndex(call, callIndex);
532                }
533            } else {
534                if (call.getState() != GsmCall.State.IDLE) {
535                    if (LOCAL_DEBUG) Log.d(LOG_TAG,
536                            "MmiCode 1: hangup foreground");
537                    //mCT.hangupForegroundResumeBackground();
538                    mCT.hangup(call);
539                } else {
540                    if (LOCAL_DEBUG) Log.d(LOG_TAG,
541                            "MmiCode 1: switchWaitingOrHoldingAndActive");
542                    mCT.switchWaitingOrHoldingAndActive();
543                }
544            }
545        } catch (CallStateException e) {
546            if (LOCAL_DEBUG) Log.d(LOG_TAG,
547                "hangup failed", e);
548            notifySuppServiceFailed(Phone.SuppService.HANGUP);
549        }
550
551        return true;
552    }
553
554    private boolean handleCallHoldIncallSupplementaryService(String dialString)
555            throws CallStateException {
556        int len = dialString.length();
557
558        if (len > 2) {
559            return false;
560        }
561
562        GsmCall call = (GsmCall) getForegroundCall();
563
564        if (len > 1) {
565            try {
566                char ch = dialString.charAt(1);
567                int callIndex = ch - '0';
568                GsmConnection conn = mCT.getConnectionByIndex(call, callIndex);
569
570                // gsm index starts at 1, up to 5 connections in a call,
571                if (conn != null && callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) {
572                    if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 2: separate call "+
573                            callIndex);
574                    mCT.separate(conn);
575                } else {
576                    if (LOCAL_DEBUG) Log.d(LOG_TAG, "separate: invalid call index "+
577                            callIndex);
578                    notifySuppServiceFailed(Phone.SuppService.SEPARATE);
579                }
580            } catch (CallStateException e) {
581                if (LOCAL_DEBUG) Log.d(LOG_TAG,
582                    "separate failed", e);
583                notifySuppServiceFailed(Phone.SuppService.SEPARATE);
584            }
585        } else {
586            try {
587                if (getRingingCall().getState() != GsmCall.State.IDLE) {
588                    if (LOCAL_DEBUG) Log.d(LOG_TAG,
589                    "MmiCode 2: accept ringing call");
590                    mCT.acceptCall();
591                } else {
592                    if (LOCAL_DEBUG) Log.d(LOG_TAG,
593                    "MmiCode 2: switchWaitingOrHoldingAndActive");
594                    mCT.switchWaitingOrHoldingAndActive();
595                }
596            } catch (CallStateException e) {
597                if (LOCAL_DEBUG) Log.d(LOG_TAG,
598                    "switch failed", e);
599                notifySuppServiceFailed(Phone.SuppService.SWITCH);
600            }
601        }
602
603        return true;
604    }
605
606    private boolean handleMultipartyIncallSupplementaryService(
607            String dialString) throws CallStateException {
608        if (dialString.length() > 1) {
609            return false;
610        }
611
612        if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 3: merge calls");
613        try {
614            conference();
615        } catch (CallStateException e) {
616            if (LOCAL_DEBUG) Log.d(LOG_TAG,
617                "conference failed", e);
618            notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
619        }
620        return true;
621    }
622
623    private boolean handleEctIncallSupplementaryService(String dialString)
624            throws CallStateException {
625
626        int len = dialString.length();
627
628        if (len != 1) {
629            return false;
630        }
631
632        if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 4: explicit call transfer");
633        try {
634            explicitCallTransfer();
635        } catch (CallStateException e) {
636            if (LOCAL_DEBUG) Log.d(LOG_TAG,
637                "transfer failed", e);
638            notifySuppServiceFailed(Phone.SuppService.TRANSFER);
639        }
640        return true;
641    }
642
643    private boolean handleCcbsIncallSupplementaryService(String dialString)
644            throws CallStateException {
645        if (dialString.length() > 1) {
646            return false;
647        }
648
649        Log.i(LOG_TAG, "MmiCode 5: CCBS not supported!");
650        // Treat it as an "unknown" service.
651        notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
652        return true;
653    }
654
655    public boolean handleInCallMmiCommands(String dialString)
656            throws CallStateException {
657        if (!isInCall()) {
658            return false;
659        }
660
661        if (TextUtils.isEmpty(dialString)) {
662            return false;
663        }
664
665        boolean result = false;
666        char ch = dialString.charAt(0);
667        switch (ch) {
668            case '0':
669                result = handleCallDeflectionIncallSupplementaryService(
670                        dialString);
671                break;
672            case '1':
673                result = handleCallWaitingIncallSupplementaryService(
674                        dialString);
675                break;
676            case '2':
677                result = handleCallHoldIncallSupplementaryService(dialString);
678                break;
679            case '3':
680                result = handleMultipartyIncallSupplementaryService(dialString);
681                break;
682            case '4':
683                result = handleEctIncallSupplementaryService(dialString);
684                break;
685            case '5':
686                result = handleCcbsIncallSupplementaryService(dialString);
687                break;
688            default:
689                break;
690        }
691
692        return result;
693    }
694
695    boolean isInCall() {
696        GsmCall.State foregroundCallState = getForegroundCall().getState();
697        GsmCall.State backgroundCallState = getBackgroundCall().getState();
698        GsmCall.State ringingCallState = getRingingCall().getState();
699
700       return (foregroundCallState.isAlive() ||
701                backgroundCallState.isAlive() ||
702                ringingCallState.isAlive());
703    }
704
705    public Connection
706    dial(String dialString) throws CallStateException {
707        return dial(dialString, null);
708    }
709
710    public Connection
711    dial (String dialString, UUSInfo uusInfo) throws CallStateException {
712        // Need to make sure dialString gets parsed properly
713        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
714
715        // handle in-call MMI first if applicable
716        if (handleInCallMmiCommands(newDialString)) {
717            return null;
718        }
719
720        // Only look at the Network portion for mmi
721        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
722        GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this);
723        if (LOCAL_DEBUG) Log.d(LOG_TAG,
724                               "dialing w/ mmi '" + mmi + "'...");
725
726        if (mmi == null) {
727            return mCT.dial(newDialString, uusInfo);
728        } else if (mmi.isTemporaryModeCLIR()) {
729            return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);
730        } else {
731            mPendingMMIs.add(mmi);
732            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
733            mmi.processCode();
734
735            // FIXME should this return null or something else?
736            return null;
737        }
738    }
739
740    public boolean handlePinMmi(String dialString) {
741        GsmMmiCode mmi = GsmMmiCode.newFromDialString(dialString, this);
742
743        if (mmi != null && mmi.isPinCommand()) {
744            mPendingMMIs.add(mmi);
745            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
746            mmi.processCode();
747            return true;
748        }
749
750        return false;
751    }
752
753    public void sendUssdResponse(String ussdMessge) {
754        GsmMmiCode mmi = GsmMmiCode.newFromUssdUserInput(ussdMessge, this);
755        mPendingMMIs.add(mmi);
756        mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
757        mmi.sendUssd(ussdMessge);
758    }
759
760    public void
761    sendDtmf(char c) {
762        if (!PhoneNumberUtils.is12Key(c)) {
763            Log.e(LOG_TAG,
764                    "sendDtmf called with invalid character '" + c + "'");
765        } else {
766            if (mCT.state ==  Phone.State.OFFHOOK) {
767                mCM.sendDtmf(c, null);
768            }
769        }
770    }
771
772    public void
773    startDtmf(char c) {
774        if (!PhoneNumberUtils.is12Key(c)) {
775            Log.e(LOG_TAG,
776                "startDtmf called with invalid character '" + c + "'");
777        } else {
778            mCM.startDtmf(c, null);
779        }
780    }
781
782    public void
783    stopDtmf() {
784        mCM.stopDtmf(null);
785    }
786
787    public void
788    sendBurstDtmf(String dtmfString) {
789        Log.e(LOG_TAG, "[GSMPhone] sendBurstDtmf() is a CDMA method");
790    }
791
792    public void
793    setRadioPower(boolean power) {
794        mSST.setRadioPower(power);
795    }
796
797    private void storeVoiceMailNumber(String number) {
798        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
799        SharedPreferences.Editor editor = sp.edit();
800        editor.putString(VM_NUMBER, number);
801        editor.apply();
802        setVmSimImsi(getSubscriberId());
803    }
804
805    public String getVoiceMailNumber() {
806        // Read from the SIM. If its null, try reading from the shared preference area.
807        String number = mIccRecords.getVoiceMailNumber();
808        if (TextUtils.isEmpty(number)) {
809            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
810            number = sp.getString(VM_NUMBER, null);
811        }
812        return number;
813    }
814
815    private String getVmSimImsi() {
816        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
817        return sp.getString(VM_SIM_IMSI, null);
818    }
819
820    private void setVmSimImsi(String imsi) {
821        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
822        SharedPreferences.Editor editor = sp.edit();
823        editor.putString(VM_SIM_IMSI, imsi);
824        editor.apply();
825    }
826
827    public String getVoiceMailAlphaTag() {
828        String ret;
829
830        ret = mIccRecords.getVoiceMailAlphaTag();
831
832        if (ret == null || ret.length() == 0) {
833            return mContext.getText(
834                com.android.internal.R.string.defaultVoiceMailAlphaTag).toString();
835        }
836
837        return ret;
838    }
839
840    public String getDeviceId() {
841        return mImei;
842    }
843
844    public String getDeviceSvn() {
845        return mImeiSv;
846    }
847
848    public String getImei() {
849        return mImei;
850    }
851
852    public String getEsn() {
853        Log.e(LOG_TAG, "[GSMPhone] getEsn() is a CDMA method");
854        return "0";
855    }
856
857    public String getMeid() {
858        Log.e(LOG_TAG, "[GSMPhone] getMeid() is a CDMA method");
859        return "0";
860    }
861
862    public String getSubscriberId() {
863        return mIccRecords.getIMSI();
864    }
865
866    public String getLine1Number() {
867        return mIccRecords.getMsisdnNumber();
868    }
869
870    @Override
871    public String getMsisdn() {
872        return mIccRecords.getMsisdnNumber();
873    }
874
875    public String getLine1AlphaTag() {
876        return mIccRecords.getMsisdnAlphaTag();
877    }
878
879    public void setLine1Number(String alphaTag, String number, Message onComplete) {
880        mIccRecords.setMsisdnNumber(alphaTag, number, onComplete);
881    }
882
883    public void setVoiceMailNumber(String alphaTag,
884                            String voiceMailNumber,
885                            Message onComplete) {
886
887        Message resp;
888        mVmNumber = voiceMailNumber;
889        resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
890        mIccRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
891    }
892
893    private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
894        switch (commandInterfaceCFReason) {
895        case CF_REASON_UNCONDITIONAL:
896        case CF_REASON_BUSY:
897        case CF_REASON_NO_REPLY:
898        case CF_REASON_NOT_REACHABLE:
899        case CF_REASON_ALL:
900        case CF_REASON_ALL_CONDITIONAL:
901            return true;
902        default:
903            return false;
904        }
905    }
906
907    private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
908        switch (commandInterfaceCFAction) {
909        case CF_ACTION_DISABLE:
910        case CF_ACTION_ENABLE:
911        case CF_ACTION_REGISTRATION:
912        case CF_ACTION_ERASURE:
913            return true;
914        default:
915            return false;
916        }
917    }
918
919    protected  boolean isCfEnable(int action) {
920        return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
921    }
922
923    public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
924        if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
925            if (LOCAL_DEBUG) Log.d(LOG_TAG, "requesting call forwarding query.");
926            Message resp;
927            if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) {
928                resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
929            } else {
930                resp = onComplete;
931            }
932            mCM.queryCallForwardStatus(commandInterfaceCFReason,0,null,resp);
933        }
934    }
935
936    public void setCallForwardingOption(int commandInterfaceCFAction,
937            int commandInterfaceCFReason,
938            String dialingNumber,
939            int timerSeconds,
940            Message onComplete) {
941        if (    (isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
942                (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
943
944            Message resp;
945            if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) {
946                resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
947                        isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, onComplete);
948            } else {
949                resp = onComplete;
950            }
951            mCM.setCallForward(commandInterfaceCFAction,
952                    commandInterfaceCFReason,
953                    CommandsInterface.SERVICE_CLASS_VOICE,
954                    dialingNumber,
955                    timerSeconds,
956                    resp);
957        }
958    }
959
960    public void getOutgoingCallerIdDisplay(Message onComplete) {
961        mCM.getCLIR(onComplete);
962    }
963
964    public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
965                                           Message onComplete) {
966        mCM.setCLIR(commandInterfaceCLIRMode,
967                obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete));
968    }
969
970    public void getCallWaiting(Message onComplete) {
971        //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
972        //class parameter in call waiting interrogation  to network
973        mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
974    }
975
976    public void setCallWaiting(boolean enable, Message onComplete) {
977        mCM.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
978    }
979
980    public void
981    getAvailableNetworks(Message response) {
982        mCM.getAvailableNetworks(response);
983    }
984
985    /**
986     * Small container class used to hold information relevant to
987     * the carrier selection process. operatorNumeric can be ""
988     * if we are looking for automatic selection. operatorAlphaLong is the
989     * corresponding operator name.
990     */
991    private static class NetworkSelectMessage {
992        public Message message;
993        public String operatorNumeric;
994        public String operatorAlphaLong;
995    }
996
997    public void
998    setNetworkSelectionModeAutomatic(Message response) {
999        // wrap the response message in our own message along with
1000        // an empty string (to indicate automatic selection) for the
1001        // operator's id.
1002        NetworkSelectMessage nsm = new NetworkSelectMessage();
1003        nsm.message = response;
1004        nsm.operatorNumeric = "";
1005        nsm.operatorAlphaLong = "";
1006
1007        // get the message
1008        Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
1009        if (LOCAL_DEBUG)
1010            Log.d(LOG_TAG, "wrapping and sending message to connect automatically");
1011
1012        mCM.setNetworkSelectionModeAutomatic(msg);
1013    }
1014
1015    public void
1016    selectNetworkManually(OperatorInfo network,
1017            Message response) {
1018        // wrap the response message in our own message along with
1019        // the operator's id.
1020        NetworkSelectMessage nsm = new NetworkSelectMessage();
1021        nsm.message = response;
1022        nsm.operatorNumeric = network.getOperatorNumeric();
1023        nsm.operatorAlphaLong = network.getOperatorAlphaLong();
1024
1025        // get the message
1026        Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
1027
1028        mCM.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg);
1029    }
1030
1031    public void
1032    getNeighboringCids(Message response) {
1033        mCM.getNeighboringCids(response);
1034    }
1035
1036    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
1037        mPostDialHandler = new Registrant(h, what, obj);
1038    }
1039
1040    public void setMute(boolean muted) {
1041        mCT.setMute(muted);
1042    }
1043
1044    public boolean getMute() {
1045        return mCT.getMute();
1046    }
1047
1048    public void getDataCallList(Message response) {
1049        mCM.getDataCallList(response);
1050    }
1051
1052    public void updateServiceLocation() {
1053        mSST.enableSingleLocationUpdate();
1054    }
1055
1056    public void enableLocationUpdates() {
1057        mSST.enableLocationUpdates();
1058    }
1059
1060    public void disableLocationUpdates() {
1061        mSST.disableLocationUpdates();
1062    }
1063
1064    public boolean getDataRoamingEnabled() {
1065        return mDataConnectionTracker.getDataOnRoamingEnabled();
1066    }
1067
1068    public void setDataRoamingEnabled(boolean enable) {
1069        mDataConnectionTracker.setDataOnRoamingEnabled(enable);
1070    }
1071
1072    /**
1073     * Removes the given MMI from the pending list and notifies
1074     * registrants that it is complete.
1075     * @param mmi MMI that is done
1076     */
1077    /*package*/ void
1078    onMMIDone(GsmMmiCode mmi) {
1079        /* Only notify complete if it's on the pending list.
1080         * Otherwise, it's already been handled (eg, previously canceled).
1081         * The exception is cancellation of an incoming USSD-REQUEST, which is
1082         * not on the list.
1083         */
1084        if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
1085            mMmiCompleteRegistrants.notifyRegistrants(
1086                new AsyncResult(null, mmi, null));
1087        }
1088    }
1089
1090
1091    private void
1092    onNetworkInitiatedUssd(GsmMmiCode mmi) {
1093        mMmiCompleteRegistrants.notifyRegistrants(
1094            new AsyncResult(null, mmi, null));
1095    }
1096
1097
1098    /** ussdMode is one of CommandsInterface.USSD_MODE_* */
1099    private void
1100    onIncomingUSSD (int ussdMode, String ussdMessage) {
1101        boolean isUssdError;
1102        boolean isUssdRequest;
1103
1104        isUssdRequest
1105            = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
1106
1107        isUssdError
1108            = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
1109                && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
1110
1111        // See comments in GsmMmiCode.java
1112        // USSD requests aren't finished until one
1113        // of these two events happen
1114        GsmMmiCode found = null;
1115        for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
1116            if(mPendingMMIs.get(i).isPendingUSSD()) {
1117                found = mPendingMMIs.get(i);
1118                break;
1119            }
1120        }
1121
1122        if (found != null) {
1123            // Complete pending USSD
1124
1125            if (isUssdError) {
1126                found.onUssdFinishedError();
1127            } else {
1128                found.onUssdFinished(ussdMessage, isUssdRequest);
1129            }
1130        } else { // pending USSD not found
1131            // The network may initiate its own USSD request
1132
1133            // ignore everything that isnt a Notify or a Request
1134            // also, discard if there is no message to present
1135            if (!isUssdError && ussdMessage != null) {
1136                GsmMmiCode mmi;
1137                mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
1138                                                   isUssdRequest,
1139                                                   GSMPhone.this);
1140                onNetworkInitiatedUssd(mmi);
1141            }
1142        }
1143    }
1144
1145    /**
1146     * Make sure the network knows our preferred setting.
1147     */
1148    protected  void syncClirSetting() {
1149        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
1150        int clirSetting = sp.getInt(CLIR_KEY, -1);
1151        if (clirSetting >= 0) {
1152            mCM.setCLIR(clirSetting, null);
1153        }
1154    }
1155
1156    @Override
1157    public void handleMessage (Message msg) {
1158        AsyncResult ar;
1159        Message onComplete;
1160
1161        switch (msg.what) {
1162            case EVENT_RADIO_AVAILABLE: {
1163                mCM.getBasebandVersion(
1164                        obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
1165
1166                mCM.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
1167                mCM.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
1168            }
1169            break;
1170
1171            case EVENT_RADIO_ON:
1172            break;
1173
1174            case EVENT_REGISTERED_TO_NETWORK:
1175                syncClirSetting();
1176                break;
1177
1178            case EVENT_SIM_RECORDS_LOADED:
1179                updateCurrentCarrierInProvider();
1180
1181                // Check if this is a different SIM than the previous one. If so unset the
1182                // voice mail number.
1183                String imsi = getVmSimImsi();
1184                String imsiFromSIM = getSubscriberId();
1185                if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) {
1186                    storeVoiceMailNumber(null);
1187                    setVmSimImsi(null);
1188                }
1189
1190            break;
1191
1192            case EVENT_GET_BASEBAND_VERSION_DONE:
1193                ar = (AsyncResult)msg.obj;
1194
1195                if (ar.exception != null) {
1196                    break;
1197                }
1198
1199                if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
1200                setSystemProperty(PROPERTY_BASEBAND_VERSION, (String)ar.result);
1201            break;
1202
1203            case EVENT_GET_IMEI_DONE:
1204                ar = (AsyncResult)msg.obj;
1205
1206                if (ar.exception != null) {
1207                    break;
1208                }
1209
1210                mImei = (String)ar.result;
1211            break;
1212
1213            case EVENT_GET_IMEISV_DONE:
1214                ar = (AsyncResult)msg.obj;
1215
1216                if (ar.exception != null) {
1217                    break;
1218                }
1219
1220                mImeiSv = (String)ar.result;
1221            break;
1222
1223            case EVENT_USSD:
1224                ar = (AsyncResult)msg.obj;
1225
1226                String[] ussdResult = (String[]) ar.result;
1227
1228                if (ussdResult.length > 1) {
1229                    try {
1230                        onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]);
1231                    } catch (NumberFormatException e) {
1232                        Log.w(LOG_TAG, "error parsing USSD");
1233                    }
1234                }
1235            break;
1236
1237            case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
1238                // Some MMI requests (eg USSD) are not completed
1239                // within the course of a CommandsInterface request
1240                // If the radio shuts off or resets while one of these
1241                // is pending, we need to clean up.
1242
1243                for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
1244                    if (mPendingMMIs.get(i).isPendingUSSD()) {
1245                        mPendingMMIs.get(i).onUssdFinishedError();
1246                    }
1247                }
1248            break;
1249
1250            case EVENT_SSN:
1251                ar = (AsyncResult)msg.obj;
1252                SuppServiceNotification not = (SuppServiceNotification) ar.result;
1253                mSsnRegistrants.notifyRegistrants(ar);
1254            break;
1255
1256            case EVENT_SET_CALL_FORWARD_DONE:
1257                ar = (AsyncResult)msg.obj;
1258                if (ar.exception == null) {
1259                    mIccRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
1260                }
1261                onComplete = (Message) ar.userObj;
1262                if (onComplete != null) {
1263                    AsyncResult.forMessage(onComplete, ar.result, ar.exception);
1264                    onComplete.sendToTarget();
1265                }
1266                break;
1267
1268            case EVENT_SET_VM_NUMBER_DONE:
1269                ar = (AsyncResult)msg.obj;
1270                if (IccVmNotSupportedException.class.isInstance(ar.exception)) {
1271                    storeVoiceMailNumber(mVmNumber);
1272                    ar.exception = null;
1273                }
1274                onComplete = (Message) ar.userObj;
1275                if (onComplete != null) {
1276                    AsyncResult.forMessage(onComplete, ar.result, ar.exception);
1277                    onComplete.sendToTarget();
1278                }
1279                break;
1280
1281
1282            case EVENT_GET_CALL_FORWARD_DONE:
1283                ar = (AsyncResult)msg.obj;
1284                if (ar.exception == null) {
1285                    handleCfuQueryResult((CallForwardInfo[])ar.result);
1286                }
1287                onComplete = (Message) ar.userObj;
1288                if (onComplete != null) {
1289                    AsyncResult.forMessage(onComplete, ar.result, ar.exception);
1290                    onComplete.sendToTarget();
1291                }
1292                break;
1293
1294            // handle the select network completion callbacks.
1295            case EVENT_SET_NETWORK_MANUAL_COMPLETE:
1296            case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
1297                handleSetSelectNetwork((AsyncResult) msg.obj);
1298                break;
1299
1300            case EVENT_SET_CLIR_COMPLETE:
1301                ar = (AsyncResult)msg.obj;
1302                if (ar.exception == null) {
1303                    saveClirSetting(msg.arg1);
1304                }
1305                onComplete = (Message) ar.userObj;
1306                if (onComplete != null) {
1307                    AsyncResult.forMessage(onComplete, ar.result, ar.exception);
1308                    onComplete.sendToTarget();
1309                }
1310                break;
1311
1312             default:
1313                 super.handleMessage(msg);
1314        }
1315    }
1316
1317    /**
1318     * Sets the "current" field in the telephony provider according to the SIM's operator
1319     *
1320     * @return true for success; false otherwise.
1321     */
1322    boolean updateCurrentCarrierInProvider() {
1323        if (mIccRecords != null) {
1324            try {
1325                Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
1326                ContentValues map = new ContentValues();
1327                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
1328                mContext.getContentResolver().insert(uri, map);
1329                return true;
1330            } catch (SQLException e) {
1331                Log.e(LOG_TAG, "Can't store current operator", e);
1332            }
1333        }
1334        return false;
1335    }
1336
1337    /**
1338     * Used to track the settings upon completion of the network change.
1339     */
1340    private void handleSetSelectNetwork(AsyncResult ar) {
1341        // look for our wrapper within the asyncresult, skip the rest if it
1342        // is null.
1343        if (!(ar.userObj instanceof NetworkSelectMessage)) {
1344            if (LOCAL_DEBUG) Log.d(LOG_TAG, "unexpected result from user object.");
1345            return;
1346        }
1347
1348        NetworkSelectMessage nsm = (NetworkSelectMessage) ar.userObj;
1349
1350        // found the object, now we send off the message we had originally
1351        // attached to the request.
1352        if (nsm.message != null) {
1353            if (LOCAL_DEBUG) Log.d(LOG_TAG, "sending original message to recipient");
1354            AsyncResult.forMessage(nsm.message, ar.result, ar.exception);
1355            nsm.message.sendToTarget();
1356        }
1357
1358        // open the shared preferences editor, and write the value.
1359        // nsm.operatorNumeric is "" if we're in automatic.selection.
1360        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
1361        SharedPreferences.Editor editor = sp.edit();
1362        editor.putString(NETWORK_SELECTION_KEY, nsm.operatorNumeric);
1363        editor.putString(NETWORK_SELECTION_NAME_KEY, nsm.operatorAlphaLong);
1364
1365        // commit and log the result.
1366        if (! editor.commit()) {
1367            Log.e(LOG_TAG, "failed to commit network selection preference");
1368        }
1369
1370    }
1371
1372    /**
1373     * Saves CLIR setting so that we can re-apply it as necessary
1374     * (in case the RIL resets it across reboots).
1375     */
1376    public void saveClirSetting(int commandInterfaceCLIRMode) {
1377        // open the shared preferences editor, and write the value.
1378        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
1379        SharedPreferences.Editor editor = sp.edit();
1380        editor.putInt(CLIR_KEY, commandInterfaceCLIRMode);
1381
1382        // commit and log the result.
1383        if (! editor.commit()) {
1384            Log.e(LOG_TAG, "failed to commit CLIR preference");
1385        }
1386    }
1387
1388    private void handleCfuQueryResult(CallForwardInfo[] infos) {
1389        if (infos == null || infos.length == 0) {
1390            // Assume the default is not active
1391            // Set unconditional CFF in SIM to false
1392            mIccRecords.setVoiceCallForwardingFlag(1, false);
1393        } else {
1394            for (int i = 0, s = infos.length; i < s; i++) {
1395                if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) {
1396                    mIccRecords.setVoiceCallForwardingFlag(1, (infos[i].status == 1));
1397                    // should only have the one
1398                    break;
1399                }
1400            }
1401        }
1402    }
1403
1404    /**
1405     * Retrieves the PhoneSubInfo of the GSMPhone
1406     */
1407    public PhoneSubInfo getPhoneSubInfo(){
1408        return mSubInfo;
1409    }
1410
1411    /**
1412     * Retrieves the IccSmsInterfaceManager of the GSMPhone
1413     */
1414    public IccSmsInterfaceManager getIccSmsInterfaceManager(){
1415        return mSimSmsIntManager;
1416    }
1417
1418    /**
1419     * Retrieves the IccPhoneBookInterfaceManager of the GSMPhone
1420     */
1421    public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
1422        return mSimPhoneBookIntManager;
1423    }
1424
1425    /**
1426     * {@inheritDoc}
1427     */
1428    public IccFileHandler getIccFileHandler(){
1429        return this.mIccFileHandler;
1430    }
1431
1432    /**
1433     * Activate or deactivate cell broadcast SMS.
1434     *
1435     * @param activate 0 = activate, 1 = deactivate
1436     * @param response Callback message is empty on completion
1437     */
1438    public void activateCellBroadcastSms(int activate, Message response) {
1439        Log.e(LOG_TAG, "[GSMPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
1440        response.sendToTarget();
1441    }
1442
1443    /**
1444     * Query the current configuration of cdma cell broadcast SMS.
1445     *
1446     * @param response Callback message is empty on completion
1447     */
1448    public void getCellBroadcastSmsConfig(Message response) {
1449        Log.e(LOG_TAG, "[GSMPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
1450        response.sendToTarget();
1451    }
1452
1453    /**
1454     * Configure cdma cell broadcast SMS.
1455     *
1456     * @param response Callback message is empty on completion
1457     */
1458    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
1459        Log.e(LOG_TAG, "[GSMPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
1460        response.sendToTarget();
1461    }
1462
1463    public boolean isCspPlmnEnabled() {
1464        return mIccRecords.isCspPlmnEnabled();
1465    }
1466}
1467