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