CdmaServiceStateTracker.java revision 049ab0421f32e6effc5d1277b69bd382cebadb18
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.IccCard;
24import com.android.internal.telephony.IccCardConstants;
25import com.android.internal.telephony.MccTable;
26import com.android.internal.telephony.PhoneConstants;
27import com.android.internal.telephony.RILConstants;
28import com.android.internal.telephony.Phone;
29import com.android.internal.telephony.ServiceStateTracker;
30import com.android.internal.telephony.TelephonyIntents;
31import com.android.internal.telephony.TelephonyProperties;
32import com.android.internal.telephony.CommandsInterface.RadioState;
33
34import android.app.AlarmManager;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.database.ContentObserver;
39import android.os.AsyncResult;
40import android.os.Handler;
41import android.os.Message;
42import android.os.PowerManager;
43import android.os.Registrant;
44import android.os.RegistrantList;
45import android.os.SystemClock;
46import android.os.SystemProperties;
47import android.provider.Settings;
48import android.provider.Settings.Secure;
49import android.provider.Settings.SettingNotFoundException;
50import android.telephony.CellInfo;
51import android.telephony.CellInfoCdma;
52import android.telephony.ServiceState;
53import android.telephony.SignalStrength;
54import android.telephony.cdma.CdmaCellLocation;
55import android.text.TextUtils;
56import android.util.EventLog;
57import android.util.Log;
58import android.util.TimeUtils;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.util.Arrays;
63import java.util.Calendar;
64import java.util.Date;
65import java.util.List;
66import java.util.TimeZone;
67
68/**
69 * {@hide}
70 */
71public class CdmaServiceStateTracker extends ServiceStateTracker {
72    static final String LOG_TAG = "CDMA";
73
74    CDMAPhone phone;
75    CdmaCellLocation cellLoc;
76    CdmaCellLocation newCellLoc;
77
78    // Min values used to by getOtasp()
79    private static final String UNACTIVATED_MIN2_VALUE = "000000";
80    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
81
82    // Current Otasp value
83    int mCurrentOtaspMode = OTASP_UNINITIALIZED;
84
85     /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
86    private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
87    private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
88            NITZ_UPDATE_SPACING_DEFAULT);
89
90    /** If mNitzUpdateSpacing hasn't been exceeded but update is > mNitzUpdate do the update */
91    private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
92    private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
93            NITZ_UPDATE_DIFF_DEFAULT);
94
95    private boolean mCdmaRoaming = false;
96    private int mRoamingIndicator;
97    private boolean mIsInPrl;
98    private int mDefaultRoamingIndicator;
99
100    /**
101     * Initially assume no data connection.
102     */
103    protected int mDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
104    protected int mNewDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
105    protected int mRegistrationState = -1;
106    protected RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
107
108    /**
109     * Sometimes we get the NITZ time before we know what country we
110     * are in. Keep the time zone information from the NITZ string so
111     * we can fix the time zone once know the country.
112     */
113    protected boolean mNeedFixZone = false;
114    private int mZoneOffset;
115    private boolean mZoneDst;
116    private long mZoneTime;
117    protected boolean mGotCountryCode = false;
118    String mSavedTimeZone;
119    long mSavedTime;
120    long mSavedAtTime;
121
122    /** Wake lock used while setting time of day. */
123    private PowerManager.WakeLock mWakeLock;
124    private static final String WAKELOCK_TAG = "ServiceStateTracker";
125
126    /** Contains the name of the registered network in CDMA (either ONS or ERI text). */
127    protected String mCurPlmn = null;
128
129    protected String mMdn;
130    protected int mHomeSystemId[] = null;
131    protected int mHomeNetworkId[] = null;
132    protected String mMin;
133    protected String mPrlVersion;
134    protected boolean mIsMinInfoReady = false;
135
136    private boolean isEriTextLoaded = false;
137    protected boolean isSubscriptionFromRuim = false;
138    private CdmaSubscriptionSourceManager mCdmaSSM;
139
140    /* Used only for debugging purposes. */
141    private String mRegistrationDeniedReason;
142
143    private ContentResolver cr;
144    private String currentCarrier = null;
145
146    private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
147        @Override
148        public void onChange(boolean selfChange) {
149            if (DBG) log("Auto time state changed");
150            revertToNitzTime();
151        }
152    };
153
154    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
155        @Override
156        public void onChange(boolean selfChange) {
157            if (DBG) log("Auto time zone state changed");
158            revertToNitzTimeZone();
159        }
160    };
161
162    public CdmaServiceStateTracker(CDMAPhone phone) {
163        this(phone, new CellInfoCdma());
164    }
165
166    protected CdmaServiceStateTracker(CDMAPhone phone, CellInfo cellInfo) {
167        super(phone, phone.mCM, cellInfo);
168
169        this.phone = phone;
170        cr = phone.getContext().getContentResolver();
171        cellLoc = new CdmaCellLocation();
172        newCellLoc = new CdmaCellLocation();
173
174        mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(phone.getContext(), cm, this,
175                EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
176        isSubscriptionFromRuim = (mCdmaSSM.getCdmaSubscriptionSource() ==
177                          CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
178
179        PowerManager powerManager =
180                (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
181        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
182
183        cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
184
185        cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
186        cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
187
188        cm.registerForCdmaPrlChanged(this, EVENT_CDMA_PRL_VERSION_CHANGED, null);
189        phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null);
190        cm.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
191
192        // System setting property AIRPLANE_MODE_ON is set in Settings.
193        int airplaneMode = Settings.System.getInt(cr, Settings.System.AIRPLANE_MODE_ON, 0);
194        mDesiredPowerState = ! (airplaneMode > 0);
195
196        cr.registerContentObserver(
197                Settings.System.getUriFor(Settings.System.AUTO_TIME), true,
198                mAutoTimeObserver);
199        cr.registerContentObserver(
200            Settings.System.getUriFor(Settings.System.AUTO_TIME_ZONE), true,
201            mAutoTimeZoneObserver);
202        setSignalStrengthDefaultValues();
203    }
204
205    @Override
206    public void dispose() {
207        checkCorrectThread();
208        // Unregister for all events.
209        cm.unregisterForRadioStateChanged(this);
210        cm.unregisterForVoiceNetworkStateChanged(this);
211        cm.unregisterForCdmaOtaProvision(this);
212        phone.unregisterForEriFileLoaded(this);
213        if (mIccCard != null) {mIccCard.unregisterForReady(this);}
214        if (mIccRecords != null) {mIccRecords.unregisterForRecordsLoaded(this);}
215        cm.unSetOnNITZTime(this);
216        cr.unregisterContentObserver(mAutoTimeObserver);
217        cr.unregisterContentObserver(mAutoTimeZoneObserver);
218        mCdmaSSM.dispose(this);
219        cm.unregisterForCdmaPrlChanged(this);
220        super.dispose();
221    }
222
223    @Override
224    protected void finalize() {
225        if (DBG) log("CdmaServiceStateTracker finalized");
226    }
227
228    /**
229     * Registration point for subscription info ready
230     * @param h handler to notify
231     * @param what what code of message when delivered
232     * @param obj placed in Message.obj
233     */
234    public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
235        Registrant r = new Registrant(h, what, obj);
236        cdmaForSubscriptionInfoReadyRegistrants.add(r);
237
238        if (isMinInfoReady()) {
239            r.notifyRegistrant();
240        }
241    }
242
243    public void unregisterForSubscriptionInfoReady(Handler h) {
244        cdmaForSubscriptionInfoReadyRegistrants.remove(h);
245    }
246
247    /**
248     * Save current source of cdma subscription
249     * @param source - 1 for NV, 0 for RUIM
250     */
251    private void saveCdmaSubscriptionSource(int source) {
252        log("Storing cdma subscription source: " + source);
253        Secure.putInt(phone.getContext().getContentResolver(),
254                Secure.CDMA_SUBSCRIPTION_MODE,
255                source );
256    }
257
258    private void getSubscriptionInfoAndStartPollingThreads() {
259        cm.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
260
261        // Get Registration Information
262        pollState();
263    }
264
265    @Override
266    public void handleMessage (Message msg) {
267        AsyncResult ar;
268        int[] ints;
269        String[] strings;
270
271        if (!phone.mIsTheCurrentActivePhone) {
272            loge("Received message " + msg + "[" + msg.what + "]" +
273                    " while being destroyed. Ignoring.");
274            return;
275        }
276
277        switch (msg.what) {
278        case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
279            handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
280            break;
281
282        case EVENT_RUIM_READY:
283            // TODO: Consider calling setCurrentPreferredNetworkType as we do in GsmSST.
284            // cm.setCurrentPreferredNetworkType();
285
286            if (phone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
287                // Subscription will be read from SIM I/O
288                if (DBG) log("Receive EVENT_RUIM_READY");
289                pollState();
290            } else {
291                if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
292                getSubscriptionInfoAndStartPollingThreads();
293            }
294            phone.prepareEri();
295            break;
296
297        case EVENT_NV_READY:
298            // For Non-RUIM phones, the subscription information is stored in
299            // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
300            // subscription info.
301            getSubscriptionInfoAndStartPollingThreads();
302            break;
303
304        case EVENT_RADIO_STATE_CHANGED:
305            if(cm.getRadioState() == RadioState.RADIO_ON) {
306                handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
307
308                // Signal strength polling stops when radio is off.
309                queueNextSignalStrengthPoll();
310            }
311            // This will do nothing in the 'radio not available' case.
312            setPowerStateToDesired();
313            pollState();
314            break;
315
316        case EVENT_NETWORK_STATE_CHANGED_CDMA:
317            pollState();
318            break;
319
320        case EVENT_GET_SIGNAL_STRENGTH:
321            // This callback is called when signal strength is polled
322            // all by itself.
323
324            if (!(cm.getRadioState().isOn())) {
325                // Polling will continue when radio turns back on.
326                return;
327            }
328            ar = (AsyncResult) msg.obj;
329            onSignalStrengthResult(ar);
330            queueNextSignalStrengthPoll();
331
332            break;
333
334        case EVENT_GET_LOC_DONE_CDMA:
335            ar = (AsyncResult) msg.obj;
336
337            if (ar.exception == null) {
338                String states[] = (String[])ar.result;
339                int baseStationId = -1;
340                int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
341                int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
342                int systemId = -1;
343                int networkId = -1;
344
345                if (states.length > 9) {
346                    try {
347                        if (states[4] != null) {
348                            baseStationId = Integer.parseInt(states[4]);
349                        }
350                        if (states[5] != null) {
351                            baseStationLatitude = Integer.parseInt(states[5]);
352                        }
353                        if (states[6] != null) {
354                            baseStationLongitude = Integer.parseInt(states[6]);
355                        }
356                        // Some carriers only return lat-lngs of 0,0
357                        if (baseStationLatitude == 0 && baseStationLongitude == 0) {
358                            baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
359                            baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
360                        }
361                        if (states[8] != null) {
362                            systemId = Integer.parseInt(states[8]);
363                        }
364                        if (states[9] != null) {
365                            networkId = Integer.parseInt(states[9]);
366                        }
367                    } catch (NumberFormatException ex) {
368                        loge("error parsing cell location data: " + ex);
369                    }
370                }
371
372                cellLoc.setCellLocationData(baseStationId, baseStationLatitude,
373                        baseStationLongitude, systemId, networkId);
374                phone.notifyLocationChanged();
375            }
376
377            // Release any temporary cell lock, which could have been
378            // acquired to allow a single-shot location update.
379            disableSingleLocationUpdate();
380            break;
381
382        case EVENT_POLL_STATE_REGISTRATION_CDMA:
383        case EVENT_POLL_STATE_OPERATOR_CDMA:
384            ar = (AsyncResult) msg.obj;
385            handlePollStateResult(msg.what, ar);
386            break;
387
388        case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
389            ar = (AsyncResult) msg.obj;
390
391            if (ar.exception == null) {
392                String cdmaSubscription[] = (String[])ar.result;
393                if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
394                    mMdn = cdmaSubscription[0];
395                    parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
396
397                    mMin = cdmaSubscription[3];
398                    mPrlVersion = cdmaSubscription[4];
399                    if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
400
401                    mIsMinInfoReady = true;
402
403                    updateOtaspState();
404                    if (mIccCard != null) {
405                        if (DBG) {
406                            log("GET_CDMA_SUBSCRIPTION broadcast Icc state changed");
407                        }
408                        mIccCard.broadcastIccStateChangedIntent(
409                                IccCardConstants.INTENT_VALUE_ICC_IMSI,
410                                null);
411                    } else {
412                        if (DBG) {
413                            log("GET_CDMA_SUBSCRIPTION mIccCard is null (probably NV type device)" +
414                                    " can't broadcast Icc state changed");
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);
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    @Override
815    protected boolean isGsmSignalStrength() {
816        return 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            newCellLoc.setStateInvalid();
836            setSignalStrengthDefaultValues();
837            mGotCountryCode = false;
838
839            pollStateDone();
840            break;
841
842        case RADIO_OFF:
843            newSS.setStateOff();
844            newCellLoc.setStateInvalid();
845            setSignalStrengthDefaultValues();
846            mGotCountryCode = false;
847
848            pollStateDone();
849            break;
850
851        default:
852            // Issue all poll-related commands at once, then count
853            // down the responses which are allowed to arrive
854            // out-of-order.
855
856            pollingContext[0]++;
857            // RIL_REQUEST_OPERATOR is necessary for CDMA
858            cm.getOperator(
859                    obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext));
860
861            pollingContext[0]++;
862            // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
863            cm.getVoiceRegistrationState(
864                    obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext));
865
866            break;
867        }
868    }
869
870    protected void fixTimeZone(String isoCountryCode) {
871        TimeZone zone = null;
872        // If the offset is (0, false) and the time zone property
873        // is set, use the time zone property rather than GMT.
874        String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
875        if (DBG) {
876            log("fixTimeZone zoneName='" + zoneName +
877                "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
878                " iso-cc='" + isoCountryCode +
879                "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode));
880        }
881        if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null)
882                && (zoneName.length() > 0)
883                && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) {
884            // For NITZ string without time zone,
885            // need adjust time to reflect default time zone setting
886            zone = TimeZone.getDefault();
887            if (mNeedFixZone) {
888                long ctm = System.currentTimeMillis();
889                long tzOffset = zone.getOffset(ctm);
890                if (DBG) {
891                    log("fixTimeZone: tzOffset=" + tzOffset +
892                            " ltod=" + TimeUtils.logTimeOfDay(ctm));
893                }
894                if (getAutoTime()) {
895                    long adj = ctm - tzOffset;
896                    if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
897                    setAndBroadcastNetworkSetTime(adj);
898                } else {
899                    // Adjust the saved NITZ time to account for tzOffset.
900                    mSavedTime = mSavedTime - tzOffset;
901                    if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
902                }
903            }
904            if (DBG) log("fixTimeZone: using default TimeZone");
905        } else if (isoCountryCode.equals("")) {
906            // Country code not found. This is likely a test network.
907            // Get a TimeZone based only on the NITZ parameters (best guess).
908            zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
909            if (DBG) log("fixTimeZone: using NITZ TimeZone");
910        } else {
911            zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode);
912            if (DBG) log("fixTimeZone: using getTimeZone(off, dst, time, iso)");
913        }
914
915        mNeedFixZone = false;
916
917        if (zone != null) {
918            log("fixTimeZone: zone != null zone.getID=" + zone.getID());
919            if (getAutoTimeZone()) {
920                setAndBroadcastNetworkSetTimeZone(zone.getID());
921            } else {
922                log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
923            }
924            saveNitzTimeZone(zone.getID());
925        } else {
926            log("fixTimeZone: zone == null, do nothing for zone");
927        }
928    }
929
930    protected void pollStateDone() {
931        if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
932
933        boolean hasRegistered =
934            ss.getState() != ServiceState.STATE_IN_SERVICE
935            && newSS.getState() == ServiceState.STATE_IN_SERVICE;
936
937        boolean hasDeregistered =
938            ss.getState() == ServiceState.STATE_IN_SERVICE
939            && newSS.getState() != ServiceState.STATE_IN_SERVICE;
940
941        boolean hasCdmaDataConnectionAttached =
942            mDataConnectionState != ServiceState.STATE_IN_SERVICE
943            && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
944
945        boolean hasCdmaDataConnectionDetached =
946            mDataConnectionState == ServiceState.STATE_IN_SERVICE
947            && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
948
949        boolean hasCdmaDataConnectionChanged =
950                       mDataConnectionState != mNewDataConnectionState;
951
952        boolean hasRadioTechnologyChanged = mRilRadioTechnology != mNewRilRadioTechnology;
953
954        boolean hasChanged = !newSS.equals(ss);
955
956        boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
957
958        boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
959
960        boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
961
962        // Add an event log when connection state changes
963        if (ss.getState() != newSS.getState() ||
964                mDataConnectionState != mNewDataConnectionState) {
965            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
966                    ss.getState(), mDataConnectionState,
967                    newSS.getState(), mNewDataConnectionState);
968        }
969
970        ServiceState tss;
971        tss = ss;
972        ss = newSS;
973        newSS = tss;
974        // clean slate for next time
975        newSS.setStateOutOfService();
976
977        CdmaCellLocation tcl = cellLoc;
978        cellLoc = newCellLoc;
979        newCellLoc = tcl;
980
981        mDataConnectionState = mNewDataConnectionState;
982        mRilRadioTechnology = mNewRilRadioTechnology;
983        // this new state has been applied - forget it until we get a new new state
984        mNewRilRadioTechnology = 0;
985
986        newSS.setStateOutOfService(); // clean slate for next time
987
988        if (hasRadioTechnologyChanged) {
989            phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
990                    ServiceState.rilRadioTechnologyToString(mRilRadioTechnology));
991        }
992
993        if (hasRegistered) {
994            mNetworkAttachedRegistrants.notifyRegistrants();
995        }
996
997        if (hasChanged) {
998            if ((cm.getRadioState().isOn()) && (!isSubscriptionFromRuim)) {
999                String eriText;
1000                // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text
1001                if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
1002                    eriText = phone.getCdmaEriText();
1003                } else {
1004                    // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for
1005                    // mRegistrationState 0,2,3 and 4
1006                    eriText = phone.getContext().getText(
1007                            com.android.internal.R.string.roamingTextSearching).toString();
1008                }
1009                ss.setOperatorAlphaLong(eriText);
1010            }
1011
1012            String operatorNumeric;
1013
1014            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
1015                    ss.getOperatorAlphaLong());
1016
1017            String prevOperatorNumeric =
1018                    SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
1019            operatorNumeric = ss.getOperatorNumeric();
1020            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
1021
1022            if (operatorNumeric == null) {
1023                if (DBG) log("operatorNumeric is null");
1024                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
1025                mGotCountryCode = false;
1026            } else {
1027                String isoCountryCode = "";
1028                String mcc = operatorNumeric.substring(0, 3);
1029                try{
1030                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
1031                            operatorNumeric.substring(0,3)));
1032                } catch ( NumberFormatException ex){
1033                    loge("pollStateDone: countryCodeForMcc error" + ex);
1034                } catch ( StringIndexOutOfBoundsException ex) {
1035                    loge("pollStateDone: countryCodeForMcc error" + ex);
1036                }
1037
1038                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
1039                        isoCountryCode);
1040                mGotCountryCode = true;
1041
1042                if (shouldFixTimeZoneNow(phone, operatorNumeric, prevOperatorNumeric,
1043                        mNeedFixZone)) {
1044                    fixTimeZone(isoCountryCode);
1045                }
1046            }
1047
1048            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
1049                    ss.getRoaming() ? "true" : "false");
1050
1051            updateSpnDisplay();
1052            phone.notifyServiceStateChanged(ss);
1053        }
1054
1055        if (hasCdmaDataConnectionAttached) {
1056            mAttachedRegistrants.notifyRegistrants();
1057        }
1058
1059        if (hasCdmaDataConnectionDetached) {
1060            mDetachedRegistrants.notifyRegistrants();
1061        }
1062
1063        if (hasCdmaDataConnectionChanged || hasRadioTechnologyChanged) {
1064            phone.notifyDataConnection(null);
1065        }
1066
1067        if (hasRoamingOn) {
1068            mRoamingOnRegistrants.notifyRegistrants();
1069        }
1070
1071        if (hasRoamingOff) {
1072            mRoamingOffRegistrants.notifyRegistrants();
1073        }
1074
1075        if (hasLocationChanged) {
1076            phone.notifyLocationChanged();
1077        }
1078        // TODO: Add CdmaCellIdenity updating, see CdmaLteServiceStateTracker.
1079    }
1080
1081    /**
1082     * Returns a TimeZone object based only on parameters from the NITZ string.
1083     */
1084    private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1085        TimeZone guess = findTimeZone(offset, dst, when);
1086        if (guess == null) {
1087            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1088            guess = findTimeZone(offset, !dst, when);
1089        }
1090        if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1091        return guess;
1092    }
1093
1094    private TimeZone findTimeZone(int offset, boolean dst, long when) {
1095        int rawOffset = offset;
1096        if (dst) {
1097            rawOffset -= 3600000;
1098        }
1099        String[] zones = TimeZone.getAvailableIDs(rawOffset);
1100        TimeZone guess = null;
1101        Date d = new Date(when);
1102        for (String zone : zones) {
1103            TimeZone tz = TimeZone.getTimeZone(zone);
1104            if (tz.getOffset(when) == offset &&
1105                    tz.inDaylightTime(d) == dst) {
1106                guess = tz;
1107                break;
1108            }
1109        }
1110
1111        return guess;
1112    }
1113
1114    /**
1115     * TODO: This code is exactly the same as in GsmServiceStateTracker
1116     * and has a TODO to not poll signal strength if screen is off.
1117     * This code should probably be hoisted to the base class so
1118     * the fix, when added, works for both.
1119     */
1120    private void
1121    queueNextSignalStrengthPoll() {
1122        if (dontPollSignalStrength) {
1123            // The radio is telling us about signal strength changes
1124            // we don't have to ask it
1125            return;
1126        }
1127
1128        Message msg;
1129
1130        msg = obtainMessage();
1131        msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1132
1133        // TODO Don't poll signal strength if screen is off
1134        sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1135    }
1136
1137    /**
1138     *  send signal-strength-changed notification if changed
1139     *  Called both for solicited and unsolicited signal strength updates
1140     */
1141    protected void onSignalStrengthResult(AsyncResult ar) {
1142        if (ar.exception != null) {
1143            // Most likely radio is resetting/disconnected change to default values.
1144            setSignalStrengthDefaultValues();
1145        } else {
1146            int[] ints = (int[])ar.result;
1147            int offset = 2;
1148            int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
1149            int cdmaEcio = (ints[offset+1] > 0) ? -ints[offset+1] : -160;
1150            int evdoRssi = (ints[offset+2] > 0) ? -ints[offset+2] : -120;
1151            int evdoEcio = (ints[offset+3] > 0) ? -ints[offset+3] : -1;
1152            int evdoSnr  = ((ints[offset+4] > 0) && (ints[offset+4] <= 8)) ? ints[offset+4] : -1;
1153
1154            synchronized(mCellInfo) {
1155                mSignalStrength.initialize(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr,
1156                        false);
1157            }
1158        }
1159
1160        notifySignalStrength();
1161    }
1162
1163
1164    protected int radioTechnologyToDataServiceState(int code) {
1165        int retVal = ServiceState.STATE_OUT_OF_SERVICE;
1166        switch(code) {
1167        case 0:
1168        case 1:
1169        case 2:
1170        case 3:
1171        case 4:
1172        case 5:
1173            break;
1174        case 6: // RADIO_TECHNOLOGY_1xRTT
1175        case 7: // RADIO_TECHNOLOGY_EVDO_0
1176        case 8: // RADIO_TECHNOLOGY_EVDO_A
1177        case 12: // RADIO_TECHNOLOGY_EVDO_B
1178        case 13: // RADIO_TECHNOLOGY_EHRPD
1179            retVal = ServiceState.STATE_IN_SERVICE;
1180            break;
1181        default:
1182            loge("radioTechnologyToDataServiceState: Wrong radioTechnology code.");
1183        break;
1184        }
1185        return(retVal);
1186    }
1187
1188    /** code is registration state 0-5 from TS 27.007 7.2 */
1189    protected int
1190    regCodeToServiceState(int code) {
1191        switch (code) {
1192        case 0: // Not searching and not registered
1193            return ServiceState.STATE_OUT_OF_SERVICE;
1194        case 1:
1195            return ServiceState.STATE_IN_SERVICE;
1196        case 2: // 2 is "searching", fall through
1197        case 3: // 3 is "registration denied", fall through
1198        case 4: // 4 is "unknown", not valid in current baseband
1199            return ServiceState.STATE_OUT_OF_SERVICE;
1200        case 5:// 5 is "Registered, roaming"
1201            return ServiceState.STATE_IN_SERVICE;
1202
1203        default:
1204            loge("regCodeToServiceState: unexpected service state " + code);
1205        return ServiceState.STATE_OUT_OF_SERVICE;
1206        }
1207    }
1208
1209    public int getCurrentDataConnectionState() {
1210        return mDataConnectionState;
1211    }
1212
1213    /**
1214     * code is registration state 0-5 from TS 27.007 7.2
1215     * returns true if registered roam, false otherwise
1216     */
1217    private boolean
1218    regCodeIsRoaming (int code) {
1219        // 5 is  "in service -- roam"
1220        return 5 == code;
1221    }
1222
1223    /**
1224     * Determine whether a roaming indicator is in the carrier-specified list of ERIs for
1225     * home system
1226     *
1227     * @param roamInd roaming indicator in String
1228     * @return true if the roamInd is in the carrier-specified list of ERIs for home network
1229     */
1230    private boolean isRoamIndForHomeSystem(String roamInd) {
1231        // retrieve the carrier-specified list of ERIs for home system
1232        String homeRoamIndicators = SystemProperties.get("ro.cdma.homesystem");
1233
1234        if (!TextUtils.isEmpty(homeRoamIndicators)) {
1235            // searches through the comma-separated list for a match,
1236            // return true if one is found.
1237            for (String homeRoamInd : homeRoamIndicators.split(",")) {
1238                if (homeRoamInd.equals(roamInd)) {
1239                    return true;
1240                }
1241            }
1242            // no matches found against the list!
1243            return false;
1244        }
1245
1246        // no system property found for the roaming indicators for home system
1247        return false;
1248    }
1249
1250    /**
1251     * Set roaming state when cdmaRoaming is true and ons is different from spn
1252     * @param cdmaRoaming TS 27.007 7.2 CREG registered roaming
1253     * @param s ServiceState hold current ons
1254     * @return true for roaming state set
1255     */
1256    private
1257    boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
1258        String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
1259
1260        // NOTE: in case of RUIM we should completely ignore the ERI data file and
1261        // mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
1262        String onsl = s.getOperatorAlphaLong();
1263        String onss = s.getOperatorAlphaShort();
1264
1265        boolean equalsOnsl = onsl != null && spn.equals(onsl);
1266        boolean equalsOnss = onss != null && spn.equals(onss);
1267
1268        return cdmaRoaming && !(equalsOnsl || equalsOnss);
1269    }
1270
1271
1272    /**
1273     * nitzReceiveTime is time_t that the NITZ time was posted
1274     */
1275
1276    private
1277    void setTimeFromNITZString (String nitz, long nitzReceiveTime)
1278    {
1279        // "yy/mm/dd,hh:mm:ss(+/-)tz"
1280        // tz is in number of quarter-hours
1281
1282        long start = SystemClock.elapsedRealtime();
1283        if (DBG) {
1284            log("NITZ: " + nitz + "," + nitzReceiveTime +
1285                        " start=" + start + " delay=" + (start - nitzReceiveTime));
1286        }
1287
1288        try {
1289            /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
1290             * offset as well (which we won't worry about until later) */
1291            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1292
1293            c.clear();
1294            c.set(Calendar.DST_OFFSET, 0);
1295
1296            String[] nitzSubs = nitz.split("[/:,+-]");
1297
1298            int year = 2000 + Integer.parseInt(nitzSubs[0]);
1299            c.set(Calendar.YEAR, year);
1300
1301            // month is 0 based!
1302            int month = Integer.parseInt(nitzSubs[1]) - 1;
1303            c.set(Calendar.MONTH, month);
1304
1305            int date = Integer.parseInt(nitzSubs[2]);
1306            c.set(Calendar.DATE, date);
1307
1308            int hour = Integer.parseInt(nitzSubs[3]);
1309            c.set(Calendar.HOUR, hour);
1310
1311            int minute = Integer.parseInt(nitzSubs[4]);
1312            c.set(Calendar.MINUTE, minute);
1313
1314            int second = Integer.parseInt(nitzSubs[5]);
1315            c.set(Calendar.SECOND, second);
1316
1317            boolean sign = (nitz.indexOf('-') == -1);
1318
1319            int tzOffset = Integer.parseInt(nitzSubs[6]);
1320
1321            int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
1322                                              : 0;
1323
1324            // The zone offset received from NITZ is for current local time,
1325            // so DST correction is already applied.  Don't add it again.
1326            //
1327            // tzOffset += dst * 4;
1328            //
1329            // We could unapply it if we wanted the raw offset.
1330
1331            tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
1332
1333            TimeZone    zone = null;
1334
1335            // As a special extension, the Android emulator appends the name of
1336            // the host computer's timezone to the nitz string. this is zoneinfo
1337            // timezone name of the form Area!Location or Area!Location!SubLocation
1338            // so we need to convert the ! into /
1339            if (nitzSubs.length >= 9) {
1340                String  tzname = nitzSubs[8].replace('!','/');
1341                zone = TimeZone.getTimeZone( tzname );
1342            }
1343
1344            String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
1345
1346            if (zone == null) {
1347                if (mGotCountryCode) {
1348                    if (iso != null && iso.length() > 0) {
1349                        zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
1350                                c.getTimeInMillis(),
1351                                iso);
1352                    } else {
1353                        // We don't have a valid iso country code.  This is
1354                        // most likely because we're on a test network that's
1355                        // using a bogus MCC (eg, "001"), so get a TimeZone
1356                        // based only on the NITZ parameters.
1357                        zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
1358                    }
1359                }
1360            }
1361
1362            if ((zone == null) || (mZoneOffset != tzOffset) || (mZoneDst != (dst != 0))){
1363                // We got the time before the country or the zone has changed
1364                // so we don't know how to identify the DST rules yet.  Save
1365                // the information and hope to fix it up later.
1366
1367                mNeedFixZone = true;
1368                mZoneOffset  = tzOffset;
1369                mZoneDst     = dst != 0;
1370                mZoneTime    = c.getTimeInMillis();
1371            }
1372            if (DBG) {
1373                log("NITZ: tzOffset=" + tzOffset + " dst=" + dst + " zone=" +
1374                        (zone!=null ? zone.getID() : "NULL") +
1375                        " iso=" + iso + " mGotCountryCode=" + mGotCountryCode +
1376                        " mNeedFixZone=" + mNeedFixZone);
1377            }
1378
1379            if (zone != null) {
1380                if (getAutoTimeZone()) {
1381                    setAndBroadcastNetworkSetTimeZone(zone.getID());
1382                }
1383                saveNitzTimeZone(zone.getID());
1384            }
1385
1386            String ignore = SystemProperties.get("gsm.ignore-nitz");
1387            if (ignore != null && ignore.equals("yes")) {
1388                if (DBG) log("NITZ: Not setting clock because gsm.ignore-nitz is set");
1389                return;
1390            }
1391
1392            try {
1393                mWakeLock.acquire();
1394
1395                /**
1396                 * Correct the NITZ time by how long its taken to get here.
1397                 */
1398                long millisSinceNitzReceived
1399                        = SystemClock.elapsedRealtime() - nitzReceiveTime;
1400
1401                if (millisSinceNitzReceived < 0) {
1402                    // Sanity check: something is wrong
1403                    if (DBG) {
1404                        log("NITZ: not setting time, clock has rolled "
1405                                        + "backwards since NITZ time was received, "
1406                                        + nitz);
1407                    }
1408                    return;
1409                }
1410
1411                if (millisSinceNitzReceived > Integer.MAX_VALUE) {
1412                    // If the time is this far off, something is wrong > 24 days!
1413                    if (DBG) {
1414                        log("NITZ: not setting time, processing has taken "
1415                                    + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
1416                                    + " days");
1417                    }
1418                    return;
1419                }
1420
1421                // Note: with range checks above, cast to int is safe
1422                c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
1423
1424                if (getAutoTime()) {
1425                    /**
1426                     * Update system time automatically
1427                     */
1428                    long gained = c.getTimeInMillis() - System.currentTimeMillis();
1429                    long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
1430                    int nitzUpdateSpacing = Settings.Secure.getInt(cr,
1431                            Settings.Secure.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
1432                    int nitzUpdateDiff = Settings.Secure.getInt(cr,
1433                            Settings.Secure.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
1434
1435                    if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
1436                            || (Math.abs(gained) > nitzUpdateDiff)) {
1437                        if (DBG) {
1438                            log("NITZ: Auto updating time of day to " + c.getTime()
1439                                + " NITZ receive delay=" + millisSinceNitzReceived
1440                                + "ms gained=" + gained + "ms from " + nitz);
1441                        }
1442
1443                        setAndBroadcastNetworkSetTime(c.getTimeInMillis());
1444                    } else {
1445                        if (DBG) {
1446                            log("NITZ: ignore, a previous update was "
1447                                + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
1448                        }
1449                        return;
1450                    }
1451                }
1452
1453                /**
1454                 * Update properties and save the time we did the update
1455                 */
1456                if (DBG) log("NITZ: update nitz time property");
1457                SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
1458                mSavedTime = c.getTimeInMillis();
1459                mSavedAtTime = SystemClock.elapsedRealtime();
1460            } finally {
1461                long end = SystemClock.elapsedRealtime();
1462                if (DBG) log("NITZ: end=" + end + " dur=" + (end - start));
1463                mWakeLock.release();
1464            }
1465        } catch (RuntimeException ex) {
1466            loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
1467        }
1468    }
1469
1470    private boolean getAutoTime() {
1471        try {
1472            return Settings.System.getInt(cr, Settings.System.AUTO_TIME) > 0;
1473        } catch (SettingNotFoundException snfe) {
1474            return true;
1475        }
1476    }
1477
1478    private boolean getAutoTimeZone() {
1479        try {
1480            return Settings.System.getInt(cr, Settings.System.AUTO_TIME_ZONE) > 0;
1481        } catch (SettingNotFoundException snfe) {
1482            return true;
1483        }
1484    }
1485
1486    private void saveNitzTimeZone(String zoneId) {
1487        mSavedTimeZone = zoneId;
1488    }
1489
1490    /**
1491     * Set the timezone and send out a sticky broadcast so the system can
1492     * determine if the timezone was set by the carrier.
1493     *
1494     * @param zoneId timezone set by carrier
1495     */
1496    private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
1497        if (DBG) log("setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
1498        AlarmManager alarm =
1499            (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
1500        alarm.setTimeZone(zoneId);
1501        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
1502        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1503        intent.putExtra("time-zone", zoneId);
1504        phone.getContext().sendStickyBroadcast(intent);
1505    }
1506
1507    /**
1508     * Set the time and Send out a sticky broadcast so the system can determine
1509     * if the time was set by the carrier.
1510     *
1511     * @param time time set by network
1512     */
1513    private void setAndBroadcastNetworkSetTime(long time) {
1514        if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
1515        SystemClock.setCurrentTimeMillis(time);
1516        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
1517        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1518        intent.putExtra("time", time);
1519        phone.getContext().sendStickyBroadcast(intent);
1520    }
1521
1522    private void revertToNitzTime() {
1523        if (Settings.System.getInt(cr, Settings.System.AUTO_TIME, 0) == 0) {
1524            return;
1525        }
1526        if (DBG) {
1527            log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
1528        }
1529        if (mSavedTime != 0 && mSavedAtTime != 0) {
1530            setAndBroadcastNetworkSetTime(mSavedTime
1531                    + (SystemClock.elapsedRealtime() - mSavedAtTime));
1532        }
1533    }
1534
1535    private void revertToNitzTimeZone() {
1536        if (Settings.System.getInt(phone.getContext().getContentResolver(),
1537                Settings.System.AUTO_TIME_ZONE, 0) == 0) {
1538            return;
1539        }
1540        if (DBG) log("revertToNitzTimeZone: tz='" + mSavedTimeZone);
1541        if (mSavedTimeZone != null) {
1542            setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
1543        }
1544    }
1545
1546    protected boolean isSidsAllZeros() {
1547        if (mHomeSystemId != null) {
1548            for (int i=0; i < mHomeSystemId.length; i++) {
1549                if (mHomeSystemId[i] != 0) {
1550                    return false;
1551                }
1552            }
1553        }
1554        return true;
1555    }
1556
1557    /**
1558     * Check whether a specified system ID that matches one of the home system IDs.
1559     */
1560    private boolean isHomeSid(int sid) {
1561        if (mHomeSystemId != null) {
1562            for (int i=0; i < mHomeSystemId.length; i++) {
1563                if (sid == mHomeSystemId[i]) {
1564                    return true;
1565                }
1566            }
1567        }
1568        return false;
1569    }
1570
1571    /**
1572     * @return true if phone is camping on a technology
1573     * that could support voice and data simultaneously.
1574     */
1575    public boolean isConcurrentVoiceAndDataAllowed() {
1576        // Note: it needs to be confirmed which CDMA network types
1577        // can support voice and data calls concurrently.
1578        // For the time-being, the return value will be false.
1579        return false;
1580    }
1581
1582    public String getMdnNumber() {
1583        return mMdn;
1584    }
1585
1586    public String getCdmaMin() {
1587         return mMin;
1588    }
1589
1590    /** Returns null if NV is not yet ready */
1591    public String getPrlVersion() {
1592        return mPrlVersion;
1593    }
1594
1595    /**
1596     * Returns IMSI as MCC + MNC + MIN
1597     */
1598    String getImsi() {
1599        // TODO: When RUIM is enabled, IMSI will come from RUIM not build-time props.
1600        String operatorNumeric = SystemProperties.get(
1601                TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
1602
1603        if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
1604            return (operatorNumeric + getCdmaMin());
1605        } else {
1606            return null;
1607        }
1608    }
1609
1610    /**
1611     * Check if subscription data has been assigned to mMin
1612     *
1613     * return true if MIN info is ready; false otherwise.
1614     */
1615    public boolean isMinInfoReady() {
1616        return mIsMinInfoReady;
1617    }
1618
1619    /**
1620     * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
1621     */
1622    int getOtasp() {
1623        int provisioningState;
1624        if (mMin == null || (mMin.length() < 6)) {
1625            if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
1626            provisioningState = OTASP_UNKNOWN;
1627        } else {
1628            if ((mMin.equals(UNACTIVATED_MIN_VALUE)
1629                    || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
1630                    || SystemProperties.getBoolean("test_cdma_setup", false)) {
1631                provisioningState = OTASP_NEEDED;
1632            } else {
1633                provisioningState = OTASP_NOT_NEEDED;
1634            }
1635        }
1636        if (DBG) log("getOtasp: state=" + provisioningState);
1637        return provisioningState;
1638    }
1639
1640    @Override
1641    protected void hangupAndPowerOff() {
1642        // hang up all active voice calls
1643        phone.mCT.ringingCall.hangupIfAlive();
1644        phone.mCT.backgroundCall.hangupIfAlive();
1645        phone.mCT.foregroundCall.hangupIfAlive();
1646        cm.setRadioPower(false, null);
1647    }
1648
1649    protected void parseSidNid (String sidStr, String nidStr) {
1650        if (sidStr != null) {
1651            String[] sid = sidStr.split(",");
1652            mHomeSystemId = new int[sid.length];
1653            for (int i = 0; i < sid.length; i++) {
1654                try {
1655                    mHomeSystemId[i] = Integer.parseInt(sid[i]);
1656                } catch (NumberFormatException ex) {
1657                    loge("error parsing system id: " + ex);
1658                }
1659            }
1660        }
1661        if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
1662
1663        if (nidStr != null) {
1664            String[] nid = nidStr.split(",");
1665            mHomeNetworkId = new int[nid.length];
1666            for (int i = 0; i < nid.length; i++) {
1667                try {
1668                    mHomeNetworkId[i] = Integer.parseInt(nid[i]);
1669                } catch (NumberFormatException ex) {
1670                    loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
1671                }
1672            }
1673        }
1674        if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
1675    }
1676
1677    protected void updateOtaspState() {
1678        int otaspMode = getOtasp();
1679        int oldOtaspMode = mCurrentOtaspMode;
1680        mCurrentOtaspMode = otaspMode;
1681
1682        // Notify apps subscription info is ready
1683        if (cdmaForSubscriptionInfoReadyRegistrants != null) {
1684            if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
1685            cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
1686        }
1687        if (oldOtaspMode != mCurrentOtaspMode) {
1688            if (DBG) {
1689                log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
1690                    oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
1691            }
1692            phone.notifyOtaspChanged(mCurrentOtaspMode);
1693        }
1694    }
1695
1696    @Override
1697    protected void onUpdateIccAvailability() {
1698        if (mUiccController == null ) {
1699            return;
1700        }
1701
1702        IccCard newIccCard = mUiccController.getIccCard();
1703
1704        if (mIccCard != newIccCard) {
1705            if (mIccCard != null) {
1706                log("Removing stale icc objects.");
1707                mIccCard.unregisterForReady(this);
1708                if (mIccRecords != null) {
1709                    mIccRecords.unregisterForRecordsLoaded(this);
1710                }
1711                mIccRecords = null;
1712                mIccCard = null;
1713            }
1714            if (newIccCard != null) {
1715                log("New card found");
1716                mIccCard = newIccCard;
1717                mIccRecords = mIccCard.getIccRecords();
1718                if (isSubscriptionFromRuim) {
1719                    mIccCard.registerForReady(this, EVENT_RUIM_READY, null);
1720                    if (mIccRecords != null) {
1721                        mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
1722                    }
1723                }
1724            }
1725        }
1726    }
1727
1728    /**
1729     * @return all available cell information or null if none.
1730     */
1731    @Override
1732    public List<CellInfo> getAllCellInfo() {
1733        return null;
1734    }
1735
1736    @Override
1737    protected void log(String s) {
1738        Log.d(LOG_TAG, "[CdmaSST] " + s);
1739    }
1740
1741    @Override
1742    protected void loge(String s) {
1743        Log.e(LOG_TAG, "[CdmaSST] " + s);
1744    }
1745
1746    @Override
1747    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1748        pw.println("CdmaServiceStateTracker extends:");
1749        super.dump(fd, pw, args);
1750        pw.println(" phone=" + phone);
1751        pw.println(" cellLoc=" + cellLoc);
1752        pw.println(" newCellLoc=" + newCellLoc);
1753        pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode);
1754        pw.println(" mCdmaRoaming=" + mCdmaRoaming);
1755        pw.println(" mRoamingIndicator=" + mRoamingIndicator);
1756        pw.println(" mIsInPrl=" + mIsInPrl);
1757        pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator);
1758        pw.println(" mDataConnectionState=" + mDataConnectionState);
1759        pw.println(" mNewDataConnectionState=" + mNewDataConnectionState);
1760        pw.println(" mRegistrationState=" + mRegistrationState);
1761        pw.println(" mNeedFixZone=" + mNeedFixZone);
1762        pw.println(" mZoneOffset=" + mZoneOffset);
1763        pw.println(" mZoneDst=" + mZoneDst);
1764        pw.println(" mZoneTime=" + mZoneTime);
1765        pw.println(" mGotCountryCode=" + mGotCountryCode);
1766        pw.println(" mSavedTimeZone=" + mSavedTimeZone);
1767        pw.println(" mSavedTime=" + mSavedTime);
1768        pw.println(" mSavedAtTime=" + mSavedAtTime);
1769        pw.println(" mWakeLock=" + mWakeLock);
1770        pw.println(" mCurPlmn=" + mCurPlmn);
1771        pw.println(" mMdn=" + mMdn);
1772        pw.println(" mHomeSystemId=" + mHomeSystemId);
1773        pw.println(" mHomeNetworkId=" + mHomeNetworkId);
1774        pw.println(" mMin=" + mMin);
1775        pw.println(" mPrlVersion=" + mPrlVersion);
1776        pw.println(" mIsMinInfoReady=" + mIsMinInfoReady);
1777        pw.println(" isEriTextLoaded=" + isEriTextLoaded);
1778        pw.println(" isSubscriptionFromRuim=" + isSubscriptionFromRuim);
1779        pw.println(" mCdmaSSM=" + mCdmaSSM);
1780        pw.println(" mRegistrationDeniedReason=" + mRegistrationDeniedReason);
1781        pw.println(" currentCarrier=" + currentCarrier);
1782    }
1783}
1784