setExactAndAllowWhileIdle with Wakelock in Doze mode - android

It seems there are a few questions related to the subject topic but I haven't found a clear yes/no answer to this.
I have a foreground service that calls setExactAndAllowWhileIdle to start a BroadcastService. Below is the code in the Broadcast Receiver.
public class StepCountUpdaterAlarm extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myExampleApp::UpdateSteps");
wakeLock.acquire(3);
StepCounterHandler handler = StepCounterHandler.getInstance();
new TaskRunner().executeAsync(
new SetStepsForDay(SaveSharedPreference.getMemberId(context),
handler.getCumulativeSteps()),result -> {
handler.setAlarm(context);
if(isTimeToReset())
handler.resetStepsCounter();
});
wakeLock.release();
}
In the setExactAndAllowWhileIdle documentation it states:
When the alarm is dispatched, the app will also be added to the
system's temporary power exemption list for approximately 10 seconds
to allow that application to acquire further wake locks in which to
complete its work.
but in the Doze mode documentation it states this as a restriction:
The system ignores wake locks.
Does that mean acquiring a partial wake lock for 3 minutes in the 10 second window provided by an alarm dispatched by setExactAndAllowWhileIdle in Doze mode will effectively be useless or will it work fine?
In my case, the Broadcast Receiver will send data to my remote server via that async task, and after that it will set the alarm again and reset my steps counter. Will this work and if it wont, what are my alternatives for sending a network request in doze mode and executing follow up code?
EDIT: Testing by debugging my app shows that when forcing a device into idle mode, i still have network access and can send data to my server. The Doze mode documentation states how to force to app into the idle state which i am fairly sure is synonymous with doze mode. Yet this is supposed to be a restriction so I am clueless as to how this could be working.

A WakeLock won't help you much if the device is in Doze mode. setExactAndAllowWhileIdle will wake your device up if you have used either AlarmManager.ELAPSED_REALTIME_WAKEUP or AlarmManager.RTC_WAKEUP as the alarm type.
However, from Android 12 you need SCHEDULE_EXACT_ALARM permission to set exact alarms. And if you are planning to release the app to PlayStore there are some acceptable use cases for setting an exact alarm. Make sure your app complies with these policies.

Yes, Doze will ignore your wakelock. However with setExactAndAllowWhile Idle you will be worken up at the correct time, and you'll have that 10s window to do any processing you wish.

Related

Aquire partial wakelock in a IntentService

My IntentService gets fired from 2 places, either by an Alarm or by the Activity and since the duration is related to the amount of data it needs do fetch from the web, from what I understood, I need to keep a partial wakelock.
Here's my implementation:
#Override
protected void onHandleIntent(Intent intent) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakeLock");
try {
wakeLock.setReferenceCounted(false);
wakeLock.acquire(3600000);
///other code here
}
catch{
}
finally{
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
My question is: will this work good enough? Will the finally make sure that the wakelock is released in any circumstances? From what I know onHandleIntent handles intent one after another, so there is no risk in having 2 intents/2 wakelocks in the same time.
Later edit:
The IntentService is called in 2 ways:
from my Activity, like
startService(new Intent(context, MyService.class).putExtra()..);
2 from a triggered Alarm using a PendingIntent
PendingIntent pendingIntent = PendingIntent.getService(context, someId, myServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Will the service have enough time to aquire wakelock when ran from the Alarm?
Whether you need to keep wake-lock or not should not be related to the amount of work your Service does - theoretically, device can go to sleep even if the amount of work is small.
Wake-locks should be considered only if you absolutely must ensure that device can't sleep while the Service is running. Cases like this are very rare. Some examples:
Alarm clock applications (need to wake you up even if the device is sleeping)
Real time messaging applications (need to notify you about new messages even if the device is sleeping)
Most applications don't have such a strict timing requirements. For example, the following are NOT good reasons to use wake locks:
Periodic synchronization of data with the server (should be delayed until device awakes)
Displaying current user's location on map (can be obtained when device awakens; but wake-lock will be needed for applications that monitor user's entire route)
If you really need to ensure that the device doesn't sleep during Service execution, then you need to acquire a wake-lock (one of the several types). Let's assume that this is the case here.
You want to be able to start the "wakeful" Service from application's UI (Activity), and using AlarmManager.
Starting from UI
Since the device should be completely awake in order for the user to interact with UI, you can safely assume that if you start the Service in response to UI interaction it will have a chance to acquire a wake-lock (but do it as soon as the Service is started).
Your solution covers this case.
Starting from AlarmManager
Unfortunately, there is no guarantee (at least no documented guarantee) that when AlarmManager starts the Service it will hold a wake lock and allow the Service to acquire its own wake-lock. This means that the device can go to sleep after alarm fired, but before your Service had a chance to acquire the wake-lock.
This means that your solution will "break" in this case.
The only documented scheme in which AlarmManager will help you with wake-locks involves broadcasts:
The Alarm Manager holds a CPU wake lock as long as the alarm
receiver's onReceive() method is executing. This guarantees that the
phone will not sleep until you have finished handling the broadcast.
Once onReceive() returns, the Alarm Manager releases this wake lock.
This means that the phone will in some cases sleep as soon as your
onReceive() method completes. If your alarm receiver called
Context.startService(), it is possible that the phone will sleep
before the requested service is launched. To prevent this, your
BroadcastReceiver and Service will need to implement a separate wake
lock policy to ensure that the phone continues running until the
service becomes available.
This is where WakefulBroadcastReceiver comes in very handy.
Note that if you use this scheme, then there is no need to support a different one for "UI initiated" case - use the same approach in both cases.
You might also want to take a look at this library developed by #CommonsWare (I didn't use it myself though).

Doze and Standby mode for VoIP app

I have an legacy VoIP app for which I want to provide Android's new doze and standby mode support. I have my own messaging/signaling mechanism for which I can't use Android's GCM feature. The documentation stated that white-listing the app will permit to use own signaling mechanism and keep the app alive in Doze mode.
Also I am generating keepAlive alarm using setExtract() and setRepeating() currently to keep alive the persistent connection of XMPP. If I add setExactAndAllowWhileIdle for newer version, its stated that - the Alarm will be triggered at most one in every 15 minutes. But I need to generate it 1 in every 12 seconds interval. I went through Android documentation, many threads in SO and found an article on it. It seems what I want to achieve is not completely possible by white-listing the app and using AlarmManager's new APIs.
What can I do to keep my functionality as like before? Keeping the app alive in doze & standby mode, generating alarm alert with 12 seconds interval to keep the connection alive and keep the network connection open?
White-listing the app by disabling battery optimization is keeping the app alive all time.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
Android Manifest:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Although the new AlarmManager API setExactAndAllowWhileIdle is not get called within twelve seconds interval as required. The solution is - we are using JNI codes from before and we are sending keep alive by using a Timer thread to send keep alive alert in twelve seconds intervals. As the app is alive, the timer thread will be alive regardless the Alarm is working or not.
My app has a very complicated requirements for which I couldn't use GCM high priority message. But most of the VoIP apps like Skype don't disable battery optimization rather use GCM to send notification/messages in doze mode.

AlarmManager doesn't wake up device

I set the alarm like this:
registerReceiver(wakeUpReceiver, new IntentFilter("com.example.yay"));
pi = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("com.example.yay"), 0);
am = (AlarmManager)(getSystemService(Context.ALARM_SERVICE));
if (android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+30*1000, pi);
} else {
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+30*1000, pi);
}
And it works if the device is awake, but if I turn off the screen, it doesn't turn on the screen (but wakeUpReceiver's onReceive() gets called)
What do I need to put in the manifest? AlarmManager docs don't seem to mention anything relevant to permissions or intent filter or anything like that. Maybe I'm just misunderstanding the docs as usual?
And yes, I've read all the other similar questions, but they're not helping.
You can use Wakelock to achieve this:
Different Levels Wake Locks:
FULL_WAKE_LOCK - Keep the screen at full brightness, keyboard
back-light illuminated, and the CPU running. SCREEN_BRIGHT_WAKE_LOCK -
Keeps the screen at full brightness and the CPU running.
SCREEN_DIM_WAKE_LOCK - Keeps the screen ON but lets it dim and the CPU
running. PARTIAL_WAKE_LOCK - Keeps the CPU running
Android permission for Wakelock:
Since wake locks has huge impact on battery life your application needs to request WAKE_LOCK permission in order to create them.
Create Wake Locks
To create a wake lock call newWakeLock on the Power manger as follows:
PowerManager.WakeLock wakeLock ;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK, "My wakelook");
Release Wake lock
wakeLock.release();
Its very important to release wakelock or else it will drain the battery.
As answered above use wakelock create WakeLock in onReceive().
Or I suggest you to use WakefulBroadcastReceiver.
This will make sure that ur phone is awake during service.
WakefulBroadcastReceiver is the best option for the 2nd thing, about wakelocks - u can check on google some examples that show how to use alarm manager with wakelocks.
Ofc, ur service can done it's work, but sometimes sleep can occour, so I suggest you to implement it even if ur tests don't show the problem, better too keep it safe.
Are you sure onReceive() being called? Create some logs and do some tests. For now there can be 2 problems: or sleep is not calling onReceive() (here check the power managment options on your phone, I had problem with "Stamina" on my phone, which was blocking my alarms) or your phone is falling asleep before u do your work (then wakelocks/WakefulBroadcastReceiver).
Also AlarmManager will not "light up" ur screen, only CPU.
To turn on the screen use getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
or android:keepScreenOn="true" in layout.
The method suggested by another user SCREEN_DIM_WAKE_LOCK was deprecated in API level 17, keep it in mind!
For more:
https://developer.android.com/training/scheduling/wakelock.html

Does periodic notifications work when device is in sleep

I am working with Android API especially Alarms, IntentService and notifications. I am using AlarmManager to schedule a periodic IntentService which might or might not fire notifications.
My questions is What happens when the device is in sleep mode?
Alarm will not fire and thus IntentService will not run at all. I am not sure if this will be the case.Will it make a difference if I make it a WakefulIntentService? I believe wake locks are needed to ensure the service keeps running after the BroadCastReciever returns. However, in this case there is no broadcast reciever.
Alarm and IntentService will run, but any notification will not have any impact since the device is sleeping. In this case, do I have to explicitly get a wakelock from PowerManager to fire notification ?
What happens when the device is in sleep mode?
That depends upon your type of alarm and the component your PendingIntent is to invoke.
If your alarm type ends in _WAKEUP, and you are using a broadcast PendingIntent, the device will wake up and remain awake through the call to onReceive() of the BroadcastReceiver. Once onReceive() returns, the device can fall asleep again. This is why WakefulIntentService and WakefulBroadcastRecevier were created -- to offer tested patterns for how to pass control to an IntentService and keep the device awake while the service completes its work.
If your alarm type ends in _WAKEUP and you are not using a broadcast PendingIntent, as the saying goes, your mileage may vary. You may not get control before the device falls back asleep. This is not a recommended pattern.
If your alarm types does not end in _WAKEUP, the device will not wake up due to your alarm.
With respect to the Notification, given the nature of the API, one hopes that it is the OS' responsibility to keep the device awake long enough for the ringtone or vibration pattern to play, as we do not know the precise instant when the Notification appears, nor do we know whether the ringtone will play (e.g., device is on silent mode).

How can I keep my Android service running when the screen is turned off?

When the screen turns off, my application service is paused.
I start my service with the following code:
if (mSharedPrefs.getBoolean("prefAutoUpdatesMain", false)) {
Intent svc = new Intent(this, MyService.class);
startService(svc);
}
How can I can avoid the service pause?
What I have to do in MyService is to download some data from Internet. If I have understand the process I have to follow is:
Acquire wakeLock
Download data
Release wakeLock
In downloading data method there are no reference to wakeLock, it is the application to have the wakeLock, is it correct?
Wake locks are reference counted by default. I think it is better a wakeLock without reference counting, to be sure to release it, am I wrong?
A partial WakeLock is what you want. It will hold the CPU open, even if the screen is off.
To acquire:
PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
To release:
wakeLock.release();
WakeLock also supports reference counting so you may have multiple things in your service that require wake functionality, and the device can sleep when none of them are active.
Things to watch out for:
If you use reference counting, make sure all control paths through your application will properly acquire/release...finally blocks come in handy here.
Also be sure to hold WakeLocks infrequently and for short periods of time. They add up in terms of battery use. Acquire your lock, do your business, and release as soon as possible.
You need a partial wake lock.
Detailed example here in a previous question:
Wake locks android service recurring
I'm just using a foregrgound service.

Categories

Resources