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