61

This question is related to the Android 6.0 Preview 3 which will be final released at the end of this month.

I'm testing some stuff in Android 6.0 in the preview 3 from Google on Nexus 5 'hammerhead'.

The new feature is the "doze mode" - something like deep sleep mode when the network is disabled and phone sleeps, only the SMS, calls or high priority GCM messages can wake it up. But like WhatsApp - in the doze mode it receives the messages after 2 hours or more depends on the timers. But there is a list of 'not optimised' apps called "white list" where u can manually add app.

Ok, I'd like to find a way to add my application programmatically without user interaction to the "white list app list" which exists in the device in battery settings.

Trying to use the reflection to get into it I found:

Within the android.os.IDeviceIdleController there is a method:

public abstract void addPowerSaveWhitelistApp (String packageNameOfApp)

But this is an interface... So we can not make an instance of interface.

There is not yet documentation about this Interface or about methods, or any inheritance tree.

Maybe you have some idea where i should look for a possibility of programmatically add there my app?

There is also a method

public abstract boolean isPowerSaveWhitelistApp (String packageName)

Which i think should be possible to access somehow?! to check if the app exist on the White List and maybe at the very end hopefully ASK user to add it to the White List.

So my question is, have anyone of you tried to make something with better result ?? cuz I'm stuck and i think its a dead end.

for more info: https://newcircle.com/s/post/1739/2015/06/12/diving-into-android-m-doze

Fransis Q
  • 761
  • 1
  • 6
  • 11

6 Answers6

55

Add permission

<uses-permission 
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>

request whitelist your app

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    Intent intent = new Intent();
    String packageName = getPackageName();
    PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
    if (!pm.isIgnoringBatteryOptimizations(packageName)) {
        intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
        intent.setData(Uri.parse("package:" + packageName));
        startActivity(intent);
    }
}
Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
jefry jacky
  • 709
  • 7
  • 11
  • 1
    Thanks for the answer. This method is asking the user to allow or deny , i am allowing the permission, but not sure about it is ignoring battery optimization or not because if i go and check in setting manually nothing is changed even if i allow the permission . can anyone clarify it ? – krishnamurthy Dec 26 '18 at 05:45
  • 2
    This might get the job done. However, if you do this, your app will **not be accepted by the Play Store** for publishing. Android Studio will give you a warning regarding the same. [Suggested reading](https://developer.android.com/training/monitoring-device-state/doze-standby) – Kathir Jan 11 '19 at 08:14
  • @Kathir Please take a look at `Acceptable use cases for whitelisting` > https://developer.android.com/training/monitoring-device-state/doze-standby.html#whitelisting-cases – Dr.jacky Apr 24 '19 at 14:37
  • So, how much time does it take to whitelist? Does it immediately whitelist the app? – Narendra Singh Jun 23 '20 at 08:31
  • 1
    dont do this, just modify fcm params to make it appear – famfamfam Mar 09 '21 at 13:12
36

It is not possible to disable battery optimizations (=whitelist application for doze mode) without user interaction on Android M preview 3.

It can be done with user interaction this way:

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);
M66B
  • 1,078
  • 1
  • 7
  • 15
  • How you define Settings ? My IDE can not find Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS somehow... found it. OK. Now to tests :) – Fransis Q Sep 17 '15 at 12:18
  • See also here: http://stackoverflow.com/questions/32316491/network-access-in-doze-mode/32424117#32424117 Although people downvote my comment, it is actually correct (tested on Android M preview 3). – M66B Sep 17 '15 at 13:26
  • Where we have to write this code? (Intent intent = new Intent(); ------- context.startActivity(intent)? – Kotik_o Mar 18 '16 at 15:59
  • @Kotik_o normally you would put it in your main activity's onCreate. You will also need to save that you have prompted the user because if they choose No, you will still be asking them everytime – behelit Oct 25 '16 at 00:42
  • @FransisQ How you define Settings ? My IDE can not find Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS. i am using eclipse. – RaRa Oct 25 '16 at 13:49
  • 1
    if `pm.isIgnoringBatteryOptimizations(packageName)` is `true` why to request it again? – dieter Jan 06 '17 at 10:02
  • This answer has some issues `pm.isIgnoringBatteryOptimizations(packageName)` if this returns true then it means that your app is already in the whitelist so no need to `startActivity` for anything ! – Sharp Edge Feb 14 '17 at 16:24
  • @SharpEdge the intention is to show the settings whether optimizations are enabled or not. – M66B Mar 03 '17 at 12:57
  • isIgnoringBatteryOptimizations is available only from API 23 . Any workaround for API 21-22 ? – android developer Aug 30 '17 at 11:39
  • 1
    @androiddeveloper doze mode was introduced in Android 6, API 23, so there is no doze mode on earlier Android versions and thus there is no workaround needed. – M66B Sep 03 '17 at 09:17
  • @M66B Correct. I was confusing it with PowerSave mode, which was on 21-22, and that can already be checked if it's enabled. Thank you and sorry. – android developer Sep 03 '17 at 11:46
21

I think this helper class should cover all your needs.

To use it to request the OS to white-list your app, you can use prepareIntentForWhiteListingOfBatteryOptimization. If you get null, it means you don't need it, or that you can't use it. You can use the other function to query a better state of what you are in.

public class PowerSaverHelper {
    public enum PowerSaveState {
        ON, OFF, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
    }

    public enum WhiteListedInBatteryOptimizations {
        WHITE_LISTED, NOT_WHITE_LISTED, ERROR_GETTING_STATE, UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING, IRRELEVANT_OLD_ANDROID_API
    }

    public enum DozeState {
        NORMAL_INTERACTIVE, DOZE_TURNED_ON_IDLE, NORMAL_NON_INTERACTIVE, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API, UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING
    }

    @NonNull
    public static DozeState getDozeState(@NonNull Context context) {
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return DozeState.IRRELEVANT_OLD_ANDROID_API;
        if (VERSION.SDK_INT < VERSION_CODES.M) {
            return DozeState.UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING;
        }
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return DozeState.ERROR_GETTING_STATE;
        return pm.isDeviceIdleMode() ? DozeState.DOZE_TURNED_ON_IDLE : pm.isInteractive() ? DozeState.NORMAL_INTERACTIVE : DozeState.NORMAL_NON_INTERACTIVE;
    }

    @NonNull
    public static PowerSaveState getPowerSaveState(@NonNull Context context) {
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return PowerSaveState.IRRELEVANT_OLD_ANDROID_API;
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return PowerSaveState.ERROR_GETTING_STATE;
        return pm.isPowerSaveMode() ? PowerSaveState.ON : PowerSaveState.OFF;
    }


    @NonNull
    public static WhiteListedInBatteryOptimizations getIfAppIsWhiteListedFromBatteryOptimizations(@NonNull Context context, @NonNull String packageName) {
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API;
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return WhiteListedInBatteryOptimizations.UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING;
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE;
        return pm.isIgnoringBatteryOptimizations(packageName) ? WhiteListedInBatteryOptimizations.WHITE_LISTED : WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED;
    }

    @TargetApi(VERSION_CODES.M)
    @RequiresPermission(permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
    @Nullable
    public static Intent prepareIntentForWhiteListingOfBatteryOptimization(@NonNull Context context, @NonNull String packageName, boolean alsoWhenWhiteListed) {
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return null;
        if (ContextCompat.checkSelfPermission(context, permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_DENIED)
            return null;
        final WhiteListedInBatteryOptimizations appIsWhiteListedFromPowerSave = getIfAppIsWhiteListedFromBatteryOptimizations(context, packageName);
        Intent intent = null;
        switch (appIsWhiteListedFromPowerSave) {
            case WHITE_LISTED:
                if (alsoWhenWhiteListed)
                    intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
                break;
            case NOT_WHITE_LISTED:
                intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:" + packageName));
                break;
            case ERROR_GETTING_STATE:
            case UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING:
            case IRRELEVANT_OLD_ANDROID_API:
            default:
                break;
        }
        return intent;
    }

    /**
     * registers a receiver to listen to power-save events. returns true iff succeeded to register the broadcastReceiver.
     */
    @TargetApi(VERSION_CODES.M)
    public static boolean registerPowerSaveReceiver(@NonNull Context context, @NonNull BroadcastReceiver receiver) {
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return false;
        IntentFilter filter = new IntentFilter();
        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        context.registerReceiver(receiver, filter);
        return true;
    }


}
android developer
  • 106,412
  • 122
  • 641
  • 1,128
  • I have tried the solution, tested it with Xiaomi, even if i was able to remove app from battery saving mode, the FCM didn't worked, I know its not the issue with your solution, you solution worked perfectly detecting the mode, But can you please help? Am kind of like stuck at this moment. – Sanoop Surendran Feb 11 '18 at 06:30
  • @Sanoop Sorry, but I've moved on. – android developer Feb 11 '18 at 07:34
  • @SanoopSurendran, china room need grant other permission, not only doze – famfamfam Mar 09 '21 at 13:13
9

Whitelisting can be done programmatically. To add device in whitelisted applications,run following command from application:

adb shell dumpsys deviceidle whitelist +<package_name>

To remove device from whitelisted applications,run following command from application:

adb shell dumpsys deviceidle whitelist -<package_name>
brnunes
  • 1,893
  • 26
  • 30
Shinoo Goyal
  • 555
  • 7
  • 9
  • 3
    Not sure this actually needs root. As far as I know, my phone is not rooted but the above command worked. – elParaguayo Jun 29 '17 at 13:32
  • Can you please explain how this works? Slightly confused here. Will this only whitelist for a specific device only or i can achieve this with whichever devices the app runs on? – Sanoop Surendran Feb 09 '18 at 07:26
  • If you run this command through your code, it will try work on every phone – Shinoo Goyal Feb 15 '18 at 21:54
  • we have to run this command every time or only once after installation is enough? – Virendra Varma Aug 07 '19 at 07:07
  • This life-saving answer to run your foreground-service even when your app is killed – Ajay Rawat Aug 23 '19 at 11:18
  • Has anyone done this programmatically? I tried doing this using Process builder in Android 10 + Kotlin. it says permission denied. Any help appreciated. My code is below: `val adbShellCommand = "dumpsys deviceidle disable" val pb = ProcessBuilder(adbShellCommand) val p: Process try { p = pb.start() val reader = BufferedReader(InputStreamReader(p.inputStream)) //Set the output to a TextView ... } catch (e: Exception) { ... } ` – Deepak V Aug 31 '20 at 04:47
  • Apologies, I could not get the code to be formatted – Deepak V Aug 31 '20 at 04:49
  • This worked great! What a lifesaver! – Scott Driggers Jan 20 '21 at 05:40
5

As far as I know , you cannot whitelist yourself from Doze. Ignore battery optimizations does not disable doze. See here: https://code.google.com/p/android-developer-preview/issues/detail?id=2225 UPDATE: In the release build of M , you can request ignore battery optimizations which will at least give you normal access to the internet while in Doze mode.

A.Sanchez.SD
  • 1,570
  • 2
  • 13
  • 21
  • 1
    FYI I have tested this several times. setexactallowwhileidle alarms can only trigger every 15 minutes, regardless if ignore batter optimizations is enabled or disabled. – A.Sanchez.SD Sep 17 '15 at 20:30
  • 1
    UPDATE: In the release build, Ignore optimizations impacts network access while in doze. – A.Sanchez.SD Oct 13 '15 at 22:17
4

UPDATE

When the device is stationary again, with screen off and on battery for a period of time, Doze applies the full CPU and network restrictions on PowerManager.WakeLock, AlarmManager alarms, and GPS/Wi-Fi scans.

Visit Use Cases for Whitelisting for more detail.

The table below highlights the acceptable use cases for requesting or being on the Battery Optimizations exceptions whitelist. In general, your app should not be on the whitelist unless Doze or App Standby break the core function of the app or there is a technical reason why your app cannot use GCM high-priority messages.

android n developer says

Doze is particularly likely to affect activities that AlarmManager alarms and timers manage, because alarms in Android 5.1 (API level 22) or lower do not fire when the system is in Doze.

Android 6.0 (API level 23) introduces two new AlarmManager methods: setAndAllowWhileIdle() and setExactAndAllowWhileIdle(). With these methods, you can set alarms that will fire even if the device is in Doze.

Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 15 minutes per app.

Testing with Doze and App Standby

Community
  • 1
  • 1
Amit Vaghela
  • 21,317
  • 19
  • 79
  • 131
  • 2
    none of the two `AlarmManager` methods are working. Maybe device dependant, on Huawei Ascend Mate 7 these methods fail and doesn´t execute. Also if time is more than 15 minutes....Android Bug? – Opiatefuchs May 23 '16 at 08:12
  • according to release from developer.android.com, please check if device has android-m than this will work but note will be applied. – Amit Vaghela May 23 '16 at 08:21
  • 1
    Thanks for that "hint" but I am pretty sure that MM is installed :) ...this could also be a behaviour of Huawei. Huawei implemented their own Energy saving manager on their devices. Here is running both, the energy managing system from MM AND Huawei. I think they bite each other. I have set my app to white list, protected it and all the other possible settings to be sure it stays alive. Nothing worked. Also contacted Huawei, but answer is still outstanding.... – Opiatefuchs May 23 '16 at 08:28
  • i am glad that it help you. thank you for appriciation. – Amit Vaghela May 23 '16 at 08:30
  • I talked to a friend, with a LG Nexus (don´t exactly know the model)...here it works. But the thing is, and this I think is not device dependant, the network connection will be suspended. This is pointed out in the docs. And if so, then much chat apps will have a problem, if they are not using GCM. Very sad..... – Opiatefuchs May 23 '16 at 09:21
  • "Android 5.1 (API level 22) or lower do not fire when the system is in Doze" Could you explain what this means exactly? I'm very confused because Doze was introduced when Android 6.0 came out.. – Jenix Jun 23 '17 at 14:33