0

I'm developing an app where I get to retrieve current date in several places, by using new Date(), I'd like that if the user has changed the date in the phone it's not allowed to take the date as it's wrong.

Due to the nature of the application, it's possible that when the operation that takes the date is done, there may be no internet connection, so instead of dealing with something like an NTP server, we can more and less ensure that the date is correct if the user has options Use network date/time and Use network timezone enabled and he has connectivity to telephony network (sure, if may be possible to tamper the date but it looks secure enough).

Problem is that it may be also be possible that there may be no telephony network where the phone is being used and I've checked the following:

  1. You can disable Use network date/time and set a date of your liking.

  2. You activate airplane mode (or similar no connectivity situation)

  3. You restart the phone.

  4. You enable Use network date/time.

Now, despite having set Use network date/time and Use network timezone, there's an incorrect date on the phone. Indeed, if I quit airplane mode date is properly set again.

Maybe a situation like the previous could be detected with something like a service that checks that the user has restarted the phone, and then a service at phone startup that determines date is taken again properly, so then app checks that service says that it's properly taken and so it allows the user to take it... But all this seems hard to do and we may bug honest users that had to restart their phones in a zone with no telephony connectivity and then try to use the app without trying to trick other date.

So, any idea on a not too complicated method and no bugging for honest users that could achieve getting the date with no connectivity of any class? Or is this impossible?

user2638180
  • 1,013
  • 16
  • 37
  • 1
    what is considered a `correct` date ? what happens if i'm out by seconds or milliseconds ? you're probably not looking for a correct date, but rather a range – a_local_nobody Oct 15 '21 at 14:15
  • Yes, as long as it's not something like I should have done the operation yesterday, but I didn't so I do it today but setting as date yesterday it's ok. – user2638180 Oct 15 '21 at 14:18
  • 1
    I guess a first start would be not to use the `new Date()` but to get the date/time in GMT. This way you already solve the problem of someone that changes timezone to cheat. You can also store the last time that the app was used. With that you can disable cheating in the past. For cheating in the future I do not see a simple solution. – Marco Oct 15 '21 at 14:21
  • 1
    The only thing you can do, and avoid these problems, is to sync with server-time. Then, after doing that, there's an easy way to know for sure. – Shark Oct 15 '21 at 15:51

3 Answers3

2

In order to get system time in Java you could use System.currentTimeMillis(), although this module synchronizes with the phone time and date. In order to get system time without relation to the phone date and time, you could use System.nanoTime(), which is independent of the phone clock.

However, the nanoTime module is not absolute- it measures in relation to some arbitrary point, so you should save some value of nanoTime in the code where it is a known date, and then compare to it.

See how to format the time from nanoSeconds to date in this article.

Related article about nanoTime.

Xiddoc
  • 3,369
  • 3
  • 11
  • 37
Roy Nevo
  • 21
  • 3
1

You are talking about

System.currentTimeMillis(). 
// user can change it by adjusting the DATE of the phone.

But this can help you.

SystemClock.elapsedRealtime()

Difference between these twos can help you.

https://developer.android.com/reference/android/os/SystemClock

  • After coding, You should convert miliseconds to Date
KUTAY ZORLU
  • 93
  • 1
  • 9
1

No offline-only solution will satisfy the "how do i know user hasn't changed the date to a wrong one" criteria.

The only thing you can do, and avoid these problems, is to sync with server-time. Then, after doing that, there's an easy way to know for sure.

You'd want to use SystemClock.elapsedRealtime() (which resets after every boot, and a new servertime sync is needed)

Something along these lines:

public class DeviceTimeProvider {

    public long getElapsedRealtimeMillis() {
        return SystemClock.elapsedRealtime();
    }

    public long getCurrentTimeMillis() {
        return System.currentTimeMillis();
    }
}

    public TimeSnapshot createTimeSnapshot() {
        long upTime = deviceTimeProvider.getElapsedRealtimeMillis();
        long clock = deviceTimeProvider.getCurrentTimeMillis();
        String bootId = bootIdProvider.getBootId();

        return new TimeSnapshot(upTime, clock, bootId);
    }

 . . .

public class TimeSnapshot {

    public final long upTime;
    public final long clockTime;
    public final String bootId;

    public TimeSnapshot(long upTime, long clockTime, String bootId) {
        this.upTime = upTime;
        this.clockTime = clockTime;
        this.bootId = bootId;
    }
. . .
}

public class BootIdProvider {
    private final Context context;
    private String cachedBootId = null;

    public BootIdProvider(Context context) {
        this.context = context;
    }

    public String getBootId() {
        if (cachedBootId != null) {
            return cachedBootId;
        }

        String bootId = null;

        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            try {
                bootId = String.valueOf(Settings.Global.getInt(context.getContentResolver(),
                        Settings.Global.BOOT_COUNT));

            } catch (Settings.SettingNotFoundException e) {
                LFLog.w(LOG_TAG, "", e);
            }
        } else {
           // try reading "proc/sys/kernel/random/boot_id" or just do this
           bootId = UUID.randomUUID().toString();
        }

        cachedBootId = bootId;
        return bootId;
    }
}

but be sure to sync with servertime at least once per boot (exactly once is fine) regardless of these, as anything else can be used to trick you.

Shark
  • 6,513
  • 3
  • 28
  • 50