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