I am writing a Android client app which keeps receiving push notifications from a server using HTTP long-polling(Comet).
I don't want to always keep WakeLock since it will drain battery, but I need to make sure the device can receive notification even when it is in sleep mode.
And I found this question:
Android: Sleep stages/levels on an Android device?
where "CommonsWare" mentioned that an incoming packet on a non-Wifi socket will wake up the device.
So my solution looks like this:
Client ------------------------- Server
---- Request----->
release WakeLock (Allow device to sleep)
<----Notification-- (Hopes it can wake up the device)
require WakeLock
process the notification
---- Request----->
release WakeLock
....
But there is a little time window between receiving the notification and requiring the wakelock, so my question is, how long will the device keep this awake state? Is it possible for the device to back to sleep during this time window?
The device will be awake for long enough to execute some short code in the BroadcastReceiver. I have not been able to find an exact number of millis, but the idea is that in your receiver, you should grab whatever WakeLock you need to proceed with your own processing.
However exact management of the WakeLock can be tricky. I recommend using #CommonsWare's WakefulIntentService:
https://github.com/commonsguy/cwac-wakeful
Related
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
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 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 program an application, which is connected to bluetooth device and used in a car (in the background) and navigation (or something else) in the foreground.
But on android 7+ (maybe also 6), application go to sleep after some time. When I'm trying to take picture from camera the "sleep mode" is immediately and my app is sleeping now (no bluetooth connection, no notifications) - just dead app.
I must go to recent apps -> click on my app and this make the "wake up". Bluetooth is now connecting to device again.
But I can't still check if the app is sleeping or not. So, how to keep app awake also in background?
I read some about WakeLock, but it looks like it's not working, cause app still sleeping. BUút maybe I'm using it wrong.
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wk = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyWakeLock");
wk.acquire();
and onDestroy
wk.release();
Thanks
From Android API 23 onward, Google has introduced Doze mode and App Standby in order to conserve battery power. When a device enters doze mode or an app enters App Standby, all tasks done by an app is deferred. We had an issue with an Alarm which was not firing because of the same. If you read upon docs, you will find that using a Wake Lock is not gonna help either.
Doze restrictions The following restrictions apply to your apps while
in Doze:
Network access is suspended.
The system ignores wake locks.
Standard AlarmManager alarms (including setExact() and setWindow()) are
deferred to the next maintenance window. If you need to set alarms
that fire while in Doze, use setAndAllowWhileIdle() or
setExactAndAllowWhileIdle(). Alarms set with setAlarmClock() continue
to fire normally — the system exits Doze shortly before those alarms
fire.
The system does not perform Wi-Fi scans.
The system does not allow sync adapters to run. The system does not allow JobScheduler to run
So if you want to bypass all these, your app must have a process in the foreground. Once again from the same doc -
The app has a process currently in the foreground (either as an
activity or foreground service, or in use by another activity or
foreground service).
In your case run a service and raise it's priority to foreground.
for wakelock to work, u should add this line to your androidmanifest file at the beginning. but as Ajay said, u should use a service so that the service works in background and do the job but if you want to keep the screen on that's something else which i think it is not the case here.
<uses-permission android:name="android.permission.WAKE_LOCK" />
xml layout file under layout type:
android:keepScreenOn="true">
why don't you use service? WakeLock basically keeps the screen on which is not an efficient mechanism
https://developer.android.com/training/scheduling/wakelock.html
I've searched a bit but had a question about the true function of the wakelock with GCM.
Does the wakelock prevent the device from going into a sleep mode that would prevent the network layer from shutting down -- so it can receive a GCM message from the network-level in the first place
Or when GCM is enabled and working (via manifest permissions, properly coded GCM code, etc), it will have a network connection to Google for GCM up regardless, and the wakelock is only needed from a UI or application level to turn the screen back on so you can do other things (interact with user, etc) after receiving it.
Or none/some of the above (please elaborate :)
Thanks
geremy
According to me you need to acquire WAKE_LOCK to start GCM Service and then release it,
as your CPU should not sleep before starting service.
CPU will go in sleep mode in some time after your screen turns off. When your device is in sleep mode your threads will be suspended.
If you acquire wake lock and does not release it, it will surely consume huge amount of battery.
You can receive GCM messages while your device is in idle mode.
When message arrives it depends on your code whether to wake device and show notification or not.
You can also delay the message till device comes out of idle state by using a flag delay_while_idle.
You can handle messages by registering BroadcastReceiver
Please refer to the questions and answer on SO :
Wakelock in deep sleep
Power management
GCM in standby
WAKE_LOCK