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