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