How to WhiteList app in Doze mode Android 6.0

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


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

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

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


}