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