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