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