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