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