Set the system battery state and level on the FuContext shared state

This allows plugins to set the battery power state of the *machine* which means
we can automatically inhibit devices with FWUPD_DEVICE_FLAG_REQUIRE_AC set.

It also allows to set the BatteryThreshold to 25% for Lenovo hardware, and we
can override other vendors with further quirks as required.

Fixes https://github.com/fwupd/fwupd/issues/3084
This commit is contained in:
Richard Hughes 2021-04-06 13:35:15 +01:00
parent 0f50a3186c
commit 4d76d18d35
11 changed files with 454 additions and 117 deletions

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -8,6 +8,8 @@
#include <gio/gio.h>
#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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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 : [

View File

@ -2,4 +2,4 @@
# The threshold to to require battery be at or above to allow updates
# Measure in percent
BatteryThreshold=10
#BatteryThreshold=10

View File

@ -0,0 +1,2 @@
[LENOVO]
BatteryThreshold = 25

View File

@ -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),