mirror of
https://gitlab.freedesktop.org/upower/power-profiles-daemon.git
synced 2025-12-20 04:30:09 +01:00
Add support for turning on/off upower at runtime
This will allow DE environments to decide whether they want to allow actions and profiles to dynamically change from the battery level or AC adapter presence.
This commit is contained in:
parent
30ea6f6610
commit
d08683ecf1
7 changed files with 206 additions and 27 deletions
19
README.md
19
README.md
|
|
@ -70,6 +70,25 @@ Actions can be enabled or disabled by running:
|
|||
powerprofilesctl configure-action <action> --disable
|
||||
```
|
||||
|
||||
## Dynamic profile and action changes
|
||||
|
||||
Some actions and profiles have the ability to respond to changes to the battery
|
||||
level and state of the AC adapter on the system. These change events are triggered
|
||||
by upower.
|
||||
|
||||
If you would prefer not let any of these dynamic changes happen you can disable
|
||||
upower support by running:
|
||||
|
||||
```sh
|
||||
powerprofilesctl configure-battery-aware --disable
|
||||
```
|
||||
|
||||
If disabled, it can be re-renabled using:
|
||||
|
||||
```sh
|
||||
powerprofilesctl configure-battery-aware --enable
|
||||
```
|
||||
|
||||
## Conflicts
|
||||
|
||||
If `power-profiles-daemon` refuses to start, it's likely that you have [a conflicting
|
||||
|
|
|
|||
|
|
@ -38,4 +38,14 @@
|
|||
</defaults>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.UPower.PowerProfiles.configure-battery-aware">
|
||||
<description>Configure action</description>
|
||||
<message>Privileges are required to configure battery-awareness.</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
||||
|
|
|
|||
|
|
@ -166,5 +166,13 @@
|
|||
-->
|
||||
<property name="Version" type="s" access="read"/>
|
||||
|
||||
<!--
|
||||
BatteryAware:
|
||||
|
||||
Whether the daemon is using upower to detect battery and AC
|
||||
adapter changes.
|
||||
-->
|
||||
<property name="BatteryAware" type="b" access="readwrite"/>
|
||||
|
||||
</interface>
|
||||
</node>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ typedef struct {
|
|||
GPtrArray *actions;
|
||||
GHashTable *profile_holds;
|
||||
|
||||
gboolean battery_support;
|
||||
GDBusProxy *upower_proxy;
|
||||
GDBusProxy *upower_display_proxy;
|
||||
gulong upower_watch_id;
|
||||
|
|
@ -170,6 +171,7 @@ typedef enum {
|
|||
PROP_DEGRADED = 1 << 4,
|
||||
PROP_ACTIVE_PROFILE_HOLDS = 1 << 5,
|
||||
PROP_VERSION = 1 << 6,
|
||||
PROP_UPOWER = 1 << 7,
|
||||
} PropertiesMask;
|
||||
|
||||
#define PROP_ALL (PROP_ACTIVE_PROFILE | \
|
||||
|
|
@ -178,7 +180,8 @@ typedef enum {
|
|||
PROP_ACTIONS | \
|
||||
PROP_DEGRADED | \
|
||||
PROP_ACTIVE_PROFILE_HOLDS | \
|
||||
PROP_VERSION)
|
||||
PROP_VERSION | \
|
||||
PROP_UPOWER)
|
||||
|
||||
static gboolean
|
||||
driver_profile_support (PpdDriver *driver,
|
||||
|
|
@ -287,6 +290,7 @@ get_legacy_actions_variant (PpdApp *data)
|
|||
|
||||
for (i = 0; i < data->actions->len; i++) {
|
||||
PpdAction *action = g_ptr_array_index (data->actions, i);
|
||||
|
||||
if (!ppd_action_get_active (action))
|
||||
continue;
|
||||
|
||||
|
|
@ -400,6 +404,10 @@ send_dbus_event_iface (PpdApp *data,
|
|||
g_variant_builder_add (&props_builder, "{sv}", "Version",
|
||||
g_variant_new_string (VERSION));
|
||||
}
|
||||
if (mask & PROP_UPOWER) {
|
||||
g_variant_builder_add (&props_builder, "{sv}", "BatteryAware",
|
||||
g_variant_new_boolean (data->battery_support));
|
||||
}
|
||||
|
||||
props_changed = g_variant_new ("(s@a{sv}@as)", iface,
|
||||
g_variant_builder_end (&props_builder),
|
||||
|
|
@ -450,6 +458,8 @@ save_configuration (PpdApp *data)
|
|||
ppd_action_get_active (action));
|
||||
}
|
||||
|
||||
g_key_file_set_boolean (data->config, "State", "battery_aware", data->battery_support);
|
||||
|
||||
if (!g_key_file_save_to_file (data->config, data->config_path, &error))
|
||||
g_warning ("Could not save configuration file '%s': %s", data->config_path, error->message);
|
||||
}
|
||||
|
|
@ -460,8 +470,17 @@ apply_configuration (PpdApp *data)
|
|||
g_autofree char *platform_driver = NULL;
|
||||
g_autofree char *profile_str = NULL;
|
||||
g_autofree char *cpu_driver = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
PpdProfile profile;
|
||||
|
||||
data->battery_support = g_key_file_get_boolean (data->config, "State", "battery_aware", &error);
|
||||
if (error != NULL) {
|
||||
g_debug("battery_aware key not found: %s, defaulting to TRUE", error->message);
|
||||
data->battery_support = TRUE;
|
||||
g_key_file_set_boolean (data->config, "State", "battery_aware", TRUE);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
cpu_driver = g_key_file_get_string (data->config, "State", "CpuDriver", NULL);
|
||||
if (PPD_IS_DRIVER_CPU (data->cpu_driver) &&
|
||||
g_strcmp0 (ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), cpu_driver) != 0)
|
||||
|
|
@ -622,6 +641,24 @@ release_all_profile_holds (PpdApp *data)
|
|||
g_hash_table_remove_all (data->profile_holds);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_battery_support (PpdApp *data, gboolean battery_support, GError **error)
|
||||
{
|
||||
if (data->battery_support == battery_support) {
|
||||
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
||||
"battery_aware is already set to %s", battery_support ? "TRUE" : "FALSE");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->battery_support = battery_support;
|
||||
g_key_file_set_boolean (data->config, "State", "battery_aware", data->battery_support);
|
||||
g_debug("battery_aware set to %s", data->battery_support ? "TRUE" : "FALSE");
|
||||
|
||||
restart_profile_drivers_for_default_app ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_active_profile (PpdApp *data,
|
||||
const char *profile,
|
||||
|
|
@ -919,6 +956,8 @@ handle_get_property (GDBusConnection *connection,
|
|||
return get_modern_actions_variant (data);
|
||||
if (g_str_equal (property_name, "Actions"))
|
||||
return get_legacy_actions_variant (data);
|
||||
if (g_str_equal (property_name, "BatteryAware"))
|
||||
return g_variant_new_boolean (data->battery_support);
|
||||
if (g_strcmp0 (property_name, "PerformanceDegraded") == 0) {
|
||||
gchar *degraded = get_performance_degraded (data);
|
||||
return g_variant_new_take_string (g_steal_pointer (°raded));
|
||||
|
|
@ -943,15 +982,12 @@ handle_set_property (GDBusConnection *connection,
|
|||
gpointer user_data)
|
||||
{
|
||||
PpdApp *data = user_data;
|
||||
const char *profile;
|
||||
|
||||
g_return_val_if_fail (data->connection, FALSE);
|
||||
|
||||
if (g_strcmp0 (property_name, "ActiveProfile") != 0) {
|
||||
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
||||
"No such property: %s", property_name);
|
||||
return FALSE;
|
||||
}
|
||||
if (g_str_equal (property_name, "ActiveProfile")) {
|
||||
const char *profile;
|
||||
|
||||
if (!check_action_permission (data, sender,
|
||||
POWER_PROFILES_POLICY_NAMESPACE ".switch-profile",
|
||||
error))
|
||||
|
|
@ -959,6 +995,20 @@ handle_set_property (GDBusConnection *connection,
|
|||
|
||||
g_variant_get (value, "&s", &profile);
|
||||
return set_active_profile (data, profile, error);
|
||||
} else if (g_str_equal (property_name, "BatteryAware")) {
|
||||
if (!check_action_permission (data,
|
||||
sender,
|
||||
POWER_PROFILES_POLICY_NAMESPACE ".configure-battery-aware",
|
||||
error)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return set_battery_support (data, g_variant_get_boolean (value), error);
|
||||
}
|
||||
|
||||
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
||||
"No such property: %s", property_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean set_action_enabled (PpdApp *data,
|
||||
|
|
@ -1018,12 +1068,14 @@ handle_method_call (GDBusConnection *connection,
|
|||
release_profile (data, parameters, invocation);
|
||||
} else if (g_strcmp0 (method_name, "SetActionEnabled") == 0) {
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
if (g_str_equal (interface_name, POWER_PROFILES_LEGACY_IFACE_NAME)) {
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
|
||||
"Method %s is not available in interface %s", method_name,
|
||||
interface_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check_action_permission (data,
|
||||
g_dbus_method_invocation_get_sender (invocation),
|
||||
POWER_PROFILES_POLICY_NAMESPACE ".configure-action",
|
||||
|
|
@ -1031,6 +1083,7 @@ handle_method_call (GDBusConnection *connection,
|
|||
g_dbus_method_invocation_return_gerror (invocation, local_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!set_action_enabled (data, parameters, &local_error)) {
|
||||
g_dbus_method_invocation_return_gerror (invocation, local_error);
|
||||
return;
|
||||
|
|
@ -1671,7 +1724,10 @@ start_profile_drivers (PpdApp *data)
|
|||
send_dbus_event (data, PROP_ALL);
|
||||
data->was_started = TRUE;
|
||||
|
||||
if (data->debug_options->disable_upower) {
|
||||
if (data->debug_options->disable_upower)
|
||||
data->battery_support = FALSE;
|
||||
|
||||
if (!data->battery_support) {
|
||||
g_debug ("upower is disabled, let's skip it");
|
||||
} else if (needs_battery_state_monitor || needs_battery_change_monitor) {
|
||||
/* start watching for power changes */
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@ def _version(_args):
|
|||
print(f"client: {client_version}\ndaemon: {daemon_ver}")
|
||||
|
||||
|
||||
@command
|
||||
def _set_profile(args):
|
||||
proxy = get_proxy()
|
||||
proxy.Set(
|
||||
"(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0])
|
||||
)
|
||||
|
||||
|
||||
@command
|
||||
def _get(_args):
|
||||
proxy = get_proxy()
|
||||
|
|
@ -55,11 +63,16 @@ def _get(_args):
|
|||
|
||||
|
||||
@command
|
||||
def _set(args):
|
||||
def _set_battery_aware(args):
|
||||
enable = args.enable
|
||||
disable = args.disable
|
||||
if enable is False and disable is True:
|
||||
raise ValueError("enable or disable is required")
|
||||
if enable is True and disable is False:
|
||||
raise ValueError("can't set both enable and disable")
|
||||
enable = enable if enable is not None else not disable
|
||||
proxy = get_proxy()
|
||||
proxy.Set(
|
||||
"(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0])
|
||||
)
|
||||
proxy.Set("(ssv)", PP_IFACE, "BatteryAware", GLib.Variant.new_boolean(enable))
|
||||
|
||||
|
||||
def get_profiles_property(prop):
|
||||
|
|
@ -166,6 +179,12 @@ def _launch(args):
|
|||
sys.exit(ret)
|
||||
|
||||
|
||||
@command
|
||||
def _query_battery_aware(_args):
|
||||
result = get_profiles_property("BatteryAware")
|
||||
print(f"Dynamic changes from charger and battery events: {result}")
|
||||
|
||||
|
||||
@command
|
||||
def _list_actions(_args):
|
||||
actions = get_profiles_property("ActionsInfo")
|
||||
|
|
@ -225,7 +244,7 @@ def get_parser():
|
|||
help="Profile to use for set command",
|
||||
choices=get_profile_choices(),
|
||||
)
|
||||
parser_set.set_defaults(func=_set)
|
||||
parser_set.set_defaults(func=_set_profile)
|
||||
parser_set_action = subparsers.add_parser(
|
||||
"configure-action", help="Configure the action to be taken for the profile"
|
||||
)
|
||||
|
|
@ -245,6 +264,26 @@ def get_parser():
|
|||
help="disable action",
|
||||
)
|
||||
parser_set_action.set_defaults(func=_configure_action)
|
||||
parser_set_battery_aware = subparsers.add_parser(
|
||||
"configure-battery-aware",
|
||||
help="Turn on or off dynamic changes from battery level or power adapter",
|
||||
)
|
||||
parser_set_battery_aware.add_argument(
|
||||
"--enable",
|
||||
action="store_true",
|
||||
help="enable battery aware",
|
||||
)
|
||||
parser_set_battery_aware.add_argument(
|
||||
"--disable",
|
||||
action="store_false",
|
||||
help="disable battery aware",
|
||||
)
|
||||
parser_set_battery_aware.set_defaults(func=_set_battery_aware)
|
||||
parser_query_battery_aware = subparsers.add_parser(
|
||||
"query-battery-aware",
|
||||
help="Query if dynamic changes from battery level or power adapter are enabled",
|
||||
)
|
||||
parser_query_battery_aware.set_defaults(func=_query_battery_aware)
|
||||
parser_launch = subparsers.add_parser(
|
||||
"launch",
|
||||
help="Launch a command while holding a power profile",
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ class Tests(dbusmock.DBusTestCase):
|
|||
"org.freedesktop.UPower.PowerProfiles.switch-profile",
|
||||
"org.freedesktop.UPower.PowerProfiles.hold-profile",
|
||||
"org.freedesktop.UPower.PowerProfiles.configure-action",
|
||||
"org.freedesktop.UPower.PowerProfiles.configure-battery-aware",
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -2483,6 +2484,52 @@ class Tests(dbusmock.DBusTestCase):
|
|||
self.assertEqual(len(profiles), 3)
|
||||
self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
|
||||
|
||||
def test_powerprofilesctl_configure_battery_aware_command(self):
|
||||
"""Check powerprofilesctl configure-battery-aware command works"""
|
||||
|
||||
self.start_dbus_template(
|
||||
"upower",
|
||||
{"DaemonVersion": "0.99", "OnBattery": False},
|
||||
)
|
||||
|
||||
self.start_daemon()
|
||||
|
||||
# verify argument is required
|
||||
cmd = subprocess.run(
|
||||
self.powerprofilesctl_command() + ["configure-battery-aware"],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
self.assertEqual(cmd.returncode, 1)
|
||||
|
||||
# check possible arguments
|
||||
for key, value in {"--disable": "False", "--enable": "True"}.items():
|
||||
cmd = subprocess.run(
|
||||
self.powerprofilesctl_command() + ["configure-battery-aware", key],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
self.assertEqual(cmd.returncode, 0)
|
||||
|
||||
cmd = subprocess.run(
|
||||
self.powerprofilesctl_command() + ["query-battery-aware"],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
self.assertEqual(cmd.returncode, 0)
|
||||
self.assertIn(
|
||||
f"Dynamic changes from charger and battery events: {value}",
|
||||
cmd.stdout.decode("utf-8"),
|
||||
)
|
||||
|
||||
# make sure can't be enabled twice
|
||||
cmd = subprocess.run(
|
||||
self.powerprofilesctl_command() + ["configure-battery-aware", "--enable"],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
self.assertEqual(cmd.returncode, 1)
|
||||
|
||||
def test_powerprofilesctl_configure_action_command(self):
|
||||
"""Check powerprofilesctl configure-action command works"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue