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