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