Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018, 2019 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib/gi18n-lib.h>
26 : : #include <glib.h>
27 : : #include <glib-object.h>
28 : : #include <gio/gio.h>
29 : : #include <libmogwai-schedule/scheduler-interface.h>
30 : : #include <libmogwai-schedule-client/schedule-entry.h>
31 : : #include <libmogwai-schedule-client/scheduler.h>
32 : :
33 : :
34 : : /* These errors do not need to be registered with
35 : : * g_dbus_error_register_error_domain() as they never go over the bus. */
36 [ + + ]: 6 : G_DEFINE_QUARK (MwscSchedulerError, mwsc_scheduler_error)
37 : :
38 : : static void mwsc_scheduler_initable_init (GInitableIface *iface);
39 : : static void mwsc_scheduler_async_initable_init (GAsyncInitableIface *iface);
40 : : static void mwsc_scheduler_constructed (GObject *object);
41 : : static void mwsc_scheduler_dispose (GObject *object);
42 : :
43 : : static void mwsc_scheduler_get_property (GObject *object,
44 : : guint property_id,
45 : : GValue *value,
46 : : GParamSpec *pspec);
47 : : static void mwsc_scheduler_set_property (GObject *object,
48 : : guint property_id,
49 : : const GValue *value,
50 : : GParamSpec *pspec);
51 : :
52 : : static gboolean mwsc_scheduler_init_failable (GInitable *initable,
53 : : GCancellable *cancellable,
54 : : GError **error);
55 : : static void mwsc_scheduler_init_async (GAsyncInitable *initable,
56 : : int io_priority,
57 : : GCancellable *cancellable,
58 : : GAsyncReadyCallback callback,
59 : : gpointer user_data);
60 : : static gboolean mwsc_scheduler_init_finish (GAsyncInitable *initable,
61 : : GAsyncResult *result,
62 : : GError **error);
63 : :
64 : : static void proxy_notify_name_owner_cb (GObject *obj,
65 : : GParamSpec *pspec,
66 : : gpointer user_data);
67 : : static void proxy_properties_changed_cb (GDBusProxy *proxy,
68 : : GVariant *changed_properties,
69 : : GStrv invalidated_properties,
70 : : gpointer user_data);
71 : :
72 : : static const GDBusErrorEntry scheduler_error_map[] =
73 : : {
74 : : { MWSC_SCHEDULER_ERROR_FULL, "com.endlessm.DownloadManager1.Scheduler.Error.Full" },
75 : : { MWSC_SCHEDULER_ERROR_IDENTIFYING_PEER, "com.endlessm.DownloadManager1.Scheduler.Error.IdentifyingPeer" },
76 : : };
77 : : G_STATIC_ASSERT (G_N_ELEMENTS (scheduler_error_map) == MWSC_SCHEDULER_N_ERRORS);
78 : : G_STATIC_ASSERT (G_N_ELEMENTS (scheduler_error_map) == G_N_ELEMENTS (scheduler_errors));
79 : :
80 : : /**
81 : : * MwscScheduler:
82 : : *
83 : : * A proxy for the scheduler in the D-Bus service. Currently, the only methods
84 : : * available on #MwscScheduler are mwsc_scheduler_schedule_async() and
85 : : * mws_scheduler_schedule_entries_async(), which should
86 : : * be used to create new #MwscScheduleEntrys. See the documentation for those
87 : : * methods for information.
88 : : *
89 : : * If the service goes away, #MwscScheduler::invalidated will be emitted, and
90 : : * all future method calls on the object will return a
91 : : * %MWSC_SCHEDULER_ERROR_INVALIDATED error.
92 : : *
93 : : * Since: 0.1.0
94 : : */
95 : : struct _MwscScheduler
96 : : {
97 : : GObject parent;
98 : :
99 : : GDBusProxy *proxy; /* (owned); NULL during initialisation */
100 : : GDBusConnection *connection; /* (owned) */
101 : : gchar *name; /* (owned); NULL if not running on a message bus */
102 : : gchar *object_path; /* (owned) */
103 : :
104 : : /* Exactly one of these will be set after initialisation completes (or
105 : : * fails). */
106 : : GError *init_error; /* nullable; owned */
107 : : gboolean init_success;
108 : : gboolean initialising;
109 : :
110 : : guint hold_count;
111 : : };
112 : :
113 : : typedef enum
114 : : {
115 : : PROP_CONNECTION = 1,
116 : : PROP_NAME,
117 : : PROP_OBJECT_PATH,
118 : : PROP_PROXY,
119 : : PROP_ALLOW_DOWNLOADS,
120 : : } MwscSchedulerProperty;
121 : :
122 [ + + + - : 19 : G_DEFINE_TYPE_WITH_CODE (MwscScheduler, mwsc_scheduler, G_TYPE_OBJECT,
+ + ]
123 : : G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
124 : : mwsc_scheduler_initable_init)
125 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
126 : : mwsc_scheduler_async_initable_init))
127 : :
128 : : static void
129 : 1 : mwsc_scheduler_class_init (MwscSchedulerClass *klass)
130 : : {
131 : 1 : GObjectClass *object_class = (GObjectClass *) klass;
132 : 1 : GParamSpec *props[PROP_ALLOW_DOWNLOADS + 1] = { NULL, };
133 : :
134 : 1 : object_class->constructed = mwsc_scheduler_constructed;
135 : 1 : object_class->dispose = mwsc_scheduler_dispose;
136 : 1 : object_class->get_property = mwsc_scheduler_get_property;
137 : 1 : object_class->set_property = mwsc_scheduler_set_property;
138 : :
139 : : /**
140 : : * MwscScheduler:connection:
141 : : *
142 : : * D-Bus connection to proxy the object from.
143 : : *
144 : : * Since: 0.1.0
145 : : */
146 : 1 : props[PROP_CONNECTION] =
147 : 1 : g_param_spec_object ("connection", "Connection",
148 : : "D-Bus connection to proxy the object from.",
149 : : G_TYPE_DBUS_CONNECTION,
150 : : G_PARAM_READWRITE |
151 : : G_PARAM_CONSTRUCT_ONLY |
152 : : G_PARAM_STATIC_STRINGS);
153 : :
154 : : /**
155 : : * MwscScheduler:name:
156 : : *
157 : : * Well-known or unique name of the peer to proxy the object from. This must
158 : : * be %NULL if and only if the #MwscScheduler:connection is not a message
159 : : * bus connection.
160 : : *
161 : : * Since: 0.1.0
162 : : */
163 : 1 : props[PROP_NAME] =
164 : 1 : g_param_spec_string ("name", "Name",
165 : : "Well-known or unique name of the peer to proxy the "
166 : : "object from.",
167 : : NULL,
168 : : G_PARAM_READWRITE |
169 : : G_PARAM_CONSTRUCT_ONLY |
170 : : G_PARAM_STATIC_STRINGS);
171 : :
172 : : /**
173 : : * MwscScheduler:object-path:
174 : : *
175 : : * Object path to proxy. The object must implement
176 : : * `com.endlessm.DownloadManager1.Scheduler`.
177 : : *
178 : : * Since: 0.1.0
179 : : */
180 : 1 : props[PROP_OBJECT_PATH] =
181 : 1 : g_param_spec_string ("object-path", "Object Path",
182 : : "Object path to proxy.",
183 : : "/",
184 : : G_PARAM_READWRITE |
185 : : G_PARAM_CONSTRUCT_ONLY |
186 : : G_PARAM_STATIC_STRINGS);
187 : :
188 : : /**
189 : : * MwscScheduler:proxy:
190 : : *
191 : : * D-Bus proxy to use when interacting with the object. If this is %NULL at
192 : : * construction time, one will be created. If provided, it **must** have
193 : : * cached copies of its properties already.
194 : : *
195 : : * Since: 0.1.0
196 : : */
197 : 1 : props[PROP_PROXY] =
198 : 1 : g_param_spec_object ("proxy", "Proxy",
199 : : "D-Bus proxy to use when interacting with the object.",
200 : : G_TYPE_DBUS_PROXY,
201 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
202 : : G_PARAM_STATIC_STRINGS);
203 : :
204 : : /**
205 : : * MwscScheduler:allow-downloads:
206 : : *
207 : : * Whether any of the currently active network connections are configured to
208 : : * allow any large downloads. It is up to the clients which use Mogwai to
209 : : * decide what ’large’ is.
210 : : *
211 : : * This is not a guarantee that a schedule entry
212 : : * will be scheduled; it is a reflection of the user’s intent for the use of
213 : : * the currently active network connections, intended to be used in UIs to
214 : : * remind the user of how they have configured the network.
215 : : *
216 : : * Programs must not use this value to check whether to schedule an entry.
217 : : * Schedule the entry unconditionally; the scheduler will work out whether
218 : : * (and when) to download the entry.
219 : : *
220 : : * Since: 0.1.0
221 : : */
222 : 1 : props[PROP_ALLOW_DOWNLOADS] =
223 : 1 : g_param_spec_boolean ("allow-downloads", "Allow Downloads",
224 : : "Whether any of the currently active network "
225 : : "connections are configured to allow any large "
226 : : "downloads.",
227 : : TRUE,
228 : : G_PARAM_READABLE |
229 : : G_PARAM_STATIC_STRINGS);
230 : :
231 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
232 : :
233 : : /**
234 : : * MwscScheduler::invalidated:
235 : : * @self: a #MwscScheduler
236 : : * @error: error which caused the scheduler to be invalidated: currently only
237 : : * %G_DBUS_ERROR_DISCONNECTED
238 : : *
239 : : * Emitted when the backing object underlying this #MwscScheduler disappears,
240 : : * or it is otherwise disconnected (due to, for example, providing invalid
241 : : * data). The most common reason for this signal to be emitted is if the
242 : : * underlying D-Bus object disappears.
243 : : *
244 : : * After this signal is emitted, all method calls to #MwscScheduler methods
245 : : * will return %MWSC_SCHEDULER_ERROR_INVALIDATED.
246 : : *
247 : : * Since: 0.1.0
248 : : */
249 : 1 : g_signal_new ("invalidated", G_TYPE_FROM_CLASS (klass),
250 : : G_SIGNAL_RUN_LAST,
251 : : 0, NULL, NULL, NULL,
252 : : G_TYPE_NONE, 1,
253 : : G_TYPE_ERROR);
254 : :
255 : : /* Error domain registration for D-Bus. We do this here, rather than in a
256 : : * #GOnce section in mwsc_scheduler_error_quark(), because not all
257 : : * #MwscSchedulerErrors map to a D-Bus error. */
258 [ + + ]: 3 : for (gsize i = 0; i < G_N_ELEMENTS (scheduler_error_map); i++)
259 : 2 : g_dbus_error_register_error (MWSC_SCHEDULER_ERROR,
260 : 2 : scheduler_error_map[i].error_code,
261 : 2 : scheduler_error_map[i].dbus_error_name);
262 : 1 : }
263 : :
264 : : static void
265 : 1 : mwsc_scheduler_initable_init (GInitableIface *iface)
266 : : {
267 : 1 : iface->init = mwsc_scheduler_init_failable;
268 : 1 : }
269 : :
270 : : static void
271 : 1 : mwsc_scheduler_async_initable_init (GAsyncInitableIface *iface)
272 : : {
273 : 1 : iface->init_async = mwsc_scheduler_init_async;
274 : 1 : iface->init_finish = mwsc_scheduler_init_finish;
275 : 1 : }
276 : :
277 : : static void
278 : 2 : mwsc_scheduler_init (MwscScheduler *self)
279 : : {
280 : : /* Nothing to do here. */
281 : 2 : }
282 : :
283 : : static void
284 : 2 : mwsc_scheduler_constructed (GObject *object)
285 : : {
286 : 2 : MwscScheduler *self = MWSC_SCHEDULER (object);
287 : :
288 : : /* Chain up to the parent class */
289 : 2 : G_OBJECT_CLASS (mwsc_scheduler_parent_class)->constructed (object);
290 : :
291 : : /* Ensure that :name is %NULL iff :connection is not a message bus
292 : : * connection. */
293 : 2 : gboolean is_message_bus = (g_dbus_connection_get_unique_name (self->connection) != NULL);
294 [ - + ]: 2 : g_assert (is_message_bus == (self->name != NULL));
295 : :
296 : : /* Check all our construct-only properties are set. */
297 [ + - + - : 2 : g_assert (self->proxy != NULL ||
+ - + - ]
298 : : (self->connection != NULL &&
299 : : self->name != NULL &&
300 : : self->object_path != NULL));
301 : 2 : }
302 : :
303 : : static void
304 : 2 : mwsc_scheduler_dispose (GObject *object)
305 : : {
306 : 2 : MwscScheduler *self = MWSC_SCHEDULER (object);
307 : :
308 [ + - ]: 2 : if (self->proxy != NULL)
309 : : {
310 : : /* Disconnect from signals. */
311 : 2 : g_signal_handlers_disconnect_by_func (self->proxy,
312 : : proxy_properties_changed_cb, self);
313 : 2 : g_signal_handlers_disconnect_by_func (self->proxy,
314 : : proxy_notify_name_owner_cb, self);
315 : : }
316 : :
317 [ + - ]: 2 : g_clear_object (&self->proxy);
318 [ + - ]: 2 : g_clear_object (&self->connection);
319 [ + - ]: 2 : g_clear_pointer (&self->name, g_free);
320 [ + - ]: 2 : g_clear_pointer (&self->object_path, g_free);
321 : 2 : g_clear_error (&self->init_error);
322 : :
323 [ - + ]: 2 : if (self->hold_count > 0)
324 : 0 : g_debug ("Disposing of MwscScheduler with hold count of %u", self->hold_count);
325 : :
326 : : /* Chain up to the parent class */
327 : 2 : G_OBJECT_CLASS (mwsc_scheduler_parent_class)->dispose (object);
328 : 2 : }
329 : :
330 : : static void
331 : 0 : mwsc_scheduler_get_property (GObject *object,
332 : : guint property_id,
333 : : GValue *value,
334 : : GParamSpec *pspec)
335 : : {
336 : 0 : MwscScheduler *self = MWSC_SCHEDULER (object);
337 : :
338 [ # # # # : 0 : switch ((MwscSchedulerProperty) property_id)
# # ]
339 : : {
340 : 0 : case PROP_CONNECTION:
341 : 0 : g_value_set_object (value, self->connection);
342 : 0 : break;
343 : 0 : case PROP_NAME:
344 : 0 : g_value_set_string (value, self->name);
345 : 0 : break;
346 : 0 : case PROP_OBJECT_PATH:
347 : 0 : g_value_set_string (value, self->object_path);
348 : 0 : break;
349 : 0 : case PROP_PROXY:
350 : 0 : g_value_set_object (value, self->proxy);
351 : 0 : break;
352 : 0 : case PROP_ALLOW_DOWNLOADS:
353 : 0 : g_value_set_boolean (value, mwsc_scheduler_get_allow_downloads (self));
354 : 0 : break;
355 : 0 : default:
356 : 0 : g_assert_not_reached ();
357 : : }
358 : 0 : }
359 : :
360 : : static void
361 : 8 : mwsc_scheduler_set_property (GObject *object,
362 : : guint property_id,
363 : : const GValue *value,
364 : : GParamSpec *pspec)
365 : : {
366 : 8 : MwscScheduler *self = MWSC_SCHEDULER (object);
367 : :
368 [ + + + + : 8 : switch ((MwscSchedulerProperty) property_id)
- - ]
369 : : {
370 : 2 : case PROP_CONNECTION:
371 : : /* Construct only. */
372 [ - + ]: 2 : g_assert (self->connection == NULL);
373 : 2 : self->connection = g_value_dup_object (value);
374 : 2 : break;
375 : 2 : case PROP_NAME:
376 : : /* Construct only. */
377 [ - + ]: 2 : g_assert (self->name == NULL);
378 [ + - - + ]: 2 : g_assert (g_value_get_string (value) == NULL ||
379 : : g_dbus_is_name (g_value_get_string (value)));
380 : 2 : self->name = g_value_dup_string (value);
381 : 2 : break;
382 : 2 : case PROP_OBJECT_PATH:
383 : : /* Construct only. */
384 [ - + ]: 2 : g_assert (self->object_path == NULL);
385 [ - + ]: 2 : g_assert (g_variant_is_object_path (g_value_get_string (value)));
386 : 2 : self->object_path = g_value_dup_string (value);
387 : 2 : break;
388 : 2 : case PROP_PROXY:
389 : : /* Construct only. */
390 [ - + ]: 2 : g_assert (self->proxy == NULL);
391 : 2 : self->proxy = g_value_dup_object (value);
392 : 2 : break;
393 : 0 : case PROP_ALLOW_DOWNLOADS:
394 : : /* Read only. */
395 : 0 : g_assert_not_reached ();
396 : : break;
397 : 0 : default:
398 : 0 : g_assert_not_reached ();
399 : : }
400 : 8 : }
401 : :
402 : : /* Report an error with the proxied object's interactions; for example,
403 : : * providing an incorrectly-typed attribute or an invalid update signal. */
404 : : static void
405 : 0 : scheduler_invalidate (MwscScheduler *self,
406 : : const GError *error)
407 : : {
408 [ # # ]: 0 : g_assert (self->proxy != NULL);
409 : :
410 : : /* Disconnect from signals. */
411 : 0 : g_signal_handlers_disconnect_by_func (self->proxy,
412 : : proxy_properties_changed_cb, self);
413 : 0 : g_signal_handlers_disconnect_by_func (self->proxy,
414 : : proxy_notify_name_owner_cb, self);
415 : :
416 : : /* Clear the proxy, which marks this #MwscScheduler as invalidated. */
417 : 0 : g_debug ("Marking scheduler (%p) as invalidated due to error: %s",
418 : : self, error->message);
419 : :
420 [ # # ]: 0 : g_clear_object (&self->proxy);
421 : 0 : g_object_notify (G_OBJECT (self), "proxy");
422 : :
423 : 0 : g_signal_emit_by_name (self, "invalidated", error);
424 : 0 : }
425 : :
426 : : static gboolean
427 : 0 : check_invalidated_with_task (MwscScheduler *self,
428 : : GTask *task)
429 : : {
430 : : /* Invalidated? */
431 [ # # ]: 0 : if (self->proxy == NULL)
432 : : {
433 [ # # ]: 0 : if (task != NULL)
434 : 0 : g_task_return_new_error (task, MWSC_SCHEDULER_ERROR,
435 : : MWSC_SCHEDULER_ERROR_INVALIDATED,
436 : 0 : _("Scheduler has been invalidated."));
437 : 0 : return FALSE;
438 : : }
439 : :
440 : 0 : return TRUE;
441 : : }
442 : :
443 : : static gboolean
444 : 0 : check_invalidated_with_error (MwscScheduler *self,
445 : : GError **error)
446 : : {
447 : : /* Invalidated? */
448 [ # # ]: 0 : if (self->proxy == NULL)
449 : : {
450 : 0 : g_set_error (error, MWSC_SCHEDULER_ERROR, MWSC_SCHEDULER_ERROR_INVALIDATED,
451 : : _("Scheduler has been invalidated."));
452 : 0 : return FALSE;
453 : : }
454 : :
455 : 0 : return TRUE;
456 : : }
457 : :
458 : : static void
459 : 0 : proxy_notify_name_owner_cb (GObject *obj,
460 : : GParamSpec *pspec,
461 : : gpointer user_data)
462 : : {
463 : 0 : MwscScheduler *self = MWSC_SCHEDULER (user_data);
464 : :
465 : 0 : g_debug ("Name owner for proxy ‘%s’ has changed.", self->object_path);
466 : :
467 [ # # ]: 0 : if (g_dbus_proxy_get_name_owner (G_DBUS_PROXY (obj)) == NULL)
468 : : {
469 : 0 : g_autoptr(GError) error = NULL;
470 : 0 : g_set_error_literal (&error, G_DBUS_ERROR, G_DBUS_ERROR_DISCONNECTED,
471 : : _("Scheduler owner has disconnected."));
472 : 0 : scheduler_invalidate (self, error);
473 : : }
474 : 0 : }
475 : :
476 : : static void
477 : 0 : proxy_properties_changed_cb (GDBusProxy *proxy,
478 : : GVariant *changed_properties,
479 : : GStrv invalidated_properties,
480 : : gpointer user_data)
481 : : {
482 : 0 : MwscScheduler *self = MWSC_SCHEDULER (user_data);
483 : :
484 : 0 : g_debug ("Properties for proxy ‘%s’ have changed.", self->object_path);
485 : :
486 : : gboolean downloads_allowed;
487 [ # # ]: 0 : if (g_variant_lookup (changed_properties, "DownloadsAllowed", "b", &downloads_allowed))
488 : 0 : g_object_notify (G_OBJECT (self), "allow-downloads");
489 : 0 : }
490 : :
491 : : static gboolean
492 : 2 : set_up_proxy (MwscScheduler *self,
493 : : GError **error)
494 : : {
495 [ - + ]: 2 : g_assert (self->proxy != NULL);
496 : :
497 : : /* Ensure the proxy has its interface info specified, so we can rely on GDBus
498 : : * to check return value types, etc. (See #GDBusProxy:g-interface-info.) */
499 [ - + ]: 2 : if (g_dbus_proxy_get_interface_info (self->proxy) == NULL)
500 : 0 : g_dbus_proxy_set_interface_info (self->proxy,
501 : : (GDBusInterfaceInfo *) &scheduler_interface);
502 : :
503 : : /* We require property caching to be enabled too. */
504 [ - + ]: 2 : g_return_val_if_fail (!(g_dbus_proxy_get_flags (self->proxy) &
505 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES), FALSE);
506 : :
507 : : /* Subscribe to signals. */
508 : 2 : g_signal_connect (self->proxy, "notify::g-name-owner",
509 : : (GCallback) proxy_notify_name_owner_cb, self);
510 : 2 : g_signal_connect (self->proxy, "g-properties-changed",
511 : : (GCallback) proxy_properties_changed_cb, self);
512 : :
513 : : /* Validate that the scheduler actually exists. */
514 : 2 : g_autoptr(GError) local_error = NULL;
515 : :
516 : 2 : g_autofree gchar *name_owner = g_dbus_proxy_get_name_owner (self->proxy);
517 : :
518 [ - + ]: 2 : if (name_owner == NULL)
519 : : {
520 : 2 : g_set_error_literal (&local_error, MWSC_SCHEDULER_ERROR,
521 : : MWSC_SCHEDULER_ERROR_INVALIDATED,
522 : : _("Scheduler does not exist on the bus."));
523 : 2 : goto done;
524 : : }
525 : :
526 : 0 : done:
527 [ + - ]: 2 : if (local_error != NULL)
528 : : {
529 : 2 : g_propagate_error (error, g_error_copy (local_error));
530 : 2 : g_propagate_error (&self->init_error, g_steal_pointer (&local_error));
531 : 2 : self->init_success = FALSE;
532 : : }
533 : : else
534 : : {
535 : 0 : self->init_success = TRUE;
536 : : }
537 : :
538 : 2 : return self->init_success;
539 : : }
540 : :
541 : : static void
542 : 1 : proxy_init_cb (GObject *obj,
543 : : GAsyncResult *result,
544 : : gpointer user_data)
545 : : {
546 [ + - ]: 2 : g_autoptr(GTask) task = G_TASK (user_data);
547 : 1 : MwscScheduler *self = g_task_get_source_object (task);
548 [ + - ]: 1 : g_autoptr(GError) local_error = NULL;
549 : :
550 : : /* Get the proxy. */
551 [ - + ]: 1 : g_assert (self->proxy == NULL);
552 : 1 : self->proxy = g_dbus_proxy_new_finish (result, &local_error);
553 : :
554 [ - + ]: 1 : g_assert (self->initialising);
555 : 1 : self->initialising = FALSE;
556 : :
557 [ - + ]: 1 : if (local_error != NULL)
558 : : {
559 : 0 : g_propagate_error (&self->init_error, g_error_copy (local_error));
560 : 0 : self->init_success = FALSE;
561 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
562 : 0 : return;
563 : : }
564 : :
565 [ - + ]: 1 : if (set_up_proxy (self, &local_error))
566 : 0 : g_task_return_boolean (task, TRUE);
567 : : else
568 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
569 : : }
570 : :
571 : : static gboolean
572 : 1 : mwsc_scheduler_init_failable (GInitable *initable,
573 : : GCancellable *cancellable,
574 : : GError **error)
575 : : {
576 : 1 : MwscScheduler *self = MWSC_SCHEDULER (initable);
577 : :
578 : : /* For the moment, this only supports the case where we’ve been constructed
579 : : * with a suitable proxy already. */
580 [ - + ]: 1 : if (self->init_error != NULL)
581 : : {
582 : 0 : g_propagate_error (error, g_error_copy (self->init_error));
583 : 0 : return FALSE;
584 : : }
585 [ - + ]: 1 : else if (self->init_success)
586 : : {
587 : 0 : return TRUE;
588 : : }
589 : : else
590 : : {
591 [ - + ]: 1 : g_assert (self->proxy == NULL);
592 : 2 : self->proxy = g_dbus_proxy_new_sync (self->connection,
593 : : G_DBUS_PROXY_FLAGS_NONE,
594 : : (GDBusInterfaceInfo *) &scheduler_interface,
595 : 1 : self->name, self->object_path,
596 : : "com.endlessm.DownloadManager1.Scheduler",
597 : : cancellable, error);
598 [ - + ]: 1 : if (self->proxy == NULL)
599 : : {
600 : 0 : self->init_success = FALSE;
601 : 0 : return FALSE;
602 : : }
603 : :
604 : 1 : return set_up_proxy (self, error);
605 : : }
606 : : }
607 : :
608 : : static void
609 : 1 : mwsc_scheduler_init_async (GAsyncInitable *initable,
610 : : int io_priority,
611 : : GCancellable *cancellable,
612 : : GAsyncReadyCallback callback,
613 : : gpointer user_data)
614 : : {
615 : 1 : MwscScheduler *self = MWSC_SCHEDULER (initable);
616 : :
617 : : /* We don’t support parallel initialisation. */
618 [ - + ]: 1 : g_assert (!self->initialising);
619 : :
620 : 2 : g_autoptr(GTask) task = g_task_new (initable, cancellable, callback, user_data);
621 [ + - ]: 1 : g_task_set_source_tag (task, mwsc_scheduler_init_async);
622 : :
623 [ - + ]: 1 : if (self->init_error != NULL)
624 : 0 : g_task_return_error (task, g_error_copy (self->init_error));
625 [ - + ]: 1 : else if (self->init_success)
626 : 0 : g_task_return_boolean (task, TRUE);
627 : : else
628 : : {
629 : 1 : self->initialising = TRUE;
630 : 1 : g_dbus_proxy_new (self->connection, G_DBUS_PROXY_FLAGS_NONE,
631 : 1 : (GDBusInterfaceInfo *) &scheduler_interface, self->name,
632 : 1 : self->object_path, "com.endlessm.DownloadManager1.Scheduler",
633 : : cancellable, proxy_init_cb, g_steal_pointer (&task));
634 : : }
635 : 1 : }
636 : :
637 : : static gboolean
638 : 1 : mwsc_scheduler_init_finish (GAsyncInitable *initable,
639 : : GAsyncResult *result,
640 : : GError **error)
641 : : {
642 : 1 : return g_task_propagate_boolean (G_TASK (result), error);
643 : : }
644 : :
645 : : /**
646 : : * mwsc_scheduler_new_from_proxy:
647 : : * @proxy: a #GDBusProxy for a `com.endlessm.DownloadManager1.Scheduler` object
648 : : * @error: return location for a #GError, or %NULL
649 : : *
650 : : * Create a #MwscScheduler object to wrap the given existing @proxy. The
651 : : * @proxy must have cached all the `Scheduler` properties already (currently,
652 : : * there are none).
653 : : *
654 : : * If any of the properties are missing or invalid, an error is returned.
655 : : *
656 : : * Returns: (transfer full): a new #MwscScheduler wrapping @proxy
657 : : * Since: 0.1.0
658 : : */
659 : : MwscScheduler *
660 : 0 : mwsc_scheduler_new_from_proxy (GDBusProxy *proxy,
661 : : GError **error)
662 : : {
663 [ # # # # : 0 : g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
# # # # ]
664 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
665 : :
666 : 0 : return g_initable_new (MWSC_TYPE_SCHEDULER, NULL, error,
667 : : "connection", g_dbus_proxy_get_connection (proxy),
668 : : "name", g_dbus_proxy_get_name (proxy),
669 : : "object-path", g_dbus_proxy_get_object_path (proxy),
670 : : "proxy", proxy,
671 : : NULL);
672 : : }
673 : :
674 : : /**
675 : : * mwsc_scheduler_new:
676 : : * @cancellable: (nullable): a #GCancellable, or %NULL
677 : : * @error: return location for a #GError, or %NULL
678 : : *
679 : : * Synchronous version of mwsc_scheduler_new_async().
680 : : *
681 : : * Returns: (transfer full): initialised #MwscScheduler, or %NULL on error
682 : : * Since: 0.2.0
683 : : */
684 : : MwscScheduler *
685 : 0 : mwsc_scheduler_new (GCancellable *cancellable,
686 : : GError **error)
687 : : {
688 : 0 : g_autoptr(GDBusConnection) connection = NULL;
689 : :
690 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
# # # # #
# ]
691 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
692 : :
693 : 0 : connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
694 [ # # ]: 0 : if (connection == NULL)
695 : 0 : return NULL;
696 : :
697 : 0 : return mwsc_scheduler_new_full (connection,
698 : : "com.endlessm.MogwaiSchedule1",
699 : : "/com/endlessm/DownloadManager1",
700 : : cancellable, error);
701 : : }
702 : :
703 : : static void get_bus_cb (GObject *obj,
704 : : GAsyncResult *result,
705 : : gpointer user_data);
706 : : static void new_cb (GObject *obj,
707 : : GAsyncResult *result,
708 : : gpointer user_data);
709 : :
710 : : /**
711 : : * mwsc_scheduler_new_async:
712 : : * @cancellable: (nullable): a #GCancellable, or %NULL
713 : : * @callback: callback to invoke on completion
714 : : * @user_data: user data to pass to @callback
715 : : *
716 : : * Convenience version of mwsc_scheduler_new_full_async() which uses the default
717 : : * D-Bus connection, name and object path.
718 : : *
719 : : * Since: 0.1.0
720 : : */
721 : : void
722 : 0 : mwsc_scheduler_new_async (GCancellable *cancellable,
723 : : GAsyncReadyCallback callback,
724 : : gpointer user_data)
725 : : {
726 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
727 : :
728 : 0 : g_autoptr(GTask) task = g_task_new (NULL, cancellable, callback, user_data);
729 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_scheduler_new_async);
730 : 0 : g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, get_bus_cb, g_steal_pointer (&task));
731 : : }
732 : :
733 : : static void
734 : 0 : get_bus_cb (GObject *obj,
735 : : GAsyncResult *result,
736 : : gpointer user_data)
737 : : {
738 [ # # ]: 0 : g_autoptr(GTask) task = G_TASK (user_data);
739 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
740 [ # # ]: 0 : g_autoptr(GError) error = NULL;
741 : :
742 [ # # ]: 0 : g_autoptr(GDBusConnection) connection = g_bus_get_finish (result, &error);
743 : :
744 [ # # ]: 0 : if (error != NULL)
745 : : {
746 : 0 : g_task_return_error (task, g_steal_pointer (&error));
747 : 0 : return;
748 : : }
749 : :
750 : 0 : mwsc_scheduler_new_full_async (connection,
751 : : "com.endlessm.MogwaiSchedule1",
752 : : "/com/endlessm/DownloadManager1",
753 : : cancellable, new_cb, g_steal_pointer (&task));
754 : : }
755 : :
756 : : static void
757 : 0 : new_cb (GObject *obj,
758 : : GAsyncResult *result,
759 : : gpointer user_data)
760 : : {
761 : 0 : g_autoptr(GTask) task = G_TASK (user_data);
762 : 0 : g_autoptr(GError) error = NULL;
763 : :
764 : 0 : g_autoptr(MwscScheduler) scheduler = mwsc_scheduler_new_full_finish (result, &error);
765 : :
766 [ # # ]: 0 : if (error != NULL)
767 : 0 : g_task_return_error (task, g_steal_pointer (&error));
768 : : else
769 : 0 : g_task_return_pointer (task, g_steal_pointer (&scheduler), g_object_unref);
770 : 0 : }
771 : :
772 : : /**
773 : : * mwsc_scheduler_new_finish:
774 : : * @result: asynchronous operation result
775 : : * @error: return location for a #GError
776 : : *
777 : : * Finish initialising a #MwscScheduler. See mwsc_scheduler_new_async().
778 : : *
779 : : * Returns: (transfer full): initialised #MwscScheduler, or %NULL on error
780 : : * Since: 0.1.0
781 : : */
782 : : MwscScheduler *
783 : 0 : mwsc_scheduler_new_finish (GAsyncResult *result,
784 : : GError **error)
785 : : {
786 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
787 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result, mwsc_scheduler_new_async), NULL);
788 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
789 : :
790 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
791 : : }
792 : :
793 : : /**
794 : : * mwsc_scheduler_new_full:
795 : : * @connection: D-Bus connection to use
796 : : * @name: (nullable): well-known or unique name of the peer to proxy from, or
797 : : * %NULL if @connection is not a message bus connection
798 : : * @object_path: path of the object to proxy
799 : : * @cancellable: (nullable): a #GCancellable, or %NULL
800 : : * @error: return location for a #GError, or %NULL
801 : : *
802 : : * Synchronous version of mwsc_scheduler_new_full_async().
803 : : *
804 : : * Returns: (transfer full): initialised #MwscScheduler, or %NULL on error
805 : : * Since: 0.2.0
806 : : */
807 : : MwscScheduler *
808 : 1 : mwsc_scheduler_new_full (GDBusConnection *connection,
809 : : const gchar *name,
810 : : const gchar *object_path,
811 : : GCancellable *cancellable,
812 : : GError **error)
813 : : {
814 [ - + + - : 1 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ - - + ]
815 [ + - - + ]: 1 : g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
816 [ - + ]: 1 : g_return_val_if_fail ((g_dbus_connection_get_unique_name (connection) == NULL) ==
817 : : (name == NULL), NULL);
818 [ + - + - ]: 1 : g_return_val_if_fail (object_path != NULL &&
819 : : g_variant_is_object_path (object_path), NULL);
820 [ - + - - : 1 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
- - - - -
- ]
821 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
822 : :
823 : 1 : return g_initable_new (MWSC_TYPE_SCHEDULER, cancellable, error,
824 : : "connection", connection,
825 : : "name", name,
826 : : "object-path", object_path,
827 : : NULL);
828 : : }
829 : :
830 : : /**
831 : : * mwsc_scheduler_new_full_async:
832 : : * @connection: D-Bus connection to use
833 : : * @name: (nullable): well-known or unique name of the peer to proxy from, or
834 : : * %NULL if @connection is not a message bus connection
835 : : * @object_path: path of the object to proxy
836 : : * @cancellable: (nullable): a #GCancellable, or %NULL
837 : : * @callback: callback to invoke on completion
838 : : * @user_data: user data to pass to @callback
839 : : *
840 : : * Create a new #MwscScheduler for the given @object_path at @name on
841 : : * @connection, and set up the proxy object. This is an asynchronous process
842 : : * which might fail; object instantiation must be finished (or the error
843 : : * returned) by calling mwsc_scheduler_new_finish().
844 : : *
845 : : * Since: 0.1.0
846 : : */
847 : : void
848 : 1 : mwsc_scheduler_new_full_async (GDBusConnection *connection,
849 : : const gchar *name,
850 : : const gchar *object_path,
851 : : GCancellable *cancellable,
852 : : GAsyncReadyCallback callback,
853 : : gpointer user_data)
854 : : {
855 [ - + + - : 1 : g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+ - - + ]
856 [ + - - + ]: 1 : g_return_if_fail (name == NULL || g_dbus_is_name (name));
857 [ - + ]: 1 : g_return_if_fail ((g_dbus_connection_get_unique_name (connection) == NULL) ==
858 : : (name == NULL));
859 [ + - + - ]: 1 : g_return_if_fail (object_path != NULL &&
860 : : g_variant_is_object_path (object_path));
861 [ - + - - : 1 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - -
- ]
862 : :
863 : 1 : g_async_initable_new_async (MWSC_TYPE_SCHEDULER, G_PRIORITY_DEFAULT,
864 : : cancellable, callback, user_data,
865 : : "connection", connection,
866 : : "name", name,
867 : : "object-path", object_path,
868 : : NULL);
869 : : }
870 : :
871 : : /**
872 : : * mwsc_scheduler_new_full_finish:
873 : : * @result: asynchronous operation result
874 : : * @error: return location for a #GError
875 : : *
876 : : * Finish initialising a #MwscScheduler. See mwsc_scheduler_new_full_async().
877 : : *
878 : : * Returns: (transfer full): initialised #MwscScheduler, or %NULL on error
879 : : * Since: 0.1.0
880 : : */
881 : : MwscScheduler *
882 : 1 : mwsc_scheduler_new_full_finish (GAsyncResult *result,
883 : : GError **error)
884 : : {
885 [ - + + - : 1 : g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
- + - + ]
886 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
887 : :
888 : 2 : g_autoptr(GObject) source_object = g_async_result_get_source_object (result);
889 : 1 : return MWSC_SCHEDULER (g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
890 : : result, error));
891 : : }
892 : :
893 : : /* Steal the given element from the array. This assumes the array’s free
894 : : * function is g_object_unref().
895 : : *
896 : : * FIXME: Use g_ptr_array_steal_index_fast() when we have a version of GLib
897 : : * supporting it. See: https://bugzilla.gnome.org/show_bug.cgi?id=795376. */
898 : : static gpointer
899 : 0 : ptr_array_steal_index_fast (GPtrArray *array,
900 : : guint index_)
901 : : {
902 : 0 : g_ptr_array_set_free_func (array, NULL);
903 : 0 : g_autoptr(GObject) obj = g_ptr_array_remove_index_fast (array, index_);
904 : 0 : g_ptr_array_set_free_func (array, (GDestroyNotify) g_object_unref);
905 : :
906 : 0 : return g_steal_pointer (&obj);
907 : : }
908 : :
909 : : /**
910 : : * mwsc_scheduler_schedule:
911 : : * @self: a #MwscScheduler
912 : : * @parameters: (nullable): #GVariant of type `a{sv}` giving initial parameters
913 : : * for the schedule entry
914 : : * @cancellable: (nullable): a #GCancellable, or %NULL
915 : : * @error: return location for a #GError, or %NULL
916 : : *
917 : : * Synchronous version of mwsc_scheduler_schedule_async().
918 : : *
919 : : * Returns: (transfer full): the new #MwscScheduleEntry
920 : : * Since: 0.2.0
921 : : */
922 : : MwscScheduleEntry *
923 : 0 : mwsc_scheduler_schedule (MwscScheduler *self,
924 : : GVariant *parameters,
925 : : GCancellable *cancellable,
926 : : GError **error)
927 : : {
928 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), NULL);
929 [ # # # # : 0 : g_return_val_if_fail (parameters == NULL ||
# # ]
930 : : (g_variant_is_normal_form (parameters) &&
931 : : g_variant_is_of_type (parameters, G_VARIANT_TYPE_VARDICT)), NULL);
932 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
# # # # #
# ]
933 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
934 : :
935 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
936 : 0 : return NULL;
937 : :
938 : 0 : g_autoptr(GPtrArray) parameters_array = g_ptr_array_new_with_free_func (NULL);
939 : 0 : g_ptr_array_add (parameters_array, parameters);
940 : :
941 : 0 : g_autoptr(GPtrArray) entries = NULL;
942 : 0 : entries = mwsc_scheduler_schedule_entries (self, parameters_array,
943 : : cancellable, error);
944 [ # # ]: 0 : if (entries == NULL)
945 : 0 : return NULL;
946 : :
947 [ # # ]: 0 : g_assert (entries->len == 1);
948 : 0 : return ptr_array_steal_index_fast (entries, 0);
949 : : }
950 : :
951 : : static void schedule_cb (GObject *obj,
952 : : GAsyncResult *result,
953 : : gpointer user_data);
954 : :
955 : : /**
956 : : * mwsc_scheduler_schedule_async:
957 : : * @self: a #MwscScheduler
958 : : * @parameters: (nullable): #GVariant of type `a{sv}` giving initial parameters
959 : : * for the schedule entry
960 : : * @cancellable: (nullable): a #GCancellable, or %NULL
961 : : * @callback: callback to invoke on completion
962 : : * @user_data: user data to pass to @callback
963 : : *
964 : : * Create a new #MwscScheduleEntry in the scheduler and return it. The entry
965 : : * will be created with the given initial @parameters (which may be %NULL to
966 : : * use the defaults). As soon as the entry is created, the scheduler may
967 : : * schedule it, which it will do by setting #MwscScheduleEntry:download-now to
968 : : * %TRUE.
969 : : *
970 : : * If @parameters is floating, it is consumed.
971 : : *
972 : : * The following @parameters are currently supported:
973 : : *
974 : : * * `resumable` (`b`): sets #MwscScheduleEntry:resumable
975 : : * * `priority` (`u`): sets #MwscScheduleEntry:priority
976 : : *
977 : : * Since: 0.1.0
978 : : */
979 : : void
980 : 0 : mwsc_scheduler_schedule_async (MwscScheduler *self,
981 : : GVariant *parameters,
982 : : GCancellable *cancellable,
983 : : GAsyncReadyCallback callback,
984 : : gpointer user_data)
985 : : {
986 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULER (self));
987 [ # # # # : 0 : g_return_if_fail (parameters == NULL ||
# # ]
988 : : (g_variant_is_normal_form (parameters) &&
989 : : g_variant_is_of_type (parameters, G_VARIANT_TYPE_VARDICT)));
990 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
991 : :
992 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
993 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_scheduler_schedule_async);
994 : :
995 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
996 : 0 : return;
997 : :
998 : 0 : g_autoptr(GPtrArray) parameters_array = g_ptr_array_new_with_free_func (NULL);
999 : 0 : g_ptr_array_add (parameters_array, parameters);
1000 : 0 : mwsc_scheduler_schedule_entries_async (self, parameters_array, cancellable,
1001 : : schedule_cb, g_steal_pointer (&task));
1002 : : }
1003 : :
1004 : : static void
1005 : 0 : schedule_cb (GObject *obj,
1006 : : GAsyncResult *result,
1007 : : gpointer user_data)
1008 : : {
1009 : 0 : MwscScheduler *self = MWSC_SCHEDULER (obj);
1010 [ # # ]: 0 : g_autoptr(GTask) task = G_TASK (user_data);
1011 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
1012 [ # # ]: 0 : g_autoptr(GPtrArray) entries = NULL;
1013 : :
1014 : 0 : entries = mwsc_scheduler_schedule_entries_finish (self, result, &local_error);
1015 : :
1016 [ # # ]: 0 : if (local_error != NULL)
1017 : : {
1018 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1019 : 0 : return;
1020 : : }
1021 : :
1022 [ # # ]: 0 : g_assert (entries->len == 1);
1023 : 0 : g_task_return_pointer (task, ptr_array_steal_index_fast (entries, 0), g_object_unref);
1024 : : }
1025 : :
1026 : : /**
1027 : : * mwsc_scheduler_schedule_finish:
1028 : : * @self: a #MwscScheduleEntry
1029 : : * @result: asynchronous operation result
1030 : : * @error: return location for a #GError
1031 : : *
1032 : : * Finish adding a #MwscScheduleEntry. See mwsc_scheduler_schedule_async().
1033 : : *
1034 : : * Returns: (transfer full): the new #MwscScheduleEntry
1035 : : * Since: 0.1.0
1036 : : */
1037 : : MwscScheduleEntry *
1038 : 0 : mwsc_scheduler_schedule_finish (MwscScheduler *self,
1039 : : GAsyncResult *result,
1040 : : GError **error)
1041 : : {
1042 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), NULL);
1043 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
1044 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result, mwsc_scheduler_schedule_async), NULL);
1045 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1046 : :
1047 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
1048 : : }
1049 : :
1050 : : /* Returns a floating reference. */
1051 : : static GVariant *
1052 : 0 : parameters_to_variant (GPtrArray *parameters)
1053 : : {
1054 : 0 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(aa{sv})"));
1055 : 0 : g_variant_builder_open (&builder, G_VARIANT_TYPE ("aa{sv}"));
1056 : :
1057 [ # # ]: 0 : for (gsize i = 0; i < parameters->len; i++)
1058 : : {
1059 : 0 : GVariant *variant = g_ptr_array_index (parameters, i);
1060 : :
1061 [ # # ]: 0 : if (variant == NULL)
1062 : 0 : variant = g_variant_new ("a{sv}", NULL);
1063 : :
1064 [ # # # # ]: 0 : g_return_val_if_fail (g_variant_is_normal_form (variant) &&
1065 : : g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT),
1066 : : NULL);
1067 : :
1068 : 0 : g_variant_builder_add_value (&builder, variant);
1069 : : }
1070 : :
1071 : 0 : g_variant_builder_close (&builder);
1072 : :
1073 : 0 : return g_variant_builder_end (&builder);
1074 : : }
1075 : :
1076 : : /**
1077 : : * mwsc_scheduler_schedule_entries:
1078 : : * @self: a #MwscScheduler
1079 : : * @parameters: non-empty array of #GVariants of type `a{sv}` giving initial
1080 : : * parameters for each of the schedule entries
1081 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1082 : : * @error: return location for a #GError, or %NULL
1083 : : *
1084 : : * Synchronous version of mwsc_scheduler_schedule_entries_async().
1085 : : *
1086 : : * Returns: (transfer full) (element-type MwscScheduleEntry): an non-empty array
1087 : : * of the new #MwscScheduleEntrys
1088 : : * Since: 0.2.0
1089 : : */
1090 : : GPtrArray *
1091 : 0 : mwsc_scheduler_schedule_entries (MwscScheduler *self,
1092 : : GPtrArray *parameters,
1093 : : GCancellable *cancellable,
1094 : : GError **error)
1095 : : {
1096 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), NULL);
1097 [ # # # # ]: 0 : g_return_val_if_fail (parameters != NULL && parameters->len > 0, NULL);
1098 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
# # # # #
# ]
1099 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1100 : :
1101 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
1102 : 0 : return NULL;
1103 : :
1104 : : /* Grab the schedule entries. */
1105 : 0 : g_autoptr(GVariant) return_value = NULL;
1106 : 0 : return_value = g_dbus_proxy_call_sync (self->proxy,
1107 : : "ScheduleEntries",
1108 : : parameters_to_variant (parameters),
1109 : : G_DBUS_CALL_FLAGS_NONE,
1110 : : -1, /* default timeout */
1111 : : cancellable,
1112 : : error);
1113 : :
1114 [ # # ]: 0 : if (return_value == NULL)
1115 : 0 : return NULL;
1116 : :
1117 : : /* Start constructing the entries in parallel. */
1118 : 0 : g_autoptr(GVariantIter) iter = NULL;
1119 : : const gchar *schedule_entry_path;
1120 : 0 : g_variant_get (return_value, "(ao)", &iter);
1121 : :
1122 : 0 : g_autoptr(GPtrArray) entries = NULL;
1123 : 0 : entries = g_ptr_array_new_with_free_func (g_object_unref);
1124 : :
1125 [ # # ]: 0 : while (g_variant_iter_loop (iter, "&o", &schedule_entry_path))
1126 : : {
1127 [ # # ]: 0 : g_autoptr(MwscScheduleEntry) entry = NULL;
1128 : :
1129 : 0 : entry = mwsc_schedule_entry_new_full (g_dbus_proxy_get_connection (self->proxy),
1130 : : g_dbus_proxy_get_name (self->proxy),
1131 : : schedule_entry_path,
1132 : : cancellable,
1133 : : error);
1134 : :
1135 [ # # ]: 0 : if (entry == NULL)
1136 : 0 : return NULL;
1137 : :
1138 : 0 : g_ptr_array_add (entries, g_steal_pointer (&entry));
1139 : : }
1140 : :
1141 : 0 : return g_steal_pointer (&entries);
1142 : : }
1143 : :
1144 : : typedef struct
1145 : : {
1146 : : gsize n_entries;
1147 : : GPtrArray *entries; /* (element-type MwscScheduleEntry) (owned) */
1148 : : } ScheduleEntriesData;
1149 : :
1150 : : static void
1151 : 0 : schedule_entries_data_free (ScheduleEntriesData *data)
1152 : : {
1153 [ # # ]: 0 : g_clear_pointer (&data->entries, g_ptr_array_unref);
1154 : 0 : g_free (data);
1155 : 0 : }
1156 : :
1157 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (ScheduleEntriesData, schedule_entries_data_free)
1158 : :
1159 : : static void schedule_entries_cb (GObject *obj,
1160 : : GAsyncResult *result,
1161 : : gpointer user_data);
1162 : : static void proxy_entries_cb (GObject *obj,
1163 : : GAsyncResult *result,
1164 : : gpointer user_data);
1165 : :
1166 : : /**
1167 : : * mwsc_scheduler_schedule_entries_async:
1168 : : * @self: a #MwscScheduler
1169 : : * @parameters: non-empty array of #GVariants of type `a{sv}` giving initial
1170 : : * parameters for each of the schedule entries
1171 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1172 : : * @callback: callback to invoke on completion
1173 : : * @user_data: user data to pass to @callback
1174 : : *
1175 : : * Create one or more new #MwscScheduleEntrys in the scheduler and return them.
1176 : : * The entries will be created with the given initial @parameters (which may be
1177 : : * %NULL to use the defaults). As soon as the entries are created, the scheduler
1178 : : * may schedule them, which it will do by setting
1179 : : * #MwscScheduleEntry:download-now to %TRUE on one or more of them.
1180 : : *
1181 : : * If any of the #GVariants in @parameters are floating, they are consumed.
1182 : : *
1183 : : * The following @parameters are currently supported:
1184 : : *
1185 : : * * `resumable` (`b`): sets #MwscScheduleEntry:resumable
1186 : : * * `priority` (`u`): sets #MwscScheduleEntry:priority
1187 : : *
1188 : : * Since: 0.1.0
1189 : : */
1190 : : void
1191 : 0 : mwsc_scheduler_schedule_entries_async (MwscScheduler *self,
1192 : : GPtrArray *parameters,
1193 : : GCancellable *cancellable,
1194 : : GAsyncReadyCallback callback,
1195 : : gpointer user_data)
1196 : : {
1197 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULER (self));
1198 [ # # # # ]: 0 : g_return_if_fail (parameters != NULL && parameters->len > 0);
1199 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
1200 : :
1201 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
1202 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_scheduler_schedule_entries_async);
1203 : :
1204 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
1205 : 0 : return;
1206 : :
1207 : 0 : g_dbus_proxy_call (self->proxy,
1208 : : "ScheduleEntries",
1209 : : parameters_to_variant (parameters),
1210 : : G_DBUS_CALL_FLAGS_NONE,
1211 : : -1, /* default timeout */
1212 : : cancellable,
1213 : : schedule_entries_cb,
1214 : : g_steal_pointer (&task));
1215 : : }
1216 : :
1217 : : static void
1218 : 0 : schedule_entries_cb (GObject *obj,
1219 : : GAsyncResult *result,
1220 : : gpointer user_data)
1221 : : {
1222 : 0 : GDBusProxy *proxy = G_DBUS_PROXY (obj);
1223 [ # # ]: 0 : g_autoptr(GTask) task = G_TASK (user_data);
1224 : 0 : MwscScheduler *self = g_task_get_source_object (task);
1225 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
1226 [ # # ]: 0 : g_autoptr(GError) error = NULL;
1227 : :
1228 : : /* Grab the schedule entries. */
1229 [ # # ]: 0 : g_autoptr(GVariant) return_value = NULL;
1230 : 0 : return_value = g_dbus_proxy_call_finish (proxy, result, &error);
1231 : :
1232 [ # # ]: 0 : if (error != NULL)
1233 : : {
1234 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1235 : 0 : return;
1236 : : }
1237 : :
1238 : : /* Start constructing the entries in parallel. */
1239 : 0 : g_autoptr(GVariantIter) iter = NULL;
1240 : : const gchar *schedule_entry_path;
1241 : 0 : g_variant_get (return_value, "(ao)", &iter);
1242 : :
1243 : : /* Set up the return closure. */
1244 : 0 : g_autoptr(ScheduleEntriesData) data = g_new0 (ScheduleEntriesData, 1);
1245 : 0 : data->n_entries = g_variant_iter_n_children (iter);
1246 : 0 : data->entries = g_ptr_array_new_with_free_func (g_object_unref);
1247 : 0 : g_task_set_task_data (task, g_steal_pointer (&data),
1248 : : (GDestroyNotify) schedule_entries_data_free);
1249 : :
1250 [ # # ]: 0 : while (g_variant_iter_loop (iter, "&o", &schedule_entry_path))
1251 : : {
1252 : 0 : mwsc_schedule_entry_new_full_async (g_dbus_proxy_get_connection (self->proxy),
1253 : : g_dbus_proxy_get_name (self->proxy),
1254 : : schedule_entry_path,
1255 : : cancellable,
1256 : : proxy_entries_cb,
1257 : : g_steal_pointer (&task));
1258 : : }
1259 : : }
1260 : :
1261 : : static void
1262 : 0 : proxy_entries_cb (GObject *obj,
1263 : : GAsyncResult *result,
1264 : : gpointer user_data)
1265 : : {
1266 [ # # ]: 0 : g_autoptr(GTask) task = G_TASK (user_data);
1267 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
1268 : 0 : ScheduleEntriesData *data = g_task_get_task_data (task);
1269 : :
1270 [ # # ]: 0 : g_autoptr(MwscScheduleEntry) entry = mwsc_schedule_entry_new_full_finish (result, &local_error);
1271 : :
1272 [ # # ]: 0 : if (entry == NULL)
1273 : : {
1274 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1275 : 0 : return;
1276 : : }
1277 : :
1278 : 0 : g_ptr_array_add (data->entries, g_steal_pointer (&entry));
1279 : :
1280 [ # # # # ]: 0 : if (data->entries->len == data->n_entries && !g_task_had_error (task))
1281 : 0 : g_task_return_pointer (task, g_steal_pointer (&data->entries),
1282 : : (GDestroyNotify) g_ptr_array_unref);
1283 : : }
1284 : :
1285 : : /**
1286 : : * mwsc_scheduler_schedule_entries_finish:
1287 : : * @self: a #MwscScheduleEntry
1288 : : * @result: asynchronous operation result
1289 : : * @error: return location for a #GError
1290 : : *
1291 : : * Finish adding one or more #MwscScheduleEntrys. See
1292 : : * mwsc_scheduler_schedule_entries_async().
1293 : : *
1294 : : * Returns: (transfer full) (element-type MwscScheduleEntry): an non-empty array
1295 : : * of the new #MwscScheduleEntrys
1296 : : * Since: 0.1.0
1297 : : */
1298 : : GPtrArray *
1299 : 0 : mwsc_scheduler_schedule_entries_finish (MwscScheduler *self,
1300 : : GAsyncResult *result,
1301 : : GError **error)
1302 : : {
1303 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), NULL);
1304 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
1305 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result, mwsc_scheduler_schedule_entries_async), NULL);
1306 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1307 : :
1308 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
1309 : : }
1310 : :
1311 : : /**
1312 : : * mwsc_scheduler_hold:
1313 : : * @self: a #MwscScheduler
1314 : : * @reason: (nullable): reason for holding the daemon, or %NULL to provide none
1315 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1316 : : * @error: return location for a #GError, or %NULL
1317 : : *
1318 : : * Synchronous version of mwsc_scheduler_hold_async().
1319 : : *
1320 : : * Returns: %TRUE on success, %FALSE otherwise
1321 : : * Since: 0.2.0
1322 : : */
1323 : : gboolean
1324 : 0 : mwsc_scheduler_hold (MwscScheduler *self,
1325 : : const gchar *reason,
1326 : : GCancellable *cancellable,
1327 : : GError **error)
1328 : : {
1329 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), FALSE);
1330 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
# # # # #
# ]
1331 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1332 : :
1333 [ # # ]: 0 : g_return_val_if_fail (self->hold_count < G_MAXUINT, FALSE);
1334 : :
1335 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
1336 : 0 : return FALSE;
1337 : :
1338 : : /* Check whether we already hold the scheduler. */
1339 [ # # ]: 0 : if (self->hold_count++ > 0)
1340 : : {
1341 : 0 : g_debug ("Already hold scheduler");
1342 : 0 : return TRUE;
1343 : : }
1344 : :
1345 : : /* Hold the scheduler over D-Bus. */
1346 : 0 : g_debug ("Holding scheduler over D-Bus with reason: %s", reason);
1347 : :
1348 [ # # ]: 0 : if (reason == NULL)
1349 : 0 : reason = "";
1350 : :
1351 : 0 : g_autoptr(GVariant) return_value = NULL;
1352 : 0 : return_value = g_dbus_proxy_call_sync (self->proxy,
1353 : : "Hold",
1354 : : g_variant_new ("(s)", reason),
1355 : : G_DBUS_CALL_FLAGS_NONE,
1356 : : -1, /* default timeout */
1357 : : cancellable,
1358 : : error);
1359 : :
1360 [ # # ]: 0 : if (return_value == NULL)
1361 : : {
1362 [ # # ]: 0 : g_assert (self->hold_count > 0);
1363 : 0 : self->hold_count--;
1364 : 0 : return FALSE;
1365 : : }
1366 : :
1367 : 0 : return TRUE;
1368 : : }
1369 : :
1370 : : static void hold_cb (GObject *obj,
1371 : : GAsyncResult *result,
1372 : : gpointer user_data);
1373 : :
1374 : : /**
1375 : : * mwsc_scheduler_hold_async:
1376 : : * @self: a #MwscScheduler
1377 : : * @reason: (nullable): reason for holding the daemon, or %NULL to provide none
1378 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1379 : : * @callback: callback to invoke on completion
1380 : : * @user_data: user data to pass to @callback
1381 : : *
1382 : : * Increment the hold count on the scheduler daemon. While the daemon is held,
1383 : : * it will not exit due to inactivity. The daemon is automatically held while a
1384 : : * schedule entry is registered with it.
1385 : : *
1386 : : * This function is typically used to hold the daemon while subscribed to
1387 : : * signals from it.
1388 : : *
1389 : : * Calls to this function must be paired with calls to
1390 : : * mwsc_scheduler_release_async() to eventually release the hold. You may call
1391 : : * this function many times, and must call mwsc_scheduler_release_async() the
1392 : : * same number of times.
1393 : : *
1394 : : * Since: 0.1.0
1395 : : */
1396 : : void
1397 : 0 : mwsc_scheduler_hold_async (MwscScheduler *self,
1398 : : const gchar *reason,
1399 : : GCancellable *cancellable,
1400 : : GAsyncReadyCallback callback,
1401 : : gpointer user_data)
1402 : : {
1403 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULER (self));
1404 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
1405 : :
1406 [ # # ]: 0 : g_return_if_fail (self->hold_count < G_MAXUINT);
1407 : :
1408 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
1409 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_scheduler_hold_async);
1410 : :
1411 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
1412 : 0 : return;
1413 : :
1414 : : /* Check whether we already hold the scheduler. */
1415 [ # # ]: 0 : if (self->hold_count++ > 0)
1416 : : {
1417 : 0 : g_debug ("Already hold scheduler");
1418 : 0 : g_task_return_boolean (task, TRUE);
1419 : 0 : return;
1420 : : }
1421 : :
1422 : : /* Hold the scheduler over D-Bus. */
1423 : 0 : g_debug ("Holding scheduler over D-Bus with reason: %s", reason);
1424 : :
1425 [ # # ]: 0 : if (reason == NULL)
1426 : 0 : reason = "";
1427 : :
1428 : 0 : g_dbus_proxy_call (self->proxy,
1429 : : "Hold",
1430 : : g_variant_new ("(s)", reason),
1431 : : G_DBUS_CALL_FLAGS_NONE,
1432 : : -1, /* default timeout */
1433 : : cancellable,
1434 : : hold_cb,
1435 : : g_steal_pointer (&task));
1436 : : }
1437 : :
1438 : : static void
1439 : 0 : hold_cb (GObject *obj,
1440 : : GAsyncResult *result,
1441 : : gpointer user_data)
1442 : : {
1443 : 0 : GDBusProxy *proxy = G_DBUS_PROXY (obj);
1444 : 0 : g_autoptr(GTask) task = G_TASK (user_data);
1445 : 0 : MwscScheduler *self = g_task_get_source_object (task);
1446 : 0 : g_autoptr(GError) local_error = NULL;
1447 : :
1448 : : /* Check for errors. */
1449 : 0 : g_autoptr(GVariant) return_value = NULL;
1450 : 0 : return_value = g_dbus_proxy_call_finish (proxy, result, &local_error);
1451 : :
1452 [ # # ]: 0 : if (local_error != NULL)
1453 : : {
1454 [ # # ]: 0 : g_assert (self->hold_count > 0);
1455 : 0 : self->hold_count--;
1456 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1457 : : }
1458 : : else
1459 : : {
1460 : 0 : g_task_return_boolean (task, TRUE);
1461 : : }
1462 : 0 : }
1463 : :
1464 : : /**
1465 : : * mwsc_scheduler_hold_finish:
1466 : : * @self: a #MwscScheduleEntry
1467 : : * @result: asynchronous operation result
1468 : : * @error: return location for a #GError
1469 : : *
1470 : : * Finish acquiring a hold on the daemon. See mwsc_scheduler_hold_async().
1471 : : *
1472 : : * Returns: %TRUE on success, %FALSE otherwise
1473 : : * Since: 0.1.0
1474 : : */
1475 : : gboolean
1476 : 0 : mwsc_scheduler_hold_finish (MwscScheduler *self,
1477 : : GAsyncResult *result,
1478 : : GError **error)
1479 : : {
1480 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), FALSE);
1481 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1482 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result, mwsc_scheduler_hold_async), FALSE);
1483 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1484 : :
1485 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
1486 : : }
1487 : :
1488 : : /**
1489 : : * mwsc_scheduler_release:
1490 : : * @self: a #MwscScheduler
1491 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1492 : : * @error: return location for a #GError, or %NULL
1493 : : *
1494 : : * Synchronous version of mwsc_scheduler_release_async().
1495 : : *
1496 : : * Returns: %TRUE on success, %FALSE otherwise
1497 : : * Since: 0.2.0
1498 : : */
1499 : : gboolean
1500 : 0 : mwsc_scheduler_release (MwscScheduler *self,
1501 : : GCancellable *cancellable,
1502 : : GError **error)
1503 : : {
1504 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), FALSE);
1505 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
# # # # #
# ]
1506 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1507 : :
1508 [ # # ]: 0 : g_return_val_if_fail (self->hold_count > 0, FALSE);
1509 : :
1510 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
1511 : 0 : return FALSE;
1512 : :
1513 : : /* Check whether we would still hold the scheduler after releasing. */
1514 [ # # ]: 0 : if (--self->hold_count > 0)
1515 : : {
1516 : 0 : g_debug ("Still hold scheduler");
1517 : 0 : return TRUE;
1518 : : }
1519 : :
1520 : : /* Release the scheduler over D-Bus. */
1521 : 0 : g_debug ("Releasing scheduler over D-Bus");
1522 : :
1523 : 0 : g_autoptr(GVariant) return_value = NULL;
1524 : 0 : return_value = g_dbus_proxy_call_sync (self->proxy,
1525 : : "Release",
1526 : : NULL, /* no arguments */
1527 : : G_DBUS_CALL_FLAGS_NONE,
1528 : : -1, /* default timeout */
1529 : : cancellable,
1530 : : error);
1531 : :
1532 [ # # ]: 0 : if (return_value == NULL)
1533 : : {
1534 [ # # ]: 0 : g_assert (self->hold_count < G_MAXUINT);
1535 : 0 : self->hold_count++;
1536 : 0 : return FALSE;
1537 : : }
1538 : :
1539 : 0 : return TRUE;
1540 : : }
1541 : :
1542 : : static void release_cb (GObject *obj,
1543 : : GAsyncResult *result,
1544 : : gpointer user_data);
1545 : :
1546 : : /**
1547 : : * mwsc_scheduler_release_async:
1548 : : * @self: a #MwscScheduler
1549 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1550 : : * @callback: callback to invoke on completion
1551 : : * @user_data: user data to pass to @callback
1552 : : *
1553 : : * Decrement the hold count on the scheduler daemon. See
1554 : : * mwsc_scheduler_hold_async() for information about the concept of holding the
1555 : : * daemon.
1556 : : *
1557 : : * Calls to this function must be paired with calls to
1558 : : * mwsc_scheduler_hold_async() to initially acquire the hold. You must call
1559 : : * this function as many times as mwsc_scheduler_hold_async() is called.
1560 : : *
1561 : : * Since: 0.1.0
1562 : : */
1563 : : void
1564 : 0 : mwsc_scheduler_release_async (MwscScheduler *self,
1565 : : GCancellable *cancellable,
1566 : : GAsyncReadyCallback callback,
1567 : : gpointer user_data)
1568 : : {
1569 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULER (self));
1570 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
1571 : :
1572 [ # # ]: 0 : g_return_if_fail (self->hold_count > 0);
1573 : :
1574 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
1575 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_scheduler_release_async);
1576 : :
1577 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
1578 : 0 : return;
1579 : :
1580 : : /* Check whether we would still hold the scheduler after releasing. */
1581 [ # # ]: 0 : if (--self->hold_count > 0)
1582 : : {
1583 : 0 : g_debug ("Still hold scheduler");
1584 : 0 : g_task_return_boolean (task, TRUE);
1585 : 0 : return;
1586 : : }
1587 : :
1588 : : /* Release the scheduler over D-Bus. */
1589 : 0 : g_debug ("Releasing scheduler over D-Bus");
1590 : :
1591 : 0 : g_dbus_proxy_call (self->proxy,
1592 : : "Release",
1593 : : NULL, /* no arguments */
1594 : : G_DBUS_CALL_FLAGS_NONE,
1595 : : -1, /* default timeout */
1596 : : cancellable,
1597 : : release_cb,
1598 : : g_steal_pointer (&task));
1599 : : }
1600 : :
1601 : : static void
1602 : 0 : release_cb (GObject *obj,
1603 : : GAsyncResult *result,
1604 : : gpointer user_data)
1605 : : {
1606 : 0 : GDBusProxy *proxy = G_DBUS_PROXY (obj);
1607 : 0 : g_autoptr(GTask) task = G_TASK (user_data);
1608 : 0 : MwscScheduler *self = g_task_get_source_object (task);
1609 : 0 : g_autoptr(GError) local_error = NULL;
1610 : :
1611 : : /* Check for errors. */
1612 : 0 : g_autoptr(GVariant) return_value = NULL;
1613 : 0 : return_value = g_dbus_proxy_call_finish (proxy, result, &local_error);
1614 : :
1615 [ # # ]: 0 : if (local_error != NULL)
1616 : : {
1617 [ # # ]: 0 : g_assert (self->hold_count < G_MAXUINT);
1618 : 0 : self->hold_count++;
1619 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1620 : : }
1621 : : else
1622 : : {
1623 : 0 : g_task_return_boolean (task, TRUE);
1624 : : }
1625 : 0 : }
1626 : :
1627 : : /**
1628 : : * mwsc_scheduler_release_finish:
1629 : : * @self: a #MwscScheduleEntry
1630 : : * @result: asynchronous operation result
1631 : : * @error: return location for a #GError
1632 : : *
1633 : : * Finish releasing a hold on the daemon. See mwsc_scheduler_release_async().
1634 : : *
1635 : : * Returns: %TRUE on success, %FALSE otherwise
1636 : : * Since: 0.1.0
1637 : : */
1638 : : gboolean
1639 : 0 : mwsc_scheduler_release_finish (MwscScheduler *self,
1640 : : GAsyncResult *result,
1641 : : GError **error)
1642 : : {
1643 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), FALSE);
1644 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1645 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result, mwsc_scheduler_release_async), FALSE);
1646 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1647 : :
1648 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
1649 : : }
1650 : :
1651 : : /**
1652 : : * mwsc_scheduler_get_allow_downloads:
1653 : : * @self: a #MwscScheduler
1654 : : *
1655 : : * Get the value of #MwscScheduler:allow-downloads.
1656 : : *
1657 : : * Returns: %TRUE if the user has indicated that at least one of the active
1658 : : * network connections should be used for large downloads, %FALSE otherwise
1659 : : * Since: 0.1.0
1660 : : */
1661 : : gboolean
1662 : 0 : mwsc_scheduler_get_allow_downloads (MwscScheduler *self)
1663 : : {
1664 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULER (self), TRUE);
1665 : :
1666 [ # # ]: 0 : if (!check_invalidated_with_task (self, NULL))
1667 : 0 : return TRUE;
1668 : :
1669 : 0 : g_autoptr(GVariant) allow_downloads_variant = NULL;
1670 : 0 : allow_downloads_variant = g_dbus_proxy_get_cached_property (self->proxy, "DownloadsAllowed");
1671 : :
1672 [ # # ]: 0 : if (allow_downloads_variant == NULL)
1673 : : {
1674 : : /* The property cache is always expected to be populated. */
1675 : 0 : g_critical ("%s: Could not get cached DownloadsAllowed property", G_STRFUNC);
1676 : 0 : return TRUE;
1677 : : }
1678 : :
1679 : 0 : return g_variant_get_boolean (allow_downloads_variant);
1680 : : }
|