Timing for Android GPS logging is inaccurate - java

In my app I am trying to get the users current location and log it. The user can select an interval. Right now the logging is not happening consistently. Sometimes the logs are just off by a couple seconds and sometimes they are off by a few hours. Also if it is not logging and you turn the GPS off/on then it will start working again. The entire app is based around the idea that you will only be able to get your location using GPS when you use it. I have a few theories as to why this may be.
I am getting the logs by using AlarmManager setExact inside a broadcast receiver. I know that this is not guaranteed to be perfectly accurate and could possible account for a few seconds here and there.
I also know that the GPS can take some time to acquire. Is there a normal range for this time. I could see this taking up to a few minutes possibly but several hours seems like a lot.
I don't know a lot about loopers and am having some difficulty understanding them. I was wondering if the looper in the requestSingleUpdate could have anything to do with it
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, Looper.myLooper());
I know loopers process tasks in the background using a queue and I was't sure if other tasks could get stuck on the queue in front of it causing delays.
My last theory is that it has something to do with not timing out while searching for the GPS signal. If I am searching for a signal every 10 seconds (the fastest allowed) but the phone cannot find a signal the AlarmManager will fire again and I will have two services trying to get a signal. I don't really understand how the services work under the hood so I don't know if this is a possibility or not.
If anyone has any ideas/resources and could point me in the right direction I would really appreciate it.
Here is the code. I can include more if that would be helpful.
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences pref = context.getSharedPreferences(SettingsActivity.PREFERENCES, Context.MODE_PRIVATE);
if(!pref.getBoolean(SettingsActivity.ARG_TRACK, true)){
return;
}
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context.getApplicationContext(), AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
long interval = pref.getLong(SettingsActivity.ARG_TRACKER_INTERVAL, 15000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmMgr.setExact(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+interval, pendingIntent);
}else{
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
}
context.startService(new Intent(context.getApplicationContext(), AlarmService.class));
}
Inside my service I am calling LocationManager requestSingleUpdate()
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, Looper.myLooper());

In my app I am trying to get the users current location and log it. The user can select an interval. Right now the logging is not happening consistently. Sometimes the logs are just off by a couple seconds and sometimes they are off by a few hours. Also if it is not logging and you turn the GPS off/on then it will start working again. The entire app is based around the idea that you will only be able to get your location using GPS when you use it. I have a few theories as to why this may be.
If i'm not wroing, in your scenario, the interval is not period of location provider. It's directly period of alarm. The story like that
User selects an interval(period), for example 10 minutes.
After 10 minutes(approximately), device wakes up and makes location request to receive single location.
In that case you are facing the gps provider's lag. So, each time when wake up, you are waiting the gps provider to be warm. This is why the logs aren't consistently.
I am getting the logs by using AlarmManager setExact inside a broadcast receiver. I know that this is not guaranteed to be perfectly accurate and could possible account for a few seconds here and there.
You are using exact settings for wake up. This is not actual reason of long lag.
I also know that the GPS can take some time to acquire. Is there a normal range for this time. I could see this taking up to a few minutes possibly but several hours seems like a lot.
You have to wait until GPS provider to be warm. This warming time could change by where you stay at this moment. If you are inside a building, takes long
I don't know a lot about loopers and am having some difficulty understanding them. I was wondering if the looper in the requestSingleUpdate could have anything to do with it
Simply, when you pass a thread's looper here, onLocationChanged() method will be used the looper. But you are already making single request. No more update will be fired. (In your scenario, each wake up is one single location request)
My last theory is that it has something to do with not timing out while searching for the GPS signal. If I am searching for a signal every 10 seconds (the fastest allowed) but the phone cannot find a signal the AlarmManager will fire again and I will have two services trying to get a signal. I don't really understand how the services work under the hood so I don't know if this is a possibility or not.
This is a problem of your scenario. If you set alarm with short period, it's so normal the next wake up could happen. You can follow below things
Due to wait until gps warm, make long alarm period at least 5 minutes
Make timeout scenario, for example wait 1 minute or more until receiving location.
If cant receive location in time, shutdown everything and wait the next wakeup.

Related

Ways to work around the Android workManager 15 minute minimum interval?

The game I am working on is a missile-oriented GPS-based combat game on Android. The app checks every 15 minutes to see if the user is under attack by any other players, and if so, sends them a notification that they are under attack. Currently, because of the minimum 15 minute interval, the app sends these notifications either too late or not at all. What I need to do is alter this so that somehow, some way, the app checks the "under attack" status of the user more often than 15 minutes. Every minute or every 30 seconds would be ideal.
here is the doWork() method which starts the notification check:
{
if(!MainActivity.GetRunning())
{
Utilities.DebugLog(context, "AlertService", "Main activity not running. Firing notification service handler.");
NotificationServiceHandler handler = new NotificationServiceHandler(context);
handler.Start();
}
return Result.SUCCESS;
}
WorkManager is not a suitable tool for what you wish to do. You will need to use a foreground service and your own in-process timing engine (e.g., ScheduledExecutorService). That will not work for very long before Doze mode and other power-saving measures take effect, but hopefully your games are only an hour or so long.
Hej theBiscuit,
instead of using WorkManager, you could set up an AlarmManager to wake up the app and check for attacks.
If you want to do it while the app is running, a CountDownTimer could help for short periods of time.

Is there any alternative for PeriodicWorkManager, if I would like to run a background work more frequently than 15 minutes?

The PeriodicTimeRequest has a minimum periodic time of 15 minutes. But I see, that for example Google Maps location sharing can update more frequently than that, and facebook messenger can also receive messages almost instantly.
I would like to send a notification to the user, when it got a new message. My application has to work on local network, so Firebase is not an option. I have to send a json request to the server, and if there is a new message, I show a notification to the user.
Regarding FCM:
FCM, which is available in all devices with Google Play takes the weight of subscribing to and receiving push events, under all the resource constraints Android has been ever introducing.
It's tightly coupled with the OS and is unified (one entity, one persistent connection for all apps in your device), which is why it works :)
Regarding Frequency of your Work:
Given your requirement of more frequent pings to the server, you'd need to have a service which runs all the time, i.e. A Foreground Service.
It is resource consuming though, so good luck convincing the user with a good reason why it should stay alive all the time.
I think you've managed to make the client-server interaction possible, since identifying a server in a local network is a huge task in itself.
use this in your service.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
CountDownTimer timer = new CountDownTimer(15 * 60 * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// execute your task here, every sec
//if you want increase the count down interval from 1000 to what you want
}
#Override
public void onFinish() {
this.start();
// it will start again.
}
};
timer.start();
return START_STICKY;
}
I am afraid it is not going to be possible without using a set of workarounds. Which means you might not get a consistent behavior.
#Arvind has done a very good job explaining the benefits of a Firebase Service and it is the recommended approach for achieving such task.
First I'd like to point out that such restrictions on the WorkManager exist because Android has been suffering (between other things) of developers trying to abuse some mechanisms to get their software working and at the end of the day, the battery of the users had been suffering from such abuses and since Android 6 Google has started trying to address these issues. There's a good read for you over here about Doze mode and how to work with it
I am pointing this stuff out because I've been trying to build a chat service that wouldn't rely on Firebase and I really don't want you to waste as much time as me banging your head against a wall. There are things that you simply can't fight. That means that if the device enters in a "deep-sleep" mode sometimes you can only accept it.
My approach
Please
keep in mind the user interests and the life of their batteries and try to be as smooth as you can with their devices and this is just a workaround over the restrictions that have been imposed upon us. And that I discourage this approach due to the amount of work that it takes to pull off and for how misused it can be.
My solution
Essentially, to get notified (ie getting your code running) in an Android App you're going to be wanting to receive system events or Broadcasts. This is where you set up a BroadcastReceiver and you get Intents delivered to it and you can act upon them accordingly. BUT YOU HAVE TO BE QUICK BECAUSE YOU HAVE ONLY 10 SECONDS OF RUNTIME BEFORE THE OS KILLS THE PROCESS AGAIN. Ideally you would have a really quick server so you can have very little IO times to ensure you can be within 10 second restriction as frequently as possible.
So essentially you would be using a combination various of services that you would like to be monitoring in order to get notifications (aka Broadcasts) whenever the state of those changes. Here are a few ideas:
WiFi state (which will also be useful to see if you can reach your local server)
Bluetooth Low Energy packets (or Nearby which may solve the entirety of your problem depending on Nearby's capabilities)
WorkManager as you already pointed out.
AlarmManager to schedule a broadcast of intents every so often.
Geofencing (although it involves reading the user's location; you can set really small geofences around the office building and get notified by a Broadacast when users go through that geofence)
So whenever you receive a Broadcast of these sources you would handle such notifications from within the same BroadcastReceiver
From the implementation body of this Broadcast receiver you would poll the local network's server to check whether if your user has new messages or not and lift up a notification. And it's important to keep the amount of work and IO times the app has to do at a minimum since those add up and you've got only 10 seconds.
You can get around the 10 second mark if you launch a ForegroundService. Then, that period of time is going to be extended until a 10 minute mark and you will need a visible notification for the user stating something that you're checking if it's got any new messages.
Keep in mind
Don't stress the user's battery too much. Or Android will penalise your app and you'll end up notified less often or even completely not notified.
Be gentle with the user. If the user has to force-kill your app at some point it will stop receiving any sort of Broadcasts or running any sort of WorkTasks.
This solution can behave differently accross devices. Since the decisions of notifying your app are made by the OS, different OS (redmi, samsung, meizu...) you are likely to not end up with a consistent behavior across all devices
You don't have control over things, the OS does
Within measure, try to time your Broadcasts to your BroadcastReceiver within spans of 3 minutes or so; so you are always receiving a Broadcast below the 15 minute mark.

setExactAndAllowOnIdle doesnt trigger right

I have a problem using setExactAndAllowOnIdle. In my AlarmReceiver class I just show simple notification that shows when alarm was triggered and set another, same alarm but hour after.(currentTimeMillis + 60*60*1000). When my phone is in use application works fine, alarms come exactly on time. But when I let it work for few alarms without waking device up, they start to trigger with few minutes delays, or sometimes even exactly on time I wake up my phone.
You probably mean setExactAndAllowWhileIdle().
You didn't tell on which OS are You testing it but probably it's because of Doze Mode.
NOTICE:
Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can
fire alarms more than once per 9 minutes, per app.
So You can't use this method to set every alarm what probably You doing.
For more information You can go here:
https://developer.android.com/training/monitoring-device-state/doze-standby.html

Wakelocks and paused app

In my application, I want to record the phone's location at fixed interval. To do that, I have a TimerTask to record the location on a regular basis and a WakeLock for the recording to continue even when the phone is aslept.
My problem is that the Wakelock doesn't prevent the app from going into the "Stop" state when it is no longer displayed in the foreground. As a consequence, my app is regularly, but apparently randomly, destroyed by the system (no crash and no call to onDestroy), and the recording stops.
How can I keep my recording process going even if my app goes into the background?
In my application, I want to record the phone's location at fixed interval.
Use AlarmManager or JobScheduler to get control at your fixed interval. Use WakefulBroadcastReceiver as the recipient of the alarm event, and have it delegate the work to a Service that gets the location. Have the service call stopSelf() and completeWakefulIntent() once either it gets the location or after some timeout, as it may not be possible to get the location right now.
Or, if you are using the fused location API from Play Services, try the versions of that API that take a PendingIntent instead of a listener, and you may be able to skip the alarms.
I have a TimerTask to record the location on a regular basis
That will only work while your process is around, and it will not be around all that long.
and a WakeLock for the recording to continue even when the phone is aslept
Your users will be extremely unhappy with you for keeping the CPU on constantly and draining the battery.

Retrieving Android Battery Metrics Every n Seconds [Efficiently]

I have found numerous sources for monitoring battery levels, but none of them describe how to check it at a fixed time interval. Suppose I want to check the battery every n seconds. What is the most efficient way to do this?
Currently, I create a BroadcastReceiver and register it in my service with a filter. I then use a ScheduledExecutor to "fetch" the information from the battery. If I understand what's going on correctly, the BroadcastReceiver I made receives all broadcasts from the battery rather at a dynamic rate as they come in, rather than the constant rate I want to check it at.
It appears that when I create the BroadcastReceiver, it receives an "initial" message with the current info. Would it be more efficient to create a receiver object every so often, receive this initial message, then destroy it every time I want to check it? Or, is there a different way that I haven't thought about?
Sources that I used for monitoring the battery, in case if anybody is interested:
http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
http://developer.android.com/reference/android/os/BatteryManager.html
Android Battery in SDK
Suppose I want to check the battery every n seconds. What is the most efficient way to do this?
Unless n is measured in hundreds or thousands of seconds, you may well be the #1 consumer of battery life. Given your comment, I will assume that you really mean "every n minutes".
Step #1: Set up an AlarmManager schedule to invoke an IntentService every n minutes (preferably not with a _WAKEUP alarm type).
Step #2: In that IntentService, call registerReceiver() with a null BroadcastReceiver and an IntentFilter that is for ACTION_BATTERY_CHANGED. The return value will be the last Intent broadcast for this event.
Step #3: Do something with the data.

Categories

Resources