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