Compare commits

...

7 commits

Author SHA1 Message Date
Sicelo A. Mhlongo
f6e63cfc40 upower: deprecate luminosity property
Since d2053eff ("linux: Remove user-space support for Logitech Unifying
devices"), the luminosity property is no longer in use and no code for it
exists except in the lib. Deprecate and eventually drop it.
2025-12-17 16:46:28 +08:00
Kate Hsuan
0f57b6bf73 Drop the incorrect "since version" and mark the capacity_level property as deprecated
Fix: 09ada72 (dbus: org.freedesktop.UPower.Device: deprecate CapacityLevel property)
2025-12-17 16:19:55 +08:00
Kate Hsuan
38ab5417b7 linux: integration-test: test for the battery recalibration
Test case for ignoring the CriticalPowerAction when performing the battery
recalibration.
2025-12-15 14:19:13 +08:00
Kate Hsuan
d9f42cfc9d up-daemon: Report UP_DEVICE_LEVEL_NONE when battery is performing recalibration
If the battery is performing recalibration and AC is online,
UP_DEVICE_LEVEL_NONE is considered.
2025-12-15 14:19:13 +08:00
Kate Hsuan
4722fcfaac up-config: introduce the ExpectBatteryReCalibration for battery recalibration
ExpectBatteryReCalibration allows the user to ignore the
CriticalPowerAction when performing the battery recalibration.
2025-12-15 14:19:13 +08:00
Kate Hsuan
1ba5f3a55e linux: up-device-supply-battery: Fix assigning an error to a non-NULL GError pointer
As some systems only allow for one charge threshold setting, the overall
process is treated as a success as long as one of the values is set
correctly. We only treat it as an error (and return FALSE) if both the
start and end threshold settings fail. For this reason, we don't need
to track specific errors for every write, so the GError parameter in
g_file_set_contents_full() is set to NULL.

Fedora upowerd[1326]: g_file_set_contents_full: assertion 'error == NULL || *error == NULL' failed
Fedora upowerd[1326]: GError set over the top of a previous GError or uninitialized memory.
                      This indicates a bug in someone's code. You must ensure an error is NULL before it's set.
                      The overwriting error message was: Failed to set charge control thresholds

Resolves: #331
2025-12-12 20:14:04 +08:00
Kate Hsuan
c5df437cc1 linux: up-device-supply: fix the resource leak
1. free the udev parent node automatically.
2. free new_model_name variable in the same scope.
2025-11-28 15:00:22 +08:00
8 changed files with 176 additions and 23 deletions

View file

@ -572,11 +572,18 @@ method return sender=:1.386 -> dest=:1.477 reply_serial=2
</property>
<property name="Luminosity" type="d" access="read">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<doc:doc>
<doc:description>
<doc:para>
Luminosity being recorded by the meter.
</doc:para>
<doc:para>
DEPRECATED
</doc:para>
<doc:para>
This property is deprecated since the code it depends on was removed in 0.99.12
</doc:para>
</doc:description>
</doc:doc>
</property>
@ -967,9 +974,6 @@ method return sender=:1.386 -> dest=:1.477 reply_serial=2
<doc:para>
This property is deprecated since it is duplicated from the 'BatteryLevel' property.
</doc:para>
<doc:para>
Since 1.91.0
</doc:para>
</doc:description>
</doc:doc>
</property>

View file

@ -88,6 +88,15 @@ TimeAction=120
# Default is false
AllowRiskyCriticalPowerAction=false
# If the user performs the battery recalibration, the battery behavior
# will be set to "force discharge" and the user expects the battery will
# be fully discharged. However, upower performs the
# CriticalPowerAction earlier than the battery was fully discharged.
# ExpectBatteryRecalibration allows upower to ignore the
# CriticalPowerAction when the user attempts to perform the battery recalibration.
# Default is false
ExpectBatteryRecalibration=false
# The action to take when "TimeAction" or "PercentageAction" above has been
# reached for the batteries (UPS or laptop batteries) supplying the computer
#

View file

@ -350,9 +350,8 @@ up_device_to_text (UpDevice *device)
if (up_exported_device_get_voltage_max_design (priv->proxy_device) > 0)
g_string_append_printf (string, " voltage-max-design: %g V\n", up_exported_device_get_voltage_max_design (priv->proxy_device));
/* Suppress the warning about the deprecated property CapacityLevel */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property.
* since 1.91.0 */
/* Eliminate the display of the deprecation warning specific to the CapacityLevel property. */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
capacity_level = up_exported_device_get_capacity_level (priv->proxy_device);
@ -377,8 +376,12 @@ up_device_to_text (UpDevice *device)
g_string_append_printf (string, " charge-cycles: %s\n", "N/A");
}
if (kind == UP_DEVICE_KIND_KEYBOARD) {
/* DEPRECATED. This property is deprecated since the code it depends on was removed in 0.99.12. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
if (up_exported_device_get_luminosity (priv->proxy_device) > 0)
g_string_append_printf (string, " luminosity: %g lx\n", up_exported_device_get_luminosity (priv->proxy_device));
#pragma GCC diagnostic pop
}
if (kind == UP_DEVICE_KIND_BATTERY ||
kind == UP_DEVICE_KIND_UPS) {
@ -694,7 +697,11 @@ up_device_set_property (GObject *object, guint prop_id, const GValue *value, GPa
up_exported_device_set_voltage (device->priv->proxy_device, g_value_get_double (value));
break;
case PROP_LUMINOSITY:
/* DEPRECATED. This property is deprecated since the code it depends on was removed in 0.99.12. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
up_exported_device_set_luminosity (device->priv->proxy_device, g_value_get_double (value));
#pragma GCC diagnostic pop
break;
case PROP_TIME_TO_EMPTY:
up_exported_device_set_time_to_empty (device->priv->proxy_device, g_value_get_int64 (value));
@ -741,9 +748,8 @@ up_device_set_property (GObject *object, guint prop_id, const GValue *value, GPa
case PROP_VOLTAGE_MAX_DESIGN:
up_exported_device_set_voltage_max_design (device->priv->proxy_device, g_value_get_double (value));
break;
/* Suppress the warning about the deprecated property CapacityLevel */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property.
* since 1.91.0 */
/* Eliminate the display of the deprecation warning specific to the CapacityLevel property. */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
case PROP_CAPACITY_LEVEL:
@ -841,7 +847,11 @@ up_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpe
g_value_set_double (value, up_exported_device_get_voltage (device->priv->proxy_device));
break;
case PROP_LUMINOSITY:
/* DEPRECATED. This property is deprecated since the code it depends on was removed in 0.99.12. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
g_value_set_double (value, up_exported_device_get_luminosity (device->priv->proxy_device));
#pragma GCC diagnostic pop
break;
case PROP_TIME_TO_EMPTY:
g_value_set_int64 (value, up_exported_device_get_time_to_empty (device->priv->proxy_device));
@ -885,9 +895,8 @@ up_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpe
case PROP_VOLTAGE_MAX_DESIGN:
g_value_set_double (value, up_exported_device_get_voltage_max_design (device->priv->proxy_device));
break;
/* Suppress the warning about the deprecated property CapacityLevel */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property.
* since 1.91.0 */
/* Eliminate the display of the deprecation warning specific to the CapacityLevel property. */
/* DEPRECATED. This property is deprecated since it is duplicated from the 'BatteryLevel' property. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
case PROP_CAPACITY_LEVEL:
@ -1193,13 +1202,16 @@ up_device_class_init (UpDeviceClass *klass)
*
* The current luminosity of the device.
*
* NOTE: As of 1.91.1, this property is deprecated since the code it
* depends on was removed in 0.99.12.
*
* Since: 0.9.19
**/
g_object_class_install_property (object_class,
PROP_LUMINOSITY,
g_param_spec_double ("luminosity", NULL, NULL,
0.0, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE));
G_PARAM_READWRITE | G_PARAM_DEPRECATED));
/**
* UpDevice:time-to-empty:
*
@ -1404,14 +1416,12 @@ up_device_class_init (UpDeviceClass *klass)
*
* DEPRECATED.
* This property is deprecated since it is duplicated from the 'BatteryLevel' property.
*
* Since 1.91.0
**/
g_object_class_install_property (object_class,
PROP_CAPACITY_LEVEL,
g_param_spec_string ("capacity-level",
NULL, NULL, NULL,
G_PARAM_READWRITE));
G_PARAM_READWRITE | G_PARAM_DEPRECATED));
}
static void

View file

@ -3511,6 +3511,106 @@ class Tests(dbusmock.DBusTestCase):
os.unlink(config.name)
def test_critical_action_is_ignored_when_performing_battery_recalibration(self):
"""check that critical action is ignored when performing battery recalibration"""
ac0 = self.testbed.add_device(
"power_supply",
"AC0",
None,
["type", "Mains", "online", "1"],
[],
)
bat0 = self.testbed.add_device(
"power_supply",
"BAT0",
None,
[
"type",
"Battery",
"present",
"1",
"status",
"Discharging",
"energy_full",
"60000000",
"energy_full_design",
"80000000",
"energy_now",
"50000000",
"voltage_now",
"12000000",
"charge_behaviour",
"[auto] inhibit-charge force-discharge",
],
[],
)
config = tempfile.NamedTemporaryFile(delete=False, mode="w")
config.write("[UPower]\n")
config.write("UsePercentageForPolicy=true\n")
config.write("PercentageAction=5\n")
config.write("AllowRiskyCriticalPowerAction=true\n")
config.write("ExpectBatteryRecalibration=true\n")
config.close()
self.start_daemon(cfgfile=config.name, warns=True)
devs = self.proxy.EnumerateDevices()
self.assertEqual(len(devs), 2)
bat0_up = devs[1]
# check warning message when CriticalPowerAction=Suspend and AllowRiskyCriticalPowerAction=true
self.daemon_log.check_line(
'The "ExpectBatteryRecalibration" setting is considered risky:'
" abrupt power loss due to battery exhaustion may lead to data"
" corruption. The system will unexpected down when the AC is disconnected."
" Use AllowRiskyCriticalPowerAction=false to disable support for"
" risky settings.",
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
)
# simulate that battery has 0%
self.testbed.set_attribute(
bat0, "charge_behaviour", "auto inhibit-charge [force-discharge]"
)
self.testbed.set_attribute(bat0, "energy_now", "0")
self.testbed.uevent(bat0, "change")
time.sleep(0.5)
self.assertEqual(
self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE
)
self.daemon_log.check_line(
"ExpectBatteryRecalibration is enabled and the AC is connected, so the battery level is not critical",
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
)
self.testbed.set_attribute(bat0, "status", "Not charging")
self.testbed.uevent(bat0, "change")
self.daemon_log.check_line(
"ExpectBatteryRecalibration is enabled and the AC is connected, so the battery level is not critical",
timeout=UP_DAEMON_ACTION_DELAY + 1.0,
)
self.testbed.set_attribute(
bat0, "charge_behaviour", "[auto] inhibit-charge force-discharge"
)
self.testbed.set_attribute(bat0, "energy_now", "800000")
self.testbed.set_attribute(bat0, "status", "Charging")
self.testbed.uevent(bat0, "change")
time.sleep(0.5)
self.assertEqual(
self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE
)
self.stop_daemon()
os.unlink(config.name)
def test_low_battery_changes_history_save_interval(self):
"""check that we save the history more quickly on low battery"""

View file

@ -711,9 +711,8 @@ up_device_supply_battery_set_battery_charge_thresholds(UpDevice *device, guint s
if (start != G_MAXUINT) {
g_string_printf (start_str, "%d", CLAMP (start, 0, 100));
if (!g_file_set_contents_full (start_filename, start_str->str, start_str->len,
G_FILE_SET_CONTENTS_ONLY_EXISTING, 0644, error)) {
G_FILE_SET_CONTENTS_ONLY_EXISTING, 0644, NULL))
err_count++;
}
} else {
g_debug ("Ignore charge_control_start_threshold setting");
}
@ -721,9 +720,8 @@ up_device_supply_battery_set_battery_charge_thresholds(UpDevice *device, guint s
if (end != G_MAXUINT) {
g_string_printf (end_str, "%d", CLAMP (end, 0, 100));
if (!g_file_set_contents_full (end_filename, end_str->str, end_str->len,
G_FILE_SET_CONTENTS_ONLY_EXISTING, 0644, error)) {
G_FILE_SET_CONTENTS_ONLY_EXISTING, 0644, NULL))
err_count++;
}
} else {
g_debug ("Ignore charge_control_end_threshold setting");
}

View file

@ -331,7 +331,9 @@ static void
up_device_supply_sibling_discovered_guess_type (UpDevice *device,
GObject *sibling)
{
GUdevDevice *input, *native_device, *parent_device, *parent_sibling;
GUdevDevice *input, *native_device;
g_autoptr (GUdevDevice) parent_device = NULL;
g_autoptr (GUdevDevice) parent_sibling = NULL;
UpDeviceKind cur_type, new_type;
gboolean is_same_parent = FALSE;
char *new_model_name;
@ -492,9 +494,10 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device,
"type", new_type,
"model", new_model_name,
NULL);
g_free (new_model_name);
} else
} else {
g_object_set (device, "type", new_type, NULL);
}
g_free (new_model_name);
}
}

View file

@ -247,6 +247,7 @@ static void
up_config_init (UpConfig *config)
{
gboolean allow_risky_critical_action = FALSE;
gboolean expect_battery_recalibration = FALSE;
g_autofree gchar *critical_action = NULL;
g_autoptr (GError) error = NULL;
g_autofree gchar *filename = NULL;
@ -308,6 +309,17 @@ up_config_init (UpConfig *config)
" risky settings.", critical_action);
}
}
expect_battery_recalibration = up_config_get_boolean (config, "ExpectBatteryRecalibration");
if (expect_battery_recalibration) {
if (allow_risky_critical_action) {
g_warning ("The \"ExpectBatteryRecalibration\" setting is considered risky:"
" abrupt power loss due to battery exhaustion may lead to data"
" corruption. The system will unexpected down when the AC is disconnected."
" Use AllowRiskyCriticalPowerAction=false to disable support for"
" risky settings.");
}
}
}
/**

View file

@ -769,6 +769,8 @@ up_daemon_compute_warning_level (UpDaemon *daemon,
gdouble percentage,
gint64 time_to_empty)
{
gboolean can_risky = FALSE;
gboolean expect_battery_recalibration = FALSE;
gboolean use_percentage = TRUE;
UpDeviceLevel default_level = UP_DEVICE_LEVEL_NONE;
@ -791,6 +793,21 @@ up_daemon_compute_warning_level (UpDaemon *daemon,
default_level = UP_DEVICE_LEVEL_DISCHARGING;
}
/* Check if the battery is performing the battery recalibration and
* AC is online, the battery level is UP_DEVICE_LEVEL_NONE. */
can_risky = up_config_get_boolean (daemon->priv->config,
"AllowRiskyCriticalPowerAction");
expect_battery_recalibration = up_config_get_boolean (daemon->priv->config,
"ExpectBatteryRecalibration");
if (can_risky && kind == UP_DEVICE_KIND_BATTERY) {
if (expect_battery_recalibration &&
up_daemon_get_on_ac_local (daemon, NULL)) {
g_debug ("ExpectBatteryRecalibration is enabled and the AC is connected, so the battery level is not critical");
return UP_DEVICE_LEVEL_NONE;
}
}
if (power_supply &&
!daemon->priv->use_percentage_for_policy &&
time_to_empty > 0.0)