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