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