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