Bug 838416 - Defend against insane clocks. r=nalexander

This commit is contained in:
Richard Newman 2013-02-14 13:50:37 -08:00
parent 03f3998dbf
commit 2b8d9f1440
4 changed files with 99 additions and 4 deletions

View File

@ -8,6 +8,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.mozilla.gecko.background.BackgroundConstants;
import org.mozilla.gecko.sync.GlobalConstants;
import org.mozilla.gecko.sync.Logger;
import android.app.AlarmManager;
@ -63,6 +64,43 @@ public class AnnouncementsBroadcastService extends IntentService {
public static void recordLastLaunch(final Context context) {
final long now = System.currentTimeMillis();
final SharedPreferences preferences = context.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, BackgroundConstants.SHARED_PREFERENCES_MODE);
// One of several things might be true, according to our logs:
//
// * The new current time is older than the last
// * or way in the future
// * or way in the distant past
// * or it's reasonable.
//
// Furthermore, when we come to calculate idle we might find that the clock
// is dramatically different that the current time is thirteen years older
// than our saved timestamp (system clock resets to 2000 on battery change),
// or it's thirty years in the future (previous timestamp was saved as 0).
//
// We should try to do something vaguely sane in these situations.
long previous = preferences.getLong(AnnouncementsConstants.PREF_LAST_LAUNCH, -1);
if (previous == -1) {
Logger.debug(LOG_TAG, "No previous launch recorded.");
}
if (now < GlobalConstants.BUILD_TIMESTAMP) {
Logger.warn(LOG_TAG, "Current time " + now + " is older than build date " +
GlobalConstants.BUILD_TIMESTAMP + ". Ignoring until clock is corrected.");
return;
}
if (now > AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP) {
Logger.warn(LOG_TAG, "Launch time " + now + " is later than max sane launch timestamp " +
AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP +
". Ignoring until clock is corrected.");
return;
}
if (previous > now) {
Logger.debug(LOG_TAG, "Previous launch " + previous + " later than current time " +
now + ", but new time is sane. Accepting new time.");
}
preferences.edit().putLong(AnnouncementsConstants.PREF_LAST_LAUNCH, now).commit();
}

View File

@ -13,6 +13,8 @@ public class AnnouncementsConstants {
// Not `final` so we have the option to turn this on at runtime with a magic addon.
public static boolean DISABLED = false;
public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
public static final String GLOBAL_LOG_TAG = "GeckoAnnounce";
public static final String ACTION_ANNOUNCEMENTS_PREF = "@ANDROID_PACKAGE_NAME@.ANNOUNCEMENTS_PREF";
@ -35,6 +37,14 @@ public class AnnouncementsConstants {
public static long DEFAULT_BACKOFF_MSEC = 2 * 24 * 60 * 60 * 1000; // Two days. Used if no Retry-After header.
public static long MINIMUM_FETCH_INTERVAL_MSEC = 60 * 60 * 1000; // 1 hour.
// Stop reporting idle counts once they hit one year.
public static long MAX_SANE_IDLE_DAYS = 365;
// Don't track last launch if the timestamp is ridiculously out of range:
// four years after build.
public static long LATEST_ACCEPTED_LAUNCH_TIMESTAMP = GlobalConstants.BUILD_TIMESTAMP +
4 * 365 * MILLISECONDS_PER_DAY;
public static String ANNOUNCE_USER_AGENT = "Firefox Announcements " + GlobalConstants.MOZ_APP_VERSION;
public static String ANNOUNCE_CHANNEL = GlobalConstants.MOZ_UPDATE_CHANNEL.replace("default", GlobalConstants.MOZ_OFFICIAL_BRANDING ? "release" : "dev");
}

View File

@ -55,12 +55,57 @@ public class AnnouncementsFetcher {
return GlobalConstants.ANDROID_CPU_ARCH;
}
protected static int getIdleDays(final long lastLaunch) {
if (lastLaunch == 0) {
/**
* Return the number of days that we've been idle, assuming that we have a
* sane last launch time and the current time is within range. If no sane idle
* time can be returned, we return -1.
*
* @param lastLaunch
* Time at which the browser was last launched, in milliseconds since epoch.
* @param now
* Milliseconds since epoch for which idle time should be calculated.
* @return number of idle days, or -1 if out of range.
*/
protected static int getIdleDays(final long lastLaunch, final long now) {
if (lastLaunch <= 0) {
return -1;
}
final long idleMillis = System.currentTimeMillis() - lastLaunch;
return (int) (idleMillis / MILLISECONDS_PER_DAY);
if (now < GlobalConstants.BUILD_TIMESTAMP) {
Logger.warn(LOG_TAG, "Current time " + now + " earlier than build date. Not calculating idle.");
return -1;
}
if (now < lastLaunch) {
Logger.warn(LOG_TAG, "Current time " + now + " earlier than last launch! Not calculating idle.");
return -1;
}
final long idleMillis = now - lastLaunch;
final int idleDays = (int) (idleMillis / MILLISECONDS_PER_DAY);
if (idleDays > AnnouncementsConstants.MAX_SANE_IDLE_DAYS) {
Logger.warn(LOG_TAG, "Idle from " + lastLaunch + " until " + now +
", which is insane. Not calculating idle.");
return -1;
}
return idleDays;
}
/**
* Return the number of days that we've been idle, assuming that we have a
* sane last launch time and the current time is within range. If no sane idle
* time can be returned, we return -1.
* The current time will be calculated from {@link System#currentTimeMillis()}.
*
* @param lastLaunch
* Unix timestamp at which the browser was last launched.
* @return number of idle days, or -1 if out of range.
*/
protected static int getIdleDays(final long lastLaunch) {
final long now = System.currentTimeMillis();
return getIdleDays(lastLaunch, now);
}
public static void fetchAnnouncements(URI uri, AnnouncementsFetchDelegate delegate) {

View File

@ -21,6 +21,8 @@ public class GlobalConstants {
public static final boolean MOZ_OFFICIAL_BRANDING = false;
#endif
public static final long BUILD_TIMESTAMP = @MOZ_BUILD_TIMESTAMP@;
public static final String MOZ_APP_VERSION = "@MOZ_APP_VERSION@";
public static final String BROWSER_INTENT_PACKAGE = "@ANDROID_PACKAGE_NAME@";
public static final String BROWSER_INTENT_CLASS = BROWSER_INTENT_PACKAGE + ".App";