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