How to synchronize the clock via NTP in Android

How to synchronize the clock via NTP in Android

The device is now considered the source of truth. This includes the time we send with up with requests  -  no longer can we trust the time when the server receives an API request, as the actual action could have happened many minutes ago. If you want the current time, you have to ask the device and trust it.

Let's add TrueTime and we will have synchronized clock via NTP. As result we may don't trust to value in new Date().

Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems over packet-switched, variable-latency data networks.

TrueTime calculates the time in following way. It makes a network (SNTP) request to a pool of NTP servers that determine the actual time once, right at the very beginning of a session with the app. Then establishes the delta between the device uptime and the response from the network request. Each time now is requested subsequently, we account for that offset and return a corrected Date object.

We can make NTP request either via AsyncTask or via RxJava.

NTP request via AsyncTask

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

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

dependencies {
    compile 'com.github.instacart.truetime-android:library:3.3'
}

Create InitTrueTimeAsyncTask.java with following code.

public class InitTrueTimeAsyncTask extends AsyncTask<Void, Void, Void> {
    private Context ctx;

    public InitTrueTimeAsyncTask (Context context){
        ctx = context;
    }

    protected Void doInBackground(Void... params) {
        try {
            TrueTime.build()
                    .withSharedPreferences(ctx)
                    .withNtpHost("time.google.com")
                    .withLoggingEnabled(false)
                    .withConnectionTimeout(31_428)
                    .initialize();
        } catch (IOException e) {
            e.printStackTrace();
            Log.d(Constants.TAG, "Exception when trying to get TrueTime", e);
        }
        return null;
    }
}

Example of MainActivty.java.

public class MainActivity extends AppCompatActivity {
    String TAG = "DBG";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initTrueTime(this);
    }

    public void onButtonClick(View v) {
        Log.d(TAG, getTrueTime().toString());
    }

    public static Date getTrueTime() {
        Date date = TrueTime.isInitialized() ? TrueTime.now() : new Date();
        return date;
    }

    public static void initTrueTime(Context ctx) {
        if (isNetworkConnected(ctx)) {
            if (!TrueTime.isInitialized()) {
                InitTrueTimeAsyncTask trueTime = new InitTrueTimeAsyncTask(ctx);
                trueTime.execute();
            }
        }
    }

    public static boolean isNetworkConnected(Context ctx) {
        ConnectivityManager cm = (ConnectivityManager) ctx
            .getSystemService (Context.CONNECTIVITY_SERVICE);
        NetworkInfo ni = cm.getActiveNetworkInfo();

        return ni != null && ni.isConnectedOrConnecting();
    }
}

NTP request via RxJava

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

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

dependencies {
    compile 'com.github.instacart.truetime-android:library-extension-rx:3.3'
}

Example of MainActivty.java.

public class MainActivity extends AppCompatActivity {
    String TAG = "DBG";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TrueTimeRx.build()
            .withConnectionTimeout(31428)
            .withRetryCount(100)
            .withSharedPreferences(this)
            .withLoggingEnabled(true)
            .initializeRx("time.google.com")
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Date>() {
                @Override
                public void accept(Date date) {
                    if (!TrueTimeRx.isInitialized()) {
                        Log.d(TAG, "Sorry TrueTime not yet initialized");
                        return;
                    }

                    Date trueTime = TrueTimeRx.now();
                    Log.d(TAG, "Date: " + trueTime);
                }
            }, new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) {
                    Log.d(TAG, "Error: ", throwable);
                }
            }, new Action() {
                @Override
                public void run() {
                    Log.d(TAG, "Completed!");
                }
            });
    }
}

At time of writing we have TrueTime 3.3 and it has a issue with testing on emulator. So use real device.

comments powered by Disqus