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