51

Is there a way to detect when the Android system clock has been reset by the user in Android?

I'm designing an app which uses system time to determine when a user is at a certain place at a certain time, and I don't want to rely on network availability at that point. Obviously it would therefore be good to know when the user has changed the system clock, so they can't "cheat".

Andrew Wyld
  • 7,133
  • 7
  • 54
  • 96

6 Answers6

65

Yes, there is. The ACTION_TIME_CHANGED Intent is broadcast when the device time is changed, and you can have a method which will trigger when this Intent is detected.

This intent has been in Android since API level 1, so it should work on any platform you might need to be compatible with.

You'll need to handle the Broadcast with a BroadcastReceiver:

public class TimeChangedReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //Do whatever you need to
    }

}

You'll also need to add something like this to your Manifest:

<receiver android:name=".TimeChangedReceiver">
  <intent-filter>
    <action android:name="android.intent.action.TIME_SET" />
  </intent-filter>
</receiver>

This will let Android know to trigger your receiver when this type of intent is detected.

It appears as though this doesn't care who edits the time, but also does not trigger on automatic adjustments when you are synced with the network. If you lose network and regain it, though, this will likely fire since your time will be slightly different (assuming you are using automatic network time).

However, while the clocks on cell phones are not particularly accurate (since they generally rely on syncing with time signals they receive) in my experience they absolutely should not lose more than about 30 seconds or a minute per hour, at the absolute maximum, so if the time change is small you can perhaps assume it was automatic. Leap seconds, when they are added, will also likely produce a time change message, although these are obviously small and infrequent.

You can use ConnectivityManager to keep track of whether the phone has a connection or not and you can change the behavior based on that (i.e. as long as network connectivity is there, and the time is automatic/network time, ignore time changes or something) but I can't find any intents regarding losing/regaining network connectivity so you will probably have to use a polling method.

matt5784
  • 3,065
  • 2
  • 24
  • 42
  • 7
    Does this method differentiate between a **user** changing the time and the time changing because of a network update? – Andrew Wyld Jun 19 '13 at 09:45
  • 1
    That is a very good question. The answer appears to be no, as it did trigger when I put my phone into airplane mode for a few minutes and then turned the radio back on. However, it does not appear to trigger when you are getting automatic time updates from the network and it makes its minor adjustments to keep your time accurate. There are a few options for getting very accurate time: you can use the timestamp on a location object, which is very accurate but cannot be requested on-demand (you can listen for them to come in and when one does you will know the time at that point). – matt5784 Jun 19 '13 at 18:27
  • See http://stackoverflow.com/a/12320918/1003511 for a possible implementation of getting the time from the location. There also appears to possibly be a way to enable the device's automatic time syncing setting but this is probably not the best thing for your users if they had their time set manually for some reason. See http://stackoverflow.com/a/8214204/1003511 for some more information on this. I've also edited my answer. – matt5784 Jun 19 '13 at 18:51
  • By the way you can register a `BroadcastReceiver` for connectivity changes, I think? – Andrew Wyld Jun 19 '13 at 21:37
  • I was looking for broadcast intents regarding connectivity in the documentation and I did not see any. I could have missed them, though. Which specific intents are you referring to? – matt5784 Jun 20 '13 at 01:27
  • With the action http://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION – Andrew Wyld Jun 20 '13 at 10:00
  • Since it is a receiver this won't work if user has wrong date time on his phone from the time he downloads the application. But I guess that is not asked in the question. – arniotaki Nov 18 '14 at 14:01
  • @matt5784 "so if the time change is small you can perhaps assume it was automatic" Is there a way to calculate how much the change in time was? – Chakradhar Reddy Veeramreddy Apr 13 '17 at 09:22
23

The system time (System.currentTimeMillis()) would change if the user adjusted the clock, but the elapsed time since boot (SystemClock.elapsedRealtime()) hopefully would not. By tracking the differential between these two, it would be possible to detect major changes to the system clock by the user.

It would be important not to track small changes that might be caused by the network updating the system clock. I'm going to assume that for my purposes, any change under about a half hour is irrelevant; the system time shouldn't change on the Locale country changing but that might be worth eliminating also.

The elapsed realtime would obviously reset on boot so I should include a BroadcastReceiver to reset the differential on receiving android.intent.action.BOOT_COMPLETED.

Andrew Wyld
  • 7,133
  • 7
  • 54
  • 96
  • 1
    cant we use SystemClock.elapsedRealtime() directly without thinking System.currentTimeMillis() – someone Nov 17 '16 at 09:27
  • This answer helped a lot... `SystemClock.elapsedRealtime()` is better in use-case where time calulations are involved – Kushal Aug 24 '21 at 11:24
20

A little notice to the answer of matt5784: I found out that on Android 4.1.2 the intent action is not available, don´t know if this is for all android versions or just at mine. Instead of

     "android.intent.action.ACTION_TIME_CHANGED"

use

     "android.intent.action.TIME_SET"
Opiatefuchs
  • 9,800
  • 2
  • 36
  • 49
  • As per [documentation](https://developer.android.com/reference/android/content/Intent.html#ACTION_TIME_CHANGED) the constant name is `ACTION_TIME_CHANGED` and the constant value is `android.intent.action.TIME_SET` – OneWorld Nov 02 '20 at 10:23
10

We can use both TIME_SET and TIMEZONE_CHANGED.

<application
    ...>

    <receiver android:name=".TimeChangedReceiver">
        <intent-filter>
            <action android:name="android.intent.action.TIME_SET"/>
            <action android:name="android.intent.action.TIMEZONE_CHANGED"/>
        </intent-filter>
    </receiver>
</application>

TimeChangedReceiver.java

public class TimeChangedReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "time change " + intent.getAction(), Toast.LENGTH_SHORT).show();
    }
}
Linh
  • 57,942
  • 23
  • 262
  • 279
1

If you don't mind your app to throw cheats in case of user's clock will get updated due to *DT/*ST shifts, occasional time syncs with NTP sources that can rewind your clock, your own process's stalling, you can implement a "consequential" check on system time so that if time went "back" you assume it's a "cheat". Otherwise, you have to rely on the server which will be your source of "valid" time. And even then, your server's time can too go backwards occasionally.

Vesper
  • 18,599
  • 6
  • 39
  • 61
  • 1
    Actually I just thought of something: I could measure the differential between `System.currentTimeMillis()` and `SystemClock.elapsedRealtime()` and if it changes by more than a few minutes I'd know something was up. Obviously I'd have to reset that on a system boot but I could add a `BroadcastReceiver` in for system boot events. Thanks for the heads-up on the automatic clock updates; I was hoping there'd be a method for detecting user changes only but I guess not! – Andrew Wyld Mar 21 '13 at 11:01
0

You can check if the current time is set manually by the user, as opposed to being auto set by the OS.

Settings.Global.getInt(context.contentResolver, Settings.Global.AUTO_TIME) == 0

You can check the timezone as well, via Settings.Global.AUTO_TIME_ZONE

levi
  • 23,693
  • 18
  • 59
  • 73