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