Sunday, June 1, 2014

Quirkiness of Android sensor library timestamps

Update 2014-06-23: Original post doesn't quite work as intended. Details at the end of the post.

I'm working on a sensor library that should give me the last timestamp at which some interesting sensor event happened.

So, the Android sensor library is a little quirky. When you get a sensor event (on a sensor thread, so it is not recommended that you do any high-impact processing there), the event has an associated timestamp. The timestamp is documented thusly: "The time in nanosecond at which the event happened". The grammatically-bizarre English should be your clue that these docs may be slightly off. ;)

It turns out after a bit of Googling (tip o' the hat to StackOverflow, as usual) that the timestamp one receives isn't based off of any particular 0-point defined in the Android OS or the API; it's an arbitrary per-sensor value intended to allow for different measurements from the same sensor to be compared, not for the measurements to be compared to other timestamped events. It's a known issue (bug concerning the documentation; bug concerning the behavior), but as of right now it's the way of the world for Android developers.

If you need to obtain reference to some external timeframe (such as the oh-so-ubiquitous "milliseconds since 1970"), you'll have to put together your own workaround. The solution that's working for me right now is:

  1. Initialize your sensor listener with empty offset values dateBase and timestampBase.
  2. For each received sensor value (i.e. each call to onSensorChanged), check if your offsets are empty
  3. If they are, make a one-time call to populate them:
    1. grab a time pretty close to when the sensor event was fired via dateBase = (new Date()).getTime()
    2. Record timestampBase = event.timestamp from the new sensor event.
  4. To log the milliseconds-since-1970 timestamp of new events, retain (event.timestamp - timestampBase) / 1000000L + dateBase
This calculation is prone to fail if for some reason onSensorChanged gets called too long after the sensor change occurs (it's fine if the difference is on the order of nanoseconds and gets significantly less fine in the 100ms-second range or higher). But it works for my needs.

Update: So it turns out that this approach also doesn't work as desired; if the phone goes to sleep, the sensor clock will skew relative to wall time (I assume the sensor clock isn't "ticking" while the phone is not active). I could detect sleep / wake events, but it becomes a guessing game regarding what scenarios could cause the phone to stop updating the sensor clock. My new plan is to do (new Date()).getTime on every sensor event and take the simple performance hit; for my purpose, I don't need highly-accurate sensing.