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