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