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