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