diff --git a/libfwupdplugin/fu-common.c b/libfwupdplugin/fu-common.c index b698b2b04..b4e4064ff 100644 --- a/libfwupdplugin/fu-common.c +++ b/libfwupdplugin/fu-common.c @@ -3280,6 +3280,32 @@ fu_common_align_up (gsize value, guint8 alignment) return value_new; } +/** + * fu_battery_state_to_string: + * @battery_state: a #FuBatteryState, e.g. %FU_BATTERY_STATE_FULLY_CHARGED + * + * Converts an enumerated type to a string. + * + * Returns: a string, or %NULL for invalid + * + * Since: 1.6.0 + **/ +const gchar * +fu_battery_state_to_string (FuBatteryState battery_state) +{ + if (battery_state == FU_BATTERY_STATE_UNKNOWN) + return "unknown"; + if (battery_state == FU_BATTERY_STATE_CHARGING) + return "charging"; + if (battery_state == FU_BATTERY_STATE_DISCHARGING) + return "discharging"; + if (battery_state == FU_BATTERY_STATE_EMPTY) + return "empty"; + if (battery_state == FU_BATTERY_STATE_FULLY_CHARGED) + return "fully-charged"; + return NULL; +} + /** * fu_xmlb_builder_insert_kv: * @bn: #JsonBuilder diff --git a/libfwupdplugin/fu-common.h b/libfwupdplugin/fu-common.h index f541ec77e..1dc9f471b 100644 --- a/libfwupdplugin/fu-common.h +++ b/libfwupdplugin/fu-common.h @@ -101,6 +101,34 @@ typedef enum { FU_CPU_VENDOR_LAST } FuCpuVendor; +/** + * FU_BATTERY_VALUE_INVALID: + * + * This value signifies the battery level is either unset, or the value cannot + * be discovered. + */ +#define FU_BATTERY_VALUE_INVALID 101 + +/** + * FuBatteryState: + * @FU_BATTERY_STATE_UNKNOWN: Unknown + * @FU_BATTERY_STATE_CHARGING: Charging + * @FU_BATTERY_STATE_DISCHARGING: Discharging + * @FU_BATTERY_STATE_EMPTY: Empty + * @FU_BATTERY_STATE_FULLY_CHARGED: Fully charged + * + * The device battery state. + **/ +typedef enum { + FU_BATTERY_STATE_UNKNOWN, + FU_BATTERY_STATE_CHARGING, + FU_BATTERY_STATE_DISCHARGING, + FU_BATTERY_STATE_EMPTY, + FU_BATTERY_STATE_FULLY_CHARGED, + /*< private >*/ + FU_BATTERY_STATE_LAST +} FuBatteryState; + typedef void (*FuOutputHandler) (const gchar *line, gpointer user_data); @@ -364,6 +392,7 @@ guint32 fu_common_crc32_full (const guint8 *buf, gchar *fu_common_uri_get_scheme (const gchar *uri); gsize fu_common_align_up (gsize value, guint8 alignment); +const gchar *fu_battery_state_to_string (FuBatteryState battery_state); void fu_xmlb_builder_insert_kv (XbBuilderNode *bn, const gchar *key, diff --git a/libfwupdplugin/fu-context.c b/libfwupdplugin/fu-context.c index f7098030f..c0c4c37a1 100644 --- a/libfwupdplugin/fu-context.c +++ b/libfwupdplugin/fu-context.c @@ -8,7 +8,6 @@ #include "config.h" -#include "fu-common.h" #include "fu-context-private.h" #include "fu-hwids.h" #include "fu-smbios-private.h" @@ -29,6 +28,9 @@ typedef struct { GHashTable *compile_versions; GPtrArray *udev_subsystems; GHashTable *firmware_gtypes; + FuBatteryState battery_state; + guint battery_level; + guint battery_threshold; } FuContextPrivate; enum { @@ -36,6 +38,14 @@ enum { SIGNAL_LAST }; +enum { + PROP_0, + PROP_BATTERY_STATE, + PROP_BATTERY_LEVEL, + PROP_BATTERY_THRESHOLD, + PROP_LAST +}; + static guint signals[SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE_WITH_PRIVATE (FuContext, fu_context, G_TYPE_OBJECT) @@ -546,6 +556,169 @@ fu_context_load_quirks (FuContext *self, FuQuirksLoadFlags flags, GError **error return TRUE; } +/** + * fu_context_get_battery_state: + * @self: A #FuContext + * + * Gets if the system is on battery power, e.g. UPS or laptop battery. + * + * Returns: a #FuBatteryState, e.g. %FU_BATTERY_STATE_DISCHARGING + * + * Since: 1.6.0 + **/ +FuBatteryState +fu_context_get_battery_state (FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_CONTEXT (self), FALSE); + return priv->battery_state; +} + +/** + * fu_context_set_battery_state: + * @self: A #FuContext + * @battery_state: a #FuBatteryState, e.g. %FU_BATTERY_STATE_DISCHARGING + * + * Sets if the system is on battery power, e.g. UPS or laptop battery. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_state (FuContext *self, FuBatteryState battery_state) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_CONTEXT (self)); + if (priv->battery_state == battery_state) + return; + priv->battery_state = battery_state; + g_debug ("battery state now %s", + fu_battery_state_to_string (battery_state)); + g_object_notify (G_OBJECT (self), "battery-state"); +} + +/** + * fu_context_get_battery_level: + * @self: A #FuContext + * + * Gets the system battery level in percent. + * + * Returns: percentage value, or %FU_BATTERY_VALUE_INVALID for unknown + * + * Since: 1.6.0 + **/ +guint +fu_context_get_battery_level (FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_CONTEXT (self), G_MAXUINT); + return priv->battery_level; +} + +/** + * fu_context_set_battery_level: + * @self: A #FuContext + * @battery_level: value + * + * Sets the system battery level in percent. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_level (FuContext *self, guint battery_level) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_CONTEXT (self)); + g_return_if_fail (battery_level <= FU_BATTERY_VALUE_INVALID); + if (priv->battery_level == battery_level) + return; + priv->battery_level = battery_level; + g_debug ("battery level now %u", battery_level); + g_object_notify (G_OBJECT (self), "battery-level"); +} + +/** + * fu_context_get_battery_threshold: + * @self: A #FuContext + * + * Gets the system battery threshold in percent. + * + * Returns: percentage value, or %FU_BATTERY_VALUE_INVALID for unknown + * + * Since: 1.6.0 + **/ +guint +fu_context_get_battery_threshold (FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_CONTEXT (self), G_MAXUINT); + return priv->battery_threshold; +} + +/** + * fu_context_set_battery_threshold: + * @self: A #FuContext + * @battery_threshold: value + * + * Sets the system battery threshold in percent. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_threshold (FuContext *self, guint battery_threshold) +{ + FuContextPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_CONTEXT (self)); + g_return_if_fail (battery_threshold <= FU_BATTERY_VALUE_INVALID); + if (priv->battery_threshold == battery_threshold) + return; + priv->battery_threshold = battery_threshold; + g_debug ("battery threshold now %u", battery_threshold); + g_object_notify (G_OBJECT (self), "battery-threshold"); +} + +static void +fu_context_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuContext *self = FU_CONTEXT (object); + FuContextPrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_BATTERY_STATE: + g_value_set_uint (value, priv->battery_state); + break; + case PROP_BATTERY_LEVEL: + g_value_set_uint (value, priv->battery_level); + break; + case PROP_BATTERY_THRESHOLD: + g_value_set_uint (value, priv->battery_threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_context_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuContext *self = FU_CONTEXT (object); + switch (prop_id) { + case PROP_BATTERY_STATE: + fu_context_set_battery_state (self, g_value_get_uint (value)); + break; + case PROP_BATTERY_LEVEL: + fu_context_set_battery_level (self, g_value_get_uint (value)); + break; + case PROP_BATTERY_THRESHOLD: + fu_context_set_battery_threshold (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void fu_context_finalize (GObject *object) { @@ -565,6 +738,34 @@ static void fu_context_class_init (FuContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->get_property = fu_context_get_property; + object_class->set_property = fu_context_set_property; + + pspec = g_param_spec_uint ("battery-state", NULL, NULL, + FU_BATTERY_STATE_UNKNOWN, + FU_BATTERY_STATE_LAST, + FU_BATTERY_STATE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_BATTERY_STATE, pspec); + + pspec = g_param_spec_uint ("battery-level", NULL, NULL, + 0, + FU_BATTERY_VALUE_INVALID, + FU_BATTERY_VALUE_INVALID, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_BATTERY_LEVEL, pspec); + + pspec = g_param_spec_uint ("battery-threshold", NULL, NULL, + 0, + FU_BATTERY_VALUE_INVALID, + FU_BATTERY_VALUE_INVALID, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_BATTERY_THRESHOLD, pspec); signals[SIGNAL_SECURITY_CHANGED] = g_signal_new ("security-changed", @@ -580,6 +781,8 @@ static void fu_context_init (FuContext *self) { FuContextPrivate *priv = GET_PRIVATE (self); + priv->battery_level = FU_BATTERY_VALUE_INVALID; + priv->battery_threshold = FU_BATTERY_VALUE_INVALID; priv->smbios = fu_smbios_new (); priv->hwids = fu_hwids_new (); priv->udev_subsystems = g_ptr_array_new_with_free_func (g_free); diff --git a/libfwupdplugin/fu-context.h b/libfwupdplugin/fu-context.h index 130393b87..4a47811fe 100644 --- a/libfwupdplugin/fu-context.h +++ b/libfwupdplugin/fu-context.h @@ -8,6 +8,8 @@ #include +#include "fu-common.h" + #define FU_TYPE_CONTEXT (fu_context_get_type ()) G_DECLARE_DERIVABLE_TYPE (FuContext, fu_context, FU, CONTEXT, GObject) @@ -59,3 +61,13 @@ gboolean fu_context_lookup_quirk_by_id_iter (FuContext *self, void fu_context_add_quirk_key (FuContext *self, const gchar *key); void fu_context_security_changed (FuContext *self); + +FuBatteryState fu_context_get_battery_state (FuContext *self); +void fu_context_set_battery_state (FuContext *self, + FuBatteryState battery_state); +guint fu_context_get_battery_level (FuContext *self); +void fu_context_set_battery_level (FuContext *self, + guint battery_level); +guint fu_context_get_battery_threshold (FuContext *self); +void fu_context_set_battery_threshold (FuContext *self, + guint battery_threshold); diff --git a/libfwupdplugin/fu-device.c b/libfwupdplugin/fu-device.c index 6a343c24f..3e3a28af6 100644 --- a/libfwupdplugin/fu-device.c +++ b/libfwupdplugin/fu-device.c @@ -2665,9 +2665,8 @@ static void fu_device_ensure_battery_inhibit (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); - if (priv->battery_level == 0) - return; - if (priv->battery_level >= fu_device_get_battery_threshold (self)) { + if (priv->battery_level == FU_BATTERY_VALUE_INVALID || + priv->battery_level >= fu_device_get_battery_threshold (self)) { fu_device_uninhibit (self, "battery"); return; } @@ -2688,7 +2687,7 @@ guint fu_device_get_battery_level (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); - g_return_val_if_fail (FU_IS_DEVICE (self), 0); + g_return_val_if_fail (FU_IS_DEVICE (self), FU_BATTERY_VALUE_INVALID); return priv->battery_level; } @@ -2697,8 +2696,10 @@ fu_device_get_battery_level (FuDevice *self) * @self: A #FuDevice * @battery_level: the percentage value * - * Sets the battery level, or 0 for invalid. Setting this allows fwupd to show - * a warning if the device change is too low to perform the update. + * Sets the battery level, or %FU_BATTERY_VALUE_INVALID. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. * * Since: 1.5.8 **/ @@ -2707,7 +2708,7 @@ fu_device_set_battery_level (FuDevice *self, guint battery_level) { FuDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_DEVICE (self)); - g_return_if_fail (battery_level <= 100); + g_return_if_fail (battery_level <= FU_BATTERY_VALUE_INVALID); if (priv->battery_level == battery_level) return; priv->battery_level = battery_level; @@ -2733,10 +2734,10 @@ guint fu_device_get_battery_threshold (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); - g_return_val_if_fail (FU_IS_DEVICE (self), 0); + g_return_val_if_fail (FU_IS_DEVICE (self), FU_BATTERY_VALUE_INVALID); /* default value */ - if (priv->battery_threshold == 0) + if (priv->battery_threshold == FU_BATTERY_VALUE_INVALID) return FU_DEVICE_DEFAULT_BATTERY_THRESHOLD; return priv->battery_threshold; @@ -2747,8 +2748,10 @@ fu_device_get_battery_threshold (FuDevice *self) * @self: A #FuDevice * @battery_threshold: the percentage value * - * Sets the battery level, or 0 for the default. Setting this allows fwupd to - * show a warning if the device change is too low to perform the update. + * Sets the battery level, or %FU_BATTERY_VALUE_INVALID for the default. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. * * Since: 1.6.0 **/ @@ -2757,7 +2760,7 @@ fu_device_set_battery_threshold (FuDevice *self, guint battery_threshold) { FuDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_DEVICE (self)); - g_return_if_fail (battery_threshold <= 100); + g_return_if_fail (battery_threshold <= FU_BATTERY_VALUE_INVALID); if (priv->battery_threshold == battery_threshold) return; priv->battery_threshold = battery_threshold; @@ -2796,9 +2799,9 @@ fu_device_add_string (FuDevice *self, guint idt, GString *str) fu_common_string_append_kv (str, idt + 1, "ProxyId", fu_device_get_id (priv->proxy)); if (priv->proxy_guid != NULL) fu_common_string_append_kv (str, idt + 1, "ProxyGuid", priv->proxy_guid); - if (priv->battery_level != 0) + if (priv->battery_level != FU_BATTERY_VALUE_INVALID) fu_common_string_append_ku (str, idt + 1, "BatteryLevel", priv->battery_level); - if (priv->battery_threshold != 0) + if (priv->battery_threshold != FU_BATTERY_VALUE_INVALID) fu_common_string_append_ku (str, idt + 1, "BatteryThreshold", priv->battery_threshold); if (priv->size_min > 0) { g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); @@ -3891,13 +3894,17 @@ fu_device_class_init (FuDeviceClass *klass) g_object_class_install_property (object_class, PROP_PROGRESS, pspec); pspec = g_param_spec_uint ("battery-level", NULL, NULL, - 0, 100, 0, + 0, + FU_BATTERY_VALUE_INVALID, + FU_BATTERY_VALUE_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_BATTERY_LEVEL, pspec); pspec = g_param_spec_uint ("battery-threshold", NULL, NULL, - 0, 100, 0, + 0, + FU_BATTERY_VALUE_INVALID, + FU_BATTERY_VALUE_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_BATTERY_THRESHOLD, pspec); @@ -3921,6 +3928,8 @@ fu_device_init (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); priv->order = G_MAXINT; + priv->battery_level = FU_BATTERY_VALUE_INVALID; + priv->battery_threshold = FU_BATTERY_VALUE_INVALID; priv->parent_guids = g_ptr_array_new_with_free_func (g_free); priv->possible_plugins = g_ptr_array_new_with_free_func (g_free); priv->retry_recs = g_ptr_array_new_with_free_func (g_free); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 9ccc46d20..385ad4148 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -713,6 +713,7 @@ LIBFWUPDPLUGIN_1.5.8 { LIBFWUPDPLUGIN_1.6.0 { global: + fu_battery_state_to_string; fu_byte_array_align_up; fu_byte_array_set_size_full; fu_common_align_up; @@ -721,6 +722,9 @@ LIBFWUPDPLUGIN_1.6.0 { fu_context_add_quirk_key; fu_context_add_runtime_version; fu_context_add_udev_subsystem; + fu_context_get_battery_level; + fu_context_get_battery_state; + fu_context_get_battery_threshold; fu_context_get_firmware_gtype_by_id; fu_context_get_firmware_gtype_ids; fu_context_get_hwid_guids; @@ -738,6 +742,9 @@ LIBFWUPDPLUGIN_1.6.0 { fu_context_lookup_quirk_by_id_iter; fu_context_new; fu_context_security_changed; + fu_context_set_battery_level; + fu_context_set_battery_state; + fu_context_set_battery_threshold; fu_context_set_compile_versions; fu_context_set_runtime_versions; fu_device_add_security_attrs; diff --git a/plugins/upower/fu-plugin-upower.c b/plugins/upower/fu-plugin-upower.c index dedfe1cb4..79525c036 100644 --- a/plugins/upower/fu-plugin-upower.c +++ b/plugins/upower/fu-plugin-upower.c @@ -11,9 +11,7 @@ #define MINIMUM_BATTERY_PERCENTAGE_FALLBACK 10 struct FuPluginData { - GDBusProxy *upower_proxy; - GDBusProxy *display_proxy; - guint64 minimum_battery; + GDBusProxy *proxy; /* nullable */ }; void @@ -27,113 +25,112 @@ void fu_plugin_destroy (FuPlugin *plugin) { FuPluginData *data = fu_plugin_get_data (plugin); - if (data->upower_proxy != NULL) - g_object_unref (data->upower_proxy); - if (data->display_proxy != NULL) - g_object_unref (data->display_proxy); + if (data->proxy != NULL) + g_object_unref (data->proxy); +} + +static void +fu_plugin_upower_rescan (FuPlugin *plugin) +{ + FuContext *ctx = fu_plugin_get_context (plugin); + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(GVariant) percentage_val = NULL; + g_autoptr(GVariant) type_val = NULL; + g_autoptr(GVariant) state_val = NULL; + + /* check that we "have" a battery */ + type_val = g_dbus_proxy_get_cached_property (data->proxy, "Type"); + if (type_val == NULL) { + g_warning ("failed to query power type"); + fu_context_set_battery_state (ctx, FU_BATTERY_STATE_UNKNOWN); + fu_context_set_battery_level (ctx, FU_BATTERY_VALUE_INVALID); + return; + } + state_val = g_dbus_proxy_get_cached_property (data->proxy, "State"); + if (state_val == NULL) { + g_warning ("failed to query power state"); + fu_context_set_battery_state (ctx, FU_BATTERY_STATE_UNKNOWN); + fu_context_set_battery_level (ctx, FU_BATTERY_VALUE_INVALID); + return; + } + fu_context_set_battery_state (ctx, g_variant_get_uint32 (state_val)); + + /* get percentage */ + percentage_val = g_dbus_proxy_get_cached_property (data->proxy, "Percentage"); + if (percentage_val == NULL) { + g_warning ("failed to query power percentage level"); + fu_context_set_battery_level (ctx, FU_BATTERY_VALUE_INVALID); + return; + } + fu_context_set_battery_level (ctx, g_variant_get_double (percentage_val)); +} + +static void +fu_plugin_upower_proxy_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + FuPlugin *plugin) +{ + fu_plugin_upower_rescan (plugin); } gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { + FuContext *ctx = fu_plugin_get_context (plugin); FuPluginData *data = fu_plugin_get_data (plugin); + guint64 minimum_battery; g_autofree gchar *name_owner = NULL; g_autofree gchar *battery_str = NULL; - data->upower_proxy = + + data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - "org.freedesktop.UPower", - "/org/freedesktop/UPower", - "org.freedesktop.UPower", - NULL, - error); - if (data->upower_proxy == NULL) { - g_prefix_error (error, "failed to connect to upower: "); - return FALSE; - } - name_owner = g_dbus_proxy_get_name_owner (data->upower_proxy); - if (name_owner == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no owner for %s", - g_dbus_proxy_get_name (data->upower_proxy)); - return FALSE; - } - data->display_proxy = - g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.UPower", "/org/freedesktop/UPower/devices/DisplayDevice", "org.freedesktop.UPower.Device", NULL, error); - if (data->display_proxy == NULL) { + if (data->proxy == NULL) { g_prefix_error (error, "failed to connect to upower: "); return FALSE; } - - battery_str = fu_plugin_get_config_value (plugin, "BatteryThreshold"); - if (battery_str == NULL) - data->minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; - else - data->minimum_battery = fu_common_strtoull (battery_str); - if (data->minimum_battery > 100) { - g_warning ("Invalid minimum battery level specified: %" G_GUINT64_FORMAT, - data->minimum_battery); - data->minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; - } - - return TRUE; -} - -static gboolean -fu_plugin_upower_check_percentage_level (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - gdouble level; - guint power_type; - g_autoptr(GVariant) percentage_val = NULL; - g_autoptr(GVariant) type_val = NULL; - - /* check that we "have" a battery */ - type_val = g_dbus_proxy_get_cached_property (data->display_proxy, "Type"); - if (type_val == NULL) { - g_warning ("Failed to query power type, assume AC power"); - return TRUE; - } - power_type = g_variant_get_uint32 (type_val); - if (power_type != 2) { - g_debug ("Not running on battery (Type: %u)", power_type); - return TRUE; - } - - /* check percentage high enough */ - percentage_val = g_dbus_proxy_get_cached_property (data->display_proxy, "Percentage"); - if (percentage_val == NULL) { - g_warning ("Failed to query power percentage level, assume enough charge"); - return TRUE; - } - level = g_variant_get_double (percentage_val); - g_debug ("System power source is %.1f%%", level); - - return level >= data->minimum_battery; -} - -static gboolean -fu_plugin_upower_check_on_battery (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr(GVariant) value = NULL; - - value = g_dbus_proxy_get_cached_property (data->upower_proxy, "OnBattery"); - if (value == NULL) { - g_warning ("failed to get OnBattery value, assume on AC power"); + name_owner = g_dbus_proxy_get_name_owner (data->proxy); + if (name_owner == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no owner for %s", + g_dbus_proxy_get_name (data->proxy)); return FALSE; } - return g_variant_get_boolean (value); + g_signal_connect (data->proxy, "g-properties-changed", + G_CALLBACK (fu_plugin_upower_proxy_changed_cb), plugin); + + battery_str = fu_plugin_get_config_value (plugin, "BatteryThreshold"); + if (battery_str == NULL) { + const gchar *vendor = fu_context_get_hwid_replace_value (ctx, + FU_HWIDS_KEY_MANUFACTURER, + NULL); + battery_str = g_strdup (fu_context_lookup_quirk_by_id (ctx, + vendor, + FU_QUIRKS_BATTERY_THRESHOLD)); + } + if (battery_str == NULL) + minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; + else + minimum_battery = fu_common_strtoull (battery_str); + if (minimum_battery > 100) { + g_warning ("invalid minimum battery level specified: %" G_GUINT64_FORMAT, + minimum_battery); + minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; + } + fu_context_set_battery_threshold (ctx, minimum_battery); + fu_plugin_upower_rescan (plugin); + + /* success */ + return TRUE; } gboolean @@ -142,13 +139,17 @@ fu_plugin_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error) { + FuContext *ctx = fu_plugin_get_context (plugin); + /* not all devices need this */ if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC)) return TRUE; + if (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) + return TRUE; - /* determine if operating on AC or battery */ - if (fu_plugin_upower_check_on_battery (plugin) && - (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) == 0) { + /* not charging */ + if (fu_context_get_battery_state (ctx) == FU_BATTERY_STATE_DISCHARGING || + fu_context_get_battery_state (ctx) == FU_BATTERY_STATE_EMPTY) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AC_POWER_REQUIRED, @@ -157,17 +158,17 @@ fu_plugin_update_prepare (FuPlugin *plugin, return FALSE; } - /* determine if battery high enough */ - if (!fu_plugin_upower_check_percentage_level (plugin) && - (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) == 0) { - FuPluginData *data = fu_plugin_get_data (plugin); + /* not enough just in case */ + if (fu_context_get_battery_level (ctx) < fu_context_get_battery_threshold (ctx)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW, "Cannot install update when system battery " - "is not at least %" G_GUINT64_FORMAT "%% unless forced", - data->minimum_battery); + "is not at least %u%% unless forced", + fu_context_get_battery_threshold (ctx)); return FALSE; } + + /* success */ return TRUE; } diff --git a/plugins/upower/meson.build b/plugins/upower/meson.build index a478e70cd..55e714e15 100644 --- a/plugins/upower/meson.build +++ b/plugins/upower/meson.build @@ -1,6 +1,9 @@ if host_machine.system() == 'linux' cargs = ['-DG_LOG_DOMAIN="FuPluginUpower"'] +install_data(['upower.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d')) + shared_module('fu_plugin_upower', fu_hash, sources : [ diff --git a/plugins/upower/upower.conf b/plugins/upower/upower.conf index 18c3b1993..70fb750d6 100644 --- a/plugins/upower/upower.conf +++ b/plugins/upower/upower.conf @@ -2,4 +2,4 @@ # The threshold to to require battery be at or above to allow updates # Measure in percent -BatteryThreshold=10 +#BatteryThreshold=10 diff --git a/plugins/upower/upower.quirk b/plugins/upower/upower.quirk new file mode 100644 index 000000000..55c5158ee --- /dev/null +++ b/plugins/upower/upower.quirk @@ -0,0 +1,2 @@ +[LENOVO] +BatteryThreshold = 25 diff --git a/src/fu-engine.c b/src/fu-engine.c index b32c2e3fa..c31067eb2 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -225,10 +225,34 @@ fu_engine_watch_device (FuEngine *self, FuDevice *device) G_CALLBACK (fu_engine_status_notify_cb), self); } +static void +fu_engine_ensure_device_battery_inhibit (FuEngine *self, FuDevice *device) +{ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && + (fu_context_get_battery_state (self->ctx) == FU_BATTERY_STATE_DISCHARGING || + fu_context_get_battery_state (self->ctx) == FU_BATTERY_STATE_EMPTY)) { + fu_device_inhibit (device, "battery-system", + "Cannot install update when not on AC power"); + return; + } + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && + fu_context_get_battery_level (self->ctx) != FU_BATTERY_VALUE_INVALID && + fu_context_get_battery_threshold (self->ctx) != FU_BATTERY_VALUE_INVALID && + fu_context_get_battery_level (self->ctx) < fu_context_get_battery_threshold (self->ctx)) { + g_autofree gchar *reason = NULL; + reason = g_strdup_printf ("Cannot install update when system battery is not at least %u%%", + fu_context_get_battery_threshold (self->ctx)); + fu_device_inhibit (device, "battery-system", reason); + return; + } + fu_device_uninhibit (device, "battery-system"); +} + static void fu_engine_device_added_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self) { fu_engine_watch_device (self, device); + fu_engine_ensure_device_battery_inhibit (self, device); g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device); } @@ -6470,6 +6494,18 @@ fu_engine_add_app_flag (FuEngine *self, FuAppFlags app_flags) self->app_flags |= app_flags; } +static void +fu_engine_context_battery_changed_cb (FuContext *ctx, GParamSpec *pspec, FuEngine *self) +{ + g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list); + + /* apply policy on any existing devices */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + fu_engine_ensure_device_battery_inhibit (self, device); + } +} + static void fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self) { @@ -6509,6 +6545,15 @@ fu_engine_init (FuEngine *self) g_signal_connect (self->ctx, "security-changed", G_CALLBACK (fu_engine_context_security_changed_cb), self); + g_signal_connect (self->ctx, "notify::battery-state", + G_CALLBACK (fu_engine_context_battery_changed_cb), + self); + g_signal_connect (self->ctx, "notify::battery-level", + G_CALLBACK (fu_engine_context_battery_changed_cb), + self); + g_signal_connect (self->ctx, "notify::battery-threshold", + G_CALLBACK (fu_engine_context_battery_changed_cb), + self); g_signal_connect (self->config, "changed", G_CALLBACK (fu_engine_config_changed_cb),