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