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