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