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