I'm writing an app that extends the SensorEventListener interface to listen for changes to the barometer, which I log in a logfile. Before I start logging, I prepend a system time in milliseconds (let's call this Millisecond Timestamp 1, or MT1), and after the logging is finished, I append another system timestamp in milliseconds (let's call this Millisecond Timestamp 2, or MT2).
The SensorEvent has its own timestamp (which I will call Nanosecond Timestamps, or NT), which I also log, between MT1 and MT2.
The problem is this: If the phone goes to sleep during the logging, the SensorEvent rate seems to no longer occur at the rate which I set (for example, SENSOR_DELAY_FASTEST). Furthermore, even though the SensorEvent timestamp is supposed to represent the nanoseconds of uptime since the phone has been rebooted, there are "missing" nanoseconds--the time gap between MT2 and MT1 is often twice or more that between NTN (where N is the number of samples) and NT1.
I've been able to sort of resolve this issue by using PowerManager.Wakelock(), but that results in my app being a huge power hog and seems like a really clumsy hack. Is there any other way to work around this problem?
Sensors are not guaranteed to work if the device goes to sleep, or even if the screen turns off (but the CPU has not necessarily yet powered down). The behavior is undocumented and definitely seems to vary by device.
Either settle for being "a huge power hog" or redesign your app to not require sensor readings except when the screen is on.
Sensors in Android are definitely designed to be used actively by foreground apps, not for long-term logging or monitoring purposes.
Related
I'm developing a set of complications that I would like have regardless of the other installed apps and watch faces. Yes, at some point I am reinventing the wheel, but at the same time I am using this as a learning project. This will also ensure that I always have all the complications I use, available and that they all have the same format and style, instead of relying on 3rd party apps to provide them separately.
The set will have complications for Heart rate, gps coordinates, hours, minutes, seconds, dd/MM date, dd/MM/yy date, battery, etc.
When I started programing all this I found several problematic pieces (most likely because this is the first time I develop complications, or an app for android wear for that matter) and hence this question.
Note that some of this behavior might be specific to the Huawei Watch 2 LTE.
1) Upgrade interval push / pull.
I understand complications as data providers, whose only responsibility is to provide the data to whatever watch face is calling them. This means that we are not certain (and we rely on the watch face developer) to know about the complication and request updates accordingly. This turns some complications completely useless if not updated in time (for example display the Seconds). Could also leave to complications displaying old data (for example old GPS coordinates, old heart rate bpm).
So ok, I decided to implement ProviderUpdateRequester with the AlarmManager to push data to the watch face. The problem again, is with complications that should happen faster, like seconds, as Android will block pending intents if they are schedule too often. So in order to get around that, I decided to use Android handlers within the same service instance, which turn out to be not a good idea because of the next topic.
2) Complication lifecycle
By debugging, I found out that the instance of the ComplicationProviderService object that is executing onComplicationActivated, onComplicationUpdate, onComplicationDeactivated can be different. This means that this is not a sticky service (single instance) that will be always running, but every update creates a new instance of the service. This is problematic because of heavy initialization complications: for example GPS, or Heart Rate monitor that need to listen for new values and it might take a while to retrieve the first value. And also, for those complications that can't rely on AlarmManager, and/or need to keep some sort of state between updates executions.
3) Display aware service
To get around the previous point , let's say you have static variables on your complication service , which are initialized onComplicationActivated and disabled at onComplicationDeactivated. For example, this could be getting a reference for the LocationProvider and starting listening for location updates. This will ensure that each invocation to onComplicationUpdate will not have to perform the heavy/cold initialization and will have access to the most up-to-date data.
However, this also means that your logic will executed regardless if onComplicationUpdate is called or not.
When in ambient mode (or screen off) the watch face can decide not to update the complication by not calling onComplicationUpdate, but it's not aware of our static logic, nor the ComplicationProviderService has a callback invocation for when the screen goes into ambient mode or turns on/off. This is a problem, because in our example, if the screen is off, we are still going to be listening for GPS coordinates, and most likely draining the battery.
Sure, we can deal with this by using a combination of BroadcastReceiver (Intent.ACTION_SCREEN_ON/OFF) and DisplayManager.DisplayListener, but then again, not sure if i'm taking the correct path here, because this will mean that we are now creating services that need to be statically aware of the state of the display.
4) Detect screen on/off
The BroadcastReceiver for Intent.ACTION_SCREEN_ON/OFF works as expected when ambient mode is disabled, but it doesn't it's enabled. When ambient mode is enabled, Intent.ACTION_SCREEN_OFF is dispatched when going into ambient mode, but Intent.ACTION_SCREEN_ON is not dispatched when coming out of ambient mode. While a bit more complex, this can be accomplished by using DisplayManager.DisplayListener to get updates on the onDisplayChanged callback.
TL;RD
1) How do you ensure watch faces display your complications in a timely manner to always have correct and most up-to-date information?
2) How do you deal heavy/cold initialization of a ComplicationProviderService if everytime onComplicationUpdate is called the service instance is different?
3) Is making a long running service display-aware something crazy to do?
4) Technically the screen is still on when in ambient mode, so why is Intent.ACTION_SCREEN_OFF being broadcasted? Why isn't Intent.ACTION_SCREEN_ON/OFF symetrical when ambient mode is enabled?
5) Maybe complications shouldn't be use for exposing realtime information?
Thanks a lot
A couple of things to unpack:
Complications are not meant to be updated frequently (think minutes, not seconds) - this is to preserve battery.
ProviderUpdateRequester is designed more for (on average infrequent) irregular updates like messages coming through a chat app.
Time dependent complications - there are not an "update" as such but Wear provide ways for developers to count up / down from certain time and for displaying date related field (world clock, day of the month) without the provider sending the system updates all the time. For this last one, please refer to docs for ComplicationText.TimeDifferenceBuilder
and ComplicationText.TimeFormatBuilder.
For your use case, a more appropriate thing maybe to consider an always-on app. User uses it for a certain time period for a specific purpose so they explicitly agree to use to use more battery to track things like GPS or heart rate. For example, a lot of running apps on Wear do this.
I hope this helps.
I found what was wrong:
So apparently http://www.epochconverter.com/ is makes assumptions of the precision of the input values, and from those assumptions values around 841073068 goes to 1996/1997. I'm not sure what is the assumption that leads to that exact date, but honestly I don't care.
Using the attached debugger I called new Date(System.currentTimeMillis()) and it correctly gave me a 10th Jan-1070 date, meaning the clock is not jumping out of the way like crazy.
Original question:
I'm running a single-board computer with Android for and IoT case (this https://developer.qualcomm.com/hardware/dragonboard-410c). The OS running is the plain vanilla Android supplied by Qualcomm.
Currently I'm testing the reliability of the board to be left executing for long periods at once and I'm seeing some very very weird behavior that I can't find an explanation for.
The board was powered up 10 days ago and it have no access to internet (WiFi is on but no access point setup and no Ethernet). The bluetooth is on and there're iBeacons and Eddystone in the office. Also there are WiFi in the area.
If I go now to Settings -> Date and Time, or check the notification shade or enter the clock app, or the calendar app, I see 10th of January 1970. Which is expected and basically showing for how long the board been running.
The app on it have an always running service, which does some data processing and some disk-logging (for debugging).
From the logs, I can see that System.currentTimeMillis() was returning an expected value when the board was initially powered on. That means, the beginning of the logs indicate an epoch time in January 1970.
But at the end of the logs (and also attaching the debugger on the live process), the value of System.currentTimeMillis() is somewhere in Sep/Oct 1996. Example values: 841073068, 841263234, 841579239
So my question is:
What is happening here?
Why System.currentTimeMillis() value changed and what could have changed it?
Why the Android UI (notification, clock app, settings) still shows me 1970? Where are they getting this value from?
edit:
There's been some confusion on the answers, and I can see my question was lacking the details.
I do not want to measure difference of time. I need an actual time stamp. Those values will be reported with bluetooth LE events via POST to our backend. This "no network" thing is a reliability test that we're running on the board, but we do expect to have network most of the time, and the boards should auto-update their times from network using the normal Android ways.
I'm just trying to understand on the current batch of testing, what went wrong and why.
Well, as you already know, the current system time (System.currentTimeMillis()) can be modified by any process if desired, it's perfectly be possible that it was modified by another process. It's not a reliable method to measure up-time.
I would rater use something like:
SystemClock.uptimeMillis()
Which returns the elapsed time (in milliseconds) since the device booted (not including time spent in deep-sleep).
I would also like to mention that I suspect that Bluetooth has something to do with it, I can imagine that Bluetooth uses the system time for pairing and security just like SSL does (but I'm no expert). GPS could also be a problem as GPS can be used to obtain an UTC time value, but I'm not sure if your board has a GPS module.
Regarding your edit:
Obtaining a valid time-stamp would be quite easy: server time minus the elapsed time reported by your board. But I suggest you either choose to accept the time reported by System.currentTimeMillis() or use the elapsed time instead. At the company I work we also work with embedded Android devices and on our server dashboard we can see both the up-time (up since) and the current device time, but they should not be mixed, at least in my opinion, especially since System.currentTimeMillis() is subject to changes and is affected by summer and winter time.
If you want to measure something, better try System.nanoTime(). Here is difference - https://stackoverflow.com/a/351571/2793494
I managed to get the heart rate sensor and it is working, with some exceptions.
I start the measurement on click, and when there is at least one result with accuracy >= LOW I stop the measurement (by unregistering the listener).
This sequence works 1-2 times, and then it just do not return a value with accuracy >= LOW. I am waiting a few minutes before I give up and close the application. But after opening the app again, most often it works the first 1-2 measurements and then it stops working with the same issue. (but other times it needs a lot of re-launches of the app in order to start giving some results)
Another issue is that if I start the measurement when the watch is not on my wrist/skin but lets say it is floating in the air, then even if I put it on my hand and wait a few minutes it doesn't start giving any valid/accurate value.
Have anybody observed similar behaviour? Is there any specific sequence that needs to be done in order for the sensor to provide more reliable data? Maybe a calibration can be done somehow to make the sensor more sensitive about my skin type or something?
I have an application which calls sensorManager.registerListener() for the accelerometer, gyroscope, and magnetometer, which registers all sensors for the handler and they are each set to SENSOR_DELAY_NORMAL. This works fine with no issues the vast majority of the time.
However, when looking at some logs I noticed the accelerometer would seemingly randomly change is frequency from roughly 179ms (which seems the be the average SENSOR_DELAY_NORMAL on my phone) to about 20ms.
After doing a fair amount of digging and testing, I found the cause to be when the phone is shaken rapidly. When this happens all other sensors will maintain their ~179ms rate, but only the accelerometer will increase its rate to ~20ms. After some period of time the rate will eventually decrease from ~20ms back to the set rate of ~179ms.
I'm not sure how long it takes for it to return to the ~179ms rate, I've tried uninstalled and reinstalling the application and if enough time has not passed yet the accelerometer will still be firing events at ~20ms.
I wanted to see if I could resolve the issue but un-registering and re-registering the listener at the correct rate when this happens, however the accelerometer will keep going at ~20ms irregardless of what I reset it to. I did find out though that I can unregister the accelerometer listener, and that seems to work, but it doesn't solve my problem.
Anybody know why the accelerometer listener would change its rate at which it's firing, and how I might be able to resolve this?
The delay that you request Android for is only a suggested delay. Android system and other applications can change this. Source
The reason why this can happen is quite simple -
There are only limited number of physical sensors available on the device, 1 accelerometer, 1 magnetometer, 1 gyroscope
Say your application, registers for all events from accelerometer every 100ms.
Another application, requests for all events from accelerometer every 10ms
Now since there is only one sensor and there 2 different needs, Android enables the accelerometer to provide the data at the lowest of all delay requests made by all the apps and then Android reports all events at this delay frequency only.
In this case, it is up to the application developer to disregard events when they are in excess to what is required.
Is there a way to synchronize the timestamps in values meassured by Sensors (e.g. acceleration) (SensorEvent.timestamp) and GPS (Location.getTime()).
SensorEvent.timestamp are the nanoseconds since the device has been booted (local time). Location.getTime() are the miliseconds since1970 (global time).
One solution I had in mind was to calculate the offset between the local time and the global (GPS) time. But the problem is, that the time in Location is the time when the GPS-message arrived at the device, but it takes some time until the GPS-unit (don't know how it is called) of the device calculates the position and forwards it to my application, so I can't calculate at what local time the message arrived. I've read somewhere (can't remember where) that the delay from arrival of the message until it has been processed can be several 100 miliseconds, so I will always have a high offset between GPS-data and data from other sensors, if I algin the local time when the app gets the location-message and the timestamp in the location.
Is there a way to figure out how long it takes exactly to process the GPS-message? Or is there another way to synchronize the meassurements?
The reason why I want to synchronize the values is, that i want to apply a filter (e.g. Kalman-Filter) on the data. For this I want to calculate the offset between a sensor value and a gps-value as exact as possible.
Usually Pulse Per Second (PPS) signals are used to link the time of measurement to the local clock. The GPS receiver toggles a pulse at the moment of measurement. This pulse is then timestamped using the local clock. On Android phones, this is not available.
In addition to the delay caused by processing and transmission time, there might be additional delays caused be filtering in the GPS software. This delay may even be depending on the speed or number of satellites used.
You might be able to 'calibrate' the delay between GPS data and your sensor data by comparing signals from both. E.g.: If you go from accelerating to decelerating, you will see this clearly in you phone's accelerometer and the GPS speed. However, I doubt if you can get a good accuracy for the delay.
You could even add a state in you Kalman filter which estimates the delay.