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 // TODO Make a constructor only has boolean gsm as parameter 675 mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, 676 -1, -1, -1, SignalStrength.INVALID_SNR, -1, true); 677 } 678 679 /** 680 * A complete "service state" from our perspective is 681 * composed of a handful of separate requests to the radio. 682 * 683 * We make all of these requests at once, but then abandon them 684 * and start over again if the radio notifies us that some 685 * event has changed 686 */ 687 private void pollState() { 688 pollingContext = new int[1]; 689 pollingContext[0] = 0; 690 691 switch (cm.getRadioState()) { 692 case RADIO_UNAVAILABLE: 693 newSS.setStateOutOfService(); 694 newCellLoc.setStateInvalid(); 695 setSignalStrengthDefaultValues(); 696 mGotCountryCode = false; 697 pollStateDone(); 698 break; 699 700 case RADIO_OFF: 701 newSS.setStateOff(); 702 newCellLoc.setStateInvalid(); 703 setSignalStrengthDefaultValues(); 704 mGotCountryCode = false; 705 pollStateDone(); 706 break; 707 708 case RUIM_NOT_READY: 709 case RUIM_READY: 710 case RUIM_LOCKED_OR_ABSENT: 711 case NV_NOT_READY: 712 case NV_READY: 713 if (DBG) log("Radio Technology Change ongoing, setting SS to off"); 714 newSS.setStateOff(); 715 newCellLoc.setStateInvalid(); 716 setSignalStrengthDefaultValues(); 717 mGotCountryCode = false; 718 719 //NOTE: pollStateDone() is not needed in this case 720 break; 721 722 default: 723 // Issue all poll-related commands at once 724 // then count down the responses, which 725 // are allowed to arrive out-of-order 726 727 pollingContext[0]++; 728 cm.getOperator( 729 obtainMessage( 730 EVENT_POLL_STATE_OPERATOR, pollingContext)); 731 732 pollingContext[0]++; 733 cm.getDataRegistrationState( 734 obtainMessage( 735 EVENT_POLL_STATE_GPRS, pollingContext)); 736 737 pollingContext[0]++; 738 cm.getVoiceRegistrationState( 739 obtainMessage( 740 EVENT_POLL_STATE_REGISTRATION, pollingContext)); 741 742 pollingContext[0]++; 743 cm.getNetworkSelectionMode( 744 obtainMessage( 745 EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext)); 746 break; 747 } 748 } 749 750 private void pollStateDone() { 751 if (DBG) { 752 log("Poll ServiceState done: " + 753 " oldSS=[" + ss + "] newSS=[" + newSS + 754 "] oldGprs=" + gprsState + " newData=" + newGPRSState + 755 " oldMaxDataCalls=" + mMaxDataCalls + 756 " mNewMaxDataCalls=" + mNewMaxDataCalls + 757 " oldReasonDataDenied=" + mReasonDataDenied + 758 " mNewReasonDataDenied=" + mNewReasonDataDenied + 759 " oldType=" + ServiceState.radioTechnologyToString(mRadioTechnology) + 760 " newType=" + ServiceState.radioTechnologyToString(mNewRadioTechnology)); 761 } 762 763 boolean hasRegistered = 764 ss.getState() != ServiceState.STATE_IN_SERVICE 765 && newSS.getState() == ServiceState.STATE_IN_SERVICE; 766 767 boolean hasDeregistered = 768 ss.getState() == ServiceState.STATE_IN_SERVICE 769 && newSS.getState() != ServiceState.STATE_IN_SERVICE; 770 771 boolean hasGprsAttached = 772 gprsState != ServiceState.STATE_IN_SERVICE 773 && newGPRSState == ServiceState.STATE_IN_SERVICE; 774 775 boolean hasGprsDetached = 776 gprsState == ServiceState.STATE_IN_SERVICE 777 && newGPRSState != ServiceState.STATE_IN_SERVICE; 778 779 boolean hasRadioTechnologyChanged = mRadioTechnology != mNewRadioTechnology; 780 781 boolean hasChanged = !newSS.equals(ss); 782 783 boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); 784 785 boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); 786 787 boolean hasLocationChanged = !newCellLoc.equals(cellLoc); 788 789 // Add an event log when connection state changes 790 if (ss.getState() != newSS.getState() || gprsState != newGPRSState) { 791 EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE, 792 ss.getState(), gprsState, newSS.getState(), newGPRSState); 793 } 794 795 ServiceState tss; 796 tss = ss; 797 ss = newSS; 798 newSS = tss; 799 // clean slate for next time 800 newSS.setStateOutOfService(); 801 802 GsmCellLocation tcl = cellLoc; 803 cellLoc = newCellLoc; 804 newCellLoc = tcl; 805 806 // Add an event log when network type switched 807 // TODO: we may add filtering to reduce the event logged, 808 // i.e. check preferred network setting, only switch to 2G, etc 809 if (hasRadioTechnologyChanged) { 810 int cid = -1; 811 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 812 if (loc != null) cid = loc.getCid(); 813 EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, mRadioTechnology, 814 mNewRadioTechnology); 815 if (DBG) { 816 log("RAT switched " + ServiceState.radioTechnologyToString(mRadioTechnology) + 817 " -> " + ServiceState.radioTechnologyToString(mNewRadioTechnology) + 818 " at cell " + cid); 819 } 820 } 821 822 gprsState = newGPRSState; 823 mReasonDataDenied = mNewReasonDataDenied; 824 mMaxDataCalls = mNewMaxDataCalls; 825 mRadioTechnology = mNewRadioTechnology; 826 // this new state has been applied - forget it until we get a new new state 827 mNewRadioTechnology = 0; 828 829 830 newSS.setStateOutOfService(); // clean slate for next time 831 832 if (hasRadioTechnologyChanged) { 833 phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, 834 ServiceState.radioTechnologyToString(mRadioTechnology)); 835 } 836 837 if (hasRegistered) { 838 mNetworkAttachedRegistrants.notifyRegistrants(); 839 } 840 841 if (hasChanged) { 842 String operatorNumeric; 843 844 updateSpnDisplay(); 845 846 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, 847 ss.getOperatorAlphaLong()); 848 849 operatorNumeric = ss.getOperatorNumeric(); 850 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric); 851 852 if (operatorNumeric == null) { 853 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); 854 mGotCountryCode = false; 855 } else { 856 String iso = ""; 857 try{ 858 iso = MccTable.countryCodeForMcc(Integer.parseInt( 859 operatorNumeric.substring(0,3))); 860 } catch ( NumberFormatException ex){ 861 loge("countryCodeForMcc error" + ex); 862 } catch ( StringIndexOutOfBoundsException ex) { 863 loge("countryCodeForMcc error" + ex); 864 } 865 866 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso); 867 mGotCountryCode = true; 868 869 if (mNeedFixZone) { 870 TimeZone zone = null; 871 // If the offset is (0, false) and the timezone property 872 // is set, use the timezone property rather than 873 // GMT. 874 String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); 875 if ((mZoneOffset == 0) && (mZoneDst == false) && 876 (zoneName != null) && (zoneName.length() > 0) && 877 (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) { 878 zone = TimeZone.getDefault(); 879 // For NITZ string without timezone, 880 // need adjust time to reflect default timezone setting 881 long tzOffset; 882 tzOffset = zone.getOffset(System.currentTimeMillis()); 883 if (getAutoTime()) { 884 setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); 885 } else { 886 // Adjust the saved NITZ time to account for tzOffset. 887 mSavedTime = mSavedTime - tzOffset; 888 } 889 } else if (iso.equals("")){ 890 // Country code not found. This is likely a test network. 891 // Get a TimeZone based only on the NITZ parameters (best guess). 892 zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); 893 } else { 894 zone = TimeUtils.getTimeZone(mZoneOffset, 895 mZoneDst, mZoneTime, iso); 896 } 897 898 mNeedFixZone = false; 899 900 if (zone != null) { 901 if (getAutoTimeZone()) { 902 setAndBroadcastNetworkSetTimeZone(zone.getID()); 903 } 904 saveNitzTimeZone(zone.getID()); 905 } 906 } 907 } 908 909 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, 910 ss.getRoaming() ? "true" : "false"); 911 912 phone.notifyServiceStateChanged(ss); 913 } 914 915 if (hasGprsAttached) { 916 mAttachedRegistrants.notifyRegistrants(); 917 } 918 919 if (hasGprsDetached) { 920 mDetachedRegistrants.notifyRegistrants(); 921 } 922 923 if (hasRadioTechnologyChanged) { 924 phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED); 925 } 926 927 if (hasRoamingOn) { 928 mRoamingOnRegistrants.notifyRegistrants(); 929 } 930 931 if (hasRoamingOff) { 932 mRoamingOffRegistrants.notifyRegistrants(); 933 } 934 935 if (hasLocationChanged) { 936 phone.notifyLocationChanged(); 937 } 938 939 if (! isGprsConsistent(gprsState, ss.getState())) { 940 if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { 941 mStartedGprsRegCheck = true; 942 943 int check_period = Settings.Secure.getInt( 944 phone.getContext().getContentResolver(), 945 Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS, 946 DEFAULT_GPRS_CHECK_PERIOD_MILLIS); 947 sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), 948 check_period); 949 } 950 } else { 951 mReportedGprsNoReg = false; 952 } 953 } 954 955 /** 956 * Check if GPRS got registered while voice is registered. 957 * 958 * @param gprsState for GPRS registration state, i.e. CGREG in GSM 959 * @param serviceState for voice registration state, i.e. CREG in GSM 960 * @return false if device only register to voice but not gprs 961 */ 962 private boolean isGprsConsistent(int gprsState, int serviceState) { 963 return !((serviceState == ServiceState.STATE_IN_SERVICE) && 964 (gprsState != ServiceState.STATE_IN_SERVICE)); 965 } 966 967 /** 968 * Returns a TimeZone object based only on parameters from the NITZ string. 969 */ 970 private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { 971 TimeZone guess = findTimeZone(offset, dst, when); 972 if (guess == null) { 973 // Couldn't find a proper timezone. Perhaps the DST data is wrong. 974 guess = findTimeZone(offset, !dst, when); 975 } 976 if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID())); 977 return guess; 978 } 979 980 private TimeZone findTimeZone(int offset, boolean dst, long when) { 981 int rawOffset = offset; 982 if (dst) { 983 rawOffset -= 3600000; 984 } 985 String[] zones = TimeZone.getAvailableIDs(rawOffset); 986 TimeZone guess = null; 987 Date d = new Date(when); 988 for (String zone : zones) { 989 TimeZone tz = TimeZone.getTimeZone(zone); 990 if (tz.getOffset(when) == offset && 991 tz.inDaylightTime(d) == dst) { 992 guess = tz; 993 break; 994 } 995 } 996 997 return guess; 998 } 999 1000 private void queueNextSignalStrengthPoll() { 1001 if (dontPollSignalStrength || (cm.getRadioState().isCdma())) { 1002 // The radio is telling us about signal strength changes 1003 // we don't have to ask it 1004 return; 1005 } 1006 1007 Message msg; 1008 1009 msg = obtainMessage(); 1010 msg.what = EVENT_POLL_SIGNAL_STRENGTH; 1011 1012 long nextTime; 1013 1014 // TODO Don't poll signal strength if screen is off 1015 sendMessageDelayed(msg, POLL_PERIOD_MILLIS); 1016 } 1017 1018 /** 1019 * Send signal-strength-changed notification if changed. 1020 * Called both for solicited and unsolicited signal strength updates. 1021 */ 1022 private void onSignalStrengthResult(AsyncResult ar) { 1023 SignalStrength oldSignalStrength = mSignalStrength; 1024 int rssi = 99; 1025 int lteSignalStrength = -1; 1026 int lteRsrp = -1; 1027 int lteRsrq = -1; 1028 int lteRssnr = SignalStrength.INVALID_SNR; 1029 int lteCqi = -1; 1030 1031 if (ar.exception != null) { 1032 // -1 = unknown 1033 // most likely radio is resetting/disconnected 1034 setSignalStrengthDefaultValues(); 1035 } else { 1036 int[] ints = (int[])ar.result; 1037 1038 // bug 658816 seems to be a case where the result is 0-length 1039 if (ints.length != 0) { 1040 rssi = ints[0]; 1041 lteSignalStrength = ints[7]; 1042 lteRsrp = ints[8]; 1043 lteRsrq = ints[9]; 1044 lteRssnr = ints[10]; 1045 lteCqi = ints[11]; 1046 } else { 1047 loge("Bogus signal strength response"); 1048 rssi = 99; 1049 } 1050 } 1051 1052 mSignalStrength = new SignalStrength(rssi, -1, -1, -1, 1053 -1, -1, -1, lteSignalStrength, lteRsrp, lteRsrq, lteRssnr, lteCqi, true); 1054 1055 if (!mSignalStrength.equals(oldSignalStrength)) { 1056 try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after 1057 // POLL_PERIOD_MILLIS) during Radio Technology Change) 1058 phone.notifySignalStrength(); 1059 } catch (NullPointerException ex) { 1060 log("onSignalStrengthResult() Phone already destroyed: " + ex 1061 + "SignalStrength not notified"); 1062 } 1063 } 1064 } 1065 1066 /** 1067 * Set restricted state based on the OnRestrictedStateChanged notification 1068 * If any voice or packet restricted state changes, trigger a UI 1069 * notification and notify registrants when sim is ready. 1070 * 1071 * @param ar an int value of RIL_RESTRICTED_STATE_* 1072 */ 1073 private void onRestrictedStateChanged(AsyncResult ar) { 1074 RestrictedState newRs = new RestrictedState(); 1075 1076 if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState); 1077 1078 if (ar.exception == null) { 1079 int[] ints = (int[])ar.result; 1080 int state = ints[0]; 1081 1082 newRs.setCsEmergencyRestricted( 1083 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || 1084 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); 1085 //ignore the normal call and data restricted state before SIM READY 1086 if (phone.getIccCard().getState() == IccCard.State.READY) { 1087 newRs.setCsNormalRestricted( 1088 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || 1089 ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); 1090 newRs.setPsRestricted( 1091 (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); 1092 } 1093 1094 if (DBG) log("onRestrictedStateChanged: new rs "+ newRs); 1095 1096 if (!mRestrictedState.isPsRestricted() && newRs.isPsRestricted()) { 1097 mPsRestrictEnabledRegistrants.notifyRegistrants(); 1098 setNotification(PS_ENABLED); 1099 } else if (mRestrictedState.isPsRestricted() && !newRs.isPsRestricted()) { 1100 mPsRestrictDisabledRegistrants.notifyRegistrants(); 1101 setNotification(PS_DISABLED); 1102 } 1103 1104 /** 1105 * There are two kind of cs restriction, normal and emergency. So 1106 * there are 4 x 4 combinations in current and new restricted states 1107 * and we only need to notify when state is changed. 1108 */ 1109 if (mRestrictedState.isCsRestricted()) { 1110 if (!newRs.isCsRestricted()) { 1111 // remove all restriction 1112 setNotification(CS_DISABLED); 1113 } else if (!newRs.isCsNormalRestricted()) { 1114 // remove normal restriction 1115 setNotification(CS_EMERGENCY_ENABLED); 1116 } else if (!newRs.isCsEmergencyRestricted()) { 1117 // remove emergency restriction 1118 setNotification(CS_NORMAL_ENABLED); 1119 } 1120 } else if (mRestrictedState.isCsEmergencyRestricted() && 1121 !mRestrictedState.isCsNormalRestricted()) { 1122 if (!newRs.isCsRestricted()) { 1123 // remove all restriction 1124 setNotification(CS_DISABLED); 1125 } else if (newRs.isCsRestricted()) { 1126 // enable all restriction 1127 setNotification(CS_ENABLED); 1128 } else if (newRs.isCsNormalRestricted()) { 1129 // remove emergency restriction and enable normal restriction 1130 setNotification(CS_NORMAL_ENABLED); 1131 } 1132 } else if (!mRestrictedState.isCsEmergencyRestricted() && 1133 mRestrictedState.isCsNormalRestricted()) { 1134 if (!newRs.isCsRestricted()) { 1135 // remove all restriction 1136 setNotification(CS_DISABLED); 1137 } else if (newRs.isCsRestricted()) { 1138 // enable all restriction 1139 setNotification(CS_ENABLED); 1140 } else if (newRs.isCsEmergencyRestricted()) { 1141 // remove normal restriction and enable emergency restriction 1142 setNotification(CS_EMERGENCY_ENABLED); 1143 } 1144 } else { 1145 if (newRs.isCsRestricted()) { 1146 // enable all restriction 1147 setNotification(CS_ENABLED); 1148 } else if (newRs.isCsEmergencyRestricted()) { 1149 // enable emergency restriction 1150 setNotification(CS_EMERGENCY_ENABLED); 1151 } else if (newRs.isCsNormalRestricted()) { 1152 // enable normal restriction 1153 setNotification(CS_NORMAL_ENABLED); 1154 } 1155 } 1156 1157 mRestrictedState = newRs; 1158 } 1159 log("onRestrictedStateChanged: X rs "+ mRestrictedState); 1160 } 1161 1162 /** code is registration state 0-5 from TS 27.007 7.2 */ 1163 private int regCodeToServiceState(int code) { 1164 switch (code) { 1165 case 0: 1166 case 2: // 2 is "searching" 1167 case 3: // 3 is "registration denied" 1168 case 4: // 4 is "unknown" no vaild in current baseband 1169 case 10:// same as 0, but indicates that emergency call is possible. 1170 case 12:// same as 2, but indicates that emergency call is possible. 1171 case 13:// same as 3, but indicates that emergency call is possible. 1172 case 14:// same as 4, but indicates that emergency call is possible. 1173 return ServiceState.STATE_OUT_OF_SERVICE; 1174 1175 case 1: 1176 return ServiceState.STATE_IN_SERVICE; 1177 1178 case 5: 1179 // in service, roam 1180 return ServiceState.STATE_IN_SERVICE; 1181 1182 default: 1183 loge("regCodeToServiceState: unexpected service state " + code); 1184 return ServiceState.STATE_OUT_OF_SERVICE; 1185 } 1186 } 1187 1188 1189 /** 1190 * code is registration state 0-5 from TS 27.007 7.2 1191 * returns true if registered roam, false otherwise 1192 */ 1193 private boolean regCodeIsRoaming (int code) { 1194 // 5 is "in service -- roam" 1195 return 5 == code; 1196 } 1197 1198 /** 1199 * Set roaming state when gsmRoaming is true and, if operator mcc is the 1200 * same as sim mcc, ons is different from spn 1201 * @param gsmRoaming TS 27.007 7.2 CREG registered roaming 1202 * @param s ServiceState hold current ons 1203 * @return true for roaming state set 1204 */ 1205 private boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) { 1206 String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty"); 1207 1208 String onsl = s.getOperatorAlphaLong(); 1209 String onss = s.getOperatorAlphaShort(); 1210 1211 boolean equalsOnsl = onsl != null && spn.equals(onsl); 1212 boolean equalsOnss = onss != null && spn.equals(onss); 1213 1214 String simNumeric = SystemProperties.get( 1215 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, ""); 1216 String operatorNumeric = s.getOperatorNumeric(); 1217 1218 boolean equalsMcc = true; 1219 try { 1220 equalsMcc = simNumeric.substring(0, 3). 1221 equals(operatorNumeric.substring(0, 3)); 1222 } catch (Exception e){ 1223 } 1224 1225 return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss)); 1226 } 1227 1228 private static int twoDigitsAt(String s, int offset) { 1229 int a, b; 1230 1231 a = Character.digit(s.charAt(offset), 10); 1232 b = Character.digit(s.charAt(offset+1), 10); 1233 1234 if (a < 0 || b < 0) { 1235 1236 throw new RuntimeException("invalid format"); 1237 } 1238 1239 return a*10 + b; 1240 } 1241 1242 /** 1243 * @return The current GPRS state. IN_SERVICE is the same as "attached" 1244 * and OUT_OF_SERVICE is the same as detached. 1245 */ 1246 int getCurrentGprsState() { 1247 return gprsState; 1248 } 1249 1250 public int getCurrentDataConnectionState() { 1251 return gprsState; 1252 } 1253 1254 /** 1255 * @return true if phone is camping on a technology (eg UMTS) 1256 * that could support voice and data simultaneously. 1257 */ 1258 public boolean isConcurrentVoiceAndDataAllowed() { 1259 return (mRadioTechnology >= ServiceState.RADIO_TECHNOLOGY_UMTS); 1260 } 1261 1262 /** 1263 * Provides the name of the algorithmic time zone for the specified 1264 * offset. Taken from TimeZone.java. 1265 */ 1266 private static String displayNameFor(int off) { 1267 off = off / 1000 / 60; 1268 1269 char[] buf = new char[9]; 1270 buf[0] = 'G'; 1271 buf[1] = 'M'; 1272 buf[2] = 'T'; 1273 1274 if (off < 0) { 1275 buf[3] = '-'; 1276 off = -off; 1277 } else { 1278 buf[3] = '+'; 1279 } 1280 1281 int hours = off / 60; 1282 int minutes = off % 60; 1283 1284 buf[4] = (char) ('0' + hours / 10); 1285 buf[5] = (char) ('0' + hours % 10); 1286 1287 buf[6] = ':'; 1288 1289 buf[7] = (char) ('0' + minutes / 10); 1290 buf[8] = (char) ('0' + minutes % 10); 1291 1292 return new String(buf); 1293 } 1294 1295 /** 1296 * nitzReceiveTime is time_t that the NITZ time was posted 1297 */ 1298 private void setTimeFromNITZString (String nitz, long nitzReceiveTime) { 1299 // "yy/mm/dd,hh:mm:ss(+/-)tz" 1300 // tz is in number of quarter-hours 1301 1302 long start = SystemClock.elapsedRealtime(); 1303 if (DBG) {log("NITZ: " + nitz + "," + nitzReceiveTime + 1304 " start=" + start + " delay=" + (start - nitzReceiveTime)); 1305 } 1306 1307 try { 1308 /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone 1309 * offset as well (which we won't worry about until later) */ 1310 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 1311 1312 c.clear(); 1313 c.set(Calendar.DST_OFFSET, 0); 1314 1315 String[] nitzSubs = nitz.split("[/:,+-]"); 1316 1317 int year = 2000 + Integer.parseInt(nitzSubs[0]); 1318 c.set(Calendar.YEAR, year); 1319 1320 // month is 0 based! 1321 int month = Integer.parseInt(nitzSubs[1]) - 1; 1322 c.set(Calendar.MONTH, month); 1323 1324 int date = Integer.parseInt(nitzSubs[2]); 1325 c.set(Calendar.DATE, date); 1326 1327 int hour = Integer.parseInt(nitzSubs[3]); 1328 c.set(Calendar.HOUR, hour); 1329 1330 int minute = Integer.parseInt(nitzSubs[4]); 1331 c.set(Calendar.MINUTE, minute); 1332 1333 int second = Integer.parseInt(nitzSubs[5]); 1334 c.set(Calendar.SECOND, second); 1335 1336 boolean sign = (nitz.indexOf('-') == -1); 1337 1338 int tzOffset = Integer.parseInt(nitzSubs[6]); 1339 1340 int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) 1341 : 0; 1342 1343 // The zone offset received from NITZ is for current local time, 1344 // so DST correction is already applied. Don't add it again. 1345 // 1346 // tzOffset += dst * 4; 1347 // 1348 // We could unapply it if we wanted the raw offset. 1349 1350 tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; 1351 1352 TimeZone zone = null; 1353 1354 // As a special extension, the Android emulator appends the name of 1355 // the host computer's timezone to the nitz string. this is zoneinfo 1356 // timezone name of the form Area!Location or Area!Location!SubLocation 1357 // so we need to convert the ! into / 1358 if (nitzSubs.length >= 9) { 1359 String tzname = nitzSubs[8].replace('!','/'); 1360 zone = TimeZone.getTimeZone( tzname ); 1361 } 1362 1363 String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY); 1364 1365 if (zone == null) { 1366 1367 if (mGotCountryCode) { 1368 if (iso != null && iso.length() > 0) { 1369 zone = TimeUtils.getTimeZone(tzOffset, dst != 0, 1370 c.getTimeInMillis(), 1371 iso); 1372 } else { 1373 // We don't have a valid iso country code. This is 1374 // most likely because we're on a test network that's 1375 // using a bogus MCC (eg, "001"), so get a TimeZone 1376 // based only on the NITZ parameters. 1377 zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); 1378 } 1379 } 1380 } 1381 1382 if (zone == null) { 1383 // We got the time before the country, so we don't know 1384 // how to identify the DST rules yet. Save the information 1385 // and hope to fix it up later. 1386 1387 mNeedFixZone = true; 1388 mZoneOffset = tzOffset; 1389 mZoneDst = dst != 0; 1390 mZoneTime = c.getTimeInMillis(); 1391 } 1392 1393 if (zone != null) { 1394 if (getAutoTimeZone()) { 1395 setAndBroadcastNetworkSetTimeZone(zone.getID()); 1396 } 1397 saveNitzTimeZone(zone.getID()); 1398 } 1399 1400 String ignore = SystemProperties.get("gsm.ignore-nitz"); 1401 if (ignore != null && ignore.equals("yes")) { 1402 log("NITZ: Not setting clock because gsm.ignore-nitz is set"); 1403 return; 1404 } 1405 1406 try { 1407 mWakeLock.acquire(); 1408 1409 if (getAutoTime()) { 1410 long millisSinceNitzReceived 1411 = SystemClock.elapsedRealtime() - nitzReceiveTime; 1412 1413 if (millisSinceNitzReceived < 0) { 1414 // Sanity check: something is wrong 1415 if (DBG) { 1416 log("NITZ: not setting time, clock has rolled " 1417 + "backwards since NITZ time was received, " 1418 + nitz); 1419 } 1420 return; 1421 } 1422 1423 if (millisSinceNitzReceived > Integer.MAX_VALUE) { 1424 // If the time is this far off, something is wrong > 24 days! 1425 if (DBG) { 1426 log("NITZ: not setting time, processing has taken " 1427 + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) 1428 + " days"); 1429 } 1430 return; 1431 } 1432 1433 // Note: with range checks above, cast to int is safe 1434 c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); 1435 1436 if (DBG) { 1437 log("NITZ: Setting time of day to " + c.getTime() 1438 + " NITZ receive delay(ms): " + millisSinceNitzReceived 1439 + " gained(ms): " 1440 + (c.getTimeInMillis() - System.currentTimeMillis()) 1441 + " from " + nitz); 1442 } 1443 1444 setAndBroadcastNetworkSetTime(c.getTimeInMillis()); 1445 Log.i(LOG_TAG, "NITZ: after Setting time of day"); 1446 } 1447 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); 1448 saveNitzTime(c.getTimeInMillis()); 1449 if (false) { 1450 long end = SystemClock.elapsedRealtime(); 1451 log("NITZ: end=" + end + " dur=" + (end - start)); 1452 } 1453 } finally { 1454 mWakeLock.release(); 1455 } 1456 } catch (RuntimeException ex) { 1457 loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex); 1458 } 1459 } 1460 1461 private boolean getAutoTime() { 1462 try { 1463 return Settings.System.getInt(phone.getContext().getContentResolver(), 1464 Settings.System.AUTO_TIME) > 0; 1465 } catch (SettingNotFoundException snfe) { 1466 return true; 1467 } 1468 } 1469 1470 private boolean getAutoTimeZone() { 1471 try { 1472 return Settings.System.getInt(phone.getContext().getContentResolver(), 1473 Settings.System.AUTO_TIME_ZONE) > 0; 1474 } catch (SettingNotFoundException snfe) { 1475 return true; 1476 } 1477 } 1478 1479 private void saveNitzTimeZone(String zoneId) { 1480 mSavedTimeZone = zoneId; 1481 } 1482 1483 private void saveNitzTime(long time) { 1484 mSavedTime = time; 1485 mSavedAtTime = SystemClock.elapsedRealtime(); 1486 } 1487 1488 /** 1489 * Set the timezone and send out a sticky broadcast so the system can 1490 * determine if the timezone was set by the carrier. 1491 * 1492 * @param zoneId timezone set by carrier 1493 */ 1494 private void setAndBroadcastNetworkSetTimeZone(String zoneId) { 1495 AlarmManager alarm = 1496 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1497 alarm.setTimeZone(zoneId); 1498 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); 1499 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1500 intent.putExtra("time-zone", zoneId); 1501 phone.getContext().sendStickyBroadcast(intent); 1502 } 1503 1504 /** 1505 * Set the time and Send out a sticky broadcast so the system can determine 1506 * if the time was set by the carrier. 1507 * 1508 * @param time time set by network 1509 */ 1510 private void setAndBroadcastNetworkSetTime(long time) { 1511 SystemClock.setCurrentTimeMillis(time); 1512 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); 1513 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1514 intent.putExtra("time", time); 1515 phone.getContext().sendStickyBroadcast(intent); 1516 } 1517 1518 private void revertToNitzTime() { 1519 if (Settings.System.getInt(phone.getContext().getContentResolver(), 1520 Settings.System.AUTO_TIME, 0) == 0) { 1521 return; 1522 } 1523 if (DBG) { 1524 log("Reverting to NITZ Time: mSavedTime=" + mSavedTime 1525 + " mSavedAtTime=" + mSavedAtTime); 1526 } 1527 if (mSavedTime != 0 && mSavedAtTime != 0) { 1528 setAndBroadcastNetworkSetTime(mSavedTime 1529 + (SystemClock.elapsedRealtime() - mSavedAtTime)); 1530 } 1531 } 1532 1533 private void revertToNitzTimeZone() { 1534 if (Settings.System.getInt(phone.getContext().getContentResolver(), 1535 Settings.System.AUTO_TIME_ZONE, 0) == 0) { 1536 return; 1537 } 1538 if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone); 1539 if (mSavedTimeZone != null) { 1540 setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); 1541 } 1542 } 1543 1544 /** 1545 * Post a notification to NotificationManager for restricted state 1546 * 1547 * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE 1548 */ 1549 private void setNotification(int notifyType) { 1550 1551 if (DBG) log("setNotification: create notification " + notifyType); 1552 Context context = phone.getContext(); 1553 1554 mNotification = new Notification(); 1555 mNotification.when = System.currentTimeMillis(); 1556 mNotification.flags = Notification.FLAG_AUTO_CANCEL; 1557 mNotification.icon = com.android.internal.R.drawable.stat_sys_warning; 1558 Intent intent = new Intent(); 1559 mNotification.contentIntent = PendingIntent 1560 .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 1561 1562 CharSequence details = ""; 1563 CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle); 1564 int notificationId = CS_NOTIFICATION; 1565 1566 switch (notifyType) { 1567 case PS_ENABLED: 1568 notificationId = PS_NOTIFICATION; 1569 details = context.getText(com.android.internal.R.string.RestrictedOnData);; 1570 break; 1571 case PS_DISABLED: 1572 notificationId = PS_NOTIFICATION; 1573 break; 1574 case CS_ENABLED: 1575 details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);; 1576 break; 1577 case CS_NORMAL_ENABLED: 1578 details = context.getText(com.android.internal.R.string.RestrictedOnNormal);; 1579 break; 1580 case CS_EMERGENCY_ENABLED: 1581 details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);; 1582 break; 1583 case CS_DISABLED: 1584 // do nothing and cancel the notification later 1585 break; 1586 } 1587 1588 if (DBG) log("setNotification: put notification " + title + " / " +details); 1589 mNotification.tickerText = title; 1590 mNotification.setLatestEventInfo(context, title, details, 1591 mNotification.contentIntent); 1592 1593 NotificationManager notificationManager = (NotificationManager) 1594 context.getSystemService(Context.NOTIFICATION_SERVICE); 1595 1596 if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { 1597 // cancel previous post notification 1598 notificationManager.cancel(notificationId); 1599 } else { 1600 // update restricted state notification 1601 notificationManager.notify(notificationId, mNotification); 1602 } 1603 } 1604 1605 @Override 1606 protected void log(String s) { 1607 Log.d(LOG_TAG, "[GsmSST] " + s); 1608 } 1609 1610 @Override 1611 protected void loge(String s) { 1612 Log.e(LOG_TAG, "[GsmSST] " + s); 1613 } 1614 1615 private static void sloge(String s) { 1616 Log.e(LOG_TAG, "[GsmSST] " + s); 1617 } 1618 } 1619