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