Giter Site home page Giter Site logo

mutime's Introduction

NOTICE: THIS LIBRARY IS NOT MAINTAINED

A rewrite into Kotlin was performed in September 2018, and pushed to master in May 2020.

No further maintenance (beyond releasing this work) is planned, although contributions (in the form of pull requests) are always welcome.

This is alpha-quality software; use at your own risk.

MuTime for Android

MuTime

NTP client for Android. Calculate the date and time "now" impervious to manual changes to device clock time.

In certain applications it becomes important to get the real or "true" date and time. On most devices, if the clock has been changed manually, then a new Date() instance gives you a time impacted by local settings.

Users may do this for a variety of reasons, like being in different timezones, trying to be punctual by setting their clocks 5 โ€“ 10 minutes early, etc. Your application or service may want a date that is unaffected by these changes and reliable as a source of truth. MuTime gives you that.

Originally a fork, now a major rewrite of Instacart's TrueTime library.

You can read more about the use case in Instacart's blog post.

Installation

We use Jitpack to host the library.

Add this to your application's build.gradle file:

repositories {
    maven {
        url "https://jitpack.io"
    }
}

dependencies {
    implementation 'com.github.medavox:MuTime:v0.7'
}

Usage

//optionally enable the disk cache
MuTime.enableDiskCache(/*Context*/ this);//this is what actually hardens MuTime against clock changes and reboots

MuTime.initialize("time.google.com");//use any ntp server address here, eg "time.apple.com"

//get the real time in unix epoch format (milliseconds since midnight on 1 january 1970)
try {
    long theActualTime = MuTime.now();//throws MissingTimeDataException if we don't know the time
}
catch (MissingTimeDataException e) {
    Log.e("MuTime", "failed to get the actual time:+e.getMessage());
}

initialize(String) must be run on a background thread. If you run it on the main (UI) thread, you will get a NetworkOnMainThreadException

How is the true time calculated?

It's pretty simple actually. We:-

  • make a request to one or more NTP servers that give us the true time.
    • DNS-resolve the user-provided strings of NTP URLs into 1 or more IP addresses.
    • For each ip address:
      • shoot multiple requests to it (currently 4 times),
      • establish the delta between device uptime at request-time and response-time.
      • picks the lowest-latency response (if any).
    • pick the response from all the queried IP addresses with the median system clock offset,
    • and persist that to disk.

On each subsequent request for the true time "now", we compute the correct time from that stored offset.

Once we have this offset information, it's valid until you reboot your device or manually change the system clock. This means if you enable the disk caching feature, after a single successful NTP request you can use the information on-disk directly without ever making another network request. This applies even across application kills -- which can happen frequently if a user has a memory starved device.

Reason For Fork

I needed a way of providing reliable time for another app, preserving the correct time across 1) android clock adjustments by the user and 2) device reboots. Although the NTP client implementation in TrueTime's library is more sophisticated than Google's hidden Android SntpClient, I needed even more reliable time-keeping for my use case. It was also apparent (at the time of forking) that instacart/Kaushik Gopal's plans for future development did not fit with my own needs (judging from development branches).

Differences From Upstream

MuTime implements a more 'stubborn' Persistence solution, which preserves information about the correct time even after clock changes and device reboots. It's a beefed-up version of TrueTime's Disk Cache functionality.

The public API has been revamped, and the underlying codebase largely rewritten, to improve maintainability (in my humble opinion).

UPDATE May 2020: Kotlin rewrite from September 2018 has been pushed to master.

Notes/tips:

  • You can read up on Wikipedia the differences between SNTP and NTP.

Troubleshooting/Exception handling:

When you execute the MuTime initialization, you are very likely to get an InvalidNtpServerResponseException because of root delay violation or root dispersion violation the first time. This is an expected occurrence as per the NTP Spec and needs to be handled.

Why does this happen?

The NTP protocol works on UDP:

It has no handshaking dialogues, and thus exposes the user's program to any unreliability of the underlying network and so there is no guarantee of delivery, ordering, or duplicate protection

UDP is suitable for purposes where error checking and correction is either not necessary or is performed in the application, avoiding the overhead of such processing at the network interface level. Time-sensitive applications often use UDP because dropping packets is preferable to waiting for delayed packets, which may not be an option in a real-time system

(Wikipedia's page, emphasis our own)

This means it is highly plausible that we get faulty data packets. These are caught by the library and surfaced to the API consumer as an InvalidNtpServerResponseException. See this portion of the code for the various checks that we guard against.

These guards are extremely important to guarantee accurate time and cannot be avoided.

If MuTime fails to initialise (because of the above exception being thrown), then a MissingTimeDataException is thrown if you try to request an actual date via MuTime.now().

How do I handle or protect against this in my application?

It's pretty simple:

  • Keep retrying the request, until you get a successful one. Yes it does happen eventually :)
  • Try picking a better NTP pool server. In our experience time.apple.com has worked best

Useful Links


License

Original Work (c) Instacart/Kaushik Gopal 2016-2017

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.

mutime's People

Contributors

dladowitz avatar eranpolo avatar erluxman avatar kaushikgopal avatar mataanin avatar medavox avatar msanders avatar npace avatar ravidsrk avatar sveltema avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

mutime's Issues

Crash when trying to use NTP

SNTP request failed for 216.239.35.12: java.lang.IllegalArgumentException: All arguments must be set. Passed Values:roundTripDelay=52; uptimeOffset=1564054980723; systemClockOffset=0
EXCEPTION			java.lang.IllegalArgumentException: All arguments must be set. Passed Values:roundTripDelay=52; uptimeOffset=1564054980723; systemClockOffset=0

I've got this exception when trying to use NTP method
Ntp.performNtpAlgorithm(Ntp.resolveMultipleNtpHosts("time.google.com", "time.apple.com"));

THIS LIBRARY IS NO LONGER SUPPORTED!

It appears that this library is no longer supported. If so, the author should add this in the README using capital letters so people are aware before deciding to use this library.

FATAL error

Today while running my app your library gave the following error message in logcat:

E/SntpClient: SNTP request failed for 216.239.35.0: java.lang.IllegalArgumentException: All arguments must be set. Passed Values:roundTripDelay=61; uptimeOffset=1585002762271; systemClockOffset=0

and just below:

E/AndroidRuntime: FATAL EXCEPTION: Thread-31
    Process: com.xxxx.xxx, PID: 21371
    java.lang.IllegalArgumentException: All arguments must be set. Passed Values:roundTripDelay=61; uptimeOffset=1585002762271; systemClockOffset=0
        at com.medavox.library.mutime.TimeData$Builder.build(TimeData.java:84)
        at com.medavox.library.mutime.SntpClient.requestTime(SntpClient.java:168)
        at com.medavox.library.mutime.SntpRequest.send(SntpRequest.java:68)
        at com.medavox.library.mutime.Ntp$4.performWork(Ntp.java:164)
        at com.medavox.library.mutime.Ntp$4.performWork(Ntp.java:160)
        at com.medavox.library.mutime.ParallelProcess$InternalWrapper.run(ParallelProcess.java:77)

What does it mean? Do I have any way to catch such errors (and also InvalidNtpServerResponseException ) when using the library?

I am using an LG V20 phone running Android 8 and the latest version of your library.

NPE TimeData.java:59 In receiver

EXCEPTION			java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.TIME_SET flg=0x25200010 } in com.medavox.library.mutime.TimeDataPreserver@c6760a4
	at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$-android_app_LoadedApk$ReceiverDispatcher$Args_52497(LoadedApk.java:1323)
	at android.app.-$Lambda$aS31cHIhRx41653CMnd4gZqshIQ.$m$7(Unknown Source:4)
	at android.app.-$Lambda$aS31cHIhRx41653CMnd4gZqshIQ.run(Unknown Source:39)
	at android.os.Handler.handleCallback(Handler.java:790)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:164)
	at android.app.ActivityThread.main(ActivityThread.java:6518)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'long com.medavox.library.mutime.TimeData.getRoundTripDelay()' on a null object reference
	at com.medavox.library.mutime.TimeData$Builder.<init>(TimeData.java:59)
	at com.medavox.library.mutime.TimeDataPreserver.onReceive(TimeDataPreserver.java:38)
	at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$-android_app_LoadedApk$ReceiverDispatcher$Args_52497(LoadedApk.java:1313)
	... 9 more

Trying to get the network time, it always times out!!

When I call:

MuTime.requestTimeFromServer("time.apple.com");

it always times out (Poll timed out). I tried with different servers and the result is always the same. It's not an Internet connection problem on my phone as all other apps work fine.

I am using an LG V20 phone with Android 8.0. Any suggestion on what may be wrong? I have pulled the latest code from your repo four or five days ago.

Thanks!

Library gets time but is not saved nor persistent

When the application first starts it says:

W/Persistence: not providing a Context to access SharedPreferences disables most of Persistence's features!
I/Persistence: instance:com.medavox.library.mutime.Persistence@be0eb7b
W/Persistence: no time data in memory, attempting to retrieve from SharedPreferences...
E/Persistence: Time Data was found to be invalid when checked! stored clock offset: 1020; stored uptime offset: 1587744810916; live clock: 1588274998296; live uptime: 46205117; Stored Clock difference: 1587744809896; live Clock difference: 1588228793178

then it tries to get time from network:

I/Ntp: Getting the time from 8 IP addresses: [time.google.com/216.239.35.12, pool.ntp.org/162.159.200.123, time.google.com/216.239.35.8, pool.ntp.org/193.29.63.150, time.google.com/216.239.35.4, pool.ntp.org/23.31.21.163, pool.ntp.org/66.228.59.187, time.google.com/216.239.35.0]...

After many NTP poll timeouts (?) the library finally gets the time:

Ntp: got time data "TimeData [Round Trip Delay: 62; System Clock offset: 184; Device Uptime offset: 1588228793362]" from 23.31.21.163

However, the next time I try to get the time from the library I get:

W/Persistence: no time data in memory, attempting to retrieve from SharedPreferences...
W/MuTime: Could not get current time.

and then after many of such lines above I get:

 W/System: A resource failed to call close. 
 I/chatty: uid=10135(com.cyphme.cyph) FinalizerDaemon identical 13 lines
 W/System: A resource failed to call close. 
 W/System: A resource failed to call close. 
 W/Persistence: no time data in memory, attempting to retrieve from SharedPreferences...
 W/MuTime: Could not get current time.
 W/System: A resource failed to call close. 
 I/chatty: uid=10135(com.cyphme.cyph) FinalizerDaemon identical 6 lines
 W/System: A resource failed to call close. 
 W/System: A resource failed to call close. 
 W/System: A resource failed to call close. 
 W/Persistence: no time data in memory, attempting to retrieve from SharedPreferences...
 W/MuTime: Could not get current time.

It looks pretty messy. Anybody has any idea on what's happening here?
Thanks.

Context to access SharedPreferences not available?

I am getting this warning:

W/Persistence: not providing a Context to access SharedPreferences disables most of Persistence's features!

However, I do not think I have control over this. Do I have to enable it somewhere?

Thanks.

RebootWatcher crashes with MissingTimeDataException, no registerRebootWatcher but in manifest..

RebootWatcher registered in manifest and this BroadcastReceiver give onReceive but MuTime system dont initialized.
Please dont register RebootWatcher in manifest!

com.medavox.library.mutime.DiskCache.getTimeData
DiskCache.kt, line 34
com.medavox.library.mutime.MissingTimeDataException: no time data in SharedPreferences. Has MuTime been run at least once?

com.medavox.library.mutime.DiskCache.getTimeData DiskCache.kt:34
com.medavox.library.mutime.RebootWatcher.onReceive RebootWatcher.kt:28
android.app.ActivityThread.handleReceiver ActivityThread.java:2419
android.app.ActivityThread.access$1700 ActivityThread.java:135
android.app.ActivityThread$H.handleMessage ActivityThread.java:1272
android.os.Handler.dispatchMessage Handler.java:102
android.os.Looper.loop Looper.java:136
android.app.ActivityThread.main ActivityThread.java:5047
java.lang.reflect.Method.invokeNative Method.java
java.lang.reflect.Method.invoke Method.java:515
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run ZygoteInit.java:828
com.android.internal.os.ZygoteInit.main ZygoteInit.java:644
dalvik.system.NativeStart.main NativeStart.java

Using full NTP of MuTime always times out but gets results (???)

When I use the call:

Ntp.performNtpAlgorithm(
    Ntp.resolveMultipleNtpHosts("pool.ntp.org", "time.google.com", "time.apple.com")
);

This is what I see in logcat:

2020-03-23 13:57:26.652 27347-27383/com.xxxxx I/Ntp: Getting the time from 6 IP addresses: [time.google.com/216.239.35.12, pool.ntp.org/198.255.68.106, time.google.com/216.239.35.8, time.google.com/216.239.35.4, pool.ntp.org/159.203.158.197, time.google.com/216.239.35.0]...
2020-03-23 13:57:56.708 27347-27559/com.xxxxx E/SntpClient: SNTP request failed for 216.239.35.12: java.net.SocketTimeoutException: Poll timed out
2020-03-23 13:58:26.771 27347-27557/com.xxxxx E/SntpClient: SNTP request failed for 216.239.35.12: java.net.SocketTimeoutException: Poll timed out
2020-03-23 13:58:26.808 27347-27555/com.xxxx V/Ntp: got time data "TimeData [Round Trip Delay: 30; System Clock offset: -2714; Device Uptime offset: 1583156701297]" from 216.239.35.12
2020-03-23 13:58:26.809 27347-27555/com.xxxxx D/Ntp: new median time:TimeData [Round Trip Delay: 30; System Clock offset: -2714; Device Uptime offset: 1583156701297]
2020-03-23 13:58:56.836 27347-27567/com.xxxxx E/SntpClient: SNTP request failed for 159.203.158.197: java.net.SocketTimeoutException: Poll timed out
2020-03-23 13:59:27.003 27347-27573/com.xxxxx E/SntpClient: SNTP request failed for 216.239.35.0: java.net.SocketTimeoutException: Poll timed out
2020-03-23 13:59:57.030 27347-27569/com.xxxxx E/SntpClient: SNTP request failed for 159.203.158.197: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:00:27.116 27347-27576/com.xxxxx E/SntpClient: SNTP request failed for 198.255.68.106: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:00:57.178 27347-27572/com.xxxxx E/SntpClient: SNTP request failed for 159.203.158.197: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:00:57.246 27347-27561/com.xxxxx V/Ntp: got time data "TimeData [Round Trip Delay: 45; System Clock offset: -2712; Device Uptime offset: 1583156701298]" from 216.239.35.4
2020-03-23 14:00:57.248 27347-27561/com.xxxxx D/Ntp: new median time:TimeData [Round Trip Delay: 45; System Clock offset: -2712; Device Uptime offset: 1583156701298]
2020-03-23 14:01:27.399 27347-27582/com.xxxxx E/SntpClient: SNTP request failed for 159.203.158.197: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:01:27.400 27347-27563/com.xxxxx V/Ntp: got time data "null" from 159.203.158.197
2020-03-23 14:01:57.429 27347-27577/com.xxxxx E/SntpClient: SNTP request failed for 216.239.35.0: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:01:57.699 27347-27556/com.xxxx V/Ntp: got time data "TimeData [Round Trip Delay: 84; System Clock offset: -2719; Device Uptime offset: 1583156701292]" from 198.255.68.106
2020-03-23 14:01:57.700 27347-27556/com.xxxxx D/Ntp: new median time:TimeData [Round Trip Delay: 30; System Clock offset: -2714; Device Uptime offset: 1583156701297]
2020-03-23 14:01:57.732 27347-27558/com.xxxx V/Ntp: got time data "TimeData [Round Trip Delay: 28; System Clock offset: -2715; Device Uptime offset: 1583156701296]" from 216.239.35.8
2020-03-23 14:02:27.761 27347-27580/com.xxxx E/SntpClient: SNTP request failed for 216.239.35.0: java.net.SocketTimeoutException: Poll timed out
2020-03-23 14:02:27.762 27347-27564/com.xxxx V/Ntp: got time data "TimeData [Round Trip Delay: 34; System Clock offset: -2716; Device Uptime offset: 1583156701295]" from 216.239.35.0
2020-03-23 14:02:27.764 27347-27564/com.xxxx D/Ntp: new median time:TimeData [Round Trip Delay: 28; System Clock offset: -2715; Device Uptime offset: 1583156701296]

I am using an LG v20 with Android 8 and the latest version of MuTime.

Error reboot class

    <receiver android:name="com.medavox.library.mutime.Persistence">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>

Unable to instantiate receiver com.medavox.library.mutime.Persistence

java.lang.RuntimeException: Unable to instantiate receiver com.medavox.library.mutime.Persistence: java.lang.IllegalAccessException: access to class not allowed
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2400)
at android.app.ActivityThread.access$1700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1272)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
at dalvik.system.NativeStart.main(Native Method)Caused by: java.lang.IllegalAccessException: access to class not allowed
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1208)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2395)
... 10 more
java.lang.IllegalAccessException: access to class not allowed
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1208)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2395)
at android.app.ActivityThread.access$1700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1272)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604) at dalvik.system.NativeStart.main(Native Method)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.