1 /* 2 * Copyright (C) 2006 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.gsm; 18 19 import com.android.internal.telephony.CommandException; 20 import com.android.internal.telephony.CommandsInterface; 21 import com.android.internal.telephony.DataConnectionTracker; 22 import com.android.internal.telephony.EventLogTags; 23 import com.android.internal.telephony.IccCard; 24 import com.android.internal.telephony.MccTable; 25 import com.android.internal.telephony.Phone; 26 import com.android.internal.telephony.RestrictedState; 27 import com.android.internal.telephony.RILConstants; 28 import com.android.internal.telephony.ServiceStateTracker; 29 import com.android.internal.telephony.TelephonyIntents; 30 import com.android.internal.telephony.TelephonyProperties; 31 32 import android.app.AlarmManager; 33 import android.app.Notification; 34 import android.app.NotificationManager; 35 import android.app.PendingIntent; 36 import android.content.BroadcastReceiver; 37 import android.content.ContentResolver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.res.Resources; 42 import android.database.ContentObserver; 43 import android.os.AsyncResult; 44 import android.os.Handler; 45 import android.os.Message; 46 import android.os.PowerManager; 47 import android.os.Registrant; 48 import android.os.RegistrantList; 49 import android.os.SystemClock; 50 import android.os.SystemProperties; 51 import android.provider.Settings; 52 import android.provider.Settings.SettingNotFoundException; 53 import android.provider.Telephony.Intents; 54 import android.telephony.ServiceState; 55 import android.telephony.SignalStrength; 56 import android.telephony.gsm.GsmCellLocation; 57 import android.text.TextUtils; 58 import android.util.EventLog; 59 import android.util.Log; 60 import android.util.TimeUtils; 61 62 import java.util.Arrays; 63 import java.util.Calendar; 64 import java.util.Date; 65 import java.util.TimeZone; 66 67 /** 68 * {@hide} 69 */ 70 final class GsmServiceStateTracker extends ServiceStateTracker { 71 static final String LOG_TAG = "GSM"; 72 static final boolean DBG = true; 73 74 GSMPhone phone; 75 GsmCellLocation cellLoc; 76 GsmCellLocation newCellLoc; 77 int mPreferredNetworkType; 78 79 private int gprsState = ServiceState.STATE_OUT_OF_SERVICE; 80 private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE; 81 private int mMaxDataCalls = 1; 82 private int mNewMaxDataCalls = 1; 83 private int mReasonDataDenied = -1; 84 private int mNewReasonDataDenied = -1; 85 86 /** 87 * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by 88 * handlePollStateResult to store CREG roaming result. 89 */ 90 private boolean mGsmRoaming = false; 91 92 /** 93 * Data roaming status solely based on TS 27.007 10.1.19 CGREG. Only used by 94 * handlePollStateResult to store CGREG roaming result. 95 */ 96 private boolean mDataRoaming = false; 97 98 /** 99 * Mark when service state is in emergency call only mode 100 */ 101 private boolean mEmergencyOnly = false; 102 103 /** 104 * Sometimes we get the NITZ time before we know what country we 105 * are in. Keep the time zone information from the NITZ string so 106 * we can fix the time zone once know the country. 107 */ 108 private boolean mNeedFixZone = false; 109 private int mZoneOffset; 110 private boolean mZoneDst; 111 private long mZoneTime; 112 private boolean mGotCountryCode = false; 113 private ContentResolver cr; 114 115 String mSavedTimeZone; 116 long mSavedTime; 117 long mSavedAtTime; 118 119 /** 120 * We can't register for SIM_RECORDS_LOADED immediately because the 121 * SIMRecords object may not be instantiated yet. 122 */ 123 private boolean mNeedToRegForSimLoaded; 124 125 /** Started the recheck process after finding gprs should registered but not. */ 126 private boolean mStartedGprsRegCheck = false; 127 128 /** Already sent the event-log for no gprs register. */ 129 private boolean mReportedGprsNoReg = false; 130 131 /** 132 * The Notification object given to the NotificationManager. 133 */ 134 private Notification mNotification; 135 136 /** Wake lock used while setting time of day. */ 137 private PowerManager.WakeLock mWakeLock; 138 private static final String WAKELOCK_TAG = "ServiceStateTracker"; 139 140 /** Keep track of SPN display rules, so we only broadcast intent if something changes. */ 141 private String curSpn = null; 142 private String curPlmn = null; 143 private int curSpnRule = 0; 144 145 /** waiting period before recheck gprs and voice registration. */ 146 static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; 147 148 /** Notification type. */ 149 static final int PS_ENABLED = 1001; // Access Control blocks data service 150 static final int PS_DISABLED = 1002; // Access Control enables data service 151 static final int CS_ENABLED = 1003; // Access Control blocks all voice/sms service 152 static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service 153 static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service 154 static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service 155 156 /** Notification id. */ 157 static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted 158 static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted 159 160 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 161 @Override 162 public void onReceive(Context context, Intent intent) { 163 if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) { 164 // update emergency string whenever locale changed 165 updateSpnDisplay(); 166 } 167 } 168 }; 169 170 private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { 171 @Override 172 public void onChange(boolean selfChange) { 173 Log.i("GsmServiceStateTracker", "Auto time state changed"); 174 revertToNitzTime(); 175 } 176 }; 177 178 private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) { 179 @Override 180 public void onChange(boolean selfChange) { 181 Log.i("GsmServiceStateTracker", "Auto time zone state changed"); 182 revertToNitzTimeZone(); 183 } 184 }; 185 186 public GsmServiceStateTracker(GSMPhone phone) { 187 super(); 188 189 this.phone = phone; 190 cm = phone.mCM; 191 ss = new ServiceState(); 192 newSS = new ServiceState(); 193 cellLoc = new GsmCellLocation(); 194 newCellLoc = new GsmCellLocation(); 195 mSignalStrength = new SignalStrength(); 196 197 PowerManager powerManager = 198 (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); 199 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); 200 201 cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); 202 cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 203 204 cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); 205 cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); 206 cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); 207 cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null); 208 cm.registerForSIMReady(this, EVENT_SIM_READY, null); 209 210 // system setting property AIRPLANE_MODE_ON is set in Settings. 211 int airplaneMode = Settings.System.getInt( 212 phone.getContext().getContentResolver(), 213 Settings.System.AIRPLANE_MODE_ON, 0); 214 mDesiredPowerState = ! (airplaneMode > 0); 215 216 cr = phone.getContext().getContentResolver(); 217 cr.registerContentObserver( 218 Settings.System.getUriFor(Settings.System.AUTO_TIME), true, 219 mAutoTimeObserver); 220 cr.registerContentObserver( 221 Settings.System.getUriFor(Settings.System.AUTO_TIME_ZONE), true, 222 mAutoTimeZoneObserver); 223 224 setSignalStrengthDefaultValues(); 225 mNeedToRegForSimLoaded = true; 226 227 // Monitor locale change 228 IntentFilter filter = new IntentFilter(); 229 filter.addAction(Intent.ACTION_LOCALE_CHANGED); 230 phone.getContext().registerReceiver(mIntentReceiver, filter); 231 232 // Gsm doesn't support OTASP so its not needed 233 phone.notifyOtaspChanged(OTASP_NOT_NEEDED); 234 } 235 236 public void dispose() { 237 // Unregister for all events. 238 cm.unregisterForAvailable(this); 239 cm.unregisterForRadioStateChanged(this); 240 cm.unregisterForVoiceNetworkStateChanged(this); 241 cm.unregisterForSIMReady(this); 242 243 phone.mIccRecords.unregisterForRecordsLoaded(this); 244 cm.unSetOnSignalStrengthUpdate(this); 245 cm.unSetOnRestrictedStateChanged(this); 246 cm.unSetOnNITZTime(this); 247 cr.unregisterContentObserver(this.mAutoTimeObserver); 248 cr.unregisterContentObserver(this.mAutoTimeZoneObserver); 249 } 250 251 protected void finalize() { 252 if(DBG) log("finalize"); 253 } 254 255 @Override 256 protected Phone getPhone() { 257 return phone; 258 } 259 260 public void handleMessage (Message msg) { 261 AsyncResult ar; 262 int[] ints; 263 String[] strings; 264 Message message; 265 266 switch (msg.what) { 267 case EVENT_RADIO_AVAILABLE: 268 //this is unnecessary 269 //setPowerStateToDesired(); 270 break; 271 272 case EVENT_SIM_READY: 273 // Set the network type, in case the radio does not restore it. 274 cm.setCurrentPreferredNetworkType(); 275 276 // The SIM is now ready i.e if it was locked 277 // it has been unlocked. At this stage, the radio is already 278 // powered on. 279 if (mNeedToRegForSimLoaded) { 280 phone.mIccRecords.registerForRecordsLoaded(this, 281 EVENT_SIM_RECORDS_LOADED, null); 282 mNeedToRegForSimLoaded = false; 283 } 284 285 boolean skipRestoringSelection = phone.getContext().getResources().getBoolean( 286 com.android.internal.R.bool.skip_restoring_network_selection); 287 288 if (!skipRestoringSelection) { 289 // restore the previous network selection. 290 phone.restoreSavedNetworkSelection(null); 291 } 292 pollState(); 293 // Signal strength polling stops when radio is off 294 queueNextSignalStrengthPoll(); 295 break; 296 297 case EVENT_RADIO_STATE_CHANGED: 298 // This will do nothing in the radio not 299 // available case 300 setPowerStateToDesired(); 301 pollState(); 302 break; 303 304 case EVENT_NETWORK_STATE_CHANGED: 305 pollState(); 306 break; 307 308 case EVENT_GET_SIGNAL_STRENGTH: 309 // This callback is called when signal strength is polled 310 // all by itself 311 312 if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isCdma())) { 313 // Polling will continue when radio turns back on and not CDMA 314 return; 315 } 316 ar = (AsyncResult) msg.obj; 317 onSignalStrengthResult(ar); 318 queueNextSignalStrengthPoll(); 319 320 break; 321 322 case EVENT_GET_LOC_DONE: 323 ar = (AsyncResult) msg.obj; 324 325 if (ar.exception == null) { 326 String states[] = (String[])ar.result; 327 int lac = -1; 328 int cid = -1; 329 if (states.length >= 3) { 330 try { 331 if (states[1] != null && states[1].length() > 0) { 332 lac = Integer.parseInt(states[1], 16); 333 } 334 if (states[2] != null && states[2].length() > 0) { 335 cid = Integer.parseInt(states[2], 16); 336 } 337 } catch (NumberFormatException ex) { 338 Log.w(LOG_TAG, "error parsing location: " + ex); 339 } 340 } 341 cellLoc.setLacAndCid(lac, cid); 342 phone.notifyLocationChanged(); 343 } 344 345 // Release any temporary cell lock, which could have been 346 // acquired to allow a single-shot location update. 347 disableSingleLocationUpdate(); 348 break; 349 350 case EVENT_POLL_STATE_REGISTRATION: 351 case EVENT_POLL_STATE_GPRS: 352 case EVENT_POLL_STATE_OPERATOR: 353 case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: 354 ar = (AsyncResult) msg.obj; 355 356 handlePollStateResult(msg.what, ar); 357 break; 358 359 case EVENT_POLL_SIGNAL_STRENGTH: 360 // Just poll signal strength...not part of pollState() 361 362 cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); 363 break; 364 365 case EVENT_NITZ_TIME: 366 ar = (AsyncResult) msg.obj; 367 368 String nitzString = (String)((Object[])ar.result)[0]; 369 long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); 370 371 setTimeFromNITZString(nitzString, nitzReceiveTime); 372 break; 373 374 case EVENT_SIGNAL_STRENGTH_UPDATE: 375 // This is a notification from 376 // CommandsInterface.setOnSignalStrengthUpdate 377 378 ar = (AsyncResult) msg.obj; 379 380 // The radio is telling us about signal strength changes 381 // we don't have to ask it 382 dontPollSignalStrength = true; 383 384 onSignalStrengthResult(ar); 385 break; 386 387 case EVENT_SIM_RECORDS_LOADED: 388 updateSpnDisplay(); 389 break; 390 391 case EVENT_LOCATION_UPDATES_ENABLED: 392 ar = (AsyncResult) msg.obj; 393 394 if (ar.exception == null) { 395 cm.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null)); 396 } 397 break; 398 399 case EVENT_SET_PREFERRED_NETWORK_TYPE: 400 ar = (AsyncResult) msg.obj; 401 // Don't care the result, only use for dereg network (COPS=2) 402 message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj); 403 cm.setPreferredNetworkType(mPreferredNetworkType, message); 404 break; 405 406 case EVENT_RESET_PREFERRED_NETWORK_TYPE: 407 ar = (AsyncResult) msg.obj; 408 if (ar.userObj != null) { 409 AsyncResult.forMessage(((Message) ar.userObj)).exception 410 = ar.exception; 411 ((Message) ar.userObj).sendToTarget(); 412 } 413 break; 414 415 case EVENT_GET_PREFERRED_NETWORK_TYPE: 416 ar = (AsyncResult) msg.obj; 417 418 if (ar.exception == null) { 419 mPreferredNetworkType = ((int[])ar.result)[0]; 420 } else { 421 mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL; 422 } 423 424 message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj); 425 int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL; 426 427 cm.setPreferredNetworkType(toggledNetworkType, message); 428 break; 429 430 case EVENT_CHECK_REPORT_GPRS: 431 if (ss != null && !isGprsConsistent(gprsState, ss.getState())) { 432 433 // Can't register data service while voice service is ok 434 // i.e. CREG is ok while CGREG is not 435 // possible a network or baseband side error 436 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 437 EventLog.writeEvent(EventLogTags.DATA_NETWORK_REGISTRATION_FAIL, 438 ss.getOperatorNumeric(), loc != null ? loc.getCid() : -1); 439 mReportedGprsNoReg = true; 440 } 441 mStartedGprsRegCheck = false; 442 break; 443 444 case EVENT_RESTRICTED_STATE_CHANGED: 445 // This is a notification from 446 // CommandsInterface.setOnRestrictedStateChanged 447 448 if (DBG) log("EVENT_RESTRICTED_STATE_CHANGED"); 449 450 ar = (AsyncResult) msg.obj; 451 452 onRestrictedStateChanged(ar); 453 break; 454 455 default: 456 super.handleMessage(msg); 457 break; 458 } 459 } 460 461 protected void setPowerStateToDesired() { 462 // If we want it on and it's off, turn it on 463 if (mDesiredPowerState 464 && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) { 465 cm.setRadioPower(true, null); 466 } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { 467 // If it's on and available and we want it off gracefully 468 DataConnectionTracker dcTracker = phone.mDataConnectionTracker; 469 powerOffRadioSafely(dcTracker); 470 } // Otherwise, we're in the desired state 471 } 472 473 @Override 474 protected void hangupAndPowerOff() { 475 // hang up all active voice calls 476 if (phone.isInCall()) { 477 phone.mCT.ringingCall.hangupIfAlive(); 478 phone.mCT.backgroundCall.hangupIfAlive(); 479 phone.mCT.foregroundCall.hangupIfAlive(); 480 } 481 482 cm.setRadioPower(false, null); 483 } 484 485 protected void updateSpnDisplay() { 486 int rule = phone.mIccRecords.getDisplayRule(ss.getOperatorNumeric()); 487 String spn = phone.mIccRecords.getServiceProviderName(); 488 String plmn = ss.getOperatorAlphaLong(); 489 490 // For emergency calls only, pass the EmergencyCallsOnly string via EXTRA_PLMN 491 if (mEmergencyOnly && cm.getRadioState().isOn()) { 492 plmn = Resources.getSystem(). 493 getText(com.android.internal.R.string.emergency_calls_only).toString(); 494 if (DBG) log("updateSpnDisplay: emergency only and radio is on plmn='" + plmn + "'"); 495 } 496 497 if (rule != curSpnRule 498 || !TextUtils.equals(spn, curSpn) 499 || !TextUtils.equals(plmn, curPlmn)) { 500 boolean showSpn = !mEmergencyOnly && !TextUtils.isEmpty(spn) 501 && (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; 502 boolean showPlmn = !TextUtils.isEmpty(plmn) && 503 (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; 504 505 if (DBG) { 506 log(String.format("updateSpnDisplay: changed sending intent" + " rule=" + rule + 507 " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'", 508 showPlmn, plmn, showSpn, spn)); 509 } 510 Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); 511 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 512 intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); 513 intent.putExtra(Intents.EXTRA_SPN, spn); 514 intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); 515 intent.putExtra(Intents.EXTRA_PLMN, plmn); 516 phone.getContext().sendStickyBroadcast(intent); 517 } 518 519 curSpnRule = rule; 520 curSpn = spn; 521 curPlmn = plmn; 522 } 523 524 /** 525 * Handle the result of one of the pollState()-related requests 526 */ 527 protected void handlePollStateResult (int what, AsyncResult ar) { 528 int ints[]; 529 String states[]; 530 531 // Ignore stale requests from last poll 532 if (ar.userObj != pollingContext) return; 533 534 if (ar.exception != null) { 535 CommandException.Error err=null; 536 537 if (ar.exception instanceof CommandException) { 538 err = ((CommandException)(ar.exception)).getCommandError(); 539 } 540 541 if (err == CommandException.Error.RADIO_NOT_AVAILABLE) { 542 // Radio has crashed or turned off 543 cancelPollState(); 544 return; 545 } 546 547 if (!cm.getRadioState().isOn()) { 548 // Radio has crashed or turned off 549 cancelPollState(); 550 return; 551 } 552 553 if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { 554 loge("RIL implementation has returned an error where it must succeed" + 555 ar.exception); 556 } 557 } else try { 558 switch (what) { 559 case EVENT_POLL_STATE_REGISTRATION: 560 states = (String[])ar.result; 561 int lac = -1; 562 int cid = -1; 563 int regState = -1; 564 int reasonRegStateDenied = -1; 565 int psc = -1; 566 if (states.length > 0) { 567 try { 568 regState = Integer.parseInt(states[0]); 569 if (states.length >= 3) { 570 if (states[1] != null && states[1].length() > 0) { 571 lac = Integer.parseInt(states[1], 16); 572 } 573 if (states[2] != null && states[2].length() > 0) { 574 cid = Integer.parseInt(states[2], 16); 575 } 576 } 577 if (states.length > 14) { 578 if (states[14] != null && states[14].length() > 0) { 579 psc = Integer.parseInt(states[14], 16); 580 } 581 } 582 } catch (NumberFormatException ex) { 583 loge("error parsing RegistrationState: " + ex); 584 } 585 } 586 587 mGsmRoaming = regCodeIsRoaming(regState); 588 newSS.setState (regCodeToServiceState(regState)); 589 590 if (regState == 10 || regState == 12 || regState == 13 || regState == 14) { 591 mEmergencyOnly = true; 592 } else { 593 mEmergencyOnly = false; 594 } 595 596 // LAC and CID are -1 if not avail 597 newCellLoc.setLacAndCid(lac, cid); 598 newCellLoc.setPsc(psc); 599 break; 600 601 case EVENT_POLL_STATE_GPRS: 602 states = (String[])ar.result; 603 604 int type = 0; 605 regState = -1; 606 mNewReasonDataDenied = -1; 607 mNewMaxDataCalls = 1; 608 if (states.length > 0) { 609 try { 610 regState = Integer.parseInt(states[0]); 611 612 // states[3] (if present) is the current radio technology 613 if (states.length >= 4 && states[3] != null) { 614 type = Integer.parseInt(states[3]); 615 } 616 if ((states.length >= 5 ) && (regState == 3)) { 617 mNewReasonDataDenied = Integer.parseInt(states[4]); 618 } 619 if (states.length >= 6) { 620 mNewMaxDataCalls = Integer.parseInt(states[5]); 621 } 622 } catch (NumberFormatException ex) { 623 loge("error parsing GprsRegistrationState: " + ex); 624 } 625 } 626 newGPRSState = regCodeToServiceState(regState); 627 mDataRoaming = regCodeIsRoaming(regState); 628 mNewRadioTechnology = type; 629 newSS.setRadioTechnology(type); 630 break; 631 632 case EVENT_POLL_STATE_OPERATOR: 633 String opNames[] = (String[])ar.result; 634 635 if (opNames != null && opNames.length >= 3) { 636 newSS.setOperatorName ( 637 opNames[0], opNames[1], opNames[2]); 638 } 639 break; 640 641 case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: 642 ints = (int[])ar.result; 643 newSS.setIsManualSelection(ints[0] == 1); 644 break; 645 } 646 647 } catch (RuntimeException ex) { 648 loge("Exception while polling service state. Probably malformed RIL response." + ex); 649 } 650 651 pollingContext[0]--; 652 653 if (pollingContext[0] == 0) { 654 /** 655 * Since the roaming states of gsm service (from +CREG) and 656 * data service (from +CGREG) could be different, the new SS 657 * is set roaming while either one is roaming. 658 * 659 * There is an exception for the above rule. The new SS is not set 660 * as roaming while gsm service reports roaming but indeed it is 661 * not roaming between operators. 662 */ 663 boolean roaming = (mGsmRoaming || mDataRoaming); 664 if (mGsmRoaming && !isRoamingBetweenOperators(mGsmRoaming, newSS)) { 665 roaming = false; 666 } 667 newSS.setRoaming(roaming); 668 newSS.setEmergencyOnly(mEmergencyOnly); 669 pollStateDone(); 670 } 671 } 672 673 private void setSignalStrengthDefaultValues() { 674 mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, true); 675 } 676 677 /** 678 * A complete "service state" from our perspective is 679 * composed of a handful of separate requests to the radio. 680 * 681 * We make all of these requests at once, but then abandon them 682 * and start over again if the radio notifies us that some 683 * event has changed 684 */ 685 private void pollState() { 686 pollingContext = new int[1]; 687 pollingContext[0] = 0; 688 689 switch (cm.getRadioState()) { 690 case RADIO_UNAVAILABLE: 691 newSS.setStateOutOfService(); 692 newCellLoc.setStateInvalid(); 693 setSignalStrengthDefaultValues(); 694 mGotCountryCode = false; 695 pollStateDone(); 696 break; 697 698 case RADIO_OFF: 699 newSS.setStateOff(); 700 newCellLoc.setStateInvalid(); 701 setSignalStrengthDefaultValues(); 702 mGotCountryCode = false; 703 pollStateDone(); 704 break; 705 706 case RUIM_NOT_READY: 707 case RUIM_READY: 708 case RUIM_LOCKED_OR_ABSENT: 709 case NV_NOT_READY: 710 case NV_READY: 711 if (DBG) log("Radio Technology Change ongoing, setting SS to off"); 712 newSS.setStateOff(); 713 newCellLoc.setStateInvalid(); 714 setSignalStrengthDefaultValues(); 715 mGotCountryCode = false; 716 717 //NOTE: pollStateDone() is not needed in this case 718 break; 719 720 default: 721 // Issue all poll-related commands at once 722 // then count down the responses, which 723 // are allowed to arrive out-of-order 724 725 pollingContext[0]++; 726 cm.getOperator( 727 obtainMessage( 728 EVENT_POLL_STATE_OPERATOR, pollingContext)); 729 730 pollingContext[0]++; 731 cm.getDataRegistrationState( 732 obtainMessage( 733 EVENT_POLL_STATE_GPRS, pollingContext)); 734 735 pollingContext[0]++; 736 cm.getVoiceRegistrationState( 737 obtainMessage( 738 EVENT_POLL_STATE_REGISTRATION, pollingContext)); 739 740 pollingContext[0]++; 741 cm.getNetworkSelectionMode( 742 obtainMessage( 743 EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext)); 744 break; 745 } 746 } 747 748 private void pollStateDone() { 749 if (DBG) { 750 log("Poll ServiceState done: " + 751 " oldSS=[" + ss + "] newSS=[" + newSS + 752 "] oldGprs=" + gprsState + " newData=" + newGPRSState + 753 " oldMaxDataCalls=" + mMaxDataCalls + 754 " mNewMaxDataCalls=" + mNewMaxDataCalls + 755 " oldReasonDataDenied=" + mReasonDataDenied + 756 " mNewReasonDataDenied=" + mNewReasonDataDenied + 757 " oldType=" + ServiceState.radioTechnologyToString(mRadioTechnology) + 758 " newType=" + ServiceState.radioTechnologyToString(mNewRadioTechnology)); 759 } 760 761 boolean hasRegistered = 762 ss.getState() != ServiceState.STATE_IN_SERVICE 763 && newSS.getState() == ServiceState.STATE_IN_SERVICE; 764 765 boolean hasDeregistered = 766 ss.getState() == ServiceState.STATE_IN_SERVICE 767 && newSS.getState() != ServiceState.STATE_IN_SERVICE; 768 769 boolean hasGprsAttached = 770 gprsState != ServiceState.STATE_IN_SERVICE 771 && newGPRSState == ServiceState.STATE_IN_SERVICE; 772 773 boolean hasGprsDetached = 774 gprsState == ServiceState.STATE_IN_SERVICE 775 && newGPRSState != ServiceState.STATE_IN_SERVICE; 776 777 boolean hasRadioTechnologyChanged = mRadioTechnology != mNewRadioTechnology; 778 779 boolean hasChanged = !newSS.equals(ss); 780 781 boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); 782 783 boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); 784 785 boolean hasLocationChanged = !newCellLoc.equals(cellLoc); 786 787 // Add an event log when connection state changes 788 if (ss.getState() != newSS.getState() || gprsState != newGPRSState) { 789 EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE, 790 ss.getState(), gprsState, newSS.getState(), newGPRSState); 791 } 792 793 ServiceState tss; 794 tss = ss; 795 ss = newSS; 796 newSS = tss; 797 // clean slate for next time 798 newSS.setStateOutOfService(); 799 800 GsmCellLocation tcl = cellLoc; 801 cellLoc = newCellLoc; 802 newCellLoc = tcl; 803 804 // Add an event log when network type switched 805 // TODO: we may add filtering to reduce the event logged, 806 // i.e. check preferred network setting, only switch to 2G, etc 807 if (hasRadioTechnologyChanged) { 808 int cid = -1; 809 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 810 if (loc != null) cid = loc.getCid(); 811 EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, mRadioTechnology, 812 mNewRadioTechnology); 813 if (DBG) { 814 log("RAT switched " + ServiceState.radioTechnologyToString(mRadioTechnology) + 815 " -> " + ServiceState.radioTechnologyToString(mNewRadioTechnology) + 816 " at cell " + cid); 817 } 818 } 819 820 gprsState = newGPRSState; 821 mReasonDataDenied = mNewReasonDataDenied; 822 mMaxDataCalls = mNewMaxDataCalls; 823 mRadioTechnology = mNewRadioTechnology; 824 // this new state has been applied - forget it until we get a new new state 825 mNewRadioTechnology = 0; 826 827 828 newSS.setStateOutOfService(); // clean slate for next time 829 830 if (hasRadioTechnologyChanged) { 831 phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, 832 ServiceState.radioTechnologyToString(mRadioTechnology)); 833 } 834 835 if (hasRegistered) { 836 mNetworkAttachedRegistrants.notifyRegistrants(); 837 } 838 839 if (hasChanged) { 840 String operatorNumeric; 841 842 updateSpnDisplay(); 843 844 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, 845 ss.getOperatorAlphaLong()); 846 847 operatorNumeric = ss.getOperatorNumeric(); 848 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric); 849 850 if (operatorNumeric == null) { 851 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); 852 mGotCountryCode = false; 853 } else { 854 String iso = ""; 855 try{ 856 iso = MccTable.countryCodeForMcc(Integer.parseInt( 857 operatorNumeric.substring(0,3))); 858 } catch ( NumberFormatException ex){ 859 loge("countryCodeForMcc error" + ex); 860 } catch ( StringIndexOutOfBoundsException ex) { 861 loge("countryCodeForMcc error" + ex); 862 } 863 864 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso); 865 mGotCountryCode = true; 866 867 if (mNeedFixZone) { 868 TimeZone zone = null; 869 // If the offset is (0, false) and the timezone property 870 // is set, use the timezone property rather than 871 // GMT. 872 String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); 873 if ((mZoneOffset == 0) && (mZoneDst == false) && 874 (zoneName != null) && (zoneName.length() > 0) && 875 (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) { 876 zone = TimeZone.getDefault(); 877 // For NITZ string without timezone, 878 // need adjust time to reflect default timezone setting 879 long tzOffset; 880 tzOffset = zone.getOffset(System.currentTimeMillis()); 881 if (getAutoTime()) { 882 setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); 883 } else { 884 // Adjust the saved NITZ time to account for tzOffset. 885 mSavedTime = mSavedTime - tzOffset; 886 } 887 } else if (iso.equals("")){ 888 // Country code not found. This is likely a test network. 889 // Get a TimeZone based only on the NITZ parameters (best guess). 890 zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); 891 } else { 892 zone = TimeUtils.getTimeZone(mZoneOffset, 893 mZoneDst, mZoneTime, iso); 894 } 895 896 mNeedFixZone = false; 897 898 if (zone != null) { 899 if (getAutoTimeZone()) { 900 setAndBroadcastNetworkSetTimeZone(zone.getID()); 901 } 902 saveNitzTimeZone(zone.getID()); 903 } 904 } 905 } 906 907 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, 908 ss.getRoaming() ? "true" : "false"); 909 910 phone.notifyServiceStateChanged(ss); 911 } 912 913 if (hasGprsAttached) { 914 mAttachedRegistrants.notifyRegistrants(); 915 } 916 917 if (hasGprsDetached) { 918 mDetachedRegistrants.notifyRegistrants(); 919 } 920 921 if (hasRadioTechnologyChanged) { 922 phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED); 923 } 924 925 if (hasRoamingOn) { 926 mRoamingOnRegistrants.notifyRegistrants(); 927 } 928 929 if (hasRoamingOff) { 930 mRoamingOffRegistrants.notifyRegistrants(); 931 } 932 933 if (hasLocationChanged) { 934 phone.notifyLocationChanged(); 935 } 936 937 if (! isGprsConsistent(gprsState, ss.getState())) { 938 if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { 939 mStartedGprsRegCheck = true; 940 941 int check_period = Settings.Secure.getInt( 942 phone.getContext().getContentResolver(), 943 Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS, 944 DEFAULT_GPRS_CHECK_PERIOD_MILLIS); 945 sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), 946 check_period); 947 } 948 } else { 949 mReportedGprsNoReg = false; 950 } 951 } 952 953 /** 954 * Check if GPRS got registered while voice is registered. 955 * 956 * @param gprsState for GPRS registration state, i.e. CGREG in GSM 957 * @param serviceState for voice registration state, i.e. CREG in GSM 958 * @return false if device only register to voice but not gprs 959 */ 960 private boolean isGprsConsistent(int gprsState, int serviceState) { 961 return !((serviceState == ServiceState.STATE_IN_SERVICE) && 962 (gprsState != ServiceState.STATE_IN_SERVICE)); 963 } 964 965 /** 966 * Returns a TimeZone object based only on parameters from the NITZ string. 967 */ 968 private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { 969 TimeZone guess = findTimeZone(offset, dst, when); 970 if (guess == null) { 971 // Couldn't find a proper timezone. Perhaps the DST data is wrong. 972 guess = findTimeZone(offset, !dst, when); 973 } 974 if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID())); 975 return guess; 976 } 977 978 private TimeZone findTimeZone(int offset, boolean dst, long when) { 979 int rawOffset = offset; 980 if (dst) { 981 rawOffset -= 3600000; 982 } 983 String[] zones = TimeZone.getAvailableIDs(rawOffset); 984 TimeZone guess = null; 985 Date d = new Date(when); 986 for (String zone : zones) { 987 TimeZone tz = TimeZone.getTimeZone(zone); 988 if (tz.getOffset(when) == offset && 989 tz.inDaylightTime(d) == dst) { 990 guess = tz; 991 break; 992 } 993 } 994 995 return guess; 996 } 997 998 private void queueNextSignalStrengthPoll() { 999 if (dontPollSignalStrength || (cm.getRadioState().isCdma())) { 1000 // The radio is telling us about signal strength changes 1001 // we don't have to ask it 1002 return; 1003 } 1004 1005 Message msg; 1006 1007 msg = obtainMessage(); 1008 msg.what = EVENT_POLL_SIGNAL_STRENGTH; 1009 1010 long nextTime; 1011 1012 // TODO Don't poll signal strength if screen is off 1013 sendMessageDelayed(msg, POLL_PERIOD_MILLIS); 1014 } 1015 1016 /** 1017 * Send signal-strength-changed notification if changed. 1018 * Called both for solicited and unsolicited signal strength updates. 1019 */ 1020 private void onSignalStrengthResult(AsyncResult ar) { 1021 SignalStrength oldSignalStrength = mSignalStrength; 1022 int rssi = 99; 1023 int lteSignalStrength = -1; 1024 int lteRsrp = -1; 1025 int lteRsrq = -1; 1026 int lteRssnr = -1; 1027 int lteCqi = -1; 1028 1029 if (ar.exception != null) { 1030 // -1 = unknown 1031 // most likely radio is resetting/disconnected 1032 setSignalStrengthDefaultValues(); 1033 } else { 1034 int[] ints = (int[])ar.result; 1035 1036 // bug 658816 seems to be a case where the result is 0-length 1037 if (ints.length != 0) { 1038 rssi = ints[0]; 1039 lteSignalStrength = ints[7]; 1040 lteRsrp = ints[8]; 1041 lteRsrq = ints[9]; 1042 lteRssnr = ints[10]; 1043 lteCqi = ints[11]; 1044 } else { 1045 loge("Bogus signal strength response"); 1046 rssi = 99; 1047 } 1048 } 1049 1050 mSignalStrength = new SignalStrength(rssi, -1, -1, -1, 1051 -1, -1, -1, lteSignalStrength, lteRsrp, lteRsrq, lteRssnr, lteCqi, true); 1052 1053 if (!mSignalStrength.equals(oldSignalStrength)) { 1054 try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after 1055 // POLL_PERIOD_MILLIS) during Radio Technology Change) 1056 phone.notifySignalStrength(); 1057 } catch (NullPointerException ex) { 1058 log("onSignalStrengthResult() Phone already destroyed: " + ex 1059 + "SignalStrength not notified"); 1060 } 1061 } 1062 } 1063 1064 /** 1065 * Set restricted state based on the OnRestrictedStateChanged notification 1066 * If any voice or packet restricted state changes, trigger a UI 1067 * notification and notify registrants when sim is ready. 1068 * 1069 * @param ar an int value of RIL_RESTRICTED_STATE_* 1070 */ 1071 private void onRestrictedStateChanged(AsyncResult ar) { 1072 RestrictedState newRs = new RestrictedState(); 1073 1074 if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState); 1075 1076 if (ar.exception == null) { 1077 int[] ints = (int[])ar.result; 1078 int state = ints[0]; 1079 1080 newRs.setCsEmergencyRestricted( 1081 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || 1082 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); 1083 //ignore the normal call and data restricted state before SIM READY 1084 if (phone.getIccCard().getState() == IccCard.State.READY) { 1085 newRs.setCsNormalRestricted( 1086 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || 1087 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); 1088 newRs.setPsRestricted( 1089 (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); 1090 } 1091 1092 if (DBG) log("onRestrictedStateChanged: new rs "+ newRs); 1093 1094 if (!mRestrictedState.isPsRestricted() && newRs.isPsRestricted()) { 1095 mPsRestrictEnabledRegistrants.notifyRegistrants(); 1096 setNotification(PS_ENABLED); 1097 } else if (mRestrictedState.isPsRestricted() && !newRs.isPsRestricted()) { 1098 mPsRestrictDisabledRegistrants.notifyRegistrants(); 1099 setNotification(PS_DISABLED); 1100 } 1101 1102 /** 1103 * There are two kind of cs restriction, normal and emergency. So 1104 * there are 4 x 4 combinations in current and new restricted states 1105 * and we only need to notify when state is changed. 1106 */ 1107 if (mRestrictedState.isCsRestricted()) { 1108 if (!newRs.isCsRestricted()) { 1109 // remove all restriction 1110 setNotification(CS_DISABLED); 1111 } else if (!newRs.isCsNormalRestricted()) { 1112 // remove normal restriction 1113 setNotification(CS_EMERGENCY_ENABLED); 1114 } else if (!newRs.isCsEmergencyRestricted()) { 1115 // remove emergency restriction 1116 setNotification(CS_NORMAL_ENABLED); 1117 } 1118 } else if (mRestrictedState.isCsEmergencyRestricted() && 1119 !mRestrictedState.isCsNormalRestricted()) { 1120 if (!newRs.isCsRestricted()) { 1121 // remove all restriction 1122 setNotification(CS_DISABLED); 1123 } else if (newRs.isCsRestricted()) { 1124 // enable all restriction 1125 setNotification(CS_ENABLED); 1126 } else if (newRs.isCsNormalRestricted()) { 1127 // remove emergency restriction and enable normal restriction 1128 setNotification(CS_NORMAL_ENABLED); 1129 } 1130 } else if (!mRestrictedState.isCsEmergencyRestricted() && 1131 mRestrictedState.isCsNormalRestricted()) { 1132 if (!newRs.isCsRestricted()) { 1133 // remove all restriction 1134 setNotification(CS_DISABLED); 1135 } else if (newRs.isCsRestricted()) { 1136 // enable all restriction 1137 setNotification(CS_ENABLED); 1138 } else if (newRs.isCsEmergencyRestricted()) { 1139 // remove normal restriction and enable emergency restriction 1140 setNotification(CS_EMERGENCY_ENABLED); 1141 } 1142 } else { 1143 if (newRs.isCsRestricted()) { 1144 // enable all restriction 1145 setNotification(CS_ENABLED); 1146 } else if (newRs.isCsEmergencyRestricted()) { 1147 // enable emergency restriction 1148 setNotification(CS_EMERGENCY_ENABLED); 1149 } else if (newRs.isCsNormalRestricted()) { 1150 // enable normal restriction 1151 setNotification(CS_NORMAL_ENABLED); 1152 } 1153 } 1154 1155 mRestrictedState = newRs; 1156 } 1157 log("onRestrictedStateChanged: X rs "+ mRestrictedState); 1158 } 1159 1160 /** code is registration state 0-5 from TS 27.007 7.2 */ 1161 private int regCodeToServiceState(int code) { 1162 switch (code) { 1163 case 0: 1164 case 2: // 2 is "searching" 1165 case 3: // 3 is "registration denied" 1166 case 4: // 4 is "unknown" no vaild in current baseband 1167 case 10:// same as 0, but indicates that emergency call is possible. 1168 case 12:// same as 2, but indicates that emergency call is possible. 1169 case 13:// same as 3, but indicates that emergency call is possible. 1170 case 14:// same as 4, but indicates that emergency call is possible. 1171 return ServiceState.STATE_OUT_OF_SERVICE; 1172 1173 case 1: 1174 return ServiceState.STATE_IN_SERVICE; 1175 1176 case 5: 1177 // in service, roam 1178 return ServiceState.STATE_IN_SERVICE; 1179 1180 default: 1181 loge("regCodeToServiceState: unexpected service state " + code); 1182 return ServiceState.STATE_OUT_OF_SERVICE; 1183 } 1184 } 1185 1186 1187 /** 1188 * code is registration state 0-5 from TS 27.007 7.2 1189 * returns true if registered roam, false otherwise 1190 */ 1191 private boolean regCodeIsRoaming (int code) { 1192 // 5 is "in service -- roam" 1193 return 5 == code; 1194 } 1195 1196 /** 1197 * Set roaming state when gsmRoaming is true and, if operator mcc is the 1198 * same as sim mcc, ons is different from spn 1199 * @param gsmRoaming TS 27.007 7.2 CREG registered roaming 1200 * @param s ServiceState hold current ons 1201 * @return true for roaming state set 1202 */ 1203 private boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) { 1204 String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty"); 1205 1206 String onsl = s.getOperatorAlphaLong(); 1207 String onss = s.getOperatorAlphaShort(); 1208 1209 boolean equalsOnsl = onsl != null && spn.equals(onsl); 1210 boolean equalsOnss = onss != null && spn.equals(onss); 1211 1212 String simNumeric = SystemProperties.get( 1213 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, ""); 1214 String operatorNumeric = s.getOperatorNumeric(); 1215 1216 boolean equalsMcc = true; 1217 try { 1218 equalsMcc = simNumeric.substring(0, 3). 1219 equals(operatorNumeric.substring(0, 3)); 1220 } catch (Exception e){ 1221 } 1222 1223 return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss)); 1224 } 1225 1226 private static int twoDigitsAt(String s, int offset) { 1227 int a, b; 1228 1229 a = Character.digit(s.charAt(offset), 10); 1230 b = Character.digit(s.charAt(offset+1), 10); 1231 1232 if (a < 0 || b < 0) { 1233 1234 throw new RuntimeException("invalid format"); 1235 } 1236 1237 return a*10 + b; 1238 } 1239 1240 /** 1241 * @return The current GPRS state. IN_SERVICE is the same as "attached" 1242 * and OUT_OF_SERVICE is the same as detached. 1243 */ 1244 int getCurrentGprsState() { 1245 return gprsState; 1246 } 1247 1248 public int getCurrentDataConnectionState() { 1249 return gprsState; 1250 } 1251 1252 /** 1253 * @return true if phone is camping on a technology (eg UMTS) 1254 * that could support voice and data simultaneously. 1255 */ 1256 public boolean isConcurrentVoiceAndDataAllowed() { 1257 return (mRadioTechnology >= ServiceState.RADIO_TECHNOLOGY_UMTS); 1258 } 1259 1260 /** 1261 * Provides the name of the algorithmic time zone for the specified 1262 * offset. Taken from TimeZone.java. 1263 */ 1264 private static String displayNameFor(int off) { 1265 off = off / 1000 / 60; 1266 1267 char[] buf = new char[9]; 1268 buf[0] = 'G'; 1269 buf[1] = 'M'; 1270 buf[2] = 'T'; 1271 1272 if (off < 0) { 1273 buf[3] = '-'; 1274 off = -off; 1275 } else { 1276 buf[3] = '+'; 1277 } 1278 1279 int hours = off / 60; 1280 int minutes = off % 60; 1281 1282 buf[4] = (char) ('0' + hours / 10); 1283 buf[5] = (char) ('0' + hours % 10); 1284 1285 buf[6] = ':'; 1286 1287 buf[7] = (char) ('0' + minutes / 10); 1288 buf[8] = (char) ('0' + minutes % 10); 1289 1290 return new String(buf); 1291 } 1292 1293 /** 1294 * nitzReceiveTime is time_t that the NITZ time was posted 1295 */ 1296 private void setTimeFromNITZString (String nitz, long nitzReceiveTime) { 1297 // "yy/mm/dd,hh:mm:ss(+/-)tz" 1298 // tz is in number of quarter-hours 1299 1300 long start = SystemClock.elapsedRealtime(); 1301 if (DBG) {log("NITZ: " + nitz + "," + nitzReceiveTime + 1302 " start=" + start + " delay=" + (start - nitzReceiveTime)); 1303 } 1304 1305 try { 1306 /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone 1307 * offset as well (which we won't worry about until later) */ 1308 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 1309 1310 c.clear(); 1311 c.set(Calendar.DST_OFFSET, 0); 1312 1313 String[] nitzSubs = nitz.split("[/:,+-]"); 1314 1315 int year = 2000 + Integer.parseInt(nitzSubs[0]); 1316 c.set(Calendar.YEAR, year); 1317 1318 // month is 0 based! 1319 int month = Integer.parseInt(nitzSubs[1]) - 1; 1320 c.set(Calendar.MONTH, month); 1321 1322 int date = Integer.parseInt(nitzSubs[2]); 1323 c.set(Calendar.DATE, date); 1324 1325 int hour = Integer.parseInt(nitzSubs[3]); 1326 c.set(Calendar.HOUR, hour); 1327 1328 int minute = Integer.parseInt(nitzSubs[4]); 1329 c.set(Calendar.MINUTE, minute); 1330 1331 int second = Integer.parseInt(nitzSubs[5]); 1332 c.set(Calendar.SECOND, second); 1333 1334 boolean sign = (nitz.indexOf('-') == -1); 1335 1336 int tzOffset = Integer.parseInt(nitzSubs[6]); 1337 1338 int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) 1339 : 0; 1340 1341 // The zone offset received from NITZ is for current local time, 1342 // so DST correction is already applied. Don't add it again. 1343 // 1344 // tzOffset += dst * 4; 1345 // 1346 // We could unapply it if we wanted the raw offset. 1347 1348 tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; 1349 1350 TimeZone zone = null; 1351 1352 // As a special extension, the Android emulator appends the name of 1353 // the host computer's timezone to the nitz string. this is zoneinfo 1354 // timezone name of the form Area!Location or Area!Location!SubLocation 1355 // so we need to convert the ! into / 1356 if (nitzSubs.length >= 9) { 1357 String tzname = nitzSubs[8].replace('!','/'); 1358 zone = TimeZone.getTimeZone( tzname ); 1359 } 1360 1361 String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY); 1362 1363 if (zone == null) { 1364 1365 if (mGotCountryCode) { 1366 if (iso != null && iso.length() > 0) { 1367 zone = TimeUtils.getTimeZone(tzOffset, dst != 0, 1368 c.getTimeInMillis(), 1369 iso); 1370 } else { 1371 // We don't have a valid iso country code. This is 1372 // most likely because we're on a test network that's 1373 // using a bogus MCC (eg, "001"), so get a TimeZone 1374 // based only on the NITZ parameters. 1375 zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); 1376 } 1377 } 1378 } 1379 1380 if (zone == null) { 1381 // We got the time before the country, so we don't know 1382 // how to identify the DST rules yet. Save the information 1383 // and hope to fix it up later. 1384 1385 mNeedFixZone = true; 1386 mZoneOffset = tzOffset; 1387 mZoneDst = dst != 0; 1388 mZoneTime = c.getTimeInMillis(); 1389 } 1390 1391 if (zone != null) { 1392 if (getAutoTimeZone()) { 1393 setAndBroadcastNetworkSetTimeZone(zone.getID()); 1394 } 1395 saveNitzTimeZone(zone.getID()); 1396 } 1397 1398 String ignore = SystemProperties.get("gsm.ignore-nitz"); 1399 if (ignore != null && ignore.equals("yes")) { 1400 log("NITZ: Not setting clock because gsm.ignore-nitz is set"); 1401 return; 1402 } 1403 1404 try { 1405 mWakeLock.acquire(); 1406 1407 if (getAutoTime()) { 1408 long millisSinceNitzReceived 1409 = SystemClock.elapsedRealtime() - nitzReceiveTime; 1410 1411 if (millisSinceNitzReceived < 0) { 1412 // Sanity check: something is wrong 1413 if (DBG) { 1414 log("NITZ: not setting time, clock has rolled " 1415 + "backwards since NITZ time was received, " 1416 + nitz); 1417 } 1418 return; 1419 } 1420 1421 if (millisSinceNitzReceived > Integer.MAX_VALUE) { 1422 // If the time is this far off, something is wrong > 24 days! 1423 if (DBG) { 1424 log("NITZ: not setting time, processing has taken " 1425 + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) 1426 + " days"); 1427 } 1428 return; 1429 } 1430 1431 // Note: with range checks above, cast to int is safe 1432 c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); 1433 1434 if (DBG) { 1435 log("NITZ: Setting time of day to " + c.getTime() 1436 + " NITZ receive delay(ms): " + millisSinceNitzReceived 1437 + " gained(ms): " 1438 + (c.getTimeInMillis() - System.currentTimeMillis()) 1439 + " from " + nitz); 1440 } 1441 1442 setAndBroadcastNetworkSetTime(c.getTimeInMillis()); 1443 Log.i(LOG_TAG, "NITZ: after Setting time of day"); 1444 } 1445 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); 1446 saveNitzTime(c.getTimeInMillis()); 1447 if (false) { 1448 long end = SystemClock.elapsedRealtime(); 1449 log("NITZ: end=" + end + " dur=" + (end - start)); 1450 } 1451 } finally { 1452 mWakeLock.release(); 1453 } 1454 } catch (RuntimeException ex) { 1455 loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex); 1456 } 1457 } 1458 1459 private boolean getAutoTime() { 1460 try { 1461 return Settings.System.getInt(phone.getContext().getContentResolver(), 1462 Settings.System.AUTO_TIME) > 0; 1463 } catch (SettingNotFoundException snfe) { 1464 return true; 1465 } 1466 } 1467 1468 private boolean getAutoTimeZone() { 1469 try { 1470 return Settings.System.getInt(phone.getContext().getContentResolver(), 1471 Settings.System.AUTO_TIME_ZONE) > 0; 1472 } catch (SettingNotFoundException snfe) { 1473 return true; 1474 } 1475 } 1476 1477 private void saveNitzTimeZone(String zoneId) { 1478 mSavedTimeZone = zoneId; 1479 } 1480 1481 private void saveNitzTime(long time) { 1482 mSavedTime = time; 1483 mSavedAtTime = SystemClock.elapsedRealtime(); 1484 } 1485 1486 /** 1487 * Set the timezone and send out a sticky broadcast so the system can 1488 * determine if the timezone was set by the carrier. 1489 * 1490 * @param zoneId timezone set by carrier 1491 */ 1492 private void setAndBroadcastNetworkSetTimeZone(String zoneId) { 1493 AlarmManager alarm = 1494 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1495 alarm.setTimeZone(zoneId); 1496 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); 1497 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1498 intent.putExtra("time-zone", zoneId); 1499 phone.getContext().sendStickyBroadcast(intent); 1500 } 1501 1502 /** 1503 * Set the time and Send out a sticky broadcast so the system can determine 1504 * if the time was set by the carrier. 1505 * 1506 * @param time time set by network 1507 */ 1508 private void setAndBroadcastNetworkSetTime(long time) { 1509 SystemClock.setCurrentTimeMillis(time); 1510 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); 1511 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1512 intent.putExtra("time", time); 1513 phone.getContext().sendStickyBroadcast(intent); 1514 } 1515 1516 private void revertToNitzTime() { 1517 if (Settings.System.getInt(phone.getContext().getContentResolver(), 1518 Settings.System.AUTO_TIME, 0) == 0) { 1519 return; 1520 } 1521 if (DBG) { 1522 log("Reverting to NITZ Time: mSavedTime=" + mSavedTime 1523 + " mSavedAtTime=" + mSavedAtTime); 1524 } 1525 if (mSavedTime != 0 && mSavedAtTime != 0) { 1526 setAndBroadcastNetworkSetTime(mSavedTime 1527 + (SystemClock.elapsedRealtime() - mSavedAtTime)); 1528 } 1529 } 1530 1531 private void revertToNitzTimeZone() { 1532 if (Settings.System.getInt(phone.getContext().getContentResolver(), 1533 Settings.System.AUTO_TIME_ZONE, 0) == 0) { 1534 return; 1535 } 1536 if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone); 1537 if (mSavedTimeZone != null) { 1538 setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); 1539 } 1540 } 1541 1542 /** 1543 * Post a notification to NotificationManager for restricted state 1544 * 1545 * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE 1546 */ 1547 private void setNotification(int notifyType) { 1548 1549 if (DBG) log("setNotification: create notification " + notifyType); 1550 Context context = phone.getContext(); 1551 1552 mNotification = new Notification(); 1553 mNotification.when = System.currentTimeMillis(); 1554 mNotification.flags = Notification.FLAG_AUTO_CANCEL; 1555 mNotification.icon = com.android.internal.R.drawable.stat_sys_warning; 1556 Intent intent = new Intent(); 1557 mNotification.contentIntent = PendingIntent 1558 .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 1559 1560 CharSequence details = ""; 1561 CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle); 1562 int notificationId = CS_NOTIFICATION; 1563 1564 switch (notifyType) { 1565 case PS_ENABLED: 1566 notificationId = PS_NOTIFICATION; 1567 details = context.getText(com.android.internal.R.string.RestrictedOnData);; 1568 break; 1569 case PS_DISABLED: 1570 notificationId = PS_NOTIFICATION; 1571 break; 1572 case CS_ENABLED: 1573 details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);; 1574 break; 1575 case CS_NORMAL_ENABLED: 1576 details = context.getText(com.android.internal.R.string.RestrictedOnNormal);; 1577 break; 1578 case CS_EMERGENCY_ENABLED: 1579 details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);; 1580 break; 1581 case CS_DISABLED: 1582 // do nothing and cancel the notification later 1583 break; 1584 } 1585 1586 if (DBG) log("setNotification: put notification " + title + " / " +details); 1587 mNotification.tickerText = title; 1588 mNotification.setLatestEventInfo(context, title, details, 1589 mNotification.contentIntent); 1590 1591 NotificationManager notificationManager = (NotificationManager) 1592 context.getSystemService(Context.NOTIFICATION_SERVICE); 1593 1594 if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { 1595 // cancel previous post notification 1596 notificationManager.cancel(notificationId); 1597 } else { 1598 // update restricted state notification 1599 notificationManager.notify(notificationId, mNotification); 1600 } 1601 } 1602 1603 @Override 1604 protected void log(String s) { 1605 Log.d(LOG_TAG, "[GsmSST] " + s); 1606 } 1607 1608 @Override 1609 protected void loge(String s) { 1610 Log.e(LOG_TAG, "[GsmSST] " + s); 1611 } 1612 1613 private static void sloge(String s) { 1614 Log.e(LOG_TAG, "[GsmSST] " + s); 1615 } 1616 } 1617