1 /* gbp-deviced-device.c
2  *
3  * Copyright 2018-2019 Christian Hergert <chergert@redhat.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "gbp-deviced-device"
22 
23 #include "gbp-deviced-device.h"
24 
25 struct _GbpDevicedDevice
26 {
27   IdeDevice      parent_instance;
28   DevdDevice    *device;
29   DevdClient    *client;
30   IdeDeviceInfo *info;
31   GQueue         connecting;
32 };
33 
34 typedef struct
35 {
36   gchar                 *local_path;
37   gchar                 *remote_path;
38   GFileProgressCallback  progress;
39   gpointer               progress_data;
40   GDestroyNotify         progress_data_destroy;
41 } InstallBundleState;
42 
43 G_DEFINE_FINAL_TYPE (GbpDevicedDevice, gbp_deviced_device, IDE_TYPE_DEVICE)
44 
45 enum {
46   PROP_0,
47   PROP_DEVICE,
48   N_PROPS
49 };
50 
51 static GParamSpec *properties [N_PROPS];
52 
53 static void
install_bundle_state_free(InstallBundleState * state)54 install_bundle_state_free (InstallBundleState *state)
55 {
56   g_clear_pointer (&state->local_path, g_free);
57   g_clear_pointer (&state->remote_path, g_free);
58   if (state->progress_data_destroy)
59     state->progress_data_destroy (state->progress_data);
60   g_slice_free (InstallBundleState, state);
61 }
62 
63 static void
gbp_deviced_device_connect_cb(GObject * object,GAsyncResult * result,gpointer user_data)64 gbp_deviced_device_connect_cb (GObject      *object,
65                                GAsyncResult *result,
66                                gpointer      user_data)
67 {
68   DevdClient *client = (DevdClient *)object;
69   g_autoptr(GbpDevicedDevice) self = user_data;
70   g_autoptr(GError) error = NULL;
71   GList *list;
72 
73   g_assert (DEVD_IS_CLIENT (client));
74   g_assert (G_IS_ASYNC_RESULT (result));
75   g_assert (GBP_IS_DEVICED_DEVICE (self));
76 
77   list = g_steal_pointer (&self->connecting.head);
78   self->connecting.head = NULL;
79   self->connecting.length = 0;
80 
81   if (!devd_client_connect_finish (client, result, &error))
82     {
83       g_debug ("%s", error->message);
84       g_clear_object (&self->client);
85 
86       for (const GList *iter = list; iter != NULL; iter = iter->next)
87         {
88           g_autoptr(IdeTask) task = iter->data;
89           ide_task_return_error (task, g_error_copy (error));
90         }
91     }
92   else
93     {
94       for (const GList *iter = list; iter != NULL; iter = iter->next)
95         {
96           g_autoptr(IdeTask) task = iter->data;
97           ide_task_return_pointer (task, g_object_ref (client), g_object_unref);
98         }
99     }
100 
101   g_list_free (list);
102 }
103 
104 void
gbp_deviced_device_get_client_async(GbpDevicedDevice * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)105 gbp_deviced_device_get_client_async (GbpDevicedDevice    *self,
106                                      GCancellable        *cancellable,
107                                      GAsyncReadyCallback  callback,
108                                      gpointer             user_data)
109 {
110   g_autoptr(IdeTask) task = NULL;
111 
112   g_assert (GBP_IS_DEVICED_DEVICE (self));
113   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
114 
115   task = ide_task_new (self, cancellable, callback, user_data);
116   ide_task_set_source_tag (task, gbp_deviced_device_get_client_async);
117 
118   if (self->client != NULL && self->connecting.length == 0)
119     {
120       ide_task_return_pointer (task, g_object_ref (self->client), g_object_unref);
121       return;
122     }
123 
124   g_queue_push_tail (&self->connecting, g_steal_pointer (&task));
125 
126   if (self->client == NULL)
127     {
128       self->client = devd_device_create_client (self->device);
129       devd_client_connect_async (self->client,
130                                  NULL,
131                                  gbp_deviced_device_connect_cb,
132                                  g_object_ref (self));
133     }
134 }
135 
136 DevdClient *
gbp_deviced_device_get_client_finish(GbpDevicedDevice * self,GAsyncResult * result,GError ** error)137 gbp_deviced_device_get_client_finish (GbpDevicedDevice  *self,
138                                       GAsyncResult      *result,
139                                       GError           **error)
140 {
141   g_assert (GBP_IS_DEVICED_DEVICE (self));
142   g_assert (IDE_IS_TASK (result));
143 
144   return ide_task_propagate_pointer (IDE_TASK (result), error);
145 }
146 
147 static void
gbp_deviced_device_get_info_connect_cb(GObject * object,GAsyncResult * result,gpointer user_data)148 gbp_deviced_device_get_info_connect_cb (GObject      *object,
149                                         GAsyncResult *result,
150                                         gpointer      user_data)
151 {
152   GbpDevicedDevice *self = (GbpDevicedDevice *)object;
153   g_autoptr(IdeDeviceInfo) info = NULL;
154   g_autoptr(IdeTriplet) triplet = NULL;
155   g_autoptr(GError) error = NULL;
156   g_autoptr(IdeTask) task = user_data;
157   g_autofree gchar *arch = NULL;
158   g_autofree gchar *kernel = NULL;
159   g_autofree gchar *system = NULL;
160   IdeDeviceKind kind = 0;
161   DevdClient *client;
162 
163   g_assert (GBP_IS_DEVICED_DEVICE (self));
164   g_assert (G_IS_ASYNC_RESULT (result));
165   g_assert (IDE_IS_TASK (task));
166 
167   if (!(client = gbp_deviced_device_get_client_finish (self, result, &error)))
168     {
169       ide_task_return_error (task, g_steal_pointer (&error));
170       return;
171     }
172 
173   arch = devd_client_get_arch (client);
174   kernel = devd_client_get_kernel (client);
175   system = devd_client_get_system (client);
176   triplet = ide_triplet_new_with_triplet (arch, kernel, system);
177 
178   switch (devd_device_get_kind (self->device))
179     {
180     case DEVD_DEVICE_KIND_TABLET:
181       kind = IDE_DEVICE_KIND_TABLET;
182       break;
183 
184     case DEVD_DEVICE_KIND_PHONE:
185       kind = IDE_DEVICE_KIND_PHONE;
186       break;
187 
188     case DEVD_DEVICE_KIND_MICRO_CONTROLLER:
189       kind = IDE_DEVICE_KIND_MICRO_CONTROLLER;
190       break;
191 
192     case DEVD_DEVICE_KIND_COMPUTER:
193     default:
194       kind = IDE_DEVICE_KIND_COMPUTER;
195       break;
196     }
197 
198   info = ide_device_info_new ();
199   ide_device_info_set_kind (info, kind);
200   ide_device_info_set_host_triplet (info, triplet);
201 
202   ide_task_return_pointer (task, g_steal_pointer (&info), g_object_unref);
203 }
204 
205 static void
gbp_deviced_device_get_info_async(IdeDevice * device,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)206 gbp_deviced_device_get_info_async (IdeDevice           *device,
207                                    GCancellable        *cancellable,
208                                    GAsyncReadyCallback  callback,
209                                    gpointer             user_data)
210 {
211   GbpDevicedDevice *self = (GbpDevicedDevice *)device;
212   g_autoptr(IdeTask) task = NULL;
213 
214   g_assert (GBP_IS_DEVICED_DEVICE (self));
215   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
216 
217   task = ide_task_new (self, cancellable, callback, user_data);
218   ide_task_set_source_tag (task, gbp_deviced_device_get_info_async);
219 
220   gbp_deviced_device_get_client_async (self,
221                                        cancellable,
222                                        gbp_deviced_device_get_info_connect_cb,
223                                        g_steal_pointer (&task));
224 }
225 
226 static IdeDeviceInfo *
gbp_deviced_device_get_info_finish(IdeDevice * device,GAsyncResult * result,GError ** error)227 gbp_deviced_device_get_info_finish (IdeDevice     *device,
228                                     GAsyncResult  *result,
229                                     GError       **error)
230 {
231   g_assert (IDE_IS_DEVICE (device));
232   g_assert (IDE_IS_TASK (result));
233 
234   return ide_task_propagate_pointer (IDE_TASK (result), error);
235 }
236 
237 static void
gbp_deviced_device_finalize(GObject * object)238 gbp_deviced_device_finalize (GObject *object)
239 {
240   GbpDevicedDevice *self = (GbpDevicedDevice *)object;
241 
242   IDE_ENTRY;
243 
244   g_clear_object (&self->device);
245   g_clear_object (&self->client);
246 
247   G_OBJECT_CLASS (gbp_deviced_device_parent_class)->finalize (object);
248 
249   IDE_EXIT;
250 }
251 
252 static void
gbp_deviced_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)253 gbp_deviced_device_get_property (GObject    *object,
254                                  guint       prop_id,
255                                  GValue     *value,
256                                  GParamSpec *pspec)
257 {
258   GbpDevicedDevice *self = GBP_DEVICED_DEVICE (object);
259 
260   switch (prop_id)
261     {
262     case PROP_DEVICE:
263       g_value_set_object (value, self->device);
264       break;
265 
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268     }
269 }
270 
271 static void
gbp_deviced_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)272 gbp_deviced_device_set_property (GObject      *object,
273                                  guint         prop_id,
274                                  const GValue *value,
275                                  GParamSpec   *pspec)
276 {
277   GbpDevicedDevice *self = GBP_DEVICED_DEVICE (object);
278 
279   switch (prop_id)
280     {
281     case PROP_DEVICE:
282       self->device = g_value_dup_object (value);
283       break;
284 
285     default:
286       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
287     }
288 }
289 
290 static void
gbp_deviced_device_class_init(GbpDevicedDeviceClass * klass)291 gbp_deviced_device_class_init (GbpDevicedDeviceClass *klass)
292 {
293   GObjectClass *object_class = G_OBJECT_CLASS (klass);
294   IdeDeviceClass *device_class = IDE_DEVICE_CLASS (klass);
295 
296   object_class->finalize = gbp_deviced_device_finalize;
297   object_class->get_property = gbp_deviced_device_get_property;
298   object_class->set_property = gbp_deviced_device_set_property;
299 
300   device_class->get_info_async = gbp_deviced_device_get_info_async;
301   device_class->get_info_finish = gbp_deviced_device_get_info_finish;
302 
303   properties [PROP_DEVICE] =
304     g_param_spec_object ("device",
305                          "Device",
306                          "The underlying libdeviced device",
307                          DEVD_TYPE_DEVICE,
308                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
309 
310   g_object_class_install_properties (object_class, N_PROPS, properties);
311 }
312 
313 static void
gbp_deviced_device_init(GbpDevicedDevice * self)314 gbp_deviced_device_init (GbpDevicedDevice *self)
315 {
316   g_queue_init (&self->connecting);
317 }
318 
319 GbpDevicedDevice *
gbp_deviced_device_new(DevdDevice * device)320 gbp_deviced_device_new (DevdDevice *device)
321 {
322   g_autofree gchar *id = NULL;
323   const gchar *name;
324   const gchar *icon_name;
325 
326   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
327   g_return_val_if_fail (DEVD_IS_DEVICE (device), NULL);
328 
329   id = g_strdup_printf ("deviced:%s", devd_device_get_id (device));
330   name = devd_device_get_name (device);
331   icon_name = devd_device_get_icon_name (device);
332 
333   return g_object_new (GBP_TYPE_DEVICED_DEVICE,
334                        "id", id,
335                        "device", device,
336                        "display-name", name,
337                        "icon-name", icon_name,
338                        NULL);
339 }
340 
341 static void
gbp_deviced_device_get_commit_list_apps_cb(GObject * object,GAsyncResult * result,gpointer user_data)342 gbp_deviced_device_get_commit_list_apps_cb (GObject      *object,
343                                             GAsyncResult *result,
344                                             gpointer      user_data)
345 {
346   DevdClient *client = (DevdClient *)object;
347   g_autoptr(GPtrArray) apps = NULL;
348   g_autoptr(GError) error = NULL;
349   g_autoptr(IdeTask) task = user_data;
350   const gchar *app_id;
351 
352   IDE_ENTRY;
353 
354   g_assert (DEVD_IS_CLIENT (client));
355   g_assert (G_IS_ASYNC_RESULT (result));
356   g_assert (IDE_IS_TASK (task));
357 
358   if (!(apps = devd_client_list_apps_finish (client, result, &error)))
359     {
360       ide_task_return_error (task, g_steal_pointer (&error));
361       IDE_EXIT;
362     }
363 
364   app_id = ide_task_get_task_data (task);
365 
366   for (guint i = 0; i < apps->len; i++)
367     {
368       DevdAppInfo *app_info = g_ptr_array_index (apps, i);
369 
370       if (g_strcmp0 (app_id, devd_app_info_get_id (app_info)) == 0)
371         {
372           const gchar *commit_id = devd_app_info_get_commit_id (app_info);
373 
374           if (commit_id != NULL)
375             {
376               ide_task_return_pointer (task, g_strdup (commit_id), g_free);
377               IDE_EXIT;
378             }
379         }
380     }
381 
382   ide_task_return_new_error (task,
383                              G_IO_ERROR,
384                              G_IO_ERROR_NOT_FOUND,
385                              "No such application \"%s\"",
386                              app_id);
387 
388   IDE_EXIT;
389 }
390 
391 static void
gbp_deviced_device_get_commit_client_cb(GObject * object,GAsyncResult * result,gpointer user_data)392 gbp_deviced_device_get_commit_client_cb (GObject      *object,
393                                          GAsyncResult *result,
394                                          gpointer      user_data)
395 {
396   GbpDevicedDevice *self = (GbpDevicedDevice *)object;
397   g_autoptr(DevdClient) client = NULL;
398   g_autoptr(GError) error = NULL;
399   g_autoptr(IdeTask) task = user_data;
400   GCancellable *cancellable;
401 
402   IDE_ENTRY;
403 
404   g_assert (GBP_IS_DEVICED_DEVICE (self));
405   g_assert (G_IS_ASYNC_RESULT (result));
406   g_assert (IDE_IS_TASK (task));
407 
408   if (!(client = gbp_deviced_device_get_client_finish (self, result, &error)))
409     {
410       ide_task_return_error (task, g_steal_pointer (&error));
411       IDE_EXIT;
412     }
413 
414   cancellable = ide_task_get_cancellable (task);
415 
416   devd_client_list_apps_async (client,
417                                cancellable,
418                                gbp_deviced_device_get_commit_list_apps_cb,
419                                g_steal_pointer (&task));
420 
421   IDE_EXIT;
422 }
423 
424 void
gbp_deviced_device_get_commit_async(GbpDevicedDevice * self,const gchar * app_id,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)425 gbp_deviced_device_get_commit_async (GbpDevicedDevice    *self,
426                                      const gchar         *app_id,
427                                      GCancellable        *cancellable,
428                                      GAsyncReadyCallback  callback,
429                                      gpointer             user_data)
430 {
431   g_autoptr(IdeTask) task = NULL;
432 
433   IDE_ENTRY;
434 
435   g_return_if_fail (GBP_IS_DEVICED_DEVICE (self));
436   g_return_if_fail (app_id != NULL);
437   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
438 
439   task = ide_task_new (self, cancellable, callback, user_data);
440   ide_task_set_source_tag (task, gbp_deviced_device_get_commit_async);
441   ide_task_set_task_data (task, g_strdup (app_id), g_free);
442 
443   gbp_deviced_device_get_client_async (self,
444                                        cancellable,
445                                        gbp_deviced_device_get_commit_client_cb,
446                                        g_steal_pointer (&task));
447 
448   IDE_EXIT;
449 }
450 
451 gchar *
gbp_deviced_device_get_commit_finish(GbpDevicedDevice * self,GAsyncResult * result,GError ** error)452 gbp_deviced_device_get_commit_finish (GbpDevicedDevice  *self,
453                                       GAsyncResult      *result,
454                                       GError           **error)
455 {
456   gchar *ret;
457 
458   IDE_ENTRY;
459 
460   g_return_val_if_fail (GBP_IS_DEVICED_DEVICE (self), NULL);
461   g_return_val_if_fail (IDE_IS_TASK (result), NULL);
462 
463   ret = ide_task_propagate_pointer (IDE_TASK (result), error);
464 
465   IDE_RETURN (ret);
466 }
467 
468 static void
install_bundle_progress(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)469 install_bundle_progress (goffset  current_num_bytes,
470                          goffset  total_num_bytes,
471                          gpointer user_data)
472 {
473   InstallBundleState *state = user_data;
474 
475   g_assert (state != NULL);
476 
477   if (state->progress && total_num_bytes)
478     state->progress (current_num_bytes, total_num_bytes, state->progress_data);
479 }
480 
481 static void
install_bundle_install_cb(GObject * object,GAsyncResult * result,gpointer user_data)482 install_bundle_install_cb (GObject      *object,
483                            GAsyncResult *result,
484                            gpointer      user_data)
485 {
486   DevdFlatpakService *service = (DevdFlatpakService *)object;
487   g_autoptr(GError) error = NULL;
488   g_autoptr(IdeTask) task = user_data;
489 
490   IDE_ENTRY;
491 
492   g_assert (DEVD_IS_FLATPAK_SERVICE (service));
493   g_assert (G_IS_ASYNC_RESULT (result));
494   g_assert (IDE_IS_TASK (task));
495 
496   if (!devd_flatpak_service_install_bundle_finish (service, result, &error))
497     ide_task_return_error (task, g_steal_pointer (&error));
498   else
499     ide_task_return_boolean (task, TRUE);
500 
501   /* TODO: Remove bundle */
502 
503   IDE_EXIT;
504 
505 }
506 
507 static void
install_bundle_put_file_cb(GObject * object,GAsyncResult * result,gpointer user_data)508 install_bundle_put_file_cb (GObject      *object,
509                             GAsyncResult *result,
510                             gpointer      user_data)
511 {
512   DevdTransferService *service = (DevdTransferService *)object;
513   g_autoptr(DevdFlatpakService) flatpak = NULL;
514   g_autoptr(GError) error = NULL;
515   g_autoptr(IdeTask) task = user_data;
516   InstallBundleState *state;
517   DevdClient *client;
518 
519   IDE_ENTRY;
520 
521   g_assert (DEVD_IS_TRANSFER_SERVICE (service));
522   g_assert (G_IS_ASYNC_RESULT (result));
523   g_assert (IDE_IS_TASK (task));
524 
525   if (!devd_transfer_service_put_file_finish (service, result, &error))
526     {
527       ide_task_return_error (task, g_steal_pointer (&error));
528       IDE_EXIT;
529     }
530 
531   client = devd_service_get_client (DEVD_SERVICE (service));
532 
533   if (!(flatpak = devd_flatpak_service_new (client, &error)))
534     {
535       ide_task_return_error (task, g_steal_pointer (&error));
536       IDE_EXIT;
537     }
538 
539   state = ide_task_get_task_data (task);
540   g_assert (state != NULL);
541   g_assert (state->remote_path != NULL);
542 
543   devd_flatpak_service_install_bundle_async (flatpak,
544                                              state->remote_path,
545                                              ide_task_get_cancellable (task),
546                                              install_bundle_install_cb,
547                                              g_object_ref (task));
548 
549   IDE_EXIT;
550 }
551 
552 static void
install_bundle_get_client_cb(GObject * object,GAsyncResult * result,gpointer user_data)553 install_bundle_get_client_cb (GObject      *object,
554                               GAsyncResult *result,
555                               gpointer      user_data)
556 {
557   GbpDevicedDevice *self = (GbpDevicedDevice *)object;
558   g_autoptr(DevdTransferService) service = NULL;
559   g_autoptr(DevdClient) client = NULL;
560   g_autoptr(GError) error = NULL;
561   g_autoptr(IdeTask) task = user_data;
562   g_autoptr(GFile) file = NULL;
563   InstallBundleState *state;
564 
565   IDE_ENTRY;
566 
567   g_assert (GBP_IS_DEVICED_DEVICE (self));
568   g_assert (G_IS_ASYNC_RESULT (result));
569   g_assert (IDE_IS_TASK (task));
570 
571   state = ide_task_get_task_data (task);
572   g_assert (state != NULL);
573   g_assert (state->local_path != NULL);
574 
575   if (!(client = gbp_deviced_device_get_client_finish (self, result, &error)))
576     {
577       ide_task_return_error (task, g_steal_pointer (&error));
578       IDE_EXIT;
579     }
580 
581   if (!(service = devd_transfer_service_new (client, &error)))
582     {
583       ide_task_return_error (task, g_steal_pointer (&error));
584       IDE_EXIT;
585     }
586 
587   file = g_file_new_for_path (state->local_path);
588 
589   devd_transfer_service_put_file_async (service,
590                                         file,
591                                         state->remote_path,
592                                         install_bundle_progress, state, NULL,
593                                         ide_task_get_cancellable (task),
594                                         install_bundle_put_file_cb,
595                                         g_object_ref (task));
596 
597   IDE_EXIT;
598 }
599 
600 void
gbp_deviced_device_install_bundle_async(GbpDevicedDevice * self,const gchar * bundle_path,GFileProgressCallback progress,gpointer progress_data,GDestroyNotify progress_data_destroy,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)601 gbp_deviced_device_install_bundle_async (GbpDevicedDevice      *self,
602                                          const gchar           *bundle_path,
603                                          GFileProgressCallback  progress,
604                                          gpointer               progress_data,
605                                          GDestroyNotify         progress_data_destroy,
606                                          GCancellable          *cancellable,
607                                          GAsyncReadyCallback    callback,
608                                          gpointer               user_data)
609 {
610   g_autoptr(IdeTask) task = NULL;
611   g_autofree gchar *name = NULL;
612   InstallBundleState *state;
613 
614   IDE_ENTRY;
615 
616   g_return_if_fail (GBP_IS_DEVICED_DEVICE (self));
617   g_return_if_fail (bundle_path != NULL);
618   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
619 
620   task = ide_task_new (self, cancellable, callback, user_data);
621   ide_task_set_source_tag (task, gbp_deviced_device_install_bundle_async);
622 
623   name = g_path_get_basename (bundle_path);
624 
625   state = g_slice_new0 (InstallBundleState);
626   state->local_path = g_strdup (bundle_path);
627   state->remote_path = g_build_filename (".cache", "deviced", name, NULL);
628   state->progress = progress;
629   state->progress_data = progress_data;
630   state->progress_data_destroy = progress_data_destroy;
631   ide_task_set_task_data (task, state, install_bundle_state_free);
632 
633   gbp_deviced_device_get_client_async (self,
634                                        cancellable,
635                                        install_bundle_get_client_cb,
636                                        g_steal_pointer (&task));
637 
638   IDE_EXIT;
639 }
640 
641 gboolean
gbp_deviced_device_install_bundle_finish(GbpDevicedDevice * self,GAsyncResult * result,GError ** error)642 gbp_deviced_device_install_bundle_finish (GbpDevicedDevice  *self,
643                                           GAsyncResult      *result,
644                                           GError           **error)
645 {
646   gboolean ret;
647 
648   IDE_ENTRY;
649 
650   g_return_val_if_fail (GBP_IS_DEVICED_DEVICE (self), FALSE);
651   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
652 
653   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
654 
655   IDE_RETURN (ret);
656 }
657 
658