Run application in background when phone in Doze

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;
    }
}

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


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>