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