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