Context
We are developing an android app that is supposed to do the following:
the user installs the app, registers and closes the app
once or twice a year an admin sends a Firebase data message with priority high to the user containing a geo fence
the FCM message starts a JobService that locates where the phone is
if the phone is inside the given area an Activity is started and user interaction starts
if the phone is outside the area the service is stopped and the user is never disturbed
I developed the app based on the Firebase push example found here
The problem
The application works fine on my older phones but on my new test phone (android 8.1.0, LineageOS 15.1) we have a problem when the phone is in sleep mode. In this case the FCM message arrives instantly but the service is first started once the phone is unlocked. So the message is stuck between 2. and 3.
We need the app to respond at once - not when the user decides to use his phone 2 hours later.
I assume the problem is due to the Doze mode introduced with android 6. I tried to solve it by adding the app to the whitelist in settings->battery->battery optimization but this did not solve the problem.
Questions
Is it Doze mode that delays my app between 2. and 3.? If so why is it not solved when the app is in the whitelist?
Is there any other way to start the location service at once? This post suggests that a foreground service can do it but this requires that a notification is shown which breaks with 5.
Is there another way to start my service at once besides whitelist and foreground service?
Yes! you are right this may be due to the Doze and App Standby features introduced in API 23 Marshmallow.
As mentioned in the documentation, the system do ignore wakelocks and system doesn't allow JobScheduler to run, which effectively prevents your app from running the Job.
An easy and effective workaround would be to run Location detecting routine inside a normal background service and start it using startService() when you receive FCM push.
Note that you might still need to white-list your app because as mentioned in another post here, only a whitelisted app can use the network and hold partial wake locks.
Is it Doze mode that delays my app between 2. and 3.?
From the documentation Doze mode affect Network access and blocks JobScheduler.
If so why is it not solved when the app is in the whitelist?
Also from the documentation: An app that is whitelisted can use the network and hold partial wake locks during Doze and App Standby. However, other restrictions still apply to the whitelisted app, just as they do to other apps.
So the blocking of JobScheduler still applies.
Is there any other way to start the location service at once? This
post suggests that a foreground service can do it but this requires
that a notification is shown which breaks with 5.
Ignore Battery Optimizations (for Internet access) in combination with an AlarmManager with setAndAllowWhileIdle() or setExactAndAllowWhileIdle() should work.
Be careful with Ignore Battery Optimizations
Google Play policies prohibit apps from requesting direct exemption from Power Management features in Android 6.0+ (Doze and App Standby) unless the core function of the app is adversely affected.
I think an important question here is: Do you really need to execute the JobScheduler immediately.
If a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode.
If the device is in Doze mode, it means the user is not using it.
if the phone is inside the given area an Activity is started and user interaction starts
This is the step Doze blocks
We need the app to respond at once - not when the user decides to use his phone 2 hours later.
If the device is in Doze it means the user is not interacting with it. Even if you show the Activity the user is not using the phone, he will see it when he starts using it 2 hours later :)
I still didn't try that,
but you might use a WakefulBroadcastReceiver:
https://developer.android.com/training/scheduling/wakelock.html
https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html
According with this links, you should declare your receiver as:
public class YourReceiver extends WakefulBroadcastReceiver {
Probably your receiver is already a WakefulBroadcastReceiver because notifications are showing..
In the receiver, you start the service (your service has to be an IntentService) using:
startWakefulService(context, your service);
Finally, remember to release the wake lock in the service:
#Override
protected void onHandleIntent(Intent intent) {
<Your_broadcast_receiver_class>.completeWakefulIntent(intent);
}
I hope it helped
Related
I have read the Android documentation regarding the new background restrictions in Android API Level 26, and with those in mind, I have designed my app as follows:
The app has a BroadcastReceiver listening for the "SMS_RECEIVED" broadcast. This is an implicit broadcast, which are now restricted in Oreo, however it is one of those in their exceptions list.
When this BroadcastReceiver runs, it enqueues a new task to be performed by a JobIntentService. When receiving the broadcast, the app should be temporarily whitelisted and therefore, the JobIntentService given a window of opportunity to do its thing.
All of this seems to be working just fine, even if I close the app from the "recent apps" list. However, I'm getting reports from some users that apparently every few days, they have to open the app again because it suddenly stops working in the background.
Why does the OS suddenly stop sending the "SMS_RECEIVED" broadcast to my app? If it is not that, then it must be the JobIntentService not being allowed to run. Why not? The app should be whitelisted immediately after receiving the SMS. What am I misunderstanding?
Besides Doze, Android also contains functionality called App Standby. As per the documentation:
App Standby defers background network activity for apps with which the user has not recently interacted.
The default 'recently interacted' time frame is 3 days, which corresponds with your reports of things stopping 'apparently every few days'.
Therefore what seems to be happening is your JobIntentService fires (your app is indeed still whitelisted after receiving the broadcast), but App Standby is preventing your JobIntentService from connecting to the network.
You can confirm this is the case by following the Testing your app with App Standby instructions:
adb shell dumpsys battery unplug
adb shell am set-inactive <packageName> true
You might consider looking through the Acceptable use cases for whitelisting to see if asking to ignore battery optimizations is an option for your use case.
I have developed a device Admin app that applies policies to the device eg restrictions etc.
How my system works
The webapp sends a push notification to the device via FCM. I used to use GCM and a wakelock.(The latter worked fine). When the push notification comes through to the device, the firebase class that receives the push calls an IntentService. This IntentService then processes the message eg "MOBILEDATA_ON" and any data associated with the message. Once the message has been processed eg MOBILEDATA_ON, the service executes code that turns the mobile data on and then calls a webservice relaying the state back to the webapp.
I chose IntentService as it is Async and is capable of making http calls to relay the state back with no extra async code.
All this works fine when the device is awake, even if the app is in the background.
The problem
If the device is unplugged and untouched for a while, it goes into Doze/Standby mode. (it is an Android 6 device). This is normal behaviour, however if i send a push to the device, the device does receive it and executes the correct code to apply the functionality but unfortunately the webcall that relays the new state of the device is not executed.
so for example, if Bluetooth is switched off on my device and it is in doze mode, i can send a push which switched bluetooth on successfully but the webapp never receives the updated state.
I have set the priority to high in FCM when sending the push, and this why i do receive the push when the device is in Doze.
My app is a Device Admin app, the docs says
The app is an active device admin app (for example, a device policy
controller). Although they generally run in the background, device
admin apps never enter App Standby because they must remain available
to receive policy from a server at any time.
Optimizing for doze
Can anyone tell me why the webcalls are not executing sending the state back to server when in Doze/Standby mode?
[EDIT1]
I used the following code to create a wakelock. At first i acquired the lock in the IntentService, executed the functionality then released it all in the same service. This was good for most cases but some of my functionality includes finding device location via another IntentService called Tracking service.
The problem is that GPS could take say 20 seconds to find a lock by which time the original Intentservice has finished and the device went back to sleep.
To get around this i created 2 methods in the application Object to acquire and release the lock. this way, if the push is one for location i can do a check in the initial service(which normally releases it) to see if the push is a location one and not release it there. Instead the tracking service can make a call to the Application Object to release when GPS is found.
A partial wakelock didn't seem to work as intended so i found the following code that uses a full wakelock. This is depreated though. Is the an alternative to using FULL_WAKE_LOCK?
public void acquireWakeLock(){
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
"MyWakelockTag");
wakeLock.acquire();
Log.e(TAG, "just acquired wakelock");
}
public void releaseWakeLock(){
wakeLock.release();
Log.e(TAG, "just released wakelock");
}
thanks
Matt
I have a foreground service that needs to get a single location update.
It is started by a specific broadcast, and upon starting it simply does a request on our backend. When it receives another broadcast, it requests a location update (once), does another backend request and then stops itself.
Edit: to clarify, the service itself does the location updates and backend requests. It is independent from the main app in that regard.
This works well on Lollipop and Marshmallow, both when the phone is awake or in Doze mode. But for some reason, on Android O the service never gets location updates when in Doze mode. Networking works fine as its first request goes through in both situations. Also, if I wake up the phone while the location request it running, it will get the update. Same if I put it in maintenance window.
Android O does introduce some limitations on background location but as the page mentions, the foreground app behavior (app visible or foreground service running) should be preserved and be similar to previous versions of Android.
Is this an issue in the developer preview or a restriction that I missed?
Take a look on LocationUpdatesForegroundService Sample: Code Samples for Android O, maybe you do something wrong.
LocationUpdatesForegroundService Sample - Shows how to use a
foreground service to get location updates when the app activities are
not visible. For apps running on Android O (even apps that target
Android 7.0 (API level 24) or lower), background updates are limited
to only a few times per hour. Using a foreground service is a way to
receive more frequent updates.
Get it on GitHub: Java
I have an android application that uses Geofence and I'm having a hard time overcoming Doze mode. My manifest has the WAKE_LOCK permission and seven setNotificationResponsiveness to 0 for each region. I even changed PendingIntent to BroadcastReceiver but the app literally sleeps or is dead after a while with the deleted smartphone screen. I've already tried using LocationRequest
LocationRequest.setFastestInterval(1000);
LocationRequest.setInterval(2000);
Even so, I did not succeed. Has anyone managed to keep the application running with the Doze?
I faced the issues in My Chatting application and Location Tracking applications, While in doze mode we will not get any push, or we will not get any network connection or location details and most of the background operation will be blocked because of battery optimization, We can solve by 2 ways.
Priority push message(I used in my chatting application) - If you send normal push it will not reach the app until the mobile come to normal, If you send priority push it will reach the app you can do some process within few seconds, but this also have some limitation.
Foreground service, - For your scenario this is the best solution, You should have on service and that service should run as Foreground service so that you can get the Location information all time even in Doze mode.
WAKE_LOCK permission in the manifest is not enough for use a Wake Lock , you need to acquire and release the lock in your code, but starting with Android 6 Doze mode it goes to deep sleep also with an acquired wake_lock.
The way that works for me also with Doze mode :
In the Main Activity onStop I acquire a Partial WakeLock and I start a Service with STARTFOREGROUND_ACTION intent, the Service calls startForeground and shows a Notification.
On Activity onResume the WakeLock is released, the Service stops with STOPFOREGROUND_ACTION intent and the Service itself calls stopForeground & stopSelf.
I ended up putting my application to receive push messages. Apparently the app is no longer being killed by the operating system.
I made the device which monitors person's health in sleep time and it connects to a smartphone via BLE.
It's working great with the iOS app.
But since Doze mode came on Android world.
It's really hard to deal with it because my device is working in his sleep time.
The main feature is that it detects particular danger and notify to the user while he is sleeping with the device. I need a network connection when it occurs.
Many articles tell me that I can use a network even in the doze mode if my app is in the whitelist. But it does not seem true after I tested Doze mode.
Am I right?
I can ensure that I can find my app in the lists, when I fire
adb shell dumpsys deviceidle
What is the best approach that I can take to make my app working correctly?
foreground services
alarm manager with SetExactAndAllowWhileIdle.
GCM (it means push, right?)
anything else
Any tips will help me.
Thanks.
Edit
Unfortunately, I tested with using GCM but it only wakes my app in short time. It means I have to send GCM as many as I want to keep it awake. I don't think I can use it.
Many articles tell me that I can use a network even in the doze mode if my app is in the whitelist. But it does not seem true after I tested Doze mode. Am I right?
You are not right. One of the restrictions of doze that are lifted when your app is on the whitelist, is the ability to use the network when doze is active.
An app that is whitelisted can use the network and hold partial wake locks during Doze and App Standby. However, other restrictions still apply to the whitelisted app, just as they do to other apps. For example, the whitelisted app’s jobs and syncs are deferred (on API level 23 and below), and its regular AlarmManager alarms do not fire.
From here.
In other words: you should be able to use the network in doze if you are on the whitelist.
What is the best approach that I can take to make my app working correctly?
Considering your app is a health monitor and thus should be able to do its work constantly or at least very regularly, you could put the functionality in a foreground service. Foreground services are not effected by doze.
You should be aware that you should have a good reason to use a foreground service since the user is aware of them, but I think you have one with the health monitoring etc.
Note: You should only use a foreground service for tasks the user expects the system to execute immediately or without interruption. Such cases include uploading a photo to social media, or playing music even while the music-player app is not in the foreground. You should not start a foreground service simply to prevent the system from determining that your app is idle.
From here.