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