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