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