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