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