Run application in background when phone in Doze - android

Application is running SDK 23 and above. I am running some tasks using Service after completing the task and scheduling the next task using AlaramManager (setExactAndAllowWhileIdle). This is working fine. If the phone is idle continuously for 2 or 3 days then its going to Doze mode. After Doze mode ,application loosing the network and wakelock also not working.
Is there way even if phone is Doze can we run the application with any network interposition issues.I tried to keep the application witelist but i needs device to be rooted.
adb shell dumpsys deviceidle whitelist +<Package Name>
Can anyone suggest me which best way to run application without interruption?

Actually there is no way of doing this without running a foreground service. Having listed in white list may not be appropriate for your application and even though it is, you ask user to give you permission which can be seen as something dangerous from the end user's point of view.
However, I have a trick about this. Listen android's broadcasts and when you catch that device will move into doze mode, start a foreground service. In most of the cases user won't be able to see your foreground notification image and won't know that you are running a service. Because device is in the doze mode meaning it is stable in somewhere user not watching. So you can do whatever is needed.
You also listen broadcasts sent when doze mode is finished. When that happens, stop your foreground service and work in a normal logic of yours with alarm managers.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(intent.getAction().equals("android.os.action.DEVICE_IDLE_MODE_CHANGED")){
if (pm.isDeviceIdleMode()) {
startForegroundService();
//stopAlarmManagerLogic();
} else {
stopForegroundService();
//startAlarmManagerLogic();
return;
}
return;
}
}

Edit-WakefulBroadcastReceiver is now deprecated
Firstly, instead of directly calling a service in the AlarmManager call a broadcast receiver which then calls the service.
The broadcast receiver should extend a WakefulBroadcastReceiver instead of a regular BroadcastReceiver.
And then, let the broadcast receiver schedule a new Alarm, start the service using startWakefulService() instead of startService()
public class MyAwesomeReceiver extends WakefulBroadcastReceiver {
int interval=2*60*60*1000;
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, MyAwesomeService.class);
Intent receiverIntent = new Intent(context, MyAwesomeReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 11, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+interval,alarmIntent);
startWakefulService(context, serviceIntent);
}
}
The WakefulBroadcastReceiver and startWakefulService() will let your app a 10 seconds window to let do what it needs to do.
Also,
You can always ask the user to let your app ignore battery optimization functionality using-
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
Intent intent=new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (powerManager.isIgnoringBatteryOptimizations(getPackageName())) {
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
}
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
and in the manifest
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"></uses-permission>

You can request android to whitelist your app for doze mode by sending a high pirority GCM message. But remember this might make your app not approved by Google Play:
Intent intent = new Intent();
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
context.startActivity(intent);
https://developer.android.com/training/monitoring-device-state/doze-standby.html#whitelisting-cases

After Android N, no app can run in background forever. However you can use Firebase Job Dispatcher which can help you to run your app even if it is in doze mode. With the help of Firebase Job Dispatcher you can tell the system that your app should run at a particular time if provided conditions are matched.

Related

BroadcastReceiver dies with app

If i let the phone sit for a long time like 15 minutes i lose my receiver but i thought it was to persist like a service after being killed for memory.
Manifest:
<receiver
android:name=".WearableReceiver"
android:enabled="false">
<intent-filter>
<action android:name="com.example.johnbravado.MESSAGE_PROCESSED"/>
</intent-filter>
</receiver>
In Activity to start receiver
ComponentName component = new ComponentName(CounterActivity.this, WearableReceiver.class);
getPackageManager()
.setComponentEnabledSetting(component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
The receiver
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
//MyConstants.getInstance().showToast("Message Rcvd");
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"com.example.johnbravado");
wakeLock.acquire();
// Do Work
MyConstants.getInstance().msgReqAction(intent.getIntExtra(MyConstants.BROADCAST_DATA_REQ, 0));
wakeLock.release();
}
The broadcast sender
String BROADCAST_ACTION_RESP = "com.example.johnbravado.MESSAGE_PROCESSED"
#Override
public void onMessageReceived(final MessageEvent messageEvent) {
nodeId = messageEvent.getSourceNodeId();
String incomingPath = messageEvent.getPath();
int incomingReq = Integer.parseInt(new String(messageEvent.getData()));
if(incomingPath.equalsIgnoreCase(MyConstants.MSG_COUNTER_REQ_PATH)) {
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(BROADCAST_ACTION_RESP);
broadcastIntent.putExtra(MyConstants.BROADCAST_DATA_REQ, incomingReq);
sendBroadcast(broadcastIntent);
}else if(incomingPath.equalsIgnoreCase(MyConstants.MSG_DEFAULT_PATH)){
}
}
only way I get this to persist for long periods of time is to invoke a service
wearableReceiverIntent = new Intent(this, WearableReceiverService.class);
if(!WearableReceiverService.isRunning())
startService(wearableReceiverIntent);
the service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
IntentFilter filter = new IntentFilter(MyConstants.BROADCAST_ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new WearableReceiver();
registerReceiver(receiver, filter);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notif_bible)
.setContentText("Preaching").build();
startForeground(MyConstants.NOTIF_COUNTING_SERVICE, notification);
isRunning = true;
return START_STICKY;
}
If I run the service it persists for long periods of time but it drains the battery unnecessarily considering I interact only once every 10 minutes. I was under impression Broadcast receiver would work like service except for short bursts of work. invoke the service if you need to do long actions.
A BroadcastReceiver handles an intent and then stops again. This handling of an intent should be fast. If you want to do a lot of stuff, you should start an Service from the BroadcastReceiver and handle it from there.
A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
A BroadcastReceiver is started using the sendBroadcast intent.
So remove android:enabled="false" and use sendBroadcast, which will startup the Receiver by Android.
http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html
Greenify was killing my app when the screen went off. I was battling something I had no hope of defending against with code. After I explicitly told Greenify to not kill my app, I never told it to kill my app to begin with, everything worked as intended.
I had the same problem due on my Asus ZenPad due to the Asus Mobile Manager app, specifically the "Auto start manager" was blocking the intent to my app.
Deactivating the app (uninstall is not possible) worth nothing, the solution has been to leave the app installed but whitelist my developing app so it can receive broadcast like PACKAGE_REPLACE. (Pay attention that the switches are confusing, you actually have to touch on "blocked" so it turns on into "allowed" to enable it.
I think another option is to update or change the ROM (choosing one without all that bloatware).
If your BroadcastReceiver is setup in your manifest, there is no need to try and adjust the PackageManager component information for your package. As long as you remove the enabled="false" part.
Your BroadcastReceiver should be very short with what it does: typically update some internal data or start another component which can do the heavy lifting of your app's operation. You can use it to trigger a Service to do this type of thing in the background. But, note that "background" in this case means without user-interaction. It does not mean a background context of execution, such as a secondary thread. It is up to you do manage the thread(s) in your Service. Your BroadcastReceiver and Service callback entry points (onReceive() and onStartIntent()) run in the context of the main thread of your app.
Power management definitely plays a roll in all of this. Is your broadcast Intent actually being sent and done in a way which will wake the device? If it does wake the device and send the Intent, the device will only stay awake long enough for the BroadcastReceiver to run its onReceive(); after that returns there are no guarantees. The device will aggressively sleep, which is why wakelocks are a thing. However, use of wakelocks can cause excessive battery drain, unless used properly. If you are running on Marshmallow or newer, the Doze functionality can also wreck havoc on your plans. Wakelocks are ignored when in Doze mode and won't be considered until the user brings the device out of doze.
I had the same issue and it was resolved by granting auto launch permission for the app.
Go to
Settings->Permissions->Manage Auto Launch
and allow auto launch for your app.

How to make Alarm Manager work when Android 6.0 in Doze mode?

I am a developer of two alarm clock apps on Google Play. I am trying to get them to work with Android 6.0. However, Doze mode makes it so they do not ring. I put them on the white list, I put a foreground notification icon up, I'm not sure what else I can do - when in Doze mode, the the Alarm Manager alarms are still ignored. The Clock app (which is a Google Play rather than AOSP app), however, is different. When the alarm is enabled on the Clock app, "adb deviceidle step" will always read "active" and never "idle", "idle_pending" or anything else.
Is Android cheating here, giving its own app more power, aka. "pulling an apple"? Are all alarm clock apps on Google Play about to become non-functional? Kind of worried here, these are quality apps that each took a year of part-time development time, and are big income sources for me. Any clues on how I could get these to work would be a huge help.
Setting the AlarmManager intent:
Intent intent = new Intent(context, ReceiverAlarm.class);
if (android.os.Build.VERSION.SDK_INT >= 16) {
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
}
amSender = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT); //FLAG_CANCEL_CURRENT seems to be required to prevent a bug where the intent doesn't fire after app reinstall in KitKat
am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, scheduleToTime+1, amSender);
and the ReceiverAlarm class:
public class ReceiverAlarm extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
if (wakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Theme.appTitle);
wakeLock.acquire();
}
X.alarmMaster.startRingingAlarm(true);
}
and the relevant parts of the X.alarmMaster.startRingingAlarm() method:
if (wakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Theme.appTitle);
wakeLock.acquire();
}
if (screenWakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
screenWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, Theme.appTitle+" scr");
screenWakeLock.acquire();
}
Intent alarmIntent = new Intent(Intent.ACTION_VIEW);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
alarmIntent.setClass(context, ActivityAlarmAlarm.class);
context.startActivity(alarmIntent);
Some of the methods have been pasted inline for easier readability.
Doze and App Standby definitely change the behavior in regards to alarms and wakelocks, but they're definitely not the end of the world for you!
Have you tried using the method setAlarmclock() instead of set()?
It's designed specifically for alarm clocks and may be able to cut through doze. There are a few adb commands you can use to manually put a phone into doze or app standby mode: https://developer.android.com/preview/features/power-mgmt.html
If that isn't able to wake your app up, there's the surefire method setExactAndAllowWhileIdle() is designed to wake the phone from doze no matter what. Worst case scenario, you can wake your app up with this method and use the wakeup to schedule the next alarm.
Another page worth a read is this blog post with its flowchart for background work and alarms: https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo
Put the application in whitelist only allows network in doze mode. AlarmManager does not affected by whitelist.
For setExactAndAllowWhileIdle() method please check the below description from SDK. It will not wake the phone from doze.
When the alarm is dispatched, the app will also be added to the
system's temporary whitelist for approximately 10 seconds to allow
that application to acquire further wake locks in which to complete
its work.

Inquiry related to AlarmManager and WakeLocks

I am developing a native android app that run a backup operation every 30 mins.
I am using AlarmManager for this purpose and it works fine. Here is the code I am using to start the alarm:
public static void startSync(Context context) {
alarmIntent = new Intent(context, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// int interval = 3600000;
int interval =30000 ;
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Toast.makeText(context, "Sync Started", Toast.LENGTH_SHORT).show();
}
And here is the the on receive method:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
PowerManager pm = (PowerManager) context.getSystemService(context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
Intent eventService = new Intent(context, SyncInBackground.class);
context.startService(eventService);
wl.release();
}
}
I noticed that when my device isn't in standby, the operation took 5 seconds (I calculated it programmatically) but when the mobile is in standby mode it took 11 seconds. That's why I used wake_lock before running the backup operation in a background service, in order to make the app takes only 5 seconds.
But I still get the same results if the mobile in standby mode... it still takes 11 seconds and 5 seconds if not in standby mode.
What can I do to make my background service run the repeating alarm in 5 seconds instead of 11 seconds?
The usual mistake: acquiring a wake lock in the OnReceive does nothing. The AlarmManager already holds a wake lock in OnReceive. Your way works out of pure luck, when/if it works. You have to either use a WakefulBroadcastReceiver or use a WakefulIntentService. The WIS will acquire a static wake lock which will be active between OnReceive returning and the service starting.
See my answer here: Wake Lock not working properly for links.
The problem is that context.startService(eventService) is an asynchronous operation which is very likely to return in just a few milliseconds. This means that when you acquire a WakeLock in your onReceive method, you keep it just for a couple of milliseconds and you release before the service starts.
A way to solve this is to share the a wakelock between your BroadcastReceiver and the service that you're trying to launch. This is how the WakefulIntentService works but you can also do this yourself, for example, by creating a singleton WakelockManager with two methods, one for acquiring and one for releasing a wakelock, then have you BroadcastReceiver call the former and your service call the latter.
Also, remember that leaking wakelocks (by acquiring one but forgetting to release it) can have serious consequences in terms of battery usage.

How to unlock device when service call

In my application, user starts the service it is open and it is called every 5 minutes. it works fine.
But I want to unlock automatically (call WAKE_LOCK ) whenever the service is called i.e. every 5 minutes.
How can I achieve this?
Any body please help me...
Edit 1#
private void handleIntent(Intent intent) { // obtain the wake lock
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, NAME);
mWakeLock.acquire();
}
You have to use the AlarmManager for that.
Set up of the alarm and its receiver : Notification activity called by AlarmManager NOT to pop up when app is closed
Then you have to convert your service to a wakeful intent service and then call WakefulIntentService.sendWakefulWork(context, YourService.class); inside onReceive(context)
At least this was the way to go - the latest android platform seems to support this (kind of) out the box : https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html

How to find whether phone is in sleep/idle mode for Android

How to find whether phone is in sleep/idle mode for Android?
My problem is I am able to wake up the phone from sleepmode by using alarm manager
But when the phone is not sleep mode and at the same instant if Alarm manager is used to wake the phone..android force closes the App..
whether there is anyway to find whether the Phone is in sleep or idle mode?(Black screen)
Update:
My requirement:
When the phone is in sleep mode ---> Intent should be launched from the service
When the phone is not in sleep mode --> The same Intent should be launched from the service
None of the solutions below worked perfectly so here is my little tweak which worked perfectly :):)
//Pending intent for my alarm manager
Intent intentaa=new Intent(this,MyBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intentaa, 0);
//Intent to be launched where MyIntent is the intent
Intent intenta = new Intent();
intenta.setClass(this, MyIntent.class);
intenta.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Real code
try
{
startActivity(intenta);
}catch(Exception E)
{
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime(),
pendingIntent);
startActivity(intenta);
}
You can check if the screen is turned off by calling isScreenOn method.
Note:This only can be used for 2.1 and above..
isScreenOn method returns only if the screen is on or off. It does not indicate the sleepy mode, because Android sleep mode is not same as Android screen off mode. In Android, sleep mode might come within milliseconds (depending on wakelocks) after screen off (idle mode) - but that's not guaranteed (referring to this post).
I think you can try using SystemClock.uptimeMillis(),SystemClock.elapsedRealtime() comparison.
Check out isInteractive()
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
return pm.isInteractive();
} else {
return pm.isScreenOn();
}
I'm not sure thats what you want, but you can use Intent.ACTION_SCREEN_OFF to determine. see here an example.

Categories

Resources