Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

isPowerSaveMode() always returns false for Huawei devices

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.

like image 737
Demitrian Avatar asked Aug 17 '17 19:08

Demitrian


2 Answers

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
    }
}
like image 150
alijandro Avatar answered Sep 30 '22 04:09

alijandro


  • 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);
    }
}
like image 29
Christocracy Avatar answered Sep 30 '22 04:09

Christocracy