isPowerSaveMode() always returns false for Huawei devices
Asked Answered
R

8

17

I am currently implement a feature where the users are requested to ignore battery optimisation for the application. The reason for doing so, is that the main functionality of the application is unfortunately drastically affected by power save mode.

To achieve my goal, I prompt the users by creating an Intent and setting the Action to ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.

Although, before firing the Intent, I both check for isPowerSaveMode() and isIgnoringBatteryOptimizations() to ensure that I don't prompt the users when power save mode is not enabled; which is a requirement for the feature. The way I do so is by:

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isPowerSaveMode = pm.isPowerSaveMode(); // always returns false for Huawei devices

This works fine for the most devices, but for Huawei devices, isPowerSaveMode() always returns false. Consequently, since the preconditions fail, the prompt is never shown.

Has anyone else possibly encountered this issue? If so, what did you do to solve it?

As a note, the same issue is also present in the Xamarin.Android SDK.

Retsina answered 17/8, 2017 at 19:25 Comment(6)
There is a workaround, which has been described hereDeliver
@Deliver Thanks for the link. It although doesn't solve the issue I experience.Retsina
Are you sure it's not related to the Android version installed on the phone? If yes, can you try this piece of code? PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && powerManager.isPowerSaveMode()) { }Edik
@hugorgor I am sure it isn't related to the OS version. I tried on several Huawei devices, including ones running on Android 6 and 7. In both cases, the devices returned false when requesting isPowerSaveMode() even though it was enabled. My thought is that the flag is exposed in a different way compared to other manufacturers, such as Samsung (which works fine). But that's just a theory.Retsina
Maybe you should try on Xamarin Test CloudEdik
Any solution so far?Bobbie
F
6

Some Chinese ROM like Huawei or Xiaomi didn't implement the standard API for power save mode query. But like other system settings, a state flag will be saved to database when user turn power save mode on/off.

So we can utilize this state flag to solve the compatibility problem. Also a specific intent will send by system when toggle power save mode, we can listen this intent action to monitor power save mode changing.

Below is the detailed kotlin code implementation for Huawei or Xiaomi devices.

object PowerManagerCompat {

    private const val TAG = "PowerManagerCompat"

    interface PowerSaveModeChangeListener {
        /**
         * will be called when power save mode change, new state can be query via [PowerManagerCompat.isPowerSaveMode]
         */
        fun onPowerSaveModeChanged()
    }

    private val POWER_SAVE_MODE_VALUES = mapOf(
            "HUAWEI" to 4,
            "XIAOMI" to 1
    )

    private val POWER_SAVE_MODE_SETTING_NAMES = arrayOf(
            "SmartModeStatus", // huawei setting name
            "POWER_SAVE_MODE_OPEN" // xiaomi setting name
    )

    private val POWER_SAVE_MODE_CHANGE_ACTIONS = arrayOf(
            "huawei.intent.action.POWER_MODE_CHANGED_ACTION",
            "miui.intent.action.POWER_SAVE_MODE_CHANGED"
    )

    private const val monitorViaBroadcast = true

    /**
     * Monitor power save mode change, only support following devices
     * * Xiaomi
     * * Huawei
     */
    fun monitorPowerSaveModeChange(context: Context, powerSaveModeChangeListener: PowerSaveModeChangeListener) {
        if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) !in POWER_SAVE_MODE_VALUES.keys) {
            Log.w(TAG, "monitorPowerSaveModeChange: doesn't know how to monitor power save mode change for ${Build.MANUFACTURER}")
        }
        if (monitorViaBroadcast) {
            context.registerReceiver(object : BroadcastReceiver() {
                override fun onReceive(context: Context?, intent: Intent?) {
                    powerSaveModeChangeListener.onPowerSaveModeChanged()
                }
            }, IntentFilter().also {
                for (a in POWER_SAVE_MODE_CHANGE_ACTIONS) {
                    it.addAction(a)
                }
            })
        } else {
            val contentObserver = object : ContentObserver(null) {
                override fun onChange(selfChange: Boolean) {
                    super.onChange(selfChange)
                    powerSaveModeChangeListener.onPowerSaveModeChanged()
                }
            }
            for (name in POWER_SAVE_MODE_SETTING_NAMES) {
                context.contentResolver.registerContentObserver(
                        Uri.parse("content://settings/system/${name}"), false, contentObserver)
            }
        }
    }

    /**
     * Check the system is currently in power save mode
     * @see [PowerManager.isPowerSaveMode]
     */
    fun isPowerSaveMode(context: Context): Boolean {
        if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) in POWER_SAVE_MODE_VALUES.keys) {
            return isPowerSaveModeCompat(context)
        }
        val powerManager = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
        return powerManager?.isPowerSaveMode ?: false
    }

    private fun isPowerSaveModeCompat(context: Context): Boolean {
        for (name in POWER_SAVE_MODE_SETTING_NAMES) {
            val mode = Settings.System.getInt(context.contentResolver, name, -1)
            if (mode != -1) {
                return POWER_SAVE_MODE_VALUES[Build.MANUFACTURER.toUpperCase(Locale.getDefault())] == mode
            }
        }
        return false
    }
}
Flavia answered 12/1, 2021 at 11:12 Comment(0)
P
1

Each oem modifies the SDK to suit their needs . Huawei devices don't use the default power saver function , instead they use something called "Protected apps". Protected apps are set of apps which are allowed to run even when the screen is turned off. So that's the reason it always returns false . Its better to throw a intent to protected apps screen but there is no way to know if your app is added to the protected apps list. What is protected apps ?

Penland answered 4/9, 2017 at 17:37 Comment(0)
U
1
  • I've found a way to manually request current Huawei Power Mode state and receive change events by adding a custom action to the IntentFilter:

(Note tested only on Huawei P20 Lite (ANE-LX3) @ EMUI 8.0.0)

// Manually request Power Save Mode:
public Boolean isPowerSaveMode(Context context) {
    if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")) {
        return isPowerSaveModeHuawei(context);
    } else {
        return isPowerSaveModeAndroid(context);
    }
}

@TargetApi(21)
private Boolean isPowerSaveModeAndroid(Context context) {
    boolean isPowerSaveMode = false;
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm != null) isPowerSaveMode = pm.isPowerSaveMode();
    }
    return isPowerSaveMode;
}

private Boolean isPowerSaveModeHuawei(Context context) {
    try {
        int value = android.provider.Settings.System.getInt(context.getContentResolver(), "SmartModeStatus");
        return (value == 4);
    } catch (Settings.SettingNotFoundException e) {
        // Setting not found?  Return standard android mechanism and hope for the best...
        return isPowerSaveModeAndroid(context);
    }
}

// Listening for changes in Power Save Mode
public void startMonitoringPowerSaveChanges(Context context) {
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (mPowerSaveChangeReceiver != null) {
            return;
        }
        // Register for PowerSaver change updates.
        mPowerSaveChangeReceiver = new PowerSaveChangeReceiver();

        // Registering the receiver
        IntentFilter filter = new IntentFilter();

        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
        // Add custom huawei action
        filter.addAction("huawei.intent.action.POWER_MODE_CHANGED_ACTION");

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            filter.addAction(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
        }
        context.registerReceiver(mPowerSaveChangeReceiver, filter);
    }
}

@TargetApi(21)
class PowerSaveChangeReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        boolean isPowerSaveMode = false;

        // Oh, Huawei...why don't you play by the same rules as everyone else?
        if (intent.getAction().equals("huawei.intent.action.POWER_MODE_CHANGED_ACTION")) {
            Bundle extras = intent.getExtras();
            if ((extras != null) && extras.containsKey("state")) {
                int state = intent.getExtras().getInt("state");
                isPowerSaveMode = (state == 1);  // ON=1; OFF=2
            }
        } else {
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            isPowerSaveMode = pm.isPowerSaveMode();
        }
        Log.d("MyTag", "[powersavechange] isPowerSaveMode? " + isPowerSaveMode);
    }
}
Usia answered 24/8, 2020 at 17:44 Comment(0)
O
0

I have faced new the same problem while inmplementation handheld and wearable devices. The only solution I found is to disable battery saver mode for all apps. I would suggest to detect the result of your methods after disabling such mode for all apps. This bug appear only on Huawei. Awful vendor.

Odont answered 5/9, 2017 at 9:33 Comment(0)
K
0
private void isPowerSaveModeHuaweiXiaomi(){
  if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) {
    try {
       int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "POWER_SAVE_MODE_OPEN");

            } catch (Settings.SettingNotFoundException e) {
                Log.d("Valor modo bateria:", "Error");
            }
        }else if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")){
            try {
                int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "SmartModeStatus");

            } catch (Settings.SettingNotFoundException e) {
                Log.d("Valor modo bateria:", "Error");
            }
        }
    }
Kylynn answered 16/7, 2021 at 14:22 Comment(0)
N
0

On new Huawei devices such as Huawei P30 lite for instance the solution to this question is unknown as for now (27.12.2021). calling getInt with the key "SmartModeStatus" will throw a key unknown exception. Therefore the best we can do is the following.

private string HuaweiPowerSaveModeSettingsName = "SmartModeStatus";
private int HuaweiPowerSaveModeValue = 4;

public bool IsBatterySaverEnabled
    => Build.Manufacturer?.ToUpper() == "HUAWEI" ? GetIsBatterySaverEnabledHuawei() : GetIsBatterySaverEnabledAllDevicesExceptHuawei();

private bool GetIsBatterySaverEnabledAllDevicesExceptHuawei()
{
    return PowerManager.FromContext(Application.Context)?.IsPowerSaveMode ?? false;
}

private bool GetIsBatterySaverEnabledHuawei()
{
    try
    {
        var mode = Settings.System.GetInt(Application.Context.ContentResolver, HuaweiPowerSaveModeSettingsName);
        return HuaweiPowerSaveModeValue == mode;
    } catch (Exception e)
    {
        return GetIsBatterySaverEnabledAllDevicesExceptHuawei();
    }
}
Newish answered 27/12, 2021 at 21:47 Comment(0)
M
0

For huawei vtr-al00, SmartModeStatus 1 could be ultra save mode or the normal mode. I've used reflection to handle this.

    final int _HX =  Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")?2
            :Build.MANUFACTURER.equalsIgnoreCase("Huawei")?1
            :0;
    // “No Kotlin”
    private boolean isPowerSaveModeCompat(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                && powerManager.isPowerSaveMode()) { // hopefully...
            return true;
        }
        if (_HX==0) {
            return false;
        }
        else if (_HX==1) {
            try {
                int value = Settings.System.getInt(getContentResolver(), "SmartModeStatus");
                CMN.debug("isPowerSaveModeCompat::huawei::"+value);
                // value 4==Save Mode; 1==Ultra Save Mode==Normal Mode;
                //  ( tested on my huawei vtr-al00 )
                if(value==4) {
                    return true;
                }
                if(value==1) {
                    // what if Ultra save mode???
                    // https://github.com/huaweigerrit
                    // https://github.com/SivanLiu/HwFrameWorkSource
                    
                    // https://mcmap.net/q/41826/-where-is-android-os-systemproperties
//                  Class sysProp= Class.forName("android.os.SystemProperties");
//                  Method sysProp_getBool = sysProp.getMethod("getBoolean", new Class[]{String.class, boolean.class});
//                  Object[] parms = new Object[]{"sys.super_power_save", false};
//                  CMN.debug("huawei::UltraPowerSave::", sysProp_getBool.invoke(null, parms));
//                  CMN.debug("huawei::UltraPowerSave::", getSystemProperty("sys.super_power_save"));
                    return "true".equals(getSystemProperty("sys.super_power_save"));
                }
            } catch (Exception e) {
                CMN.debug(e);
            }
        }
        else if (_HX==2){
            try {
                int value = Settings.System.getInt(getContentResolver(), "POWER_SAVE_MODE_OPEN");
                CMN.debug("isPowerSaveModeCompat::xiaomi::"+value);
                // dont have xiaomi. not tested.
                return value==1;
            } catch (Exception e) {
                CMN.debug(e);
            }
        }
        // else if...
        return false;
    }
    
    // https://mcmap.net/q/746617/-how-to-get-the-build-prop-values
    public String getSystemProperty(String key) {
        String value = null;
        
        try {
            value = (String) Class.forName("android.os.SystemProperties")
                    .getMethod("get", String.class).invoke(null, key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return value;
    }

Java is just shorter kotlin, even with so many comments and dirty tests! :)

Maneuver answered 20/1, 2022 at 21:21 Comment(0)
D
0

I use this code

val isHuawei: Boolean by lazy {
    val str = Build.MANUFACTURER.lowercase(Locale.getDefault())
    (str == MESSAGE_SYSTEM_SOURCE_HUAWEI) || (str == "honor")
}
val isXiaomi: Boolean by lazy { (Build.MANUFACTURER.lowercase(Locale.getDefault()) == MESSAGE_SYSTEM_SOURCE_XIAOMI) }

/**
 * is open power save mode
 * @param context context
 * @return true - open, false - close
 */
fun getPowerSaveMode(context: Context): Boolean {
    if (isXiaomi) {
        try {
            val value = Settings.System.getInt(context.contentResolver, "POWER_SAVE_MODE_OPEN")
            return value == 1
        } catch (_: Settings.SettingNotFoundException) {
        }
    } else if (isHuawei) {
        try {
            val value = Settings.System.getInt(context.contentResolver, "SmartModeStatus")
            return value == 1
        } catch (_: Settings.SettingNotFoundException) {
        }
    }
    var powerMode = false
    val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager?
    if (powerManager != null) {
        powerMode = powerManager.isPowerSaveMode
    }
    return powerMode
}
Dunfermline answered 30/4, 2024 at 9:46 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.