33

The issue is that System.currentTimeMillis() returns the wrong milliseconds with different ranges of time mainly within the future sometimes up to 6 months, but it varies from a few seconds to months.

The device that this is occurring on is a Tablet model Huawei M2-A201W on android 5.1.1 the kernel version is: **3.10.74-gdbd9055**

My first assumption was that the NTP was somehow messing up with the time but I have thousands of those tablets and some of them have no network connection, no SIM card so no GSM/3G/4G.

Im using the System.currentTimeMillis() to save in a column for a table for when was a row created in the local sqlite database.

This anomally happens very often(30% of each System.currentTimeMillis() call) on the tablets that I use.

9
  • 1
    Does it return the wrong time every time, or just about 30% of calls? If it mostly works, you can call it like 10 times in a row, and then guess from the returned values which is the correct one (majority of them should be like it). (but the answers look more interesting, this is a secondary option).
    – Ped7g
    Commented Aug 7, 2017 at 10:26
  • not every time but 30% of the time, if I use the sqlite database to generate the timestamp how do we know that it actually is working so what native method does Sqlite actually use? Commented Aug 7, 2017 at 10:28
  • 1
    As you are hitting some kind of bug, the best way to answer it is to try it. Do some app (if you can't easily test with the current one modified), which will randomly insert some data into database (probably interleave it with some for delaying loops doing some calculation, to be not in sync with anything timer-based (like delay(msec)), then check the data, if the stored timestamp grows evenly and are connected to the current time, or there are some jumps in the data. If it's truly 30%, then you should see it in few thousand of records easily.
    – Ped7g
    Commented Aug 7, 2017 at 10:54
  • I will try your solution. Also since the devices are offline Im trying to get the GPS time. Commented Aug 7, 2017 at 10:57
  • Stack Overflow is for programming questions. What is your question? Commented Aug 9, 2017 at 15:43

6 Answers 6

7
+200

As a workaround for using System.currentTimeMillis(), maybe you can let sqlite handle the timestamp creation and see if that solves your problem?

Define/Alter your "created"-column with timestamp default current_timestamp or with default(strftime('%Y-%m-%d %H:%M:%f', 'now')) if you need milliseconds as well, like this:

sqlite> create table my_table(id integer primary key autoincrement not null, name text, created timestamp default(strftime('%Y-%m-%d %H:%M:%f', 'now')) not null);

sqlite> insert into my_table(name) values ('MyTestRow1');
sqlite> insert into my_table(name) values ('MyTestRow2');

sqlite> select * from my_table;

1|MyTestRow1|2017-08-07 10:08:50.898
2|MyTestRow2|2017-08-07 10:08:54.701

2
  • how does the sqlite generate the timestamp? does it also somehow use the same native call which System.currentMillis Commented Aug 7, 2017 at 10:25
  • 2
    Looks like they have their own implementation in c for this, sqlite.org/src/doc/trunk/src/date.c Not sure it will solve your problem though, you will have to try it out and test on the devices showing weird results.
    – jbilander
    Commented Aug 7, 2017 at 10:45
7

When you don't have SIM card so no GSM/3G/4G, your phone can't update correct time based on the network provided time/zone.

enter image description here

So, devices with the network show the correct time, while other devices without the network can show incorrect time -- you have to manually set the correct time. System.currentTimeMilis() reads the time from your system. g But on power turn, the clock works.

Check if NTP (UDP port 123) is blocked by apps using Socket or DatagramSocket. Note: NTP applies to the scenario where clocks of all hosts or routers on the network must be the same. If your device switch to two (or more) different network and gets time updated from different sources, it can fluctuate time.

Ultimately, your system time is being changed, that's why it is fluctuating. If you manually System.currentTimeMilis() after you manually disable automatic date and time, I believe, it doesn't fluctuate (no anomaly). If this is the case then your Huewai tablet doesn't have bugs.

6

The java API call System.currentTimeMillis() in Android platform use the POSIX api gettimeofday to get the time in milli seconds. See here.

static jlong System_currentTimeMillis(JNIEnv*, jclass) {
    timeval now;
    gettimeofday(&now, NULL);
    jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000;
    return when;
}

It assume every call to gettimeofday will be successful. I guess your problem might happen here.

It's better to check the return value of every API call and determine what to do next if error happen.

So I suggest a more reliable method in JNI with your own implementation like below. Call these POSIX APIs in order, if gettimeofday failed, call clock_gettime, if it failed again, call time.

struct timeval now;
if (gettimeofday(&now, NULL) != 0) {
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
        now.tv_sec = ts.tv_sec;
        now.tv_usec = ts.tv_nsec / 1000LL;
    } else {
        now.tv_sec = time(NULL);
        now.tv_usec = 0;
    }
}
jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000LL;
__android_log_print(ANDROID_LOG_INFO, "TAG", "%lld", when);
1
  • Can you explain how do the other API calls clock_gettime() and time() differ from clock_gettime() method ? Commented Aug 9, 2017 at 15:07
4

What about fetching the timestamp natively by running "date +%s" command in Linux kernel?

Here, "+%s" is seconds since 1970-01-01 00:00:00 UTC. (GNU Coreutils 8.24 Date manual)

  try {
            // Run the command
            Process process = Runtime.getRuntime().exec("date +%s");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            // Grab the results
            StringBuilder log = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                log.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

If you print this,

Log.e("unix_time: ", "" + log.toString());

You'll get an Unix timestamp e.g. 1502187111

To convert it back to a date object, multiply by 1000, since java is expecting milliseconds,

Date time = new Date(Long.parseLong(log.toString()) * 1000);
Log.e("date_time: ", "" + time.toString());

This will give you a plain date format. e.g. Tue Aug 08 16:15:58 GMT+06:00 2017

3
  • why do you think that getting the timestamp natively differ from System.currentMillis ? Commented Aug 8, 2017 at 11:41
  • 1
    I'm suspecting the issue is related to firmware rather than hardware. Most probably because all Huawei phone goes through tons of software customizations e.g. Emotion UI. Again, I'm not completely sure, But I highly recommend you to test with a result from kernel too. @Arlind Commented Aug 8, 2017 at 12:20
  • Also, make sure all devices are synced with the same time from phone's settings. @Arlind Commented Aug 8, 2017 at 12:55
4

Since you mentioned that most of the calls are getting the proper time and it only happens 30% of the cases, I would create an receiver for ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED intent broadcasts to find out when the time changes. Maybe this will give you a clue on what is changing the time.

Together with the ConnectivityManager you can detect if the device is connected and what type of a connection you have, maybe some connection is triggering the time change.

// init the register and register the intents, in onStart, using:
receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      getNetworkInfo();
      if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))
         Log.d(this.getClass().getName(), "Detected a time change. isWifiConn: " +
             isWifiConn + " isMobileConn: " + isMobileConn);
      if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction()))
         Log.d(this.getClass().getName(), "Detected a timezone change. isWifiConn: " +
             isWifiConn + " isMobileConn: " + isMobileConn);
    }
};
IntentFilter filters = new IntentFilter();
filters.addAction(Intent.ACTION_TIME_CHANGED);
filters.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(receiver, filters);
Log.d(DEBUG_TAG, "Receiver registered");
// do not forget to unregister the receiver, eg. onStop, using:
unregisterReceiver(receiver);
//...
private void getNetworkInfo() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    isWifiConn = networkInfo.isConnected();
    networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
    isMobileConn = networkInfo.isConnected();
    Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
    Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
}
1

Does this device have a real hardware RTC? I couldn't find a definitive answer from googling and reading spec sheets. A run of:

$ dmesg -s 65535 | grep -i rtc

from a shell should give you an answer. You should see something like this (will vary with chipset and kernel version):

[    3.816058] rtc_cmos 00:02: RTC can wake from S4
[    3.816429] rtc_cmos 00:02: rtc core: registered rtc_cmos as rtc0
[    3.816510] rtc_cmos 00:02: alarms up to one month, y3k, 242 bytes nvram, hpet irqs

If there is no message returned by grep (and you grepped against the whole kernel message buffer), then there's your answer. You have no clock to keep time on these devices. You will always need NTP and a working network connection to the Internet to keep such a device without a clock chip in sync with world time.

RTC: https://en.wikipedia.org/wiki/Real-time_clock

6
  • <6>[3005741.222717s][2017:08:14 09:31:48][pid:3293,cpu0,system_server]rtc-pl031 fff04000.rtc: pl031_suspend+. <6>[3005741.222747s][2017:08:14 09:31:48][pid:3293,cpu0,system_server]rtc-pl031 fff04000.rtc: pl031_suspend-. <4>[3005750.005432s][2017:08:14 09:31:48][pid:3293,cpu0,system_server]wake up irq num: 86, irq name: RTC0<6>[3005750.005493s][2017:08:14 09:31:48][pid:3293,cpu0,system_server]report_handle: id = 7 length = 5 buff = RTC0 Commented Aug 14, 2017 at 7:40
  • Thats a part of the output Commented Aug 14, 2017 at 7:40
  • @Arlind we will need the earliest boot message that has "rtc". And this must be from a cold boot, not a resume-from-suspend. Commented Aug 14, 2017 at 14:45
  • I dont know what you mean exactly how can I get that info ? Commented Aug 18, 2017 at 12:59
  • When you reboot this host, and quickly run "dmesg" after boot, you should see a bunch of kernel messages related with the kernel loading and starting up. Do you see them? Some examples of typical dmesg output: tldp.org/LDP/LG/issue59/nazario.html Commented Aug 18, 2017 at 17:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.