| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony.gsm; |
| |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_SIM_OPERATOR_ALPHA; |
| import static com.android.internal.telephony.TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC; |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.internal.telephony.Phone; |
| import android.app.AlarmManager; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.ContentObserver; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.provider.Checkin; |
| import android.provider.Settings; |
| import android.provider.Settings.SettingNotFoundException; |
| import android.provider.Telephony.Intents; |
| import android.telephony.gsm.GsmCellLocation; |
| import android.telephony.ServiceState; |
| import android.text.TextUtils; |
| import android.util.Config; |
| import android.util.Log; |
| import android.util.TimeUtils; |
| import android.util.EventLog; |
| |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.TimeZone; |
| |
| /** |
| * {@hide} |
| */ |
| final class ServiceStateTracker extends Handler |
| { |
| /** |
| * The access technology currently in use: |
| * 0 = unknown |
| * 1 = GPRS only |
| * 2 = EDGE |
| * 3 = UMTS |
| */ |
| static final int DATA_ACCESS_UNKNOWN = 0; |
| static final int DATA_ACCESS_GPRS = 1; |
| static final int DATA_ACCESS_EDGE = 2; |
| static final int DATA_ACCESS_UMTS = 3; |
| |
| //***** Instance Variables |
| |
| GSMPhone phone; |
| CommandsInterface cm; |
| |
| ServiceState ss; |
| ServiceState newSS; |
| GsmCellLocation cellLoc; |
| GsmCellLocation newCellLoc; |
| int mPreferredNetworkType; |
| |
| int rssi = 99; // signal strength 0-31, 99=unknown |
| // That's "received signal strength indication" fyi |
| |
| int[] pollingContext; // Used as a unique identifier to |
| // track requests associated with a poll |
| // and ignore stale responses. |
| // The value is a count-down of expected responses |
| // in this pollingContext |
| |
| boolean mDesiredPowerState; |
| |
| boolean dontPollSignalStrength = false; // Default is to poll strength |
| // If we're getting unsolicited signal strength updates from the radio, |
| // set value to true and don't bother polling any more |
| |
| private int gprsState = ServiceState.STATE_OUT_OF_SERVICE; |
| private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE; |
| |
| /** |
| * The access technology currently in use: DATA_ACCESS_ |
| */ |
| private int networkType = 0; |
| private int newNetworkType = 0; |
| /* gsm roaming status solely based on TS 27.007 7.2 CREG */ |
| private boolean mGsmRoaming = false; |
| |
| private RegistrantList networkAttachedRegistrants = new RegistrantList(); |
| private RegistrantList gprsAttachedRegistrants = new RegistrantList(); |
| private RegistrantList gprsDetachedRegistrants = new RegistrantList(); |
| private RegistrantList roamingOnRegistrants = new RegistrantList(); |
| private RegistrantList roamingOffRegistrants = new RegistrantList(); |
| |
| // Sometimes we get the NITZ time before we know what country we are in. |
| // Keep the time zone information from the NITZ string so we can fix |
| // the time zone once know the country. |
| private boolean mNeedFixZone = false; |
| private int mZoneOffset; |
| private boolean mZoneDst; |
| private long mZoneTime; |
| private boolean mGotCountryCode = false; |
| |
| String mSavedTimeZone; |
| long mSavedTime; |
| long mSavedAtTime; |
| |
| // We can't register for SIM_RECORDS_LOADED immediately because the |
| // SIMRecords object may not be instantiated yet. |
| private boolean mNeedToRegForSimLoaded; |
| |
| // Started the recheck process after finding gprs should registerd but not |
| private boolean mStartedGprsRegCheck = false; |
| // Already sent the event-log for no gprs register |
| private boolean mReportedGprsNoReg = false; |
| |
| |
| // Keep track of SPN display rules, so we only broadcast intent if something changes. |
| private String curSpn = null; |
| private String curPlmn = null; |
| private int curSpnRule = 0; |
| |
| //***** Constants |
| |
| static final boolean DBG = true; |
| static final String LOG_TAG = "GSM"; |
| |
| // signal strength poll rate |
| static final int POLL_PERIOD_MILLIS = 20 * 1000; |
| |
| // waiting period before recheck gprs and voice registration |
| static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; |
| |
| //***** Events |
| static final int EVENT_RADIO_STATE_CHANGED = 1; |
| static final int EVENT_NETWORK_STATE_CHANGED = 2; |
| static final int EVENT_GET_SIGNAL_STRENGTH = 3; |
| static final int EVENT_POLL_STATE_REGISTRATION = 4; |
| static final int EVENT_POLL_STATE_GPRS = 5; |
| static final int EVENT_POLL_STATE_OPERATOR = 6; |
| static final int EVENT_POLL_SIGNAL_STRENGTH = 10; |
| static final int EVENT_NITZ_TIME = 11; |
| static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12; |
| static final int EVENT_RADIO_AVAILABLE = 13; |
| static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14; |
| static final int EVENT_GET_LOC_DONE = 15; |
| static final int EVENT_SIM_RECORDS_LOADED = 16; |
| static final int EVENT_SIM_READY = 17; |
| static final int EVENT_LOCATION_UPDATES_ENABLED = 18; |
| static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; |
| static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; |
| static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; |
| static final int EVENT_CHECK_REPORT_GPRS = 22; |
| |
| //***** Time Zones |
| |
| private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; |
| |
| // List of ISO codes for countries that can have an offset of GMT+0 |
| // when not in daylight savings time. This ignores some small places |
| // such as the Canary Islands (Spain) and Danmarkshavn (Denmark). |
| // The list must be sorted by code. |
| private static final String[] GMT_COUNTRY_CODES = { |
| "bf", // Burkina Faso |
| "ci", // Cote d'Ivoire |
| "eh", // Western Sahara |
| "fo", // Faroe Islands, Denmark |
| "gh", // Ghana |
| "gm", // Gambia |
| "gn", // Guinea |
| "gw", // Guinea Bissau |
| "ie", // Ireland |
| "lr", // Liberia |
| "is", // Iceland |
| "ma", // Morocco |
| "ml", // Mali |
| "mr", // Mauritania |
| "pt", // Portugal |
| "sl", // Sierra Leone |
| "sn", // Senegal |
| "st", // Sao Tome and Principe |
| "tg", // Togo |
| "uk", // U.K |
| }; |
| |
| private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { |
| @Override |
| public void onChange(boolean selfChange) { |
| Log.i("ServiceStateTracker", "Auto time state changed"); |
| revertToNitz(); |
| } |
| }; |
| |
| |
| //***** Constructors |
| |
| ServiceStateTracker(GSMPhone phone) |
| { |
| this.phone = phone; |
| cm = phone.mCM; |
| ss = new ServiceState(); |
| newSS = new ServiceState(); |
| cellLoc = new GsmCellLocation(); |
| newCellLoc = new GsmCellLocation(); |
| |
| cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); |
| cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); |
| |
| cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); |
| cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); |
| cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); |
| |
| cm.registerForSIMReady(this, EVENT_SIM_READY, null); |
| |
| // system setting property AIRPLANE_MODE_ON is set in Settings. |
| int airplaneMode = Settings.System.getInt( |
| phone.getContext().getContentResolver(), |
| Settings.System.AIRPLANE_MODE_ON, 0); |
| mDesiredPowerState = ! (airplaneMode > 0); |
| |
| ContentResolver cr = phone.getContext().getContentResolver(); |
| cr.registerContentObserver( |
| Settings.System.getUriFor(Settings.System.AUTO_TIME), true, |
| mAutoTimeObserver); |
| setRssiDefaultValues(); |
| mNeedToRegForSimLoaded = true; |
| } |
| |
| /** |
| * Registration point for transition into GPRS attached. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| /*protected*/ void |
| registerForGprsAttached(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| gprsAttachedRegistrants.add(r); |
| |
| if (gprsState == ServiceState.STATE_IN_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| void registerForNetworkAttach(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| networkAttachedRegistrants.add(r); |
| |
| if (ss.getState() == ServiceState.STATE_IN_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| /** |
| * Registration point for transition into GPRS detached. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| /*protected*/ void |
| registerForGprsDetached(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| gprsDetachedRegistrants.add(r); |
| |
| if (gprsState == ServiceState.STATE_OUT_OF_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| /** |
| * Registration point for combined roaming on |
| * combined roaming is true when roaming is true and ONS differs SPN |
| * |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| void registerForRoamingOn(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| roamingOnRegistrants.add(r); |
| |
| if (ss.getRoaming()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| /** |
| * Registration point for combined roaming off |
| * combined roaming is true when roaming is true and ONS differs SPN |
| * |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| void registerForRoamingOff(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| roamingOffRegistrants.add(r); |
| |
| if (!ss.getRoaming()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| /** |
| * Reregister network through toggle perferred network type |
| * This is a work aorund to deregister and register network since there is |
| * no ril api to set COPS=2 (deregister) only. |
| * |
| * @param onComplete is dispatched when this is complete. it will be |
| * an AsyncResult, and onComplete.obj.exception will be non-null |
| * on failure. |
| */ |
| void reRegisterNetwork(Message onComplete) { |
| cm.getPreferredNetworkType( |
| obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete)); |
| } |
| |
| //***** Called from GSMPhone |
| |
| public void |
| setRadioPower(boolean power) |
| { |
| mDesiredPowerState = power; |
| |
| setPowerStateToDesired(); |
| } |
| |
| public void |
| getLacAndCid(Message onComplete) { |
| cm.getRegistrationState(obtainMessage( |
| EVENT_GET_LOC_DONE, onComplete)); |
| } |
| |
| /*package*/ void enableLocationUpdates() { |
| cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); |
| } |
| |
| /*package*/ void disableLocationUpdates() { |
| cm.setLocationUpdates(false, null); |
| } |
| //***** Overridden from Handler |
| |
| public void |
| handleMessage (Message msg) |
| { |
| AsyncResult ar; |
| int[] ints; |
| String[] strings; |
| Message message; |
| |
| switch (msg.what) { |
| case EVENT_RADIO_AVAILABLE: |
| //this is unnecessary |
| //setPowerStateToDesired(); |
| break; |
| |
| case EVENT_SIM_READY: |
| // The SIM is now ready i.e if it was locked |
| // it has been unlocked. At this stage, the radio is already |
| // powered on. |
| if (mNeedToRegForSimLoaded) { |
| phone.mSIMRecords.registerForRecordsLoaded(this, |
| EVENT_SIM_RECORDS_LOADED, null); |
| mNeedToRegForSimLoaded = false; |
| } |
| // restore the previous network selection. |
| phone.restoreSavedNetworkSelection(null); |
| pollState(); |
| // Signal strength polling stops when radio is off |
| queueNextSignalStrengthPoll(); |
| break; |
| |
| case EVENT_RADIO_STATE_CHANGED: |
| // This will do nothing in the radio not |
| // available case |
| setPowerStateToDesired(); |
| pollState(); |
| break; |
| |
| case EVENT_NETWORK_STATE_CHANGED: |
| pollState(); |
| break; |
| |
| case EVENT_GET_SIGNAL_STRENGTH: |
| // This callback is called when signal strength is polled |
| // all by itself |
| |
| if (!(cm.getRadioState().isOn())) { |
| // Polling will continue when radio turns back on |
| return; |
| } |
| ar = (AsyncResult) msg.obj; |
| onSignalStrengthResult(ar); |
| queueNextSignalStrengthPoll(); |
| |
| break; |
| |
| case EVENT_GET_LOC_DONE: |
| ar = (AsyncResult) msg.obj; |
| |
| if (ar.exception == null) { |
| String states[] = (String[])ar.result; |
| int lac = -1; |
| int cid = -1; |
| if (states.length == 3) { |
| try { |
| if (states[1] != null && states[1].length() > 0) { |
| lac = Integer.parseInt(states[1], 16); |
| } |
| if (states[2] != null && states[2].length() > 0) { |
| cid = Integer.parseInt(states[2], 16); |
| } |
| } catch (NumberFormatException ex) { |
| Log.w(LOG_TAG, "error parsing location: " + ex); |
| } |
| } |
| |
| // only update if lac or cid changed |
| if (cellLoc.getCid() != cid || cellLoc.getLac() != lac) { |
| cellLoc.setLacAndCid(lac, cid); |
| phone.notifyLocationChanged(); |
| } |
| } |
| |
| if (ar.userObj != null) { |
| AsyncResult.forMessage(((Message) ar.userObj)).exception |
| = ar.exception; |
| ((Message) ar.userObj).sendToTarget(); |
| } |
| break; |
| |
| case EVENT_POLL_STATE_REGISTRATION: |
| case EVENT_POLL_STATE_GPRS: |
| case EVENT_POLL_STATE_OPERATOR: |
| case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: |
| ar = (AsyncResult) msg.obj; |
| |
| handlePollStateResult(msg.what, ar); |
| break; |
| |
| case EVENT_POLL_SIGNAL_STRENGTH: |
| // Just poll signal strength...not part of pollState() |
| |
| cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); |
| break; |
| |
| case EVENT_NITZ_TIME: |
| ar = (AsyncResult) msg.obj; |
| |
| String nitzString = (String)((Object[])ar.result)[0]; |
| long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); |
| |
| setTimeFromNITZString(nitzString, nitzReceiveTime); |
| break; |
| |
| case EVENT_SIGNAL_STRENGTH_UPDATE: |
| // This is a notification from |
| // CommandsInterface.setOnSignalStrengthUpdate |
| |
| ar = (AsyncResult) msg.obj; |
| |
| // The radio is telling us about signal strength changes |
| // we don't have to ask it |
| dontPollSignalStrength = true; |
| |
| onSignalStrengthResult(ar); |
| break; |
| |
| case EVENT_SIM_RECORDS_LOADED: |
| updateSpnDisplay(); |
| break; |
| |
| case EVENT_LOCATION_UPDATES_ENABLED: |
| ar = (AsyncResult) msg.obj; |
| |
| if (ar.exception == null) { |
| getLacAndCid(null); |
| } |
| break; |
| |
| case EVENT_SET_PREFERRED_NETWORK_TYPE: |
| ar = (AsyncResult) msg.obj; |
| // Don't care the result, only use for dereg network (COPS=2) |
| message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj); |
| cm.setPreferredNetworkType(mPreferredNetworkType, message); |
| break; |
| |
| case EVENT_RESET_PREFERRED_NETWORK_TYPE: |
| ar = (AsyncResult) msg.obj; |
| if (ar.userObj != null) { |
| AsyncResult.forMessage(((Message) ar.userObj)).exception |
| = ar.exception; |
| ((Message) ar.userObj).sendToTarget(); |
| } |
| break; |
| |
| case EVENT_GET_PREFERRED_NETWORK_TYPE: |
| ar = (AsyncResult) msg.obj; |
| |
| if (ar.exception == null) { |
| mPreferredNetworkType = ((int[])ar.result)[0]; |
| } else { |
| mPreferredNetworkType = Phone.NT_AUTO_TYPE; |
| } |
| |
| message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj); |
| int toggledNetworkType = |
| (mPreferredNetworkType == Phone.NT_AUTO_TYPE) ? |
| Phone.NT_GSM_TYPE : Phone.NT_AUTO_TYPE; |
| |
| cm.setPreferredNetworkType(toggledNetworkType, message); |
| break; |
| |
| case EVENT_CHECK_REPORT_GPRS: |
| if (ss != null && !isGprsConsistant(gprsState, ss.getState())) { |
| |
| // Can't register data sevice while voice service is ok |
| // i.e. CREG is ok while CGREG is not |
| // possible a network or baseband side error |
| int cid = -1; |
| GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); |
| if (loc != null) cid = loc.getCid(); |
| |
| EventLog.List val = new EventLog.List(ss.getOperatorNumeric(), cid); |
| EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CGREG_FAIL, val); |
| mReportedGprsNoReg = true; |
| } |
| mStartedGprsRegCheck = false; |
| break; |
| |
| } |
| } |
| |
| //***** Private Instance Methods |
| |
| private void updateSpnDisplay() { |
| int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric()); |
| String spn = phone.mSIMRecords.getServiceProvideName(); |
| String plmn = ss.getOperatorAlphaLong(); |
| |
| if (rule != curSpnRule |
| || !TextUtils.equals(spn, curSpn) |
| || !TextUtils.equals(plmn, curPlmn)) { |
| boolean showSpn = |
| (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; |
| boolean showPlmn = |
| (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; |
| Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); |
| intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); |
| intent.putExtra(Intents.EXTRA_SPN, spn); |
| intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); |
| intent.putExtra(Intents.EXTRA_PLMN, plmn); |
| phone.getContext().sendStickyBroadcast(intent); |
| } |
| curSpnRule = rule; |
| curSpn = spn; |
| curPlmn = plmn; |
| } |
| |
| private void |
| setPowerStateToDesired() |
| { |
| // If we want it on and it's off, turn it on |
| if (mDesiredPowerState |
| && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF |
| ) { |
| cm.setRadioPower(true, null); |
| } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { |
| DataConnectionTracker dcTracker = phone.mDataConnection; |
| if (! dcTracker.isDataConnectionAsDesired()) { |
| |
| EventLog.List val = new EventLog.List( |
| dcTracker.getStateInString(), |
| (dcTracker.getAnyDataEnabled() ? 1 : 0) ); |
| EventLog.writeEvent(TelephonyEventLog.EVENT_DATA_STATE_RADIO_OFF, val); |
| } |
| // If it's on and available and we want it off.. |
| cm.setRadioPower(false, null); |
| } // Otherwise, we're in the desired state |
| } |
| |
| /** Cancel a pending (if any) pollState() operation */ |
| private void |
| cancelPollState() |
| { |
| // This will effectively cancel the rest of the poll requests |
| pollingContext = new int[1]; |
| } |
| |
| /** |
| * Handle the result of one of the pollState()-related requests |
| */ |
| |
| private void |
| handlePollStateResult (int what, AsyncResult ar) |
| { |
| int ints[]; |
| String states[]; |
| |
| // Ignore stale requests from last poll |
| if (ar.userObj != pollingContext) return; |
| |
| if (ar.exception != null) { |
| CommandException.Error err=null; |
| |
| if (ar.exception instanceof CommandException) { |
| err = ((CommandException)(ar.exception)).getCommandError(); |
| } |
| |
| if (err == CommandException.Error.RADIO_NOT_AVAILABLE) { |
| // Radio has crashed or turned off |
| cancelPollState(); |
| return; |
| } |
| |
| if (!cm.getRadioState().isOn()) { |
| // Radio has crashed or turned off |
| cancelPollState(); |
| return; |
| } |
| |
| if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW && |
| err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { |
| Log.e(LOG_TAG, |
| "RIL implementation has returned an error where it must succeed" + |
| ar.exception); |
| } |
| } else try { |
| switch (what) { |
| case EVENT_POLL_STATE_REGISTRATION: |
| states = (String[])ar.result; |
| int lac = -1; |
| int cid = -1; |
| int regState = -1; |
| if (states.length > 0) { |
| try { |
| regState = Integer.parseInt(states[0]); |
| if (states.length == 3) { |
| if (states[1] != null && states[1].length() > 0) { |
| lac = Integer.parseInt(states[1], 16); |
| } |
| if (states[2] != null && states[2].length() > 0) { |
| cid = Integer.parseInt(states[2], 16); |
| } |
| } |
| } catch (NumberFormatException ex) { |
| Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); |
| } |
| } |
| |
| mGsmRoaming = regCodeIsRoaming(regState); |
| newSS.setState (regCodeToServiceState(regState)); |
| |
| // LAC and CID are -1 if not avail |
| newCellLoc.setLacAndCid(lac, cid); |
| break; |
| |
| case EVENT_POLL_STATE_GPRS: |
| states = (String[])ar.result; |
| |
| int type = 0; |
| regState = -1; |
| if (states.length > 0) { |
| try { |
| regState = Integer.parseInt(states[0]); |
| |
| // states[3] (if present) is the current radio technology |
| if (states.length >= 4 && states[3] != null) { |
| type = Integer.parseInt(states[3]); |
| } |
| } catch (NumberFormatException ex) { |
| Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex); |
| } |
| } |
| newGPRSState = regCodeToServiceState(regState); |
| newNetworkType = type; |
| break; |
| |
| case EVENT_POLL_STATE_OPERATOR: |
| String opNames[] = (String[])ar.result; |
| |
| if (opNames != null && opNames.length >= 3) { |
| newSS.setOperatorName ( |
| opNames[0], opNames[1], opNames[2]); |
| } |
| break; |
| |
| case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: |
| ints = (int[])ar.result; |
| newSS.setIsManualSelection(ints[0] == 1); |
| break; |
| } |
| |
| } catch (RuntimeException ex) { |
| Log.e(LOG_TAG, "Exception while polling service state. " |
| + "Probably malformed RIL response.", ex); |
| } |
| |
| pollingContext[0]--; |
| |
| if (pollingContext[0] == 0) { |
| newSS.setRoaming(isRoamingBetweenOperators(mGsmRoaming, newSS)); |
| pollStateDone(); |
| } |
| |
| } |
| |
| private void |
| setRssiDefaultValues() |
| { |
| rssi = 99; |
| } |
| |
| /** |
| * A complete "service state" from our perspective is |
| * composed of a handful of separate requests to the radio. |
| * |
| * We make all of these requests at once, but then abandon them |
| * and start over again if the radio notifies us that some |
| * event has changed |
| */ |
| |
| private void |
| pollState() |
| { |
| pollingContext = new int[1]; |
| pollingContext[0] = 0; |
| |
| switch (cm.getRadioState()) { |
| case RADIO_UNAVAILABLE: |
| newSS.setStateOutOfService(); |
| newCellLoc.setStateInvalid(); |
| setRssiDefaultValues(); |
| mGotCountryCode = false; |
| |
| pollStateDone(); |
| break; |
| |
| |
| case RADIO_OFF: |
| newSS.setStateOff(); |
| newCellLoc.setStateInvalid(); |
| setRssiDefaultValues(); |
| mGotCountryCode = false; |
| |
| pollStateDone(); |
| break; |
| |
| default: |
| // Issue all poll-related commands at once |
| // then count down the responses, which |
| // are allowed to arrive out-of-order |
| |
| pollingContext[0]++; |
| cm.getOperator( |
| obtainMessage( |
| EVENT_POLL_STATE_OPERATOR, pollingContext)); |
| |
| pollingContext[0]++; |
| cm.getGPRSRegistrationState( |
| obtainMessage( |
| EVENT_POLL_STATE_GPRS, pollingContext)); |
| |
| pollingContext[0]++; |
| cm.getRegistrationState( |
| obtainMessage( |
| EVENT_POLL_STATE_REGISTRATION, pollingContext)); |
| |
| pollingContext[0]++; |
| cm.getNetworkSelectionMode( |
| obtainMessage( |
| EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext)); |
| break; |
| } |
| } |
| |
| private static String networkTypeToString(int type) { |
| String ret = "unknown"; |
| |
| switch (type) { |
| case DATA_ACCESS_GPRS: |
| ret = "GPRS"; |
| break; |
| case DATA_ACCESS_EDGE: |
| ret = "EDGE"; |
| break; |
| case DATA_ACCESS_UMTS: |
| ret = "UMTS"; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| private void |
| pollStateDone() |
| { |
| if (DBG) { |
| Log.d(LOG_TAG, "Poll ServiceState done: " + |
| " oldSS=[" + ss + "] newSS=[" + newSS + |
| "] oldGprs=" + gprsState + " newGprs=" + newGPRSState + |
| " oldType=" + networkTypeToString(networkType) + |
| " newType=" + networkTypeToString(newNetworkType)); |
| } |
| |
| boolean hasRegistered = |
| ss.getState() != ServiceState.STATE_IN_SERVICE |
| && newSS.getState() == ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasDeregistered = |
| ss.getState() == ServiceState.STATE_IN_SERVICE |
| && newSS.getState() != ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasGprsAttached = |
| gprsState != ServiceState.STATE_IN_SERVICE |
| && newGPRSState == ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasGprsDetached = |
| gprsState == ServiceState.STATE_IN_SERVICE |
| && newGPRSState != ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasNetworkTypeChanged = networkType != newNetworkType; |
| |
| boolean hasChanged = !newSS.equals(ss); |
| |
| boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); |
| |
| boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); |
| |
| boolean hasLocationChanged = !newCellLoc.equals(cellLoc); |
| |
| ServiceState tss; |
| tss = ss; |
| ss = newSS; |
| newSS = tss; |
| // clean slate for next time |
| newSS.setStateOutOfService(); |
| |
| GsmCellLocation tcl = cellLoc; |
| cellLoc = newCellLoc; |
| newCellLoc = tcl; |
| |
| gprsState = newGPRSState; |
| networkType = newNetworkType; |
| |
| newSS.setStateOutOfService(); // clean slate for next time |
| |
| if (hasNetworkTypeChanged) { |
| phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE, |
| networkTypeToString(networkType)); |
| } |
| |
| if (hasRegistered) { |
| Checkin.updateStats(phone.getContext().getContentResolver(), |
| Checkin.Stats.Tag.PHONE_GSM_REGISTERED, 1, 0.0); |
| networkAttachedRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasChanged) { |
| String operatorNumeric; |
| |
| phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA, |
| ss.getOperatorAlphaLong()); |
| |
| operatorNumeric = ss.getOperatorNumeric(); |
| phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric); |
| |
| if (operatorNumeric == null) { |
| phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); |
| } else { |
| String iso = ""; |
| try{ |
| iso = MccTable.countryCodeForMcc(Integer.parseInt( |
| operatorNumeric.substring(0,3))); |
| } catch ( NumberFormatException ex){ |
| Log.w(LOG_TAG, "countryCodeForMcc error" + ex); |
| } catch ( StringIndexOutOfBoundsException ex) { |
| Log.w(LOG_TAG, "countryCodeForMcc error" + ex); |
| } |
| |
| phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); |
| mGotCountryCode = true; |
| |
| if (mNeedFixZone) { |
| TimeZone zone = null; |
| // If the offset is (0, false) and the timezone property |
| // is set, use the timezone property rather than |
| // GMT. |
| String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); |
| if ((mZoneOffset == 0) && (mZoneDst == false) && |
| (zoneName != null) && (zoneName.length() > 0) && |
| (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) { |
| zone = TimeZone.getDefault(); |
| // For NITZ string without timezone, |
| // need adjust time to reflect default timezone setting |
| long tzOffset; |
| tzOffset = zone.getOffset(System.currentTimeMillis()); |
| if (getAutoTime()) { |
| setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); |
| } else { |
| // Adjust the saved NITZ time to account for tzOffset. |
| mSavedTime = mSavedTime - tzOffset; |
| } |
| } else if (iso.equals("")){ |
| // Country code not found. This is likely a test network. |
| // Get a TimeZone based only on the NITZ parameters (best guess). |
| zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); |
| } else { |
| zone = TimeUtils.getTimeZone(mZoneOffset, |
| mZoneDst, mZoneTime, iso); |
| } |
| |
| mNeedFixZone = false; |
| |
| if (zone != null) { |
| if (getAutoTime()) { |
| setAndBroadcastNetworkSetTimeZone(zone.getID()); |
| } |
| saveNitzTimeZone(zone.getID()); |
| } |
| } |
| } |
| |
| phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, |
| ss.getRoaming() ? "true" : "false"); |
| |
| updateSpnDisplay(); |
| phone.notifyServiceStateChanged(ss); |
| } |
| |
| if (hasGprsAttached) { |
| gprsAttachedRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasGprsDetached) { |
| gprsDetachedRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasNetworkTypeChanged) { |
| phone.notifyDataConnection(null); |
| } |
| |
| if (hasRoamingOn) { |
| roamingOnRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasRoamingOff) { |
| roamingOffRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasLocationChanged) { |
| phone.notifyLocationChanged(); |
| } |
| |
| if (! isGprsConsistant(gprsState, ss.getState())) { |
| if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { |
| mStartedGprsRegCheck = true; |
| |
| int check_period = Settings.Gservices.getInt( |
| phone.getContext().getContentResolver(), |
| Settings.Gservices.GPRS_REGISTER_CHECK_PERIOD_MS, |
| DEFAULT_GPRS_CHECK_PERIOD_MILLIS); |
| sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), |
| check_period); |
| } |
| } else { |
| mReportedGprsNoReg = false; |
| } |
| } |
| |
| /** |
| * Check if GPRS got registred while voice is registered |
| * |
| * @param gprsState for GPRS registration state, i.e. CGREG in GSM |
| * @param serviceState for voice registration state, i.e. CREG in GSM |
| * @return false if device only register to voice but not gprs |
| */ |
| private boolean isGprsConsistant (int gprsState, int serviceState) { |
| return !((serviceState == ServiceState.STATE_IN_SERVICE) && |
| (gprsState != ServiceState.STATE_IN_SERVICE)); |
| } |
| |
| /** |
| * Returns a TimeZone object based only on parameters from the NITZ string. |
| */ |
| private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { |
| TimeZone guess = findTimeZone(offset, dst, when); |
| if (guess == null) { |
| // Couldn't find a proper timezone. Perhaps the DST data is wrong. |
| guess = findTimeZone(offset, !dst, when); |
| } |
| if (DBG) { |
| Log.d(LOG_TAG, "getNitzTimeZone returning " |
| + (guess == null ? guess : guess.getID())); |
| } |
| return guess; |
| } |
| |
| private TimeZone findTimeZone(int offset, boolean dst, long when) { |
| int rawOffset = offset; |
| if (dst) { |
| rawOffset -= 3600000; |
| } |
| String[] zones = TimeZone.getAvailableIDs(rawOffset); |
| TimeZone guess = null; |
| Date d = new Date(when); |
| for (String zone : zones) { |
| TimeZone tz = TimeZone.getTimeZone(zone); |
| if (tz.getOffset(when) == offset && |
| tz.inDaylightTime(d) == dst) { |
| guess = tz; |
| break; |
| } |
| } |
| |
| return guess; |
| } |
| |
| private void |
| queueNextSignalStrengthPoll() |
| { |
| if (dontPollSignalStrength) { |
| // The radio is telling us about signal strength changes |
| // we don't have to ask it |
| return; |
| } |
| |
| Message msg; |
| |
| msg = obtainMessage(); |
| msg.what = EVENT_POLL_SIGNAL_STRENGTH; |
| |
| long nextTime; |
| |
| // TODO Done't poll signal strength if screen is off |
| sendMessageDelayed(msg, POLL_PERIOD_MILLIS); |
| } |
| |
| /** |
| * send signal-strength-changed notification if rssi changed |
| * Called both for solicited and unsolicited signal stength updates |
| */ |
| private void |
| onSignalStrengthResult(AsyncResult ar) |
| { |
| int oldRSSI = rssi; |
| |
| if (ar.exception != null) { |
| // 99 = unknown |
| // most likely radio is resetting/disconnected |
| rssi = 99; |
| } else { |
| int[] ints = (int[])ar.result; |
| |
| // bug 658816 seems to be a case where the result is 0-length |
| if (ints.length != 0) { |
| rssi = ints[0]; |
| } else { |
| Log.e(LOG_TAG, "Bogus signal strength response"); |
| rssi = 99; |
| } |
| } |
| |
| if (rssi != oldRSSI) { |
| phone.notifySignalStrength(); |
| } |
| } |
| |
| /** code is registration state 0-5 from TS 27.007 7.2 */ |
| private int |
| regCodeToServiceState(int code) |
| { |
| switch (code) { |
| case 0: |
| case 2: // 2 is "searching" |
| case 3: // 3 is "registration denied" |
| case 4: // 4 is "unknown" no vaild in current baseband |
| return ServiceState.STATE_OUT_OF_SERVICE; |
| |
| case 1: |
| return ServiceState.STATE_IN_SERVICE; |
| |
| case 5: |
| // in service, roam |
| return ServiceState.STATE_IN_SERVICE; |
| |
| default: |
| Log.w(LOG_TAG, "unexpected service state " + code); |
| return ServiceState.STATE_OUT_OF_SERVICE; |
| } |
| } |
| |
| |
| /** |
| * code is registration state 0-5 from TS 27.007 7.2 |
| * returns true if registered roam, false otherwise |
| */ |
| private boolean |
| regCodeIsRoaming (int code) |
| { |
| // 5 is "in service -- roam" |
| return 5 == code; |
| } |
| |
| /** |
| * Set roaming state when gsmRoaming is true and, if operator mcc is the |
| * same as sim mcc, ons is different from spn |
| * @param gsmRoaming TS 27.007 7.2 CREG registered roaming |
| * @param s ServiceState hold current ons |
| * @return true for roaming state set |
| */ |
| private |
| boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) { |
| String spn = SystemProperties.get(PROPERTY_SIM_OPERATOR_ALPHA, "empty"); |
| |
| String onsl = s.getOperatorAlphaLong(); |
| String onss = s.getOperatorAlphaShort(); |
| |
| boolean equalsOnsl = onsl != null && spn.equals(onsl); |
| boolean equalsOnss = onss != null && spn.equals(onss); |
| |
| String simNumeric = SystemProperties.get(PROPERTY_SIM_OPERATOR_NUMERIC, ""); |
| String operatorNumeric = s.getOperatorNumeric(); |
| |
| boolean equalsMcc = true; |
| try { |
| equalsMcc = simNumeric.substring(0, 3). |
| equals(operatorNumeric.substring(0, 3)); |
| } catch (Exception e){ |
| } |
| |
| return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss)); |
| } |
| |
| private static |
| int twoDigitsAt(String s, int offset) |
| { |
| int a, b; |
| |
| a = Character.digit(s.charAt(offset), 10); |
| b = Character.digit(s.charAt(offset+1), 10); |
| |
| if (a < 0 || b < 0) { |
| |
| throw new RuntimeException("invalid format"); |
| } |
| |
| return a*10 + b; |
| } |
| |
| /** |
| * @return The current GPRS state. IN_SERVICE is the same as "attached" |
| * and OUT_OF_SERVICE is the same as detached. |
| */ |
| /*package*/ int getCurrentGprsState() { |
| return gprsState; |
| } |
| |
| /** |
| * @return true if phone is camping on a technology (eg UMTS) |
| * that could support voice and data simultaniously. |
| */ |
| boolean isConcurrentVoiceAndData() { |
| return (networkType == DATA_ACCESS_UMTS); |
| } |
| |
| /** |
| * Provides the name of the algorithmic time zone for the specified |
| * offset. Taken from TimeZone.java. |
| */ |
| private static String displayNameFor(int off) { |
| off = off / 1000 / 60; |
| |
| char[] buf = new char[9]; |
| buf[0] = 'G'; |
| buf[1] = 'M'; |
| buf[2] = 'T'; |
| |
| if (off < 0) { |
| buf[3] = '-'; |
| off = -off; |
| } else { |
| buf[3] = '+'; |
| } |
| |
| int hours = off / 60; |
| int minutes = off % 60; |
| |
| buf[4] = (char) ('0' + hours / 10); |
| buf[5] = (char) ('0' + hours % 10); |
| |
| buf[6] = ':'; |
| |
| buf[7] = (char) ('0' + minutes / 10); |
| buf[8] = (char) ('0' + minutes % 10); |
| |
| return new String(buf); |
| } |
| |
| /** |
| * nitzReceiveTime is time_t that the NITZ time was posted |
| */ |
| |
| private |
| void setTimeFromNITZString (String nitz, long nitzReceiveTime) |
| { |
| // "yy/mm/dd,hh:mm:ss(+/-)tz" |
| // tz is in number of quarter-hours |
| |
| long start = SystemClock.elapsedRealtime(); |
| Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + |
| " start=" + start + " delay=" + (start - nitzReceiveTime)); |
| |
| try { |
| /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone |
| * offset as well (which we won't worry about until later) */ |
| Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); |
| |
| c.clear(); |
| c.set(Calendar.DST_OFFSET, 0); |
| |
| String[] nitzSubs = nitz.split("[/:,+-]"); |
| |
| int year = 2000 + Integer.parseInt(nitzSubs[0]); |
| c.set(Calendar.YEAR, year); |
| |
| // month is 0 based! |
| int month = Integer.parseInt(nitzSubs[1]) - 1; |
| c.set(Calendar.MONTH, month); |
| |
| int date = Integer.parseInt(nitzSubs[2]); |
| c.set(Calendar.DATE, date); |
| |
| int hour = Integer.parseInt(nitzSubs[3]); |
| c.set(Calendar.HOUR, hour); |
| |
| int minute = Integer.parseInt(nitzSubs[4]); |
| c.set(Calendar.MINUTE, minute); |
| |
| int second = Integer.parseInt(nitzSubs[5]); |
| c.set(Calendar.SECOND, second); |
| |
| boolean sign = (nitz.indexOf('-') == -1); |
| |
| int tzOffset = Integer.parseInt(nitzSubs[6]); |
| |
| int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) |
| : 0; |
| |
| // The zone offset received from NITZ is for current local time, |
| // so DST correction is already applied. Don't add it again. |
| // |
| // tzOffset += dst * 4; |
| // |
| // We could unapply it if we wanted the raw offset. |
| |
| tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; |
| |
| TimeZone zone = null; |
| |
| // As a special extension, the Android emulator appends the name of |
| // the host computer's timezone to the nitz string. this is zoneinfo |
| // timezone name of the form Area!Location or Area!Location!SubLocation |
| // so we need to convert the ! into / |
| if (nitzSubs.length >= 9) { |
| String tzname = nitzSubs[8].replace('!','/'); |
| zone = TimeZone.getTimeZone( tzname ); |
| } |
| |
| String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY); |
| |
| if (zone == null) { |
| |
| if (mGotCountryCode) { |
| if (iso != null && iso.length() > 0) { |
| zone = TimeUtils.getTimeZone(tzOffset, dst != 0, |
| c.getTimeInMillis(), |
| iso); |
| } else { |
| // We don't have a valid iso country code. This is |
| // most likely because we're on a test network that's |
| // using a bogus MCC (eg, "001"), so get a TimeZone |
| // based only on the NITZ parameters. |
| zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); |
| } |
| } |
| } |
| |
| if (zone == null) { |
| // We got the time before the country, so we don't know |
| // how to identify the DST rules yet. Save the information |
| // and hope to fix it up later. |
| |
| mNeedFixZone = true; |
| mZoneOffset = tzOffset; |
| mZoneDst = dst != 0; |
| mZoneTime = c.getTimeInMillis(); |
| } |
| |
| if (zone != null) { |
| if (getAutoTime()) { |
| setAndBroadcastNetworkSetTimeZone(zone.getID()); |
| } |
| saveNitzTimeZone(zone.getID()); |
| } |
| |
| String ignore = SystemProperties.get("gsm.ignore-nitz"); |
| if (ignore != null && ignore.equals("yes")) { |
| Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); |
| return; |
| } |
| |
| if (getAutoTime()) { |
| long millisSinceNitzReceived |
| = SystemClock.elapsedRealtime() - nitzReceiveTime; |
| |
| if (millisSinceNitzReceived < 0) { |
| // Sanity check: something is wrong |
| Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " |
| + "backwards since NITZ time was received, " |
| + nitz); |
| return; |
| } |
| |
| if (millisSinceNitzReceived > Integer.MAX_VALUE) { |
| // If the time is this far off, something is wrong > 24 days! |
| Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " |
| + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) |
| + " days"); |
| return; |
| } |
| |
| // Note: with range checks above, cast to int is safe |
| c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); |
| |
| Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() |
| + " NITZ receive delay(ms): " + millisSinceNitzReceived |
| + " gained(ms): " |
| + (c.getTimeInMillis() - System.currentTimeMillis()) |
| + " from " + nitz); |
| |
| setAndBroadcastNetworkSetTime(c.getTimeInMillis()); |
| } |
| SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); |
| saveNitzTime(c.getTimeInMillis()); |
| if (Config.LOGV) { |
| long end = SystemClock.elapsedRealtime(); |
| Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); |
| } |
| } catch (RuntimeException ex) { |
| Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); |
| } |
| } |
| |
| private boolean getAutoTime() { |
| try { |
| return Settings.System.getInt(phone.getContext().getContentResolver(), |
| Settings.System.AUTO_TIME) > 0; |
| } catch (SettingNotFoundException snfe) { |
| return true; |
| } |
| } |
| |
| private void saveNitzTimeZone(String zoneId) { |
| mSavedTimeZone = zoneId; |
| } |
| |
| private void saveNitzTime(long time) { |
| mSavedTime = time; |
| mSavedAtTime = SystemClock.elapsedRealtime(); |
| } |
| |
| /** |
| * Set the timezone and send out a sticky broadcast so the system can |
| * determine if the timezone was set by the carrier. |
| * |
| * @param zoneId timezone set by carrier |
| */ |
| private void setAndBroadcastNetworkSetTimeZone(String zoneId) { |
| AlarmManager alarm = |
| (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); |
| alarm.setTimeZone(zoneId); |
| Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); |
| intent.putExtra("time-zone", zoneId); |
| phone.getContext().sendStickyBroadcast(intent); |
| } |
| |
| /** |
| * Set the time and Send out a sticky broadcast so the system can determine |
| * if the time was set by the carrier. |
| * |
| * @param time time set by network |
| */ |
| private void setAndBroadcastNetworkSetTime(long time) { |
| SystemClock.setCurrentTimeMillis(time); |
| Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); |
| intent.putExtra("time", time); |
| phone.getContext().sendStickyBroadcast(intent); |
| } |
| |
| private void revertToNitz() { |
| if (Settings.System.getInt(phone.getContext().getContentResolver(), |
| Settings.System.AUTO_TIME, 0) == 0) { |
| return; |
| } |
| Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone |
| + "' mSavedTime=" + mSavedTime |
| + " mSavedAtTime=" + mSavedAtTime); |
| if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { |
| setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); |
| setAndBroadcastNetworkSetTime(mSavedTime |
| + (SystemClock.elapsedRealtime() - mSavedAtTime)); |
| } |
| } |
| } |