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