Periodically sync time on all connected devices

Sync time every 43 hours, 53 minutes and 23 seconds.
Interval is a bit smaller than 2 days.
Interval is a prime (in seconds) so time of sync will slide over time.

If next DST change is less than 48 hours in future, wait for it.
This commit is contained in:
Davis Mosenkovs 2024-02-15 22:07:08 +02:00
parent 64c024a5ce
commit 2e9176560d
3 changed files with 32 additions and 20 deletions

View File

@ -246,7 +246,7 @@ public class GBApplication extends Application {
loadAppsPebbleBlackList(); loadAppsPebbleBlackList();
PeriodicExporter.enablePeriodicExport(context); PeriodicExporter.enablePeriodicExport(context);
TimeChangeReceiver.scheduleNextDstChange(context); TimeChangeReceiver.scheduleNextDstChangeOrPeriodicSync(context);
if (isRunningMarshmallowOrLater()) { if (isRunningMarshmallowOrLater()) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

View File

@ -44,7 +44,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class TimeChangeReceiver extends BroadcastReceiver { public class TimeChangeReceiver extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(TimeChangeReceiver.class); private static final Logger LOG = LoggerFactory.getLogger(TimeChangeReceiver.class);
public static final String ACTION_DST_CHANGED = "nodomain.freeyourgadget.gadgetbridge.DST_CHANGED"; public static final String ACTION_DST_CHANGED_OR_PERIODIC_SYNC = "nodomain.freeyourgadget.gadgetbridge.DST_CHANGED_OR_PERIODIC_SYNC";
public static final long PERIODIC_SYNC_INTERVAL_MS = 158003000; // 43:53:23.000
public static final long PERIODIC_SYNC_INTERVAL_MAX_MS = 172800000; // 48 hours
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -63,7 +65,7 @@ public class TimeChangeReceiver extends BroadcastReceiver {
switch (action) { switch (action) {
case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_TIME_CHANGED:
case Intent.ACTION_TIMEZONE_CHANGED: case Intent.ACTION_TIMEZONE_CHANGED:
case ACTION_DST_CHANGED: case ACTION_DST_CHANGED_OR_PERIODIC_SYNC:
// Continue after the switch // Continue after the switch
break; break;
default: default:
@ -72,38 +74,48 @@ public class TimeChangeReceiver extends BroadcastReceiver {
} }
final Date newTime = GregorianCalendar.getInstance().getTime(); final Date newTime = GregorianCalendar.getInstance().getTime();
LOG.info("Time or Timezone changed, syncing with device: {} ({}), {}", DateTimeUtils.formatDate(newTime), newTime.toGMTString(), intent.getAction()); LOG.info("Time/Timezone changed or periodic sync, syncing with device: {} ({}), {}", DateTimeUtils.formatDate(newTime), newTime.toGMTString(), intent.getAction());
GBApplication.deviceService().onSetTime(); GBApplication.deviceService().onSetTime();
// Reschedule the next DST change, since the timezone may have changed // Reschedule the next DST change, since the timezone may have changed
scheduleNextDstChange(context); scheduleNextDstChangeOrPeriodicSync(context);
} }
/** /**
* Schedule an alarm to trigger on the next DST change, since ACTION_TIMEZONE_CHANGED is not broadcast otherwise. * Schedule an alarm to trigger on the next DST change, since ACTION_TIMEZONE_CHANGED is not broadcast otherwise
* or schedule an alarm to trigger after PERIODIC_SYNC_INTERVAL_MS (whichever is earlier).
* *
* @param context the context * @param context the context
*/ */
public static void scheduleNextDstChange(final Context context) { public static void scheduleNextDstChangeOrPeriodicSync(final Context context) {
final ZoneId zoneId = ZoneId.systemDefault(); final ZoneId zoneId = ZoneId.systemDefault();
final ZoneRules zoneRules = zoneId.getRules(); final ZoneRules zoneRules = zoneId.getRules();
final Instant now = Instant.now(); final Instant now = Instant.now();
final ZoneOffsetTransition transition = zoneRules.nextTransition(now); final ZoneOffsetTransition transition = zoneRules.nextTransition(now);
if (transition == null) {
LOG.warn("No DST transition found for {}", zoneId);
return;
}
final long nextDstMillis = transition.getInstant().toEpochMilli(); final Intent i = new Intent(ACTION_DST_CHANGED_OR_PERIODIC_SYNC);
final long delayMillis = nextDstMillis - now.toEpochMilli() + 5000L;
final Intent i = new Intent(ACTION_DST_CHANGED);
final PendingIntent pi = PendingIntentUtils.getBroadcast(context, 0, i, 0, false); final PendingIntent pi = PendingIntentUtils.getBroadcast(context, 0, i, 0, false);
final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final boolean exactAlarm = canScheduleExactAlarms(context, am);
LOG.info("Scheduling next DST change: {} (in {} millis) (exact = {})", nextDstMillis, delayMillis, exactAlarm); boolean exactAlarm = false;
long delayMillis = PERIODIC_SYNC_INTERVAL_MS;
if (transition != null) {
final long nextDstMillis = transition.getInstant().toEpochMilli();
final long dstDelayMillis = nextDstMillis - now.toEpochMilli() + 5000L;
if (dstDelayMillis < PERIODIC_SYNC_INTERVAL_MAX_MS) {
exactAlarm = canScheduleExactAlarms(context, am);
delayMillis = dstDelayMillis;
LOG.info("Scheduling next DST change: {} (in {} millis) (exact = {})", nextDstMillis, delayMillis, exactAlarm);
}
} else {
LOG.warn("No DST transition found for {}", zoneId);
}
if (delayMillis == PERIODIC_SYNC_INTERVAL_MS) {
LOG.info("Scheduling next periodic time sync in {} millis (exact = {})", delayMillis, exactAlarm);
}
am.cancel(pi); am.cancel(pi);
@ -113,7 +125,7 @@ public class TimeChangeReceiver extends BroadcastReceiver {
am.setExact(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delayMillis, pi); am.setExact(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delayMillis, pi);
scheduledExact = true; scheduledExact = true;
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Failed to schedule exact alarm for next DST change", e); LOG.error("Failed to schedule exact alarm for next DST change or periodic time sync", e);
} }
} }
@ -126,7 +138,7 @@ public class TimeChangeReceiver extends BroadcastReceiver {
am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delayMillis, pi); am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delayMillis, pi);
} }
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Failed to schedule inexact alarm next DST change", e); LOG.error("Failed to schedule inexact alarm for next DST change or periodic time sync", e);
} }
} }
} }

View File

@ -1179,7 +1179,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.TIME_SET"); filter.addAction("android.intent.action.TIME_SET");
filter.addAction("android.intent.action.TIMEZONE_CHANGED"); filter.addAction("android.intent.action.TIMEZONE_CHANGED");
filter.addAction(TimeChangeReceiver.ACTION_DST_CHANGED); filter.addAction(TimeChangeReceiver.ACTION_DST_CHANGED_OR_PERIODIC_SYNC);
registerReceiver(mTimeChangeReceiver, filter); registerReceiver(mTimeChangeReceiver, filter);
} }
if (mBlueToothPairingRequestReceiver == null) { if (mBlueToothPairingRequestReceiver == null) {