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