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