Coverage Report

Created: 2026-04-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-device-event.c
Line
Count
Source
1
/*
2
 * Copyright 2024 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuDeviceEvent"
8
9
#include "config.h"
10
11
#include "fu-device-event-private.h"
12
#include "fu-mem.h"
13
#include "fu-string.h"
14
15
/**
16
 * FuDeviceEvent:
17
 *
18
 * A device event, used to enumulate hardware.
19
 *
20
 * See also: [class@FuDevice]
21
 */
22
23
typedef struct {
24
  GType gtype;
25
  GRefString *key;
26
  gpointer data;
27
  GDestroyNotify data_destroy;
28
} FuDeviceEventBlob;
29
30
struct _FuDeviceEvent {
31
  GObject parent_instance;
32
  gchar *id;
33
  gchar *id_uncompressed;
34
  GPtrArray *values; /* element-type FuDeviceEventBlob */
35
};
36
37
static void
38
fu_device_event_codec_iface_init(FwupdCodecInterface *iface);
39
40
2.26k
G_DEFINE_TYPE_WITH_CODE(FuDeviceEvent,
41
2.26k
      fu_device_event,
42
2.26k
      G_TYPE_OBJECT,
43
2.26k
      G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_device_event_codec_iface_init))
44
2.26k
45
2.26k
/*
46
2.26k
 * NOTE: We use an event *counter* that gets the next event in the emulation, and this ID is only
47
2.26k
 * used as a sanity check in case we have to skip an entry.
48
2.26k
 */
49
2.26k
#define FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE 8
50
51
static void
52
fu_device_event_blob_free(FuDeviceEventBlob *blob)
53
282
{
54
282
  g_ref_string_release(blob->key);
55
282
  if (blob->data_destroy != NULL)
56
282
    blob->data_destroy(blob->data);
57
282
  g_free(blob);
58
282
}
59
60
static FuDeviceEventBlob *
61
fu_device_event_blob_new_internal(GType gtype,
62
          GRefString *key,
63
          gpointer data,
64
          GDestroyNotify data_destroy)
65
282
{
66
282
  FuDeviceEventBlob *blob = g_new0(FuDeviceEventBlob, 1);
67
282
  blob->key = g_ref_string_acquire(key);
68
282
  blob->gtype = gtype;
69
282
  blob->data = data;
70
282
  blob->data_destroy = data_destroy;
71
282
  return blob;
72
282
}
73
74
static FuDeviceEventBlob *
75
fu_device_event_blob_new(GType gtype, const gchar *key, gpointer data, GDestroyNotify data_destroy)
76
282
{
77
282
  g_autoptr(GRefString) key_ref = NULL;
78
282
  const gchar *known_keys[] = {
79
282
      "Data",
80
282
      "DataOut",
81
282
      "Error",
82
282
      "ErrorMsg",
83
282
      "Rc",
84
282
      NULL,
85
282
  };
86
282
  key_ref = g_strv_contains(known_keys, key) ? g_ref_string_new_intern(key)
87
282
               : g_ref_string_new(key);
88
282
  return fu_device_event_blob_new_internal(gtype, key_ref, data, data_destroy);
89
282
}
90
91
/**
92
 * fu_device_event_build_id:
93
 * @id: a string
94
 *
95
 * Return the hash of the event ID.
96
 *
97
 * Returns: string hash prefix
98
 *
99
 * Since: 2.0.3
100
 **/
101
gchar *
102
fu_device_event_build_id(const gchar *id)
103
0
{
104
0
  guint8 buf[20] = {0};
105
0
  gsize bufsz = sizeof(buf);
106
0
  g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA1);
107
0
  g_autoptr(GString) id_hash = g_string_sized_new(FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE + 1);
108
109
0
  g_return_val_if_fail(id != NULL, NULL);
110
111
  /* IMPORTANT: if you're reading this we're not using the SHA1 prefix for any kind of secure
112
   * hash, just because it is a tiny string that takes up less memory than the full ID. */
113
0
  g_checksum_update(csum, (const guchar *)id, strlen(id));
114
0
  g_checksum_get_digest(csum, buf, &bufsz);
115
0
  g_string_append_c(id_hash, '#');
116
0
  for (guint i = 0; i < FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE / 2; i++)
117
0
    g_string_append_printf(id_hash, "%02x", buf[i]);
118
0
  return g_string_free(g_steal_pointer(&id_hash), FALSE);
119
0
}
120
121
/**
122
 * fu_device_event_get_id:
123
 * @self: a #FuDeviceEvent
124
 *
125
 * Return the truncated SHA1 of the #FuDeviceEvent key, which is normally set when creating the
126
 * object.
127
 *
128
 * Returns: (nullable): string
129
 *
130
 * Since: 2.0.0
131
 **/
132
const gchar *
133
fu_device_event_get_id(FuDeviceEvent *self)
134
0
{
135
0
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL);
136
0
  return self->id;
137
0
}
138
139
/**
140
 * fu_device_event_set_str:
141
 * @self: a #FuDeviceEvent
142
 * @key: (not nullable): a unique key, e.g. `Name`
143
 * @value: (nullable): a string
144
 *
145
 * Sets a string value on the event.
146
 *
147
 * Since: 2.0.0
148
 **/
149
void
150
fu_device_event_set_str(FuDeviceEvent *self, const gchar *key, const gchar *value)
151
0
{
152
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
153
0
  g_return_if_fail(key != NULL);
154
0
  g_ptr_array_add(self->values,
155
0
      fu_device_event_blob_new(G_TYPE_STRING, key, g_strdup(value), g_free));
156
0
}
157
158
/**
159
 * fu_device_event_set_i64:
160
 * @self: a #FuDeviceEvent
161
 * @key: (not nullable): a unique key, e.g. `Name`
162
 * @value: a string
163
 *
164
 * Sets an integer value on the string.
165
 *
166
 * Since: 2.0.0
167
 **/
168
void
169
fu_device_event_set_i64(FuDeviceEvent *self, const gchar *key, gint64 value)
170
0
{
171
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
172
0
  g_return_if_fail(key != NULL);
173
174
0
  g_ptr_array_add(
175
0
      self->values,
176
0
      fu_device_event_blob_new(G_TYPE_INT, key, g_memdup2(&value, sizeof(value)), g_free));
177
0
}
178
179
/**
180
 * fu_device_event_set_bytes:
181
 * @self: a #FuDeviceEvent
182
 * @key: (not nullable): a unique key, e.g. `Name`
183
 * @value: (not nullable): a #GBytes
184
 *
185
 * Sets a blob on the event. Note: blobs are stored internally as BASE-64 strings.
186
 *
187
 * Since: 2.0.0
188
 **/
189
void
190
fu_device_event_set_bytes(FuDeviceEvent *self, const gchar *key, GBytes *value)
191
282
{
192
282
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
193
282
  g_return_if_fail(key != NULL);
194
282
  g_return_if_fail(value != NULL);
195
282
  g_ptr_array_add(self->values,
196
282
      fu_device_event_blob_new(
197
282
          G_TYPE_STRING,
198
282
          key,
199
282
          g_base64_encode(g_bytes_get_data(value, NULL), g_bytes_get_size(value)),
200
282
          g_free));
201
282
}
202
203
/**
204
 * fu_device_event_set_byte_array:
205
 * @self: a #FuDeviceEvent
206
 * @key: (not nullable): a unique key, e.g. `Name`
207
 * @value: (not nullable): a #GByteArray
208
 *
209
 * Sets a blob on the event. Note: blobs are stored internally as BASE-64 strings.
210
 *
211
 * Since: 2.1.1
212
 **/
213
void
214
fu_device_event_set_byte_array(FuDeviceEvent *self, const gchar *key, GByteArray *value)
215
0
{
216
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
217
0
  g_return_if_fail(key != NULL);
218
0
  g_return_if_fail(value != NULL);
219
0
  g_ptr_array_add(self->values,
220
0
      fu_device_event_blob_new(G_TYPE_STRING,
221
0
             key,
222
0
             g_base64_encode(value->data, value->len),
223
0
             g_free));
224
0
}
225
226
/**
227
 * fu_device_event_set_data:
228
 * @self: a #FuDeviceEvent
229
 * @key: (not nullable): a unique key, e.g. `Name`
230
 * @buf: (nullable): a buffer
231
 * @bufsz: size of @buf
232
 *
233
 * Sets a memory buffer on the event. Note: memory buffers are stored internally as BASE-64 strings.
234
 *
235
 * Since: 2.0.0
236
 **/
237
void
238
fu_device_event_set_data(FuDeviceEvent *self, const gchar *key, const guint8 *buf, gsize bufsz)
239
0
{
240
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
241
0
  g_return_if_fail(key != NULL);
242
0
  g_ptr_array_add(
243
0
      self->values,
244
0
      fu_device_event_blob_new(G_TYPE_STRING, key, g_base64_encode(buf, bufsz), g_free));
245
0
}
246
247
/**
248
 * fu_device_event_set_error:
249
 * @self: a #FuDeviceEvent
250
 * @error: (not nullable): a #GError with domain #FwupdError
251
 *
252
 * Sets an error on the event.
253
 *
254
 * Since: 2.0.6
255
 **/
256
void
257
fu_device_event_set_error(FuDeviceEvent *self, const GError *error)
258
0
{
259
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
260
0
  g_return_if_fail(error != NULL);
261
0
  g_return_if_fail(error->domain == FWUPD_ERROR);
262
0
  fu_device_event_set_i64(self, "Error", error->code);
263
0
  fu_device_event_set_str(self, "ErrorMsg", error->message);
264
0
}
265
266
/**
267
 * fu_device_event_check_error:
268
 * @self: a #FuDeviceEvent
269
 * @error: (nullable): optional return location for an error
270
 *
271
 * Sets an error from the event if possible.
272
 *
273
 * Returns: %FALSE if @error was set
274
 *
275
 * Since: 2.0.6
276
 **/
277
gboolean
278
fu_device_event_check_error(FuDeviceEvent *self, GError **error)
279
0
{
280
0
  gint64 code;
281
0
  const gchar *message;
282
283
0
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE);
284
285
  /* nothing to do */
286
0
  if (error == NULL)
287
0
    return TRUE;
288
289
  /* anything set */
290
0
  code = fu_device_event_get_i64(self, "Error", NULL);
291
0
  if (code == G_MAXINT64)
292
0
    return TRUE;
293
0
  message = fu_device_event_get_str(self, "ErrorMsg", NULL);
294
0
  if (message == NULL)
295
0
    message = fwupd_error_to_string(code);
296
297
  /* success, in a way */
298
0
  g_set_error_literal(error, FWUPD_ERROR, code, message);
299
0
  return FALSE;
300
0
}
301
302
static gpointer
303
fu_device_event_lookup(FuDeviceEvent *self, const gchar *key, GType gtype, GError **error)
304
852
{
305
852
  FuDeviceEventBlob *blob = NULL;
306
307
1.42k
  for (guint i = 0; i < self->values->len; i++) {
308
852
    FuDeviceEventBlob *blob_tmp = g_ptr_array_index(self->values, i);
309
852
    if (g_strcmp0(blob_tmp->key, key) == 0) {
310
284
      blob = blob_tmp;
311
284
      break;
312
284
    }
313
852
  }
314
852
  if (blob == NULL) {
315
568
    g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no event for key %s", key);
316
568
    return NULL;
317
568
  }
318
284
  if (blob->gtype != gtype) {
319
0
    g_set_error(error,
320
0
          FWUPD_ERROR,
321
0
          FWUPD_ERROR_INVALID_DATA,
322
0
          "invalid event type for key %s",
323
0
          key);
324
0
    return NULL;
325
0
  }
326
284
  return blob->data;
327
284
}
328
329
/**
330
 * fu_device_event_get_str:
331
 * @self: a #FuDeviceEvent
332
 * @key: (not nullable): a unique key, e.g. `Name`
333
 * @error: (nullable): optional return location for an error
334
 *
335
 * Gets a string value from the event.
336
 *
337
 * Returns: (nullable): string, or %NULL on error
338
 *
339
 * Since: 2.0.0
340
 **/
341
const gchar *
342
fu_device_event_get_str(FuDeviceEvent *self, const gchar *key, GError **error)
343
0
{
344
0
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL);
345
0
  g_return_val_if_fail(key != NULL, NULL);
346
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
347
0
  return (const gchar *)fu_device_event_lookup(self, key, G_TYPE_STRING, error);
348
0
}
349
350
/**
351
 * fu_device_event_get_i64:
352
 * @self: a #FuDeviceEvent
353
 * @key: (not nullable): a unique key, e.g. `Name`
354
 * @error: (nullable): optional return location for an error
355
 *
356
 * Gets an integer value from the event.
357
 *
358
 * Returns: integer, or %G_MAXINT64 on error
359
 *
360
 * Since: 2.0.0
361
 **/
362
gint64
363
fu_device_event_get_i64(FuDeviceEvent *self, const gchar *key, GError **error)
364
568
{
365
568
  gint64 *val;
366
568
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), G_MAXINT64);
367
568
  g_return_val_if_fail(key != NULL, G_MAXINT64);
368
568
  g_return_val_if_fail(error == NULL || *error == NULL, G_MAXINT64);
369
568
  val = fu_device_event_lookup(self, key, G_TYPE_INT, error);
370
568
  if (val == NULL)
371
568
    return G_MAXINT64;
372
0
  return *val;
373
568
}
374
375
/**
376
 * fu_device_event_get_bytes:
377
 * @self: a #FuDeviceEvent
378
 * @key: (not nullable): a unique key, e.g. `Name`
379
 * @error: (nullable): optional return location for an error
380
 *
381
 * Gets a memory blob from the event.
382
 *
383
 * Returns: (transfer full) (nullable): byes data, or %NULL on error
384
 *
385
 * Since: 2.0.0
386
 **/
387
GBytes *
388
fu_device_event_get_bytes(FuDeviceEvent *self, const gchar *key, GError **error)
389
0
{
390
0
  const gchar *blobstr;
391
0
  gsize bufsz = 0;
392
0
  g_autofree guchar *buf = NULL;
393
394
0
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL);
395
0
  g_return_val_if_fail(key != NULL, NULL);
396
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
397
398
0
  blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error);
399
0
  if (blobstr == NULL)
400
0
    return NULL;
401
0
  if (blobstr[0] == '\0')
402
0
    return g_bytes_new(NULL, 0);
403
0
  buf = g_base64_decode(blobstr, &bufsz);
404
0
  return g_bytes_new_take(g_steal_pointer(&buf), bufsz);
405
0
}
406
407
/**
408
 * fu_device_event_get_byte_array:
409
 * @self: a #FuDeviceEvent
410
 * @key: (not nullable): a unique key, e.g. `Name`
411
 * @error: (nullable): optional return location for an error
412
 *
413
 * Gets a memory blob from the event.
414
 *
415
 * Returns: (transfer full) (nullable): a #GByteArray, or %NULL on error
416
 *
417
 * Since: 2.1.1
418
 **/
419
GByteArray *
420
fu_device_event_get_byte_array(FuDeviceEvent *self, const gchar *key, GError **error)
421
0
{
422
0
  const gchar *blobstr;
423
0
  gsize bufsz = 0;
424
0
  g_autofree guchar *buf = NULL;
425
426
0
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL);
427
0
  g_return_val_if_fail(key != NULL, NULL);
428
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
429
430
0
  blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error);
431
0
  if (blobstr == NULL)
432
0
    return NULL;
433
0
  if (blobstr[0] == '\0')
434
0
    return g_byte_array_new();
435
0
  buf = g_base64_decode(blobstr, &bufsz);
436
0
  return g_byte_array_new_take(g_steal_pointer(&buf), bufsz);
437
0
}
438
439
/**
440
 * fu_device_event_copy_data:
441
 * @self: a #FuDeviceEvent
442
 * @key: (not nullable): a unique key, e.g. `Name`
443
 * @buf: (nullable): a buffer
444
 * @bufsz: size of @buf
445
 * @actual_length: (out) (optional): the actual number of bytes sent, or %NULL
446
 * @error: (nullable): optional return location for an error
447
 *
448
 * Copies memory from the event.
449
 *
450
 * Returns: %TRUE if the buffer was copied
451
 *
452
 * Since: 2.0.0
453
 **/
454
gboolean
455
fu_device_event_copy_data(FuDeviceEvent *self,
456
        const gchar *key,
457
        guint8 *buf,
458
        gsize bufsz,
459
        gsize *actual_length,
460
        GError **error)
461
284
{
462
284
  const gchar *blobstr;
463
284
  gsize bufsz_src = 0;
464
284
  g_autofree guchar *buf_src = NULL;
465
466
284
  g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE);
467
284
  g_return_val_if_fail(key != NULL, FALSE);
468
284
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
469
470
284
  blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error);
471
284
  if (blobstr == NULL)
472
0
    return FALSE;
473
284
  buf_src = g_base64_decode(blobstr, &bufsz_src);
474
284
  if (actual_length != NULL)
475
284
    *actual_length = bufsz_src;
476
284
  if (buf != NULL)
477
284
    return fu_memcpy_safe(buf, bufsz, 0x0, buf_src, bufsz_src, 0x0, bufsz_src, error);
478
0
  return TRUE;
479
284
}
480
481
static void
482
fu_device_event_add_json(FwupdCodec *codec, FwupdJsonObject *json_obj, FwupdCodecFlags flags)
483
0
{
484
0
  FuDeviceEvent *self = FU_DEVICE_EVENT(codec);
485
486
0
  if (self->id_uncompressed != NULL && (flags & FWUPD_CODEC_FLAG_COMPRESSED) == 0) {
487
0
    fwupd_json_object_add_string(json_obj, "Id", self->id_uncompressed);
488
0
  } else if (self->id != NULL) {
489
0
    fwupd_json_object_add_string(json_obj, "Id", self->id);
490
0
  }
491
492
0
  for (guint i = 0; i < self->values->len; i++) {
493
0
    FuDeviceEventBlob *blob = g_ptr_array_index(self->values, i);
494
0
    if (blob->gtype == G_TYPE_INT) {
495
0
      fwupd_json_object_add_integer(json_obj, blob->key, *((gint64 *)blob->data));
496
0
    } else if (blob->gtype == G_TYPE_BYTES || blob->gtype == G_TYPE_STRING) {
497
0
      fwupd_json_object_add_string(json_obj,
498
0
                 blob->key,
499
0
                 (const gchar *)blob->data);
500
0
    } else {
501
0
      g_warning("invalid GType %s, ignoring", g_type_name(blob->gtype));
502
0
    }
503
0
  }
504
0
}
505
506
static void
507
fu_device_event_set_id(FuDeviceEvent *self, const gchar *id)
508
0
{
509
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(self));
510
0
  g_return_if_fail(id != NULL);
511
512
0
  g_clear_pointer(&self->id, g_free);
513
0
  g_clear_pointer(&self->id_uncompressed, g_free);
514
515
  /* already a truncated SHA1 hash? */
516
0
  if (g_str_has_prefix(id, "#")) {
517
0
    self->id = g_strdup(id);
518
0
  } else {
519
0
    self->id_uncompressed = g_strdup(id);
520
0
    self->id = fu_device_event_build_id(id);
521
0
  }
522
0
}
523
524
static void
525
fu_device_event_set_id_from_json(FuDeviceEvent *self, const gchar *id)
526
0
{
527
0
  g_clear_pointer(&self->id, g_free);
528
0
  if (g_str_has_prefix(id, "#")) {
529
0
    self->id = g_strdup(id);
530
0
  } else {
531
0
    self->id = fu_device_event_build_id(id);
532
0
  }
533
0
}
534
535
static gboolean
536
fu_device_event_from_json(FwupdCodec *codec, FwupdJsonObject *json_obj, GError **error)
537
0
{
538
0
  FuDeviceEvent *self = FU_DEVICE_EVENT(codec);
539
0
  for (guint i = 0; i < fwupd_json_object_get_size(json_obj); i++) {
540
0
    GRefString *key = fwupd_json_object_get_key_for_index(json_obj, i, NULL);
541
0
    g_autoptr(FwupdJsonNode) json_node = NULL;
542
543
0
    json_node = fwupd_json_object_get_node_for_index(json_obj, i, error);
544
0
    if (json_node == NULL)
545
0
      return FALSE;
546
0
    if (fwupd_json_node_get_kind(json_node) == FWUPD_JSON_NODE_KIND_STRING) {
547
0
      GRefString *str = fwupd_json_node_get_string(json_node, NULL);
548
0
      if (g_strcmp0(key, "Id") == 0) {
549
0
        fu_device_event_set_id_from_json(self, str);
550
0
      } else if (str != NULL) {
551
0
        g_ptr_array_add(self->values,
552
0
            fu_device_event_blob_new_internal(
553
0
                G_TYPE_STRING,
554
0
                key,
555
0
                g_ref_string_acquire(str),
556
0
                (GDestroyNotify)g_ref_string_release));
557
0
      } else {
558
0
        g_ptr_array_add(self->values,
559
0
            fu_device_event_blob_new_internal(G_TYPE_STRING,
560
0
                      key,
561
0
                      NULL,
562
0
                      NULL));
563
0
      }
564
0
    } else if (fwupd_json_node_get_kind(json_node) == FWUPD_JSON_NODE_KIND_RAW) {
565
0
      GRefString *str;
566
0
      gint64 value = 0;
567
568
0
      str = fwupd_json_node_get_raw(json_node, error);
569
0
      if (str == NULL)
570
0
        return FALSE;
571
0
      if (!fu_strtoll(str,
572
0
          &value,
573
0
          G_MININT64,
574
0
          G_MAXINT64,
575
0
          FU_INTEGER_BASE_AUTO,
576
0
          error))
577
0
        return FALSE;
578
0
      g_ptr_array_add(
579
0
          self->values,
580
0
          fu_device_event_blob_new_internal(G_TYPE_INT,
581
0
                    key,
582
0
                    g_memdup2(&value, sizeof(value)),
583
0
                    g_free));
584
0
    }
585
0
  }
586
587
  /* we do not need this again, so avoid keeping all the tree data in memory */
588
0
  fwupd_json_object_clear(json_obj);
589
590
  /* success */
591
0
  return TRUE;
592
0
}
593
594
static void
595
fu_device_event_init(FuDeviceEvent *self)
596
282
{
597
282
  self->values = g_ptr_array_new_with_free_func((GDestroyNotify)fu_device_event_blob_free);
598
282
}
599
600
static void
601
fu_device_event_finalize(GObject *object)
602
282
{
603
282
  FuDeviceEvent *self = FU_DEVICE_EVENT(object);
604
282
  g_free(self->id);
605
282
  g_free(self->id_uncompressed);
606
282
  g_ptr_array_unref(self->values);
607
282
  G_OBJECT_CLASS(fu_device_event_parent_class)->finalize(object);
608
282
}
609
610
static void
611
fu_device_event_class_init(FuDeviceEventClass *klass)
612
1
{
613
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
614
1
  object_class->finalize = fu_device_event_finalize;
615
1
}
616
617
static void
618
fu_device_event_codec_iface_init(FwupdCodecInterface *iface)
619
1
{
620
1
  iface->add_json = fu_device_event_add_json;
621
1
  iface->from_json = fu_device_event_from_json;
622
1
}
623
624
/**
625
 * fu_device_event_new:
626
 * @id: a cache key, which is converted to a truncated SHA1 hash if required
627
 *
628
 * Return value: (transfer full): a new #FuDeviceEvent object.
629
 *
630
 * Since: 2.0.0
631
 **/
632
FuDeviceEvent *
633
fu_device_event_new(const gchar *id)
634
282
{
635
282
  FuDeviceEvent *self = g_object_new(FU_TYPE_DEVICE_EVENT, NULL);
636
282
  if (id != NULL)
637
0
    fu_device_event_set_id(self, id);
638
282
  return FU_DEVICE_EVENT(self);
639
282
}