CdmaServiceStateTracker.java revision e70617d81dcd42350a737b11c25532e1d43df4ff
1/*
2 * Copyright (C) 2012 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.cdma;
18
19import android.app.AlarmManager;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.database.ContentObserver;
24import android.os.AsyncResult;
25import android.os.Build;
26import android.os.Handler;
27import android.os.Message;
28import android.os.PowerManager;
29import android.os.Registrant;
30import android.os.RegistrantList;
31import android.os.SystemClock;
32import android.os.SystemProperties;
33import android.os.UserHandle;
34import android.provider.Settings;
35import android.provider.Settings.SettingNotFoundException;
36import android.telephony.CellInfo;
37import android.telephony.CellInfoCdma;
38import android.telephony.Rlog;
39import android.telephony.ServiceState;
40import android.telephony.SignalStrength;
41import android.telephony.TelephonyManager;
42import android.telephony.SubscriptionManager;
43import android.telephony.cdma.CdmaCellLocation;
44import android.text.TextUtils;
45import android.util.EventLog;
46import android.util.TimeUtils;
47
48import com.android.internal.telephony.CommandException;
49import com.android.internal.telephony.CommandsInterface;
50import com.android.internal.telephony.CommandsInterface.RadioState;
51import com.android.internal.telephony.EventLogTags;
52import com.android.internal.telephony.MccTable;
53import com.android.internal.telephony.Phone;
54import com.android.internal.telephony.PhoneConstants;
55import com.android.internal.telephony.PhoneFactory;
56import com.android.internal.telephony.ServiceStateTracker;
57import com.android.internal.telephony.TelephonyIntents;
58import com.android.internal.telephony.TelephonyProperties;
59import com.android.internal.telephony.dataconnection.DcTrackerBase;
60import com.android.internal.telephony.uicc.UiccCardApplication;
61import com.android.internal.telephony.uicc.UiccController;
62import com.android.internal.telephony.HbpcdUtils;
63
64import java.io.FileDescriptor;
65import java.io.PrintWriter;
66import java.util.Arrays;
67import java.util.Calendar;
68import java.util.Date;
69import java.util.List;
70import java.util.TimeZone;
71
72/**
73 * {@hide}
74 */
75public class CdmaServiceStateTracker extends ServiceStateTracker {
76    static final String LOG_TAG = "CdmaSST";
77
78    CDMAPhone mPhone;
79    CdmaCellLocation mCellLoc;
80    CdmaCellLocation mNewCellLoc;
81
82    // Min values used to by getOtasp()
83    private static final String UNACTIVATED_MIN2_VALUE = "000000";
84    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
85
86    private static final int MS_PER_HOUR = 60 * 60 * 1000;
87
88    // Current Otasp value
89    int mCurrentOtaspMode = OTASP_UNINITIALIZED;
90
91     /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
92    private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
93    private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
94            NITZ_UPDATE_SPACING_DEFAULT);
95
96    /** If mNitzUpdateSpacing hasn't been exceeded but update is > mNitzUpdate do the update */
97    private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
98    private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
99            NITZ_UPDATE_DIFF_DEFAULT);
100
101    private int mRoamingIndicator;
102    private boolean mIsInPrl;
103    private int mDefaultRoamingIndicator;
104
105    /**
106     * Initially assume no data connection.
107     */
108    protected int mRegistrationState = -1;
109    protected RegistrantList mCdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
110
111    /**
112     * Sometimes we get the NITZ time before we know what country we
113     * are in. Keep the time zone information from the NITZ string so
114     * we can fix the time zone once know the country.
115     */
116    protected boolean mNeedFixZone = false;
117    private int mZoneOffset;
118    private boolean mZoneDst;
119    private long mZoneTime;
120    protected boolean mGotCountryCode = false;
121    String mSavedTimeZone;
122    long mSavedTime;
123    long mSavedAtTime;
124
125    /** Wake lock used while setting time of day. */
126    private PowerManager.WakeLock mWakeLock;
127    private static final String WAKELOCK_TAG = "ServiceStateTracker";
128
129    protected String mMdn;
130    protected int mHomeSystemId[] = null;
131    protected int mHomeNetworkId[] = null;
132    protected String mMin;
133    protected String mPrlVersion;
134    protected boolean mIsMinInfoReady = false;
135
136    private boolean mIsEriTextLoaded = false;
137    protected boolean mIsSubscriptionFromRuim = false;
138    private CdmaSubscriptionSourceManager mCdmaSSM;
139
140    protected static final String INVALID_MCC = "000";
141    protected static final String DEFAULT_MNC = "00";
142
143    protected HbpcdUtils mHbpcdUtils = null;
144
145    /* Used only for debugging purposes. */
146    private String mRegistrationDeniedReason;
147
148    private ContentResolver mCr;
149    private String mCurrentCarrier = null;
150
151    private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
152        @Override
153        public void onChange(boolean selfChange) {
154            if (DBG) log("Auto time state changed");
155            revertToNitzTime();
156        }
157    };
158
159    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
160        @Override
161        public void onChange(boolean selfChange) {
162            if (DBG) log("Auto time zone state changed");
163            revertToNitzTimeZone();
164        }
165    };
166
167    public CdmaServiceStateTracker(CDMAPhone phone) {
168        this(phone, new CellInfoCdma());
169    }
170
171    protected CdmaServiceStateTracker(CDMAPhone phone, CellInfo cellInfo) {
172        super(phone, phone.mCi, cellInfo);
173
174        mPhone = phone;
175        mCr = phone.getContext().getContentResolver();
176        mCellLoc = new CdmaCellLocation();
177        mNewCellLoc = new CdmaCellLocation();
178
179        mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(phone.getContext(), mCi, this,
180                EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
181        mIsSubscriptionFromRuim = (mCdmaSSM.getCdmaSubscriptionSource() ==
182                          CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
183
184        PowerManager powerManager =
185                (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
186        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
187
188        mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
189
190        mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
191        mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
192
193        mCi.registerForCdmaPrlChanged(this, EVENT_CDMA_PRL_VERSION_CHANGED, null);
194        phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null);
195        mCi.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
196
197        // System setting property AIRPLANE_MODE_ON is set in Settings.
198        int airplaneMode = Settings.Global.getInt(mCr, Settings.Global.AIRPLANE_MODE_ON, 0);
199        mDesiredPowerState = ! (airplaneMode > 0);
200
201        mCr.registerContentObserver(
202                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
203                mAutoTimeObserver);
204        mCr.registerContentObserver(
205            Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
206            mAutoTimeZoneObserver);
207        setSignalStrengthDefaultValues();
208
209        mHbpcdUtils = new HbpcdUtils(phone.getContext());
210
211        // Reset OTASP state in case previously set by another service
212        phone.notifyOtaspChanged(OTASP_UNINITIALIZED);
213    }
214
215    @Override
216    public void dispose() {
217        checkCorrectThread();
218        log("ServiceStateTracker dispose");
219
220        // Unregister for all events.
221        mCi.unregisterForRadioStateChanged(this);
222        mCi.unregisterForVoiceNetworkStateChanged(this);
223        mCi.unregisterForCdmaOtaProvision(this);
224        mPhone.unregisterForEriFileLoaded(this);
225        if (mUiccApplcation != null) {mUiccApplcation.unregisterForReady(this);}
226        if (mIccRecords != null) {mIccRecords.unregisterForRecordsLoaded(this);}
227        mCi.unSetOnNITZTime(this);
228        mCr.unregisterContentObserver(mAutoTimeObserver);
229        mCr.unregisterContentObserver(mAutoTimeZoneObserver);
230        mCdmaSSM.dispose(this);
231        mCi.unregisterForCdmaPrlChanged(this);
232        super.dispose();
233    }
234
235    @Override
236    protected void finalize() {
237        if (DBG) log("CdmaServiceStateTracker finalized");
238    }
239
240    /**
241     * Registration point for subscription info ready
242     * @param h handler to notify
243     * @param what what code of message when delivered
244     * @param obj placed in Message.obj
245     */
246    public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
247        Registrant r = new Registrant(h, what, obj);
248        mCdmaForSubscriptionInfoReadyRegistrants.add(r);
249
250        if (isMinInfoReady()) {
251            r.notifyRegistrant();
252        }
253    }
254
255    public void unregisterForSubscriptionInfoReady(Handler h) {
256        mCdmaForSubscriptionInfoReadyRegistrants.remove(h);
257    }
258
259    /**
260     * Save current source of cdma subscription
261     * @param source - 1 for NV, 0 for RUIM
262     */
263    private void saveCdmaSubscriptionSource(int source) {
264        log("Storing cdma subscription source: " + source);
265        Settings.Global.putInt(mPhone.getContext().getContentResolver(),
266                Settings.Global.CDMA_SUBSCRIPTION_MODE,
267                source );
268        log("Read from settings: " + Settings.Global.getInt(mPhone.getContext().getContentResolver(),
269                    Settings.Global.CDMA_SUBSCRIPTION_MODE, -1));
270    }
271
272    private void getSubscriptionInfoAndStartPollingThreads() {
273        mCi.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
274
275        // Get Registration Information
276        pollState();
277    }
278
279    @Override
280    public void handleMessage (Message msg) {
281        AsyncResult ar;
282        int[] ints;
283        String[] strings;
284
285        if (!mPhone.mIsTheCurrentActivePhone) {
286            loge("Received message " + msg + "[" + msg.what + "]" +
287                    " while being destroyed. Ignoring.");
288            return;
289        }
290
291        switch (msg.what) {
292        case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
293            handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
294            break;
295
296        case EVENT_RUIM_READY:
297            if (mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
298                // Subscription will be read from SIM I/O
299                if (DBG) log("Receive EVENT_RUIM_READY");
300                pollState();
301            } else {
302                if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
303                getSubscriptionInfoAndStartPollingThreads();
304            }
305
306            // Only support automatic selection mode in CDMA.
307            mCi.getNetworkSelectionMode(obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE));
308
309            mPhone.prepareEri();
310            break;
311
312        case EVENT_NV_READY:
313            updatePhoneObject();
314
315            // Only support automatic selection mode in CDMA.
316            mCi.getNetworkSelectionMode(obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE));
317
318            // For Non-RUIM phones, the subscription information is stored in
319            // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
320            // subscription info.
321            getSubscriptionInfoAndStartPollingThreads();
322            break;
323
324        case EVENT_RADIO_STATE_CHANGED:
325            if(mCi.getRadioState() == RadioState.RADIO_ON) {
326                handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
327
328                // Signal strength polling stops when radio is off.
329                queueNextSignalStrengthPoll();
330            }
331            // This will do nothing in the 'radio not available' case.
332            setPowerStateToDesired();
333            pollState();
334            break;
335
336        case EVENT_NETWORK_STATE_CHANGED_CDMA:
337            pollState();
338            break;
339
340        case EVENT_GET_SIGNAL_STRENGTH:
341            // This callback is called when signal strength is polled
342            // all by itself.
343
344            if (!(mCi.getRadioState().isOn())) {
345                // Polling will continue when radio turns back on.
346                return;
347            }
348            ar = (AsyncResult) msg.obj;
349            onSignalStrengthResult(ar, false);
350            queueNextSignalStrengthPoll();
351
352            break;
353
354        case EVENT_GET_LOC_DONE_CDMA:
355            ar = (AsyncResult) msg.obj;
356
357            if (ar.exception == null) {
358                String states[] = (String[])ar.result;
359                int baseStationId = -1;
360                int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
361                int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
362                int systemId = -1;
363                int networkId = -1;
364
365                if (states.length > 9) {
366                    try {
367                        if (states[4] != null) {
368                            baseStationId = Integer.parseInt(states[4]);
369                        }
370                        if (states[5] != null) {
371                            baseStationLatitude = Integer.parseInt(states[5]);
372                        }
373                        if (states[6] != null) {
374                            baseStationLongitude = Integer.parseInt(states[6]);
375                        }
376                        // Some carriers only return lat-lngs of 0,0
377                        if (baseStationLatitude == 0 && baseStationLongitude == 0) {
378                            baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
379                            baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
380                        }
381                        if (states[8] != null) {
382                            systemId = Integer.parseInt(states[8]);
383                        }
384                        if (states[9] != null) {
385                            networkId = Integer.parseInt(states[9]);
386                        }
387                    } catch (NumberFormatException ex) {
388                        loge("error parsing cell location data: " + ex);
389                    }
390                }
391
392                mCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
393                        baseStationLongitude, systemId, networkId);
394                mPhone.notifyLocationChanged();
395            }
396
397            // Release any temporary cell lock, which could have been
398            // acquired to allow a single-shot location update.
399            disableSingleLocationUpdate();
400            break;
401
402        case EVENT_POLL_STATE_REGISTRATION_CDMA:
403        case EVENT_POLL_STATE_OPERATOR_CDMA:
404        case EVENT_POLL_STATE_GPRS:
405            ar = (AsyncResult) msg.obj;
406            handlePollStateResult(msg.what, ar);
407            break;
408
409        case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
410            ar = (AsyncResult) msg.obj;
411
412            if (ar.exception == null) {
413                String cdmaSubscription[] = (String[])ar.result;
414                if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
415                    mMdn = cdmaSubscription[0];
416                    parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
417
418                    mMin = cdmaSubscription[3];
419                    mPrlVersion = cdmaSubscription[4];
420                    if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
421
422                    mIsMinInfoReady = true;
423
424                    updateOtaspState();
425                    if (!mIsSubscriptionFromRuim && mIccRecords != null) {
426                        if (DBG) {
427                            log("GET_CDMA_SUBSCRIPTION set imsi in mIccRecords");
428                        }
429                        mIccRecords.setImsi(getImsi());
430                    } else {
431                        if (DBG) {
432                            log("GET_CDMA_SUBSCRIPTION either mIccRecords is null  or NV type device" +
433                                    " - not setting Imsi in mIccRecords");
434                        }
435                    }
436                } else {
437                    if (DBG) {
438                        log("GET_CDMA_SUBSCRIPTION: error parsing cdmaSubscription params num="
439                            + cdmaSubscription.length);
440                    }
441                }
442            }
443            break;
444
445        case EVENT_POLL_SIGNAL_STRENGTH:
446            // Just poll signal strength...not part of pollState()
447
448            mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
449            break;
450
451        case EVENT_NITZ_TIME:
452            ar = (AsyncResult) msg.obj;
453
454            String nitzString = (String)((Object[])ar.result)[0];
455            long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
456
457            setTimeFromNITZString(nitzString, nitzReceiveTime);
458            break;
459
460        case EVENT_SIGNAL_STRENGTH_UPDATE:
461            // This is a notification from CommandsInterface.setOnSignalStrengthUpdate.
462
463            ar = (AsyncResult) msg.obj;
464
465            // The radio is telling us about signal strength changes,
466            // so we don't have to ask it.
467            mDontPollSignalStrength = true;
468
469            onSignalStrengthResult(ar, false);
470            break;
471
472        case EVENT_RUIM_RECORDS_LOADED:
473            log("EVENT_RUIM_RECORDS_LOADED: what=" + msg.what);
474            updatePhoneObject();
475            updateSpnDisplay();
476            break;
477
478        case EVENT_LOCATION_UPDATES_ENABLED:
479            ar = (AsyncResult) msg.obj;
480
481            if (ar.exception == null) {
482                mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
483            }
484            break;
485
486        case EVENT_ERI_FILE_LOADED:
487            // Repoll the state once the ERI file has been loaded.
488            if (DBG) log("[CdmaServiceStateTracker] ERI file has been loaded, repolling.");
489            pollState();
490            break;
491
492        case EVENT_OTA_PROVISION_STATUS_CHANGE:
493            ar = (AsyncResult)msg.obj;
494            if (ar.exception == null) {
495                ints = (int[]) ar.result;
496                int otaStatus = ints[0];
497                if (otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
498                    || otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
499                    if (DBG) log("EVENT_OTA_PROVISION_STATUS_CHANGE: Complete, Reload MDN");
500                    mCi.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
501                }
502            }
503            break;
504
505        case EVENT_CDMA_PRL_VERSION_CHANGED:
506            ar = (AsyncResult)msg.obj;
507            if (ar.exception == null) {
508                ints = (int[]) ar.result;
509                mPrlVersion = Integer.toString(ints[0]);
510            }
511            break;
512
513        case EVENT_CHANGE_IMS_STATE:
514            if (DBG) log("EVENT_CHANGE_IMS_STATE");
515            setPowerStateToDesired();
516            break;
517
518        case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
519            if (DBG) log("EVENT_POLL_STATE_NETWORK_SELECTION_MODE");
520            ar = (AsyncResult) msg.obj;
521            if (ar.exception == null && ar.result != null) {
522                ints = (int[])ar.result;
523                if (ints[0] == 1) {  // Manual selection.
524                    mPhone.setNetworkSelectionModeAutomatic(null);
525                }
526            } else {
527                log("Unable to getNetworkSelectionMode");
528            }
529            break;
530
531        default:
532            super.handleMessage(msg);
533        break;
534        }
535    }
536
537    private void handleCdmaSubscriptionSource(int newSubscriptionSource) {
538        log("Subscription Source : " + newSubscriptionSource);
539        mIsSubscriptionFromRuim =
540            (newSubscriptionSource == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
541        log("isFromRuim: " + mIsSubscriptionFromRuim);
542        saveCdmaSubscriptionSource(newSubscriptionSource);
543        if (!mIsSubscriptionFromRuim) {
544            // NV is ready when subscription source is NV
545            sendMessage(obtainMessage(EVENT_NV_READY));
546        }
547    }
548
549    @Override
550    protected void setPowerStateToDesired() {
551        // If we want it on and it's off, turn it on
552        if (mDesiredPowerState
553            && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
554            mCi.setRadioPower(true, null);
555        } else if (!mDesiredPowerState && mCi.getRadioState().isOn()) {
556            DcTrackerBase dcTracker = mPhone.mDcTracker;
557
558            // If it's on and available and we want it off gracefully
559            powerOffRadioSafely(dcTracker);
560        } else if (mDeviceShuttingDown && mCi.getRadioState().isAvailable()) {
561            mCi.requestShutdown(null);
562        }
563    }
564
565    @Override
566    protected void updateSpnDisplay() {
567        // mOperatorAlphaLong contains the ERI text
568        String plmn = mSS.getOperatorAlphaLong();
569        boolean showPlmn = false;
570
571        if (!TextUtils.equals(plmn, mCurPlmn)) {
572            // Allow A blank plmn, "" to set showPlmn to true. Previously, we
573            // would set showPlmn to true only if plmn was not empty, i.e. was not
574            // null and not blank. But this would cause us to incorrectly display
575            // "No Service". Now showPlmn is set to true for any non null string.
576            showPlmn = plmn != null;
577            if (DBG) {
578                log(String.format("updateSpnDisplay: changed sending intent" +
579                            " showPlmn='%b' plmn='%s'", showPlmn, plmn));
580            }
581            Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
582            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
583            intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, false);
584            intent.putExtra(TelephonyIntents.EXTRA_SPN, "");
585            intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
586            intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
587            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
588            mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
589
590            if (!mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(),
591                    showPlmn, plmn, false, "")) {
592                mSpnUpdatePending = true;
593            }
594        }
595
596        mCurShowSpn = false;
597        mCurShowPlmn = showPlmn;
598        mCurSpn = "";
599        mCurPlmn = plmn;
600    }
601
602    @Override
603    protected Phone getPhone() {
604        return mPhone;
605    }
606
607    /**
608    * Hanlde the PollStateResult message
609    */
610    protected void handlePollStateResultMessage(int what, AsyncResult ar){
611        int ints[];
612        String states[];
613        switch (what) {
614            case EVENT_POLL_STATE_GPRS: {
615                states = (String[])ar.result;
616                if (DBG) {
617                    log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS states.length=" +
618                            states.length + " states=" + states);
619                }
620
621                int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
622                int dataRadioTechnology = 0;
623
624                if (states.length > 0) {
625                    try {
626                        regState = Integer.parseInt(states[0]);
627
628                        // states[3] (if present) is the current radio technology
629                        if (states.length >= 4 && states[3] != null) {
630                            dataRadioTechnology = Integer.parseInt(states[3]);
631                        }
632                    } catch (NumberFormatException ex) {
633                        loge("handlePollStateResultMessage: error parsing GprsRegistrationState: "
634                                        + ex);
635                    }
636                }
637
638                int dataRegState = regCodeToServiceState(regState);
639                mNewSS.setDataRegState(dataRegState);
640                mNewSS.setRilDataRadioTechnology(dataRadioTechnology);
641                mNewSS.setDataRoaming(regCodeIsRoaming(regState));
642                if (DBG) {
643                    log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
644                            + " regState=" + regState
645                            + " dataRadioTechnology=" + dataRadioTechnology);
646                }
647                break;
648            }
649
650            case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
651                states = (String[])ar.result;
652
653                int registrationState = 4;     //[0] registrationState
654                int radioTechnology = -1;      //[3] radioTechnology
655                int baseStationId = -1;        //[4] baseStationId
656                //[5] baseStationLatitude
657                int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
658                //[6] baseStationLongitude
659                int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
660                int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
661                int systemId = 0;              //[8] systemId
662                int networkId = 0;             //[9] networkId
663                int roamingIndicator = -1;     //[10] Roaming indicator
664                int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
665                int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
666                int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
667
668                if (states.length >= 14) {
669                    try {
670                        if (states[0] != null) {
671                            registrationState = Integer.parseInt(states[0]);
672                        }
673                        if (states[3] != null) {
674                            radioTechnology = Integer.parseInt(states[3]);
675                        }
676                        if (states[4] != null) {
677                            baseStationId = Integer.parseInt(states[4]);
678                        }
679                        if (states[5] != null) {
680                            baseStationLatitude = Integer.parseInt(states[5]);
681                        }
682                        if (states[6] != null) {
683                            baseStationLongitude = Integer.parseInt(states[6]);
684                        }
685                        // Some carriers only return lat-lngs of 0,0
686                        if (baseStationLatitude == 0 && baseStationLongitude == 0) {
687                            baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
688                            baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
689                        }
690                        if (states[7] != null) {
691                            cssIndicator = Integer.parseInt(states[7]);
692                        }
693                        if (states[8] != null) {
694                            systemId = Integer.parseInt(states[8]);
695                        }
696                        if (states[9] != null) {
697                            networkId = Integer.parseInt(states[9]);
698                        }
699                        if (states[10] != null) {
700                            roamingIndicator = Integer.parseInt(states[10]);
701                        }
702                        if (states[11] != null) {
703                            systemIsInPrl = Integer.parseInt(states[11]);
704                        }
705                        if (states[12] != null) {
706                            defaultRoamingIndicator = Integer.parseInt(states[12]);
707                        }
708                        if (states[13] != null) {
709                            reasonForDenial = Integer.parseInt(states[13]);
710                        }
711                    } catch (NumberFormatException ex) {
712                        loge("EVENT_POLL_STATE_REGISTRATION_CDMA: error parsing: " + ex);
713                    }
714                } else {
715                    throw new RuntimeException("Warning! Wrong number of parameters returned from "
716                                         + "RIL_REQUEST_REGISTRATION_STATE: expected 14 or more "
717                                         + "strings and got " + states.length + " strings");
718                }
719
720                mRegistrationState = registrationState;
721                // When registration state is roaming and TSB58
722                // roaming indicator is not in the carrier-specified
723                // list of ERIs for home system, mCdmaRoaming is true.
724                boolean cdmaRoaming =
725                        regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
726                mNewSS.setVoiceRoaming(cdmaRoaming);
727                mNewSS.setState (regCodeToServiceState(registrationState));
728
729                mNewSS.setRilVoiceRadioTechnology(radioTechnology);
730
731                mNewSS.setCssIndicator(cssIndicator);
732                mNewSS.setSystemAndNetworkId(systemId, networkId);
733                mRoamingIndicator = roamingIndicator;
734                mIsInPrl = (systemIsInPrl == 0) ? false : true;
735                mDefaultRoamingIndicator = defaultRoamingIndicator;
736
737
738                // Values are -1 if not available.
739                mNewCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
740                        baseStationLongitude, systemId, networkId);
741
742                if (reasonForDenial == 0) {
743                    mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
744                } else if (reasonForDenial == 1) {
745                    mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
746                } else {
747                    mRegistrationDeniedReason = "";
748                }
749
750                if (mRegistrationState == 3) {
751                    if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
752                }
753                break;
754
755            case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR
756                String opNames[] = (String[])ar.result;
757
758                if (opNames != null && opNames.length >= 3) {
759                    // TODO: Do we care about overriding in this case.
760                    // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC
761                    if ((opNames[2] == null) || (opNames[2].length() < 5)
762                            || ("00000".equals(opNames[2]))) {
763                        opNames[2] = SystemProperties.get(
764                                CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000");
765                        if (DBG) {
766                            log("RIL_REQUEST_OPERATOR.response[2], the numeric, " +
767                                    " is bad. Using SystemProperties '" +
768                                            CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC +
769                                    "'= " + opNames[2]);
770                        }
771                    }
772
773                    if (!mIsSubscriptionFromRuim) {
774                        // NV device (as opposed to CSIM)
775                        mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
776                    } else {
777                        String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null ?
778                            mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() : null;
779                        if (brandOverride != null) {
780                            mNewSS.setOperatorName(brandOverride, brandOverride, opNames[2]);
781                        } else {
782                            mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
783                        }
784                    }
785                } else {
786                    if (DBG) log("EVENT_POLL_STATE_OPERATOR_CDMA: error parsing opNames");
787                }
788                break;
789
790            default:
791                loge("handlePollStateResultMessage: RIL response handle in wrong phone!"
792                        + " Expected CDMA RIL request and get GSM RIL request.");
793                break;
794        }
795    }
796
797    /**
798     * Handle the result of one of the pollState() - related requests
799     */
800    @Override
801    protected void handlePollStateResult(int what, AsyncResult ar) {
802        // Ignore stale requests from last poll.
803        if (ar.userObj != mPollingContext) return;
804
805        if (ar.exception != null) {
806            CommandException.Error err=null;
807
808            if (ar.exception instanceof CommandException) {
809                err = ((CommandException)(ar.exception)).getCommandError();
810            }
811
812            if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
813                // Radio has crashed or turned off.
814                cancelPollState();
815                return;
816            }
817
818            if (!mCi.getRadioState().isOn()) {
819                // Radio has crashed or turned off.
820                cancelPollState();
821                return;
822            }
823
824            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
825                loge("handlePollStateResult: RIL returned an error where it must succeed"
826                        + ar.exception);
827            }
828        } else try {
829            handlePollStateResultMessage(what, ar);
830        } catch (RuntimeException ex) {
831            loge("handlePollStateResult: Exception while polling service state. "
832                    + "Probably malformed RIL response." + ex);
833        }
834
835        mPollingContext[0]--;
836
837        if (mPollingContext[0] == 0) {
838            boolean namMatch = false;
839            if (!isSidsAllZeros() && isHomeSid(mNewSS.getSystemId())) {
840                namMatch = true;
841            }
842
843            // Setting SS Roaming (general)
844            if (mIsSubscriptionFromRuim) {
845                mNewSS.setVoiceRoaming(isRoamingBetweenOperators(mNewSS.getVoiceRoaming(), mNewSS));
846            }
847            // For CDMA, voice and data should have the same roaming status
848            final boolean isVoiceInService =
849                    (mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
850            final int dataRegType = mNewSS.getRilDataRadioTechnology();
851            if (isVoiceInService && ServiceState.isCdma(dataRegType)) {
852                mNewSS.setDataRoaming(mNewSS.getVoiceRoaming());
853            }
854
855            // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
856            mNewSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
857            mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
858            boolean isPrlLoaded = true;
859            if (TextUtils.isEmpty(mPrlVersion)) {
860                isPrlLoaded = false;
861            }
862            if (!isPrlLoaded || (mNewSS.getRilVoiceRadioTechnology()
863                                        == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
864                log("Turn off roaming indicator if !isPrlLoaded or voice RAT is unknown");
865                mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
866            } else if (!isSidsAllZeros()) {
867                if (!namMatch && !mIsInPrl) {
868                    // Use default
869                    mNewSS.setCdmaRoamingIndicator(mDefaultRoamingIndicator);
870                } else if (namMatch && !mIsInPrl) {
871                    // TODO this will be removed when we handle roaming on LTE on CDMA+LTE phones
872                    if (mNewSS.getRilVoiceRadioTechnology()
873                            == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
874                        log("Turn off roaming indicator as voice is LTE");
875                        mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
876                    } else {
877                        mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
878                    }
879                } else if (!namMatch && mIsInPrl) {
880                    // Use the one from PRL/ERI
881                    mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
882                } else {
883                    // It means namMatch && mIsInPrl
884                    if ((mRoamingIndicator <= 2)) {
885                        mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
886                    } else {
887                        // Use the one from PRL/ERI
888                        mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
889                    }
890                }
891            }
892
893            int roamingIndicator = mNewSS.getCdmaRoamingIndicator();
894            mNewSS.setCdmaEriIconIndex(mPhone.mEriManager.getCdmaEriIconIndex(roamingIndicator,
895                    mDefaultRoamingIndicator));
896            mNewSS.setCdmaEriIconMode(mPhone.mEriManager.getCdmaEriIconMode(roamingIndicator,
897                    mDefaultRoamingIndicator));
898
899            // NOTE: Some operator may require overriding mCdmaRoaming
900            // (set by the modem), depending on the mRoamingIndicator.
901
902            if (DBG) {
903                log("Set CDMA Roaming Indicator to: " + mNewSS.getCdmaRoamingIndicator()
904                    + ". voiceRoaming = " + mNewSS.getVoiceRoaming()
905                    + ". dataRoaming = " + mNewSS.getDataRoaming()
906                    + ", isPrlLoaded = " + isPrlLoaded
907                    + ". namMatch = " + namMatch + " , mIsInPrl = " + mIsInPrl
908                    + ", mRoamingIndicator = " + mRoamingIndicator
909                    + ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator);
910            }
911            pollStateDone();
912        }
913
914    }
915
916    /**
917     * Set both voice and data roaming type,
918     * judging from the roaming indicator
919     * or ISO country of SIM VS network.
920     */
921    protected void setRoamingType(ServiceState currentServiceState) {
922        final boolean isVoiceInService =
923                (currentServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
924        if (isVoiceInService) {
925            if (currentServiceState.getVoiceRoaming()) {
926                // some carrier defines international roaming by indicator
927                int[] intRoamingIndicators = mPhone.getContext().getResources().getIntArray(
928                        com.android.internal.R.array.config_cdma_international_roaming_indicators);
929                if ((intRoamingIndicators != null) && (intRoamingIndicators.length > 0)) {
930                    // It's domestic roaming at least now
931                    currentServiceState.setVoiceRoamingType(ServiceState.ROAMING_TYPE_DOMESTIC);
932                    int curRoamingIndicator = currentServiceState.getCdmaRoamingIndicator();
933                    for (int i = 0; i < intRoamingIndicators.length; i++) {
934                        if (curRoamingIndicator == intRoamingIndicators[i]) {
935                            currentServiceState.setVoiceRoamingType(
936                                    ServiceState.ROAMING_TYPE_INTERNATIONAL);
937                            break;
938                        }
939                    }
940                } else {
941                    // check roaming type by MCC
942                    if (inSameCountry(currentServiceState.getVoiceOperatorNumeric())) {
943                        currentServiceState.setVoiceRoamingType(
944                                ServiceState.ROAMING_TYPE_DOMESTIC);
945                    } else {
946                        currentServiceState.setVoiceRoamingType(
947                                ServiceState.ROAMING_TYPE_INTERNATIONAL);
948                    }
949                }
950            } else {
951                currentServiceState.setVoiceRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
952            }
953        }
954        final boolean isDataInService =
955                (currentServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
956        final int dataRegType = currentServiceState.getRilDataRadioTechnology();
957        if (isDataInService) {
958            if (!currentServiceState.getDataRoaming()) {
959                currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
960            } else if (ServiceState.isCdma(dataRegType)) {
961                if (isVoiceInService) {
962                    // CDMA data should have the same state as voice
963                    currentServiceState.setDataRoamingType(currentServiceState
964                            .getVoiceRoamingType());
965                } else {
966                    // we can not decide CDMA data roaming type without voice
967                    // set it as same as last time
968                    currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_UNKNOWN);
969                }
970            } else {
971                // take it as 3GPP roaming
972                if (inSameCountry(currentServiceState.getDataOperatorNumeric())) {
973                    currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_DOMESTIC);
974                } else {
975                    currentServiceState.setDataRoamingType(
976                            ServiceState.ROAMING_TYPE_INTERNATIONAL);
977                }
978            }
979        }
980    }
981
982    protected String getHomeOperatorNumeric() {
983        String numeric = ((TelephonyManager) mPhone.getContext().
984                getSystemService(Context.TELEPHONY_SERVICE)).
985                getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
986        if (TextUtils.isEmpty(numeric)) {
987            numeric = SystemProperties.get(CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "");
988        }
989        return numeric;
990    }
991
992    protected void setSignalStrengthDefaultValues() {
993        mSignalStrength = new SignalStrength( false);
994    }
995
996    /**
997     * A complete "service state" from our perspective is
998     * composed of a handful of separate requests to the radio.
999     *
1000     * We make all of these requests at once, but then abandon them
1001     * and start over again if the radio notifies us that some
1002     * event has changed
1003     */
1004    @Override
1005    public void pollState() {
1006        mPollingContext = new int[1];
1007        mPollingContext[0] = 0;
1008
1009        switch (mCi.getRadioState()) {
1010        case RADIO_UNAVAILABLE:
1011            mNewSS.setStateOutOfService();
1012            mNewCellLoc.setStateInvalid();
1013            setSignalStrengthDefaultValues();
1014            mGotCountryCode = false;
1015
1016            pollStateDone();
1017            break;
1018
1019        case RADIO_OFF:
1020            mNewSS.setStateOff();
1021            mNewCellLoc.setStateInvalid();
1022            setSignalStrengthDefaultValues();
1023            mGotCountryCode = false;
1024
1025            pollStateDone();
1026            break;
1027
1028        default:
1029            // Issue all poll-related commands at once, then count
1030            // down the responses which are allowed to arrive
1031            // out-of-order.
1032
1033            mPollingContext[0]++;
1034            // RIL_REQUEST_OPERATOR is necessary for CDMA
1035            mCi.getOperator(
1036                    obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, mPollingContext));
1037
1038            mPollingContext[0]++;
1039            // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
1040            mCi.getVoiceRegistrationState(
1041                    obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, mPollingContext));
1042
1043            mPollingContext[0]++;
1044            // RIL_REQUEST_DATA_REGISTRATION_STATE
1045            mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS,
1046                                        mPollingContext));
1047            break;
1048        }
1049    }
1050
1051    protected void fixTimeZone(String isoCountryCode) {
1052        TimeZone zone = null;
1053        // If the offset is (0, false) and the time zone property
1054        // is set, use the time zone property rather than GMT.
1055        String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
1056        if (DBG) {
1057            log("fixTimeZone zoneName='" + zoneName +
1058                "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
1059                " iso-cc='" + isoCountryCode +
1060                "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode));
1061        }
1062        if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null)
1063                && (zoneName.length() > 0)
1064                && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) {
1065            // For NITZ string without time zone,
1066            // need adjust time to reflect default time zone setting
1067            zone = TimeZone.getDefault();
1068            if (mNeedFixZone) {
1069                long ctm = System.currentTimeMillis();
1070                long tzOffset = zone.getOffset(ctm);
1071                if (DBG) {
1072                    log("fixTimeZone: tzOffset=" + tzOffset +
1073                            " ltod=" + TimeUtils.logTimeOfDay(ctm));
1074                }
1075                if (getAutoTime()) {
1076                    long adj = ctm - tzOffset;
1077                    if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
1078                    setAndBroadcastNetworkSetTime(adj);
1079                } else {
1080                    // Adjust the saved NITZ time to account for tzOffset.
1081                    mSavedTime = mSavedTime - tzOffset;
1082                    if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
1083                }
1084            }
1085            if (DBG) log("fixTimeZone: using default TimeZone");
1086        } else if (isoCountryCode.equals("")) {
1087            // Country code not found. This is likely a test network.
1088            // Get a TimeZone based only on the NITZ parameters (best guess).
1089            zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1090            if (DBG) log("fixTimeZone: using NITZ TimeZone");
1091        } else {
1092            zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode);
1093            if (DBG) log("fixTimeZone: using getTimeZone(off, dst, time, iso)");
1094        }
1095
1096        mNeedFixZone = false;
1097
1098        if (zone != null) {
1099            log("fixTimeZone: zone != null zone.getID=" + zone.getID());
1100            if (getAutoTimeZone()) {
1101                setAndBroadcastNetworkSetTimeZone(zone.getID());
1102            } else {
1103                log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
1104            }
1105            saveNitzTimeZone(zone.getID());
1106        } else {
1107            log("fixTimeZone: zone == null, do nothing for zone");
1108        }
1109    }
1110
1111    protected void pollStateDone() {
1112        if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
1113
1114        if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric()) ||
1115                mPhone.isSidMarkedAsNonRoaming(mNewSS.getSystemId())) {
1116            log("pollStateDone: override - marked as non-roaming.");
1117            mNewSS.setVoiceRoaming(false);
1118            mNewSS.setDataRoaming(false);
1119            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
1120        } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric()) ||
1121                mPhone.isSidMarkedAsRoaming(mNewSS.getSystemId())) {
1122            log("pollStateDone: override - marked as roaming.");
1123            mNewSS.setVoiceRoaming(true);
1124            mNewSS.setDataRoaming(true);
1125            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
1126            mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
1127        }
1128
1129        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
1130            mNewSS.setVoiceRoaming(true);
1131            mNewSS.setDataRoaming(true);
1132        }
1133
1134        useDataRegStateForDataOnlyDevices();
1135
1136        boolean hasRegistered =
1137            mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
1138            && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
1139
1140        boolean hasDeregistered =
1141            mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
1142            && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
1143
1144        boolean hasCdmaDataConnectionAttached =
1145            mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
1146            && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
1147
1148        boolean hasCdmaDataConnectionDetached =
1149            mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
1150            && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
1151
1152        boolean hasCdmaDataConnectionChanged =
1153                       mSS.getDataRegState() != mNewSS.getDataRegState();
1154
1155        boolean hasRilVoiceRadioTechnologyChanged =
1156                mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
1157
1158        boolean hasRilDataRadioTechnologyChanged =
1159                mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology();
1160
1161        boolean hasChanged = !mNewSS.equals(mSS);
1162
1163        boolean hasVoiceRoamingOn = !mSS.getVoiceRoaming() && mNewSS.getVoiceRoaming();
1164
1165        boolean hasVoiceRoamingOff = mSS.getVoiceRoaming() && !mNewSS.getVoiceRoaming();
1166
1167        boolean hasDataRoamingOn = !mSS.getDataRoaming() && mNewSS.getDataRoaming();
1168
1169        boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
1170
1171        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
1172
1173        TelephonyManager tm =
1174                (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
1175
1176        // Add an event log when connection state changes
1177        if (mSS.getVoiceRegState() != mNewSS.getVoiceRegState() ||
1178                mSS.getDataRegState() != mNewSS.getDataRegState()) {
1179            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
1180                    mSS.getVoiceRegState(), mSS.getDataRegState(),
1181                    mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
1182        }
1183
1184        ServiceState tss;
1185        tss = mSS;
1186        mSS = mNewSS;
1187        mNewSS = tss;
1188        // clean slate for next time
1189        mNewSS.setStateOutOfService();
1190
1191        CdmaCellLocation tcl = mCellLoc;
1192        mCellLoc = mNewCellLoc;
1193        mNewCellLoc = tcl;
1194
1195        if (hasRilVoiceRadioTechnologyChanged) {
1196            updatePhoneObject();
1197        }
1198
1199        if (hasRilDataRadioTechnologyChanged) {
1200            tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
1201        }
1202
1203        if (hasRegistered) {
1204            mNetworkAttachedRegistrants.notifyRegistrants();
1205        }
1206
1207        if (hasChanged) {
1208            if ((mCi.getRadioState().isOn()) && (!mIsSubscriptionFromRuim)) {
1209                String eriText;
1210                // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text
1211                if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
1212                    eriText = mPhone.getCdmaEriText();
1213                } else {
1214                    // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for
1215                    // mRegistrationState 0,2,3 and 4
1216                    eriText = mPhone.getContext().getText(
1217                            com.android.internal.R.string.roamingTextSearching).toString();
1218                }
1219                mSS.setOperatorAlphaLong(eriText);
1220            }
1221
1222            String operatorNumeric;
1223
1224            tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlphaLong());
1225
1226            String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
1227            operatorNumeric = mSS.getOperatorNumeric();
1228
1229            // try to fix the invalid Operator Numeric
1230            if (isInvalidOperatorNumeric(operatorNumeric)) {
1231                int sid = mSS.getSystemId();
1232                operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
1233            }
1234
1235            tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
1236            updateCarrierMccMncConfiguration(operatorNumeric,
1237                    prevOperatorNumeric, mPhone.getContext());
1238
1239            if (isInvalidOperatorNumeric(operatorNumeric)) {
1240                if (DBG) log("operatorNumeric "+ operatorNumeric +"is invalid");
1241                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
1242                mGotCountryCode = false;
1243            } else {
1244                String isoCountryCode = "";
1245                String mcc = operatorNumeric.substring(0, 3);
1246                try{
1247                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
1248                            operatorNumeric.substring(0,3)));
1249                } catch ( NumberFormatException ex){
1250                    loge("pollStateDone: countryCodeForMcc error" + ex);
1251                } catch ( StringIndexOutOfBoundsException ex) {
1252                    loge("pollStateDone: countryCodeForMcc error" + ex);
1253                }
1254
1255                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), isoCountryCode);
1256                mGotCountryCode = true;
1257
1258                setOperatorIdd(operatorNumeric);
1259
1260                if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
1261                        mNeedFixZone)) {
1262                    fixTimeZone(isoCountryCode);
1263                }
1264            }
1265
1266            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
1267                    (mSS.getVoiceRoaming() || mSS.getDataRoaming()));
1268
1269            updateSpnDisplay();
1270            // set roaming type
1271            setRoamingType(mSS);
1272            log("Broadcasting ServiceState : " + mSS);
1273            mPhone.notifyServiceStateChanged(mSS);
1274        }
1275
1276        if (hasCdmaDataConnectionAttached) {
1277            mAttachedRegistrants.notifyRegistrants();
1278        }
1279
1280        if (hasCdmaDataConnectionDetached) {
1281            mDetachedRegistrants.notifyRegistrants();
1282        }
1283
1284        if (hasCdmaDataConnectionChanged || hasRilDataRadioTechnologyChanged) {
1285            notifyDataRegStateRilRadioTechnologyChanged();
1286            mPhone.notifyDataConnection(null);
1287        }
1288
1289        if (hasVoiceRoamingOn) {
1290            mVoiceRoamingOnRegistrants.notifyRegistrants();
1291        }
1292
1293        if (hasVoiceRoamingOff) {
1294            mVoiceRoamingOffRegistrants.notifyRegistrants();
1295        }
1296
1297        if (hasDataRoamingOn) {
1298            mDataRoamingOnRegistrants.notifyRegistrants();
1299        }
1300
1301        if (hasDataRoamingOff) {
1302            mDataRoamingOffRegistrants.notifyRegistrants();
1303        }
1304
1305        if (hasLocationChanged) {
1306            mPhone.notifyLocationChanged();
1307        }
1308        // TODO: Add CdmaCellIdenity updating, see CdmaLteServiceStateTracker.
1309    }
1310
1311    protected boolean isInvalidOperatorNumeric(String operatorNumeric) {
1312        return operatorNumeric == null || operatorNumeric.length() < 5 ||
1313                    operatorNumeric.startsWith(INVALID_MCC);
1314    }
1315
1316    protected String fixUnknownMcc(String operatorNumeric, int sid) {
1317        if (sid <= 0) {
1318            // no cdma information is available, do nothing
1319            return operatorNumeric;
1320        }
1321
1322        // resolve the mcc from sid;
1323        // if mSavedTimeZone is null, TimeZone would get the default timeZone,
1324        // and the fixTimeZone couldn't help, because it depends on operator Numeric;
1325        // if the sid is conflict and timezone is unavailable, the mcc may be not right.
1326        boolean isNitzTimeZone = false;
1327        int timeZone = 0;
1328        TimeZone tzone = null;
1329        if (mSavedTimeZone != null) {
1330             timeZone =
1331                     TimeZone.getTimeZone(mSavedTimeZone).getRawOffset()/MS_PER_HOUR;
1332             isNitzTimeZone = true;
1333        } else {
1334             tzone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1335             if (tzone != null)
1336                     timeZone = tzone.getRawOffset()/MS_PER_HOUR;
1337        }
1338
1339        int mcc = mHbpcdUtils.getMcc(sid,
1340                timeZone, (mZoneDst ? 1 : 0), isNitzTimeZone);
1341        if (mcc > 0) {
1342            operatorNumeric = Integer.toString(mcc) + DEFAULT_MNC;
1343        }
1344        return operatorNumeric;
1345    }
1346
1347    protected void setOperatorIdd(String operatorNumeric) {
1348        // Retrieve the current country information
1349        // with the MCC got from opeatorNumeric.
1350        String idd = mHbpcdUtils.getIddByMcc(
1351                Integer.parseInt(operatorNumeric.substring(0,3)));
1352        if (idd != null && !idd.isEmpty()) {
1353            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING,
1354                     idd);
1355        } else {
1356            // use default "+", since we don't know the current IDP
1357            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING, "+");
1358        }
1359    }
1360
1361    /**
1362     * Returns a TimeZone object based only on parameters from the NITZ string.
1363     */
1364    private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1365        TimeZone guess = findTimeZone(offset, dst, when);
1366        if (guess == null) {
1367            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1368            guess = findTimeZone(offset, !dst, when);
1369        }
1370        if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1371        return guess;
1372    }
1373
1374    private TimeZone findTimeZone(int offset, boolean dst, long when) {
1375        int rawOffset = offset;
1376        if (dst) {
1377            rawOffset -= MS_PER_HOUR;
1378        }
1379        String[] zones = TimeZone.getAvailableIDs(rawOffset);
1380        TimeZone guess = null;
1381        Date d = new Date(when);
1382        for (String zone : zones) {
1383            TimeZone tz = TimeZone.getTimeZone(zone);
1384            if (tz.getOffset(when) == offset &&
1385                    tz.inDaylightTime(d) == dst) {
1386                guess = tz;
1387                break;
1388            }
1389        }
1390
1391        return guess;
1392    }
1393
1394    /**
1395     * TODO: This code is exactly the same as in GsmServiceStateTracker
1396     * and has a TODO to not poll signal strength if screen is off.
1397     * This code should probably be hoisted to the base class so
1398     * the fix, when added, works for both.
1399     */
1400    private void
1401    queueNextSignalStrengthPoll() {
1402        if (mDontPollSignalStrength) {
1403            // The radio is telling us about signal strength changes
1404            // we don't have to ask it
1405            return;
1406        }
1407
1408        Message msg;
1409
1410        msg = obtainMessage();
1411        msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1412
1413        // TODO Don't poll signal strength if screen is off
1414        sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1415    }
1416
1417    protected int radioTechnologyToDataServiceState(int code) {
1418        int retVal = ServiceState.STATE_OUT_OF_SERVICE;
1419        switch(code) {
1420        case 0:
1421        case 1:
1422        case 2:
1423        case 3:
1424        case 4:
1425        case 5:
1426            break;
1427        case 6: // RADIO_TECHNOLOGY_1xRTT
1428        case 7: // RADIO_TECHNOLOGY_EVDO_0
1429        case 8: // RADIO_TECHNOLOGY_EVDO_A
1430        case 12: // RADIO_TECHNOLOGY_EVDO_B
1431        case 13: // RADIO_TECHNOLOGY_EHRPD
1432            retVal = ServiceState.STATE_IN_SERVICE;
1433            break;
1434        default:
1435            loge("radioTechnologyToDataServiceState: Wrong radioTechnology code.");
1436        break;
1437        }
1438        return(retVal);
1439    }
1440
1441    /** code is registration state 0-5 from TS 27.007 7.2 */
1442    protected int
1443    regCodeToServiceState(int code) {
1444        switch (code) {
1445        case 0: // Not searching and not registered
1446            return ServiceState.STATE_OUT_OF_SERVICE;
1447        case 1:
1448            return ServiceState.STATE_IN_SERVICE;
1449        case 2: // 2 is "searching", fall through
1450        case 3: // 3 is "registration denied", fall through
1451        case 4: // 4 is "unknown", not valid in current baseband
1452            return ServiceState.STATE_OUT_OF_SERVICE;
1453        case 5:// 5 is "Registered, roaming"
1454            return ServiceState.STATE_IN_SERVICE;
1455
1456        default:
1457            loge("regCodeToServiceState: unexpected service state " + code);
1458        return ServiceState.STATE_OUT_OF_SERVICE;
1459        }
1460    }
1461
1462    @Override
1463    public int getCurrentDataConnectionState() {
1464        return mSS.getDataRegState();
1465    }
1466
1467    /**
1468     * code is registration state 0-5 from TS 27.007 7.2
1469     * returns true if registered roam, false otherwise
1470     */
1471    protected boolean
1472    regCodeIsRoaming (int code) {
1473        // 5 is  "in service -- roam"
1474        return 5 == code;
1475    }
1476
1477    /**
1478     * Determine whether a roaming indicator is in the carrier-specified list of ERIs for
1479     * home system
1480     *
1481     * @param roamInd roaming indicator in String
1482     * @return true if the roamInd is in the carrier-specified list of ERIs for home network
1483     */
1484    private boolean isRoamIndForHomeSystem(String roamInd) {
1485        // retrieve the carrier-specified list of ERIs for home system
1486        String[] homeRoamIndicators = mPhone.getContext().getResources()
1487                .getStringArray(com.android.internal.R.array.config_cdma_home_system);
1488
1489        if (homeRoamIndicators != null) {
1490            // searches through the comma-separated list for a match,
1491            // return true if one is found.
1492            for (String homeRoamInd : homeRoamIndicators) {
1493                if (homeRoamInd.equals(roamInd)) {
1494                    return true;
1495                }
1496            }
1497            // no matches found against the list!
1498            return false;
1499        }
1500
1501        // no system property found for the roaming indicators for home system
1502        return false;
1503    }
1504
1505    /**
1506     * Set roaming state when cdmaRoaming is true and ons is different from spn
1507     * @param cdmaRoaming TS 27.007 7.2 CREG registered roaming
1508     * @param s ServiceState hold current ons
1509     * @return true for roaming state set
1510     */
1511    private
1512    boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
1513        String spn = ((TelephonyManager) mPhone.getContext().
1514                getSystemService(Context.TELEPHONY_SERVICE)).
1515                getSimOperatorNameForPhone(mPhoneBase.getPhoneId());
1516
1517        // NOTE: in case of RUIM we should completely ignore the ERI data file and
1518        // mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
1519        String onsl = s.getVoiceOperatorAlphaLong();
1520        String onss = s.getVoiceOperatorAlphaShort();
1521
1522        boolean equalsOnsl = onsl != null && spn.equals(onsl);
1523        boolean equalsOnss = onss != null && spn.equals(onss);
1524
1525        return cdmaRoaming && !(equalsOnsl || equalsOnss);
1526    }
1527
1528
1529    /**
1530     * nitzReceiveTime is time_t that the NITZ time was posted
1531     */
1532
1533    private
1534    void setTimeFromNITZString (String nitz, long nitzReceiveTime)
1535    {
1536        // "yy/mm/dd,hh:mm:ss(+/-)tz"
1537        // tz is in number of quarter-hours
1538
1539        long start = SystemClock.elapsedRealtime();
1540        if (DBG) {
1541            log("NITZ: " + nitz + "," + nitzReceiveTime +
1542                        " start=" + start + " delay=" + (start - nitzReceiveTime));
1543        }
1544
1545        try {
1546            /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
1547             * offset as well (which we won't worry about until later) */
1548            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1549
1550            c.clear();
1551            c.set(Calendar.DST_OFFSET, 0);
1552
1553            String[] nitzSubs = nitz.split("[/:,+-]");
1554
1555            int year = 2000 + Integer.parseInt(nitzSubs[0]);
1556            c.set(Calendar.YEAR, year);
1557
1558            // month is 0 based!
1559            int month = Integer.parseInt(nitzSubs[1]) - 1;
1560            c.set(Calendar.MONTH, month);
1561
1562            int date = Integer.parseInt(nitzSubs[2]);
1563            c.set(Calendar.DATE, date);
1564
1565            int hour = Integer.parseInt(nitzSubs[3]);
1566            c.set(Calendar.HOUR, hour);
1567
1568            int minute = Integer.parseInt(nitzSubs[4]);
1569            c.set(Calendar.MINUTE, minute);
1570
1571            int second = Integer.parseInt(nitzSubs[5]);
1572            c.set(Calendar.SECOND, second);
1573
1574            boolean sign = (nitz.indexOf('-') == -1);
1575
1576            int tzOffset = Integer.parseInt(nitzSubs[6]);
1577
1578            int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
1579                                              : 0;
1580
1581            // The zone offset received from NITZ is for current local time,
1582            // so DST correction is already applied.  Don't add it again.
1583            //
1584            // tzOffset += dst * 4;
1585            //
1586            // We could unapply it if we wanted the raw offset.
1587
1588            tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
1589
1590            TimeZone    zone = null;
1591
1592            // As a special extension, the Android emulator appends the name of
1593            // the host computer's timezone to the nitz string. this is zoneinfo
1594            // timezone name of the form Area!Location or Area!Location!SubLocation
1595            // so we need to convert the ! into /
1596            if (nitzSubs.length >= 9) {
1597                String  tzname = nitzSubs[8].replace('!','/');
1598                zone = TimeZone.getTimeZone( tzname );
1599            }
1600
1601            String iso = ((TelephonyManager) mPhone.getContext().
1602                    getSystemService(Context.TELEPHONY_SERVICE)).
1603                    getNetworkCountryIsoForPhone(mPhone.getPhoneId());
1604
1605            if (zone == null) {
1606                if (mGotCountryCode) {
1607                    if (iso != null && iso.length() > 0) {
1608                        zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
1609                                c.getTimeInMillis(),
1610                                iso);
1611                    } else {
1612                        // We don't have a valid iso country code.  This is
1613                        // most likely because we're on a test network that's
1614                        // using a bogus MCC (eg, "001"), so get a TimeZone
1615                        // based only on the NITZ parameters.
1616                        zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
1617                    }
1618                }
1619            }
1620
1621            if ((zone == null) || (mZoneOffset != tzOffset) || (mZoneDst != (dst != 0))){
1622                // We got the time before the country or the zone has changed
1623                // so we don't know how to identify the DST rules yet.  Save
1624                // the information and hope to fix it up later.
1625
1626                mNeedFixZone = true;
1627                mZoneOffset  = tzOffset;
1628                mZoneDst     = dst != 0;
1629                mZoneTime    = c.getTimeInMillis();
1630            }
1631            if (DBG) {
1632                log("NITZ: tzOffset=" + tzOffset + " dst=" + dst + " zone=" +
1633                        (zone!=null ? zone.getID() : "NULL") +
1634                        " iso=" + iso + " mGotCountryCode=" + mGotCountryCode +
1635                        " mNeedFixZone=" + mNeedFixZone);
1636            }
1637
1638            if (zone != null) {
1639                if (getAutoTimeZone()) {
1640                    setAndBroadcastNetworkSetTimeZone(zone.getID());
1641                }
1642                saveNitzTimeZone(zone.getID());
1643            }
1644
1645            String ignore = SystemProperties.get("gsm.ignore-nitz");
1646            if (ignore != null && ignore.equals("yes")) {
1647                if (DBG) log("NITZ: Not setting clock because gsm.ignore-nitz is set");
1648                return;
1649            }
1650
1651            try {
1652                mWakeLock.acquire();
1653
1654                /**
1655                 * Correct the NITZ time by how long its taken to get here.
1656                 */
1657                long millisSinceNitzReceived
1658                        = SystemClock.elapsedRealtime() - nitzReceiveTime;
1659
1660                if (millisSinceNitzReceived < 0) {
1661                    // Sanity check: something is wrong
1662                    if (DBG) {
1663                        log("NITZ: not setting time, clock has rolled "
1664                                        + "backwards since NITZ time was received, "
1665                                        + nitz);
1666                    }
1667                    return;
1668                }
1669
1670                if (millisSinceNitzReceived > Integer.MAX_VALUE) {
1671                    // If the time is this far off, something is wrong > 24 days!
1672                    if (DBG) {
1673                        log("NITZ: not setting time, processing has taken "
1674                                    + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
1675                                    + " days");
1676                    }
1677                    return;
1678                }
1679
1680                // Note: with range checks above, cast to int is safe
1681                c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
1682
1683                if (getAutoTime()) {
1684                    /**
1685                     * Update system time automatically
1686                     */
1687                    long gained = c.getTimeInMillis() - System.currentTimeMillis();
1688                    long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
1689                    int nitzUpdateSpacing = Settings.Global.getInt(mCr,
1690                            Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
1691                    int nitzUpdateDiff = Settings.Global.getInt(mCr,
1692                            Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
1693
1694                    if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
1695                            || (Math.abs(gained) > nitzUpdateDiff)) {
1696                        if (DBG) {
1697                            log("NITZ: Auto updating time of day to " + c.getTime()
1698                                + " NITZ receive delay=" + millisSinceNitzReceived
1699                                + "ms gained=" + gained + "ms from " + nitz);
1700                        }
1701
1702                        setAndBroadcastNetworkSetTime(c.getTimeInMillis());
1703                    } else {
1704                        if (DBG) {
1705                            log("NITZ: ignore, a previous update was "
1706                                + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
1707                        }
1708                        return;
1709                    }
1710                }
1711
1712                /**
1713                 * Update properties and save the time we did the update
1714                 */
1715                if (DBG) log("NITZ: update nitz time property");
1716                SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
1717                mSavedTime = c.getTimeInMillis();
1718                mSavedAtTime = SystemClock.elapsedRealtime();
1719            } finally {
1720                long end = SystemClock.elapsedRealtime();
1721                if (DBG) log("NITZ: end=" + end + " dur=" + (end - start));
1722                mWakeLock.release();
1723            }
1724        } catch (RuntimeException ex) {
1725            loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
1726        }
1727    }
1728
1729    private boolean getAutoTime() {
1730        try {
1731            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
1732        } catch (SettingNotFoundException snfe) {
1733            return true;
1734        }
1735    }
1736
1737    private boolean getAutoTimeZone() {
1738        try {
1739            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
1740        } catch (SettingNotFoundException snfe) {
1741            return true;
1742        }
1743    }
1744
1745    private void saveNitzTimeZone(String zoneId) {
1746        mSavedTimeZone = zoneId;
1747    }
1748
1749    /**
1750     * Set the timezone and send out a sticky broadcast so the system can
1751     * determine if the timezone was set by the carrier.
1752     *
1753     * @param zoneId timezone set by carrier
1754     */
1755    private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
1756        if (DBG) log("setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
1757        AlarmManager alarm =
1758            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1759        alarm.setTimeZone(zoneId);
1760        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
1761        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1762        intent.putExtra("time-zone", zoneId);
1763        mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1764    }
1765
1766    /**
1767     * Set the time and Send out a sticky broadcast so the system can determine
1768     * if the time was set by the carrier.
1769     *
1770     * @param time time set by network
1771     */
1772    private void setAndBroadcastNetworkSetTime(long time) {
1773        if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
1774        SystemClock.setCurrentTimeMillis(time);
1775        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
1776        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1777        intent.putExtra("time", time);
1778        mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1779    }
1780
1781    private void revertToNitzTime() {
1782        if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME, 0) == 0) {
1783            return;
1784        }
1785        if (DBG) {
1786            log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
1787        }
1788        if (mSavedTime != 0 && mSavedAtTime != 0) {
1789            setAndBroadcastNetworkSetTime(mSavedTime
1790                    + (SystemClock.elapsedRealtime() - mSavedAtTime));
1791        }
1792    }
1793
1794    private void revertToNitzTimeZone() {
1795        if (Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1796                Settings.Global.AUTO_TIME_ZONE, 0) == 0) {
1797            return;
1798        }
1799        if (DBG) log("revertToNitzTimeZone: tz='" + mSavedTimeZone);
1800        if (mSavedTimeZone != null) {
1801            setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
1802        }
1803    }
1804
1805    protected boolean isSidsAllZeros() {
1806        if (mHomeSystemId != null) {
1807            for (int i=0; i < mHomeSystemId.length; i++) {
1808                if (mHomeSystemId[i] != 0) {
1809                    return false;
1810                }
1811            }
1812        }
1813        return true;
1814    }
1815
1816    /**
1817     * Check whether a specified system ID that matches one of the home system IDs.
1818     */
1819    private boolean isHomeSid(int sid) {
1820        if (mHomeSystemId != null) {
1821            for (int i=0; i < mHomeSystemId.length; i++) {
1822                if (sid == mHomeSystemId[i]) {
1823                    return true;
1824                }
1825            }
1826        }
1827        return false;
1828    }
1829
1830    /**
1831     * @return true if phone is camping on a technology
1832     * that could support voice and data simultaneously.
1833     */
1834    @Override
1835    public boolean isConcurrentVoiceAndDataAllowed() {
1836        // Note: it needs to be confirmed which CDMA network types
1837        // can support voice and data calls concurrently.
1838        // For the time-being, the return value will be false.
1839        return false;
1840    }
1841
1842    public String getMdnNumber() {
1843        return mMdn;
1844    }
1845
1846    public String getCdmaMin() {
1847         return mMin;
1848    }
1849
1850    /** Returns null if NV is not yet ready */
1851    public String getPrlVersion() {
1852        return mPrlVersion;
1853    }
1854
1855    /**
1856     * Returns IMSI as MCC + MNC + MIN
1857     */
1858    String getImsi() {
1859        // TODO: When RUIM is enabled, IMSI will come from RUIM not build-time props.
1860        String operatorNumeric = ((TelephonyManager) mPhone.getContext().
1861                getSystemService(Context.TELEPHONY_SERVICE)).
1862                getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
1863
1864        if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
1865            return (operatorNumeric + getCdmaMin());
1866        } else {
1867            return null;
1868        }
1869    }
1870
1871    /**
1872     * Check if subscription data has been assigned to mMin
1873     *
1874     * return true if MIN info is ready; false otherwise.
1875     */
1876    public boolean isMinInfoReady() {
1877        return mIsMinInfoReady;
1878    }
1879
1880    /**
1881     * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
1882     */
1883    int getOtasp() {
1884        int provisioningState;
1885        // for ruim, min is null means require otasp.
1886        if (mIsSubscriptionFromRuim && mMin == null) {
1887            return OTASP_NEEDED;
1888        }
1889        if (mMin == null || (mMin.length() < 6)) {
1890            if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
1891            provisioningState = OTASP_UNKNOWN;
1892        } else {
1893            if ((mMin.equals(UNACTIVATED_MIN_VALUE)
1894                    || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
1895                    || SystemProperties.getBoolean("test_cdma_setup", false)) {
1896                provisioningState = OTASP_NEEDED;
1897            } else {
1898                provisioningState = OTASP_NOT_NEEDED;
1899            }
1900        }
1901        if (DBG) log("getOtasp: state=" + provisioningState);
1902        return provisioningState;
1903    }
1904
1905    @Override
1906    protected void hangupAndPowerOff() {
1907        // hang up all active voice calls
1908        mPhone.mCT.mRingingCall.hangupIfAlive();
1909        mPhone.mCT.mBackgroundCall.hangupIfAlive();
1910        mPhone.mCT.mForegroundCall.hangupIfAlive();
1911        mCi.setRadioPower(false, null);
1912    }
1913
1914    protected void parseSidNid (String sidStr, String nidStr) {
1915        if (sidStr != null) {
1916            String[] sid = sidStr.split(",");
1917            mHomeSystemId = new int[sid.length];
1918            for (int i = 0; i < sid.length; i++) {
1919                try {
1920                    mHomeSystemId[i] = Integer.parseInt(sid[i]);
1921                } catch (NumberFormatException ex) {
1922                    loge("error parsing system id: " + ex);
1923                }
1924            }
1925        }
1926        if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
1927
1928        if (nidStr != null) {
1929            String[] nid = nidStr.split(",");
1930            mHomeNetworkId = new int[nid.length];
1931            for (int i = 0; i < nid.length; i++) {
1932                try {
1933                    mHomeNetworkId[i] = Integer.parseInt(nid[i]);
1934                } catch (NumberFormatException ex) {
1935                    loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
1936                }
1937            }
1938        }
1939        if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
1940    }
1941
1942    protected void updateOtaspState() {
1943        int otaspMode = getOtasp();
1944        int oldOtaspMode = mCurrentOtaspMode;
1945        mCurrentOtaspMode = otaspMode;
1946
1947        // Notify apps subscription info is ready
1948        if (mCdmaForSubscriptionInfoReadyRegistrants != null) {
1949            if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
1950            mCdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
1951        }
1952        if (oldOtaspMode != mCurrentOtaspMode) {
1953            if (DBG) {
1954                log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
1955                    oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
1956            }
1957            mPhone.notifyOtaspChanged(mCurrentOtaspMode);
1958        }
1959    }
1960
1961    protected UiccCardApplication getUiccCardApplication() {
1962            return  mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
1963                    UiccController.APP_FAM_3GPP2);
1964    }
1965
1966    @Override
1967    protected void onUpdateIccAvailability() {
1968        if (mUiccController == null ) {
1969            return;
1970        }
1971
1972        UiccCardApplication newUiccApplication = getUiccCardApplication();
1973
1974        if (mUiccApplcation != newUiccApplication) {
1975            if (mUiccApplcation != null) {
1976                log("Removing stale icc objects.");
1977                mUiccApplcation.unregisterForReady(this);
1978                if (mIccRecords != null) {
1979                    mIccRecords.unregisterForRecordsLoaded(this);
1980                }
1981                mIccRecords = null;
1982                mUiccApplcation = null;
1983            }
1984            if (newUiccApplication != null) {
1985                log("New card found");
1986                mUiccApplcation = newUiccApplication;
1987                mIccRecords = mUiccApplcation.getIccRecords();
1988                if (mIsSubscriptionFromRuim) {
1989                    mUiccApplcation.registerForReady(this, EVENT_RUIM_READY, null);
1990                    if (mIccRecords != null) {
1991                        mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
1992                    }
1993                }
1994            }
1995        }
1996    }
1997
1998    @Override
1999    protected void log(String s) {
2000        Rlog.d(LOG_TAG, "[CdmaSST] " + s);
2001    }
2002
2003    @Override
2004    protected void loge(String s) {
2005        Rlog.e(LOG_TAG, "[CdmaSST] " + s);
2006    }
2007
2008    @Override
2009    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2010        pw.println("CdmaServiceStateTracker extends:");
2011        super.dump(fd, pw, args);
2012        pw.flush();
2013        pw.println(" mPhone=" + mPhone);
2014        pw.println(" mSS=" + mSS);
2015        pw.println(" mNewSS=" + mNewSS);
2016        pw.println(" mCellLoc=" + mCellLoc);
2017        pw.println(" mNewCellLoc=" + mNewCellLoc);
2018        pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode);
2019        pw.println(" mRoamingIndicator=" + mRoamingIndicator);
2020        pw.println(" mIsInPrl=" + mIsInPrl);
2021        pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator);
2022        pw.println(" mRegistrationState=" + mRegistrationState);
2023        pw.println(" mNeedFixZone=" + mNeedFixZone);
2024        pw.flush();
2025        pw.println(" mZoneOffset=" + mZoneOffset);
2026        pw.println(" mZoneDst=" + mZoneDst);
2027        pw.println(" mZoneTime=" + mZoneTime);
2028        pw.println(" mGotCountryCode=" + mGotCountryCode);
2029        pw.println(" mSavedTimeZone=" + mSavedTimeZone);
2030        pw.println(" mSavedTime=" + mSavedTime);
2031        pw.println(" mSavedAtTime=" + mSavedAtTime);
2032        pw.println(" mWakeLock=" + mWakeLock);
2033        pw.println(" mCurPlmn=" + mCurPlmn);
2034        pw.println(" mMdn=" + mMdn);
2035        pw.println(" mHomeSystemId=" + mHomeSystemId);
2036        pw.println(" mHomeNetworkId=" + mHomeNetworkId);
2037        pw.println(" mMin=" + mMin);
2038        pw.println(" mPrlVersion=" + mPrlVersion);
2039        pw.println(" mIsMinInfoReady=" + mIsMinInfoReady);
2040        pw.println(" mIsEriTextLoaded=" + mIsEriTextLoaded);
2041        pw.println(" mIsSubscriptionFromRuim=" + mIsSubscriptionFromRuim);
2042        pw.println(" mCdmaSSM=" + mCdmaSSM);
2043        pw.println(" mRegistrationDeniedReason=" + mRegistrationDeniedReason);
2044        pw.println(" mCurrentCarrier=" + mCurrentCarrier);
2045        pw.flush();
2046    }
2047
2048    @Override
2049    public void setImsRegistrationState(boolean registered) {
2050        log("ImsRegistrationState - registered : " + registered);
2051
2052        if (mImsRegistrationOnOff && !registered) {
2053            if (mAlarmSwitch) {
2054                mImsRegistrationOnOff = registered;
2055
2056                Context context = mPhone.getContext();
2057                AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2058                am.cancel(mRadioOffIntent);
2059                mAlarmSwitch = false;
2060
2061                sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
2062                return;
2063            }
2064        }
2065        mImsRegistrationOnOff = registered;
2066    }
2067}
2068