Merge "If no NITZ information use Country Code to set TimeZone."

This commit is contained in:
Wink Saville
2012-03-01 10:28:56 -08:00
committed by Android (Google) Code Review
4 changed files with 231 additions and 42 deletions

View File

@@ -25,6 +25,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.TimeZone;
import java.util.Date;
@@ -35,18 +37,26 @@ import com.android.internal.util.XmlUtils;
*/
public class TimeUtils {
/** @hide */ public TimeUtils() {}
private static final boolean DBG = false;
private static final String TAG = "TimeUtils";
/** Cached results of getTineZones */
private static final Object sLastLockObj = new Object();
private static ArrayList<TimeZone> sLastZones = null;
private static String sLastCountry = null;
/** Cached results of getTimeZonesWithUniqueOffsets */
private static final Object sLastUniqueLockObj = new Object();
private static ArrayList<TimeZone> sLastUniqueZoneOffsets = null;
private static String sLastUniqueCountry = null;
/**
* Tries to return a time zone that would have had the specified offset
* and DST value at the specified moment in the specified country.
* Returns null if no suitable zone could be found.
*/
public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
if (country == null) {
return null;
}
TimeZone best = null;
Resources r = Resources.getSystem();
@@ -58,6 +68,107 @@ public class TimeUtils {
int currentOffset = current.getOffset(when);
boolean currentDst = current.inDaylightTime(d);
for (TimeZone tz : getTimeZones(country)) {
// If the current time zone is from the right country
// and meets the other known properties, keep it
// instead of changing to another one.
if (tz.getID().equals(currentName)) {
if (currentOffset == offset && currentDst == dst) {
return current;
}
}
// Otherwise, take the first zone from the right
// country that has the correct current offset and DST.
// (Keep iterating instead of returning in case we
// haven't encountered the current time zone yet.)
if (best == null) {
if (tz.getOffset(when) == offset &&
tz.inDaylightTime(d) == dst) {
best = tz;
}
}
}
return best;
}
/**
* Return list of unique time zones for the country. Do not modify
*
* @param country to find
* @return list of unique time zones, maybe empty but never null. Do not modify.
* @hide
*/
public static ArrayList<TimeZone> getTimeZonesWithUniqueOffsets(String country) {
synchronized(sLastUniqueLockObj) {
if ((country != null) && country.equals(sLastUniqueCountry)) {
if (DBG) {
Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
country + "): return cached version");
}
return sLastUniqueZoneOffsets;
}
}
Collection<TimeZone> zones = getTimeZones(country);
ArrayList<TimeZone> uniqueTimeZones = new ArrayList<TimeZone>();
for (TimeZone zone : zones) {
// See if we already have this offset,
// Using slow but space efficient and these are small.
boolean found = false;
for (int i = 0; i < uniqueTimeZones.size(); i++) {
if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
found = true;
break;
}
}
if (found == false) {
if (DBG) {
Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
zone.getRawOffset() + " zone.getID=" + zone.getID());
}
uniqueTimeZones.add(zone);
}
}
synchronized(sLastUniqueLockObj) {
// Cache the last result
sLastUniqueZoneOffsets = uniqueTimeZones;
sLastUniqueCountry = country;
return sLastUniqueZoneOffsets;
}
}
/**
* Returns the time zones for the country, which is the code
* attribute of the timezone element in time_zones_by_country.xml. Do not modify.
*
* @param country is a two character country code.
* @return TimeZone list, maybe empty but never null. Do not modify.
* @hide
*/
public static ArrayList<TimeZone> getTimeZones(String country) {
synchronized (sLastLockObj) {
if ((country != null) && country.equals(sLastCountry)) {
if (DBG) Log.d(TAG, "getTimeZones(" + country + "): return cached version");
return sLastZones;
}
}
ArrayList<TimeZone> tzs = new ArrayList<TimeZone>();
if (country == null) {
if (DBG) Log.d(TAG, "getTimeZones(null): return empty list");
return tzs;
}
Resources r = Resources.getSystem();
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
try {
XmlUtils.beginDocument(parser, "timezones");
@@ -73,43 +184,33 @@ public class TimeUtils {
if (country.equals(code)) {
if (parser.next() == XmlPullParser.TEXT) {
String maybe = parser.getText();
// If the current time zone is from the right country
// and meets the other known properties, keep it
// instead of changing to another one.
if (maybe.equals(currentName)) {
if (currentOffset == offset && currentDst == dst) {
return current;
}
}
// Otherwise, take the first zone from the right
// country that has the correct current offset and DST.
// (Keep iterating instead of returning in case we
// haven't encountered the current time zone yet.)
if (best == null) {
TimeZone tz = TimeZone.getTimeZone(maybe);
if (tz.getOffset(when) == offset &&
tz.inDaylightTime(d) == dst) {
best = tz;
String zoneIdString = parser.getText();
TimeZone tz = TimeZone.getTimeZone(zoneIdString);
if (tz.getID().startsWith("GMT") == false) {
// tz.getID doesn't start not "GMT" so its valid
tzs.add(tz);
if (DBG) {
Log.d(TAG, "getTimeZone('" + country + "'): found tz.getID=="
+ ((tz != null) ? tz.getID() : "<no tz>"));
}
}
}
}
}
} catch (XmlPullParserException e) {
Log.e(TAG, "Got exception while getting preferred time zone.", e);
Log.e(TAG, "Got xml parser exception getTimeZone('" + country + "'): e=", e);
} catch (IOException e) {
Log.e(TAG, "Got exception while getting preferred time zone.", e);
Log.e(TAG, "Got IO exception getTimeZone('" + country + "'): e=", e);
} finally {
parser.close();
}
return best;
synchronized(sLastLockObj) {
// Cache the last result;
sLastZones = tzs;
sLastCountry = country;
return sLastZones;
}
}
/**

View File

@@ -2566,13 +2566,20 @@ public final class RIL extends BaseCommands implements CommandsInterface {
result[0] = ret;
result[1] = Long.valueOf(nitzReceiveTime);
if (mNITZTimeRegistrant != null) {
boolean ignoreNitz = SystemProperties.getBoolean(
TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
mNITZTimeRegistrant
.notifyRegistrant(new AsyncResult (null, result, null));
if (ignoreNitz) {
if (RILJ_LOGD) riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
} else {
// in case NITZ time registrant isnt registered yet
mLastNITZTimeInfo = result;
if (mNITZTimeRegistrant != null) {
mNITZTimeRegistrant
.notifyRegistrant(new AsyncResult (null, result, null));
} else {
// in case NITZ time registrant isnt registered yet
mLastNITZTimeInfo = result;
}
}
break;

View File

@@ -182,4 +182,9 @@ public interface TelephonyProperties
* in commercial configuration.
*/
static final String PROPERTY_TEST_CSIM = "persist.radio.test-csim";
/**
* Ignore RIL_UNSOL_NITZ_TIME_RECEIVED completely, used for debugging/testing.
*/
static final String PROPERTY_IGNORE_NITZ = "telephony.test.ignore.nitz";
}

View File

@@ -59,9 +59,12 @@ import android.util.EventLog;
import android.util.Log;
import android.util.TimeUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.TimeZone;
/**
@@ -112,6 +115,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
private boolean mGotCountryCode = false;
private ContentResolver cr;
/** Boolean is true is setTimeFromNITZString was called */
private boolean mNitzUpdatedTime = false;
String mSavedTimeZone;
long mSavedTime;
long mSavedAtTime;
@@ -698,6 +704,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
newCellLoc.setStateInvalid();
setSignalStrengthDefaultValues();
mGotCountryCode = false;
mNitzUpdatedTime = false;
pollStateDone();
break;
@@ -706,6 +713,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
newCellLoc.setStateInvalid();
setSignalStrengthDefaultValues();
mGotCountryCode = false;
mNitzUpdatedTime = false;
pollStateDone();
break;
@@ -826,6 +834,12 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
if (hasRegistered) {
mNetworkAttachedRegistrants.notifyRegistrants();
if (DBG) {
log("pollStateDone: registering current mNitzUpdatedTime=" +
mNitzUpdatedTime + " changing to false");
}
mNitzUpdatedTime = false;
}
if (hasChanged) {
@@ -840,28 +854,71 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
if (operatorNumeric == null) {
if (DBG) {
log("pollStateDone: operatorNumeric is null:" +
" clear PROPERTY_OPERATOR_ISO_COUNTRY");
}
phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
mGotCountryCode = false;
mNitzUpdatedTime = false;
} else {
String iso = "";
String mcc = operatorNumeric.substring(0, 3);
try{
iso = MccTable.countryCodeForMcc(Integer.parseInt(
operatorNumeric.substring(0,3)));
iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
} catch ( NumberFormatException ex){
loge("countryCodeForMcc error" + ex);
loge("pollStateDone: countryCodeForMcc error" + ex);
} catch ( StringIndexOutOfBoundsException ex) {
loge("countryCodeForMcc error" + ex);
loge("pollStateDone: countryCodeForMcc error" + ex);
}
if (DBG) {
log("pollStateDone: operatorNumeric=" + operatorNumeric +
" mcc=" + mcc + " iso=" + iso);
}
phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
mGotCountryCode = true;
TimeZone zone = null;
if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso) &&
getAutoTimeZone()) {
// Test both paths if ignore nitz is true
boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
TelephonyProperties.PROPERTY_IGNORE_NITZ, false) &&
((SystemClock.uptimeMillis() & 1) == 0);
ArrayList<TimeZone> uniqueZones = TimeUtils.getTimeZonesWithUniqueOffsets(iso);
if ((uniqueZones.size() == 1) || testOneUniqueOffsetPath) {
zone = uniqueZones.get(0);
if (DBG) {
log("pollStateDone: no nitz but one TZ for iso=" + iso +
" with zone.getID=" + zone.getID() +
" testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
}
setAndBroadcastNetworkSetTimeZone(zone.getID());
} else {
if (DBG) {
log("pollStateDone: there are " + uniqueZones.size() +
" unique offsets for iso='" + iso +
" testOneUniqueOffsetPath=" + testOneUniqueOffsetPath +
"', do nothing");
}
}
}
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 (DBG) {
log("pollStateDone: mNeedFixZone==true zoneName='" + zoneName +
"' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
" iso='" + iso +
"' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
}
if ((mZoneOffset == 0) && (mZoneDst == false) &&
(zoneName != null) && (zoneName.length() > 0) &&
(Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
@@ -876,22 +933,36 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
// Adjust the saved NITZ time to account for tzOffset.
mSavedTime = mSavedTime - tzOffset;
}
if (DBG) log("pollStateDone: using default TimeZone");
} 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);
if (DBG) log("pollStateDone: using NITZ TimeZone");
} else {
zone = TimeUtils.getTimeZone(mZoneOffset,
mZoneDst, mZoneTime, iso);
zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, iso);
if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
}
mNeedFixZone = false;
if (zone != null) {
log("pollStateDone: zone != null zone.getID=" + zone.getID());
if (getAutoTimeZone()) {
setAndBroadcastNetworkSetTimeZone(zone.getID());
}
saveNitzTimeZone(zone.getID());
} else {
log("pollStateDone: zone == null");
}
} else {
if (DBG) {
String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
zone = TimeZone.getDefault();
log("pollStateDone: mNeedFixZone==false zoneName='" + zoneName +
"' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
" iso='" + iso +
"' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
}
}
}
@@ -1440,6 +1511,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
long end = SystemClock.elapsedRealtime();
log("NITZ: end=" + end + " dur=" + (end - start));
}
mNitzUpdatedTime = true;
} finally {
mWakeLock.release();
}
@@ -1489,6 +1561,10 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("time-zone", zoneId);
phone.getContext().sendStickyBroadcast(intent);
if (DBG) {
log("setAndBroadcastNetworkSetTimeZone: call alarm.setTimeZone and broadcast zoneId=" +
zoneId);
}
}
/**