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