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