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