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