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