Add network connection state
[platform/core/connectivity/wifi-mesh-manager.git] / src / mesh-gdbus.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "mesh-log.h"
18 #include "mesh-util.h"
19 #include "mesh-gdbus.h"
20 #include "mesh-request.h"
21
22 #include "nl80211.h"
23
24 static GDBusProxy *_gproxy_connman = NULL;
25 static GDBusProxy *_gproxy_connman_mesh = NULL;
26 static GDBusProxy *_gproxy_connman_technology = NULL;
27
28 static int _meshd_close_gdbus_call(mesh_service *service);
29 static int _mesh_ipc_get_mesh_network_property(mesh_service *service,
30         const gchar* object_path, mesh_network_info_s *result);
31
32 static int __channel_to_frequency(int channel, enum nl80211_band band)
33 {
34         if (channel <= 0)
35                 return 0;
36
37         switch (band) {
38         case NL80211_BAND_2GHZ:
39                 if (channel == 14)
40                         return 2484;
41                 else if (channel < 14)
42                         return 2407 + channel * 5;
43                 break;
44         case NL80211_BAND_5GHZ:
45                 if (channel >= 182 && channel <= 196)
46                         return 4000 + channel * 5;
47                 else
48                         return 5000 + channel * 5;
49                 break;
50         default:
51                 break;
52         }
53
54         /* not supported */
55         return 0;
56 }
57
58 static int __frequency_to_channel(int freq)
59 {
60         if (freq == 2484)
61                 return 14;
62         else if (freq < 2484)
63                 return (freq - 2407) / 5;
64         else if (freq >= 4910 && freq <= 4980)
65                 return (freq - 4000) / 5;
66         else if (freq <= 45000)
67                 return (freq - 5000) / 5;
68         else if (freq >= 58320 && freq <= 64800)
69                 return (freq - 56160) / 2160;
70         else
71                 return 0;
72 }
73
74 static GDBusProxy *_proxy_get_connman(mesh_service *service)
75 {
76         GDBusProxy *proxy = NULL;
77         meshd_check_null_ret_error("service", service, NULL);
78
79         if (NULL == _gproxy_connman) {
80                 proxy = g_dbus_proxy_new_sync(service->connection,
81                         G_DBUS_PROXY_FLAGS_NONE, NULL,
82                         CONNMAN_SERVER_NAME,
83                         CONNMAN_OBJECT_PATH,
84                         CONNMAN_INTERFACE_MANAGER,
85                         NULL, NULL);
86         } else
87                 proxy = _gproxy_connman;
88
89         return proxy;
90 }
91
92 static GDBusProxy *_proxy_get_connman_mesh(mesh_service *service)
93 {
94         GDBusProxy *proxy = NULL;
95         meshd_check_null_ret_error("service", service, NULL);
96
97         if (NULL == _gproxy_connman_mesh) {
98                 proxy = g_dbus_proxy_new_sync(service->connection,
99                         G_DBUS_PROXY_FLAGS_NONE, NULL,
100                         CONNMAN_SERVER_NAME,
101                         CONNMAN_OBJECT_PATH_MESH,
102                         CONNMAN_INTERFACE_MESH,
103                         NULL, NULL);
104         } else
105                 proxy = _gproxy_connman_mesh;
106
107         return proxy;
108 }
109
110 static GDBusProxy *_proxy_get_connman_technology(mesh_service *service)
111 {
112         GDBusProxy *proxy = NULL;
113         meshd_check_null_ret_error("service", service, NULL);
114
115         if (NULL == _gproxy_connman_technology) {
116                 proxy = g_dbus_proxy_new_sync(service->connection,
117                         G_DBUS_PROXY_FLAGS_NONE, NULL,
118                         CONNMAN_SERVER_NAME,
119                         CONNMAN_OBJECT_PATH_TECH_MESH,
120                         CONNMAN_INTERFACE_TECH,
121                         NULL, NULL);
122         } else
123                 proxy = _gproxy_connman_technology;
124
125         return proxy;
126 }
127
128 static void _dbus_name_owner_notify(GObject *object, GParamSpec *pspec,
129                 gpointer *user_data)
130 {
131         GDBusProxy *proxy = G_DBUS_PROXY(object);
132         gchar *name_owner = g_dbus_proxy_get_name_owner(proxy);
133         mesh_service *service = (mesh_service*)user_data;
134
135         NOTUSED(pspec);
136
137         if (NULL == name_owner) {
138                 MESH_LOGE("name_owner is not exists !");
139                 _meshd_close_gdbus_call(service);
140         }
141
142         g_free(name_owner);
143 }
144
145 static int _meshd_create_gdbus_call(mesh_service *service)
146 {
147         int id;
148         GError *error = NULL;
149
150         if (NULL == service)
151                 return MESHD_ERROR_INVALID_PARAMETER;
152
153         if (NULL != service->connection)
154                 return MESHD_ERROR_ALREADY_REGISTERED;
155
156         service->connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
157         if (service->connection == NULL) {
158                 if (error != NULL) {
159                         MESH_LOGE("Failed to connect to the D-BUS daemon [%s]", error->message);
160                         g_error_free(error);
161                 }
162                 return MESHD_ERROR_IO_ERROR;
163         }
164
165         id = g_signal_connect(service->connection, "notify::g-name-owner",
166                         G_CALLBACK(_dbus_name_owner_notify), service);
167         if (0 == id) {
168                 MESH_LOGE("g_signal_connect() Fail");
169                 g_object_unref(service->connection);
170                 service->connection = NULL;
171                 return MESHD_ERROR_IO_ERROR;
172         }
173
174         return MESHD_ERROR_NONE;
175 }
176
177 static int _meshd_close_gdbus_call(mesh_service *service)
178 {
179         /* CHECK: is connection ref count required? */
180         g_object_unref(service->connection);
181         service->connection = NULL;
182
183         return MESHD_ERROR_NONE;
184 }
185
186 static void _meshd_signal_handler(GDBusConnection *connection,
187                 const gchar *sender_name, const gchar *object_path, const gchar *interface_name,
188                 const gchar *signal_name, GVariant *parameters, gpointer user_data)
189 {
190         mesh_service *service = (mesh_service*)user_data;
191         mesh_network_info_s network_info = { 0, 0, 0, 0, 0 };
192         int ret = MESHD_ERROR_NONE;
193
194         meshd_check_null_ret("user_data", user_data);
195         NOTUSED(connection);
196         NOTUSED(sender_name);
197         NOTUSED(interface_name);
198
199         MESH_LOGD("signal received = %s", signal_name);
200         if (0 == g_strcmp0(signal_name, "ScanDone")) {
201                 /* TODO: Handle event */
202                 mesh_notify_scan_done();
203         } else if (0 == g_strcmp0(signal_name, "PropertyChanged")) {
204                 const gchar* var = NULL;
205                 gchar* key = NULL;
206                 GVariant *variant = NULL;
207                 meshd_connection_state_e state = MESHD_CONNECTION_STATE_DISCONNECTED;
208
209                 if (NULL == parameters) {
210                         MESH_LOGE("Unexpected parameter");
211                         return;
212                 }
213
214                 g_variant_get(parameters, "(sv)", &key, &variant);
215                 if (NULL == variant) {
216                         MESH_LOGE("Invalid variant");
217                         return;
218                 }
219
220                 /* State [???] */
221                 var = g_variant_get_string(variant, NULL);
222                 MESH_LOGD("  %s [%s]", key, var);
223                 MESH_LOGD("    %s", object_path);
224
225                 ret = _mesh_ipc_get_mesh_network_property(service, object_path, &network_info);
226                 if (MESHD_ERROR_NONE != ret)
227                         MESH_LOGE("Cannot get valid network property !");
228
229                 if (g_strcmp0("association", var) == 0) {
230                         /* Joined mesh network */
231                         state = MESHD_CONNECTION_STATE_ASSOCIATION;
232                 } else if (g_strcmp0("configuration", var) == 0) {
233                         /* Trying to get IP address */
234                         state = MESHD_CONNECTION_STATE_CONFIGURATION;
235                 } else if (g_strcmp0("ready", var) == 0 || g_strcmp0("online", var) == 0) {
236                         /* IP address is obtained */
237                         state = MESHD_CONNECTION_STATE_CONNECTED;
238                 } else if (g_strcmp0("disconnect", var) == 0 || g_strcmp0("failure", var) == 0) {
239                         state = MESHD_CONNECTION_STATE_DISCONNECTED;
240                 } else {
241                         MESH_LOGE("  Unhandled state !");
242                         g_free(network_info.mesh_id);
243                         g_free(network_info.bssid);
244                         return;
245                 }
246
247                 mesh_notify_connection_state(network_info.mesh_id, network_info.bssid,
248                         network_info.channel, state);
249
250                 g_free(network_info.mesh_id);
251                 g_free(network_info.bssid);
252         }
253 }
254
255 static void _meshd_subscribe_event(mesh_service *service)
256 {
257         unsigned int id = 0;
258
259         meshd_check_null_ret("service", service);
260
261         id = g_dbus_connection_signal_subscribe(
262                         (GDBusConnection *)service->connection,
263                         CONNMAN_SERVER_NAME, CONNMAN_INTERFACE_MANAGER,
264                         "ScanDone", "/", NULL,
265                         G_DBUS_CALL_FLAGS_NONE, _meshd_signal_handler, service, NULL);
266         if (0 == id) {
267                 MESH_LOGE("g_dbus_connection_signal_subscribe(ScanDone) Fail(%d)", errno);
268                 return;
269         }
270         service->dbus_sub_ids = g_list_append(service->dbus_sub_ids, GUINT_TO_POINTER(id));
271         MESH_LOGD("[Signal subscribe] : ScanDone (%d)", id);
272
273         /* To monitor mesh profiles */
274         id = g_dbus_connection_signal_subscribe(
275                         (GDBusConnection *)service->connection,
276                         CONNMAN_SERVER_NAME,
277                         CONNMAN_INTERFACE_MESH,
278                         "PropertyChanged",
279                         NULL, /* Path */
280                         NULL,
281                         G_DBUS_CALL_FLAGS_NONE, _meshd_signal_handler, service, NULL);
282         if (0 == id) {
283                 MESH_LOGE("g_dbus_connection_signal_subscribe(ScanDone) Fail(%d)", errno);
284                 return;
285         }
286         service->dbus_sub_ids = g_list_append(service->dbus_sub_ids, GUINT_TO_POINTER(id));
287         MESH_LOGD("[Signal subscribe] : PropertyChanged (%d)", id);
288
289         /* End of subscription */
290 }
291
292 static void _on_unsubscribe_ids(gpointer data, gpointer user_data)
293 {
294         unsigned int id = GPOINTER_TO_UINT(data);
295         mesh_service *service = (mesh_service*)user_data;
296
297         MESH_LOGD("[Signal unsubscribe] : %d", id);
298         g_dbus_connection_signal_unsubscribe(
299                 (GDBusConnection *)service->connection, id);
300 }
301
302 static void _meshd_unsubscribe_event(mesh_service *service)
303 {
304         meshd_check_null_ret("service", service);
305
306         g_list_foreach(service->dbus_sub_ids, _on_unsubscribe_ids, service);
307
308         g_list_free(service->dbus_sub_ids);
309         service->dbus_sub_ids = NULL;
310 }
311
312 int meshd_dbus_start(mesh_service *service)
313 {
314         int rv;
315
316         rv = _meshd_create_gdbus_call(service);
317         if (MESHD_ERROR_NONE != rv)
318                 return rv;
319
320         service->ca = g_cancellable_new();
321
322         /* Create all required proxies here */
323         _gproxy_connman = _proxy_get_connman(service);
324         meshd_check_null_ret_error("_gproxy_connman", _gproxy_connman,
325                                 MESHD_ERROR_IO_ERROR);
326         g_dbus_proxy_set_default_timeout(
327                         G_DBUS_PROXY(_gproxy_connman), MESH_DBUS_PROXY_TIMEOUT);
328
329         _gproxy_connman_mesh = _proxy_get_connman_mesh(service);
330         meshd_check_null_ret_error("_gproxy_connman_mesh", _gproxy_connman_mesh,
331                                 MESHD_ERROR_IO_ERROR);
332         g_dbus_proxy_set_default_timeout(
333                         G_DBUS_PROXY(_gproxy_connman_mesh), MESH_DBUS_PROXY_TIMEOUT);
334
335         _gproxy_connman_technology = _proxy_get_connman_technology(service);
336         meshd_check_null_ret_error("_gproxy_connman_technology", _gproxy_connman_technology,
337                                 MESHD_ERROR_IO_ERROR);
338         g_dbus_proxy_set_default_timeout(
339                         G_DBUS_PROXY(_gproxy_connman_technology), MESH_DBUS_PROXY_TIMEOUT);
340
341         /* Subscribe events */
342         _meshd_subscribe_event(service);
343
344         return MESHD_ERROR_NONE;
345 }
346
347 int meshd_dbus_stop(mesh_service *service)
348 {
349         int rv;
350
351         if (NULL == service)
352                 return MESHD_ERROR_INVALID_PARAMETER;
353
354         /* Unsubscribe events */
355         _meshd_unsubscribe_event(service);
356
357         /* Unref all proxies here */
358         if (_gproxy_connman) {
359                 g_object_unref(_gproxy_connman);
360                 _gproxy_connman = NULL;
361         }
362         if (_gproxy_connman_mesh) {
363                 g_object_unref(_gproxy_connman_mesh);
364                 _gproxy_connman_mesh = NULL;
365         }
366         if (_gproxy_connman_technology) {
367                 g_object_unref(_gproxy_connman_technology);
368                 _gproxy_connman_technology = NULL;
369         }
370
371         g_cancellable_cancel(service->ca);
372         g_object_unref(service->ca);
373         service->ca = NULL;
374
375         rv = _meshd_close_gdbus_call(service);
376         return rv;
377 }
378
379 int mesh_ipc_create_mesh_interface(mesh_service *service)
380 {
381         int ret = MESHD_ERROR_NONE;
382         GVariant *variant = NULL;
383         GError *error = NULL;
384         GVariant *var_dict = NULL;
385         GVariantDict dict;
386         mesh_interface_s *info = NULL;
387
388         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
389         meshd_check_null_ret_error("connection", service->connection,
390                         MESHD_ERROR_INVALID_PARAMETER);
391         meshd_check_null_ret_error("_gproxy_connman_technology",
392                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
393
394         info = service->interface_info;
395
396         g_variant_dict_init(&dict, NULL);
397         g_variant_dict_insert(&dict, "Ifname", "s", info->mesh_interface);
398         g_variant_dict_insert(&dict, "ParentIfname", "s", info->base_interface);
399         g_variant_dict_insert(&dict, "BridgeIfname", "s", info->bridge_interface);
400         var_dict = g_variant_dict_end(&dict);
401
402         variant = g_dbus_proxy_call_sync(_gproxy_connman_technology, "MeshCommands",
403                                 g_variant_new("(sv)", "MeshInterfaceAdd", var_dict),
404                                 G_DBUS_CALL_FLAGS_NONE,
405                                 -1, NULL, &error);
406         if (variant) {
407                 MESH_LOGD("Successfully requested. [MeshInterfaceAdd]");
408         } else if (error) {
409                 ret = MESHD_ERROR_IO_ERROR;
410                 MESH_LOGE("Failed DBus call [%s]", error->message);
411
412                 /* Interface not exists */
413                 if (g_strrstr(error->message, "No such device"))
414                         ret = MESHD_ERROR_INVALID_PARAMETER;
415                 g_error_free(error);
416         }
417
418         return ret;
419 }
420
421 int mesh_ipc_remove_mesh_interface(mesh_service *service)
422 {
423         GVariant *variant = NULL;
424         GError *error = NULL;
425         GVariant *var_dict = NULL;
426         GVariantDict dict;
427         mesh_interface_s *info = NULL;
428
429         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
430         meshd_check_null_ret_error("connection", service->connection,
431                         MESHD_ERROR_INVALID_PARAMETER);
432         meshd_check_null_ret_error("_gproxy_connman_technology",
433                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
434
435         info = service->interface_info;
436
437         g_variant_dict_init(&dict, NULL);
438         g_variant_dict_insert(&dict, "Ifname", "s", info->mesh_interface);
439         var_dict = g_variant_dict_end(&dict);
440
441         variant = g_dbus_proxy_call_sync(_gproxy_connman_technology, "MeshCommands",
442                                 g_variant_new("(sv)", "MeshInterfaceRemove", var_dict),
443                                 G_DBUS_CALL_FLAGS_NONE,
444                                 -1, NULL, &error);
445         if (variant) {
446                 MESH_LOGD("Successfully requested. [MeshInterfaceRemove]");
447         } else if (error) {
448                 MESH_LOGE("Failed DBus call [%s]", error->message);
449                 g_error_free(error);
450                 return MESHD_ERROR_IO_ERROR;
451         }
452
453         return MESHD_ERROR_NONE;
454 }
455
456 int mesh_ipc_mesh_scan(mesh_service *service)
457 {
458         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
459         meshd_check_null_ret_error("connection", service->connection,
460                         MESHD_ERROR_INVALID_PARAMETER);
461         meshd_check_null_ret_error("_gproxy_connman_technology",
462                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
463
464         g_dbus_proxy_call(_gproxy_connman_technology, "Scan",
465                                 NULL,
466                                 G_DBUS_CALL_FLAGS_NONE,
467                                 -1, NULL, NULL, NULL);
468
469         MESH_LOGD("Successfully requested. [Scan]");
470
471         return MESHD_ERROR_NONE;
472 }
473
474 int mesh_ipc_mesh_specific_scan(mesh_service *service, gchar *mesh_id,
475         gint channel)
476 {
477         GVariant *variant = NULL;
478         GError *error = NULL;
479         GVariant *var_dict = NULL;
480         GVariantDict dict;
481
482         enum nl80211_band band = (channel <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
483         gushort freq = __channel_to_frequency(channel, band);
484
485         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
486         meshd_check_null_ret_error("connection", service->connection,
487                         MESHD_ERROR_INVALID_PARAMETER);
488         meshd_check_null_ret_error("_gproxy_connman_technology",
489                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
490
491         g_variant_dict_init(&dict, NULL);
492         g_variant_dict_insert(&dict, "Name", "s", mesh_id);
493         g_variant_dict_insert(&dict, "Frequency", "q", freq);
494         var_dict = g_variant_dict_end(&dict);
495
496         variant = g_dbus_proxy_call_sync(_gproxy_connman_technology, "MeshCommands",
497                                 g_variant_new("(sv)", "MeshSpecificScan", var_dict),
498                                 G_DBUS_CALL_FLAGS_NONE,
499                                 -1, NULL, &error);
500         if (variant) {
501                 MESH_LOGD("Successfully requested. [MeshSpecificScan]");
502         } else if (error) {
503                 MESH_LOGE("Failed DBus call [%s]", error->message);
504                 g_error_free(error);
505                 return MESHD_ERROR_IO_ERROR;
506         }
507
508         return MESHD_ERROR_NONE;
509 }
510
511 int mesh_ipc_mesh_cancel_scan(mesh_service *service)
512 {
513         GVariant *variant = NULL;
514         GError *error = NULL;
515         GVariant *var_dict = NULL;
516         GVariantDict dict;
517
518         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
519         meshd_check_null_ret_error("connection", service->connection,
520                         MESHD_ERROR_INVALID_PARAMETER);
521         meshd_check_null_ret_error("_gproxy_connman_technology",
522                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
523
524         g_variant_dict_init(&dict, NULL);
525         var_dict = g_variant_dict_end(&dict);
526
527         variant = g_dbus_proxy_call_sync(_gproxy_connman_technology, "MeshCommands",
528                                 g_variant_new("(sv)", "AbortScan", var_dict),
529                                 G_DBUS_CALL_FLAGS_NONE,
530                                 -1, NULL, &error);
531         if (variant) {
532                 MESH_LOGD("Successfully requested. [AbortScan]");
533         } else if (error) {
534                 MESH_LOGE("Failed DBus call [%s]", error->message);
535                 g_error_free(error);
536                 return MESHD_ERROR_IO_ERROR;
537         }
538
539         return MESHD_ERROR_NONE;
540 }
541
542 static void _on_scan_result_destroy(gpointer data)
543 {
544         mesh_scan_result_s *scan_item = (mesh_scan_result_s *)data;
545
546         if (scan_item) {
547                 g_free(scan_item->mesh_id);
548                 g_free(scan_item->bssid);
549                 g_free(scan_item->object_path);
550         }
551 }
552
553 static void _on_peer_info_destroy(gpointer data)
554 {
555         mesh_peer_info_s *peer = (mesh_peer_info_s *)data;
556         if (peer)
557                 g_free(peer->address);
558 }
559
560 static void _get_joined_network(mesh_service *service, GVariant *variant)
561 {
562         GVariantIter *peer = NULL;
563         GVariantIter *property = NULL;
564         gchar *key = NULL;
565         GVariant *val = NULL;
566         gsize len = 0;
567         GVariant *child;
568         const gchar* obj_path = NULL;
569         const gchar* buf = NULL;
570
571         g_variant_get(variant, "(a(oa{sv}))", &peer);
572         while ((child = g_variant_iter_next_value(peer))) {
573                 mesh_network_info_s *joined_info = NULL;
574                 gboolean valid_state = TRUE;
575
576                 g_variant_get(child, "(oa{sv})", &obj_path, &property);
577                 MESH_LOGD("  Object: [%s]", obj_path);
578                 if (NULL == obj_path) {
579                         MESH_LOGE("Null object");
580                         continue;
581                 }
582
583                 /* Create an information structure for joined network */
584                 joined_info = g_try_new0(mesh_network_info_s, 1);
585                 if (NULL == joined_info) {
586                         MESH_LOGE("Failed to allocate !");
587                         return;
588                 }
589
590                 while (g_variant_iter_loop(property, "{sv}", &key, &val)) {
591                         if (strcasecmp(key, "Name") == 0)  {
592                                 buf = g_variant_get_string(val, &len);
593                                 joined_info->mesh_id = g_strdup(buf);
594                         }
595                         else if (strcasecmp(key, "Address") == 0)  {
596                                 buf = g_variant_get_string(val, &len);
597                                 joined_info->bssid = g_strdup(buf);
598                         }
599                         else if (strcasecmp(key, "State") == 0)  {
600                                 buf = g_variant_get_string(val, &len);
601                                 MESH_LOGD("    State : %s", buf);
602
603                                 /* Skip ignorable state */
604                                 if (g_strcmp0(buf, "idle") == 0
605                                         || g_strcmp0(buf, "disconnect") == 0
606                                         || g_strcmp0(buf, "failure") == 0) {
607                                         valid_state = FALSE;
608                                         break;
609                                 } else if (g_strcmp0(buf, "association") == 0) {
610                                         joined_info->state = MESHD_CONNECTION_STATE_ASSOCIATION;
611                                 } else if (g_strcmp0(buf, "configuration") == 0) {
612                                         joined_info->state = MESHD_CONNECTION_STATE_CONFIGURATION;
613                                 } else if (g_strcmp0(buf, "ready") == 0 || g_strcmp0(buf, "online") == 0) {
614                                         joined_info->state = MESHD_CONNECTION_STATE_CONNECTED;
615                                 }
616                         }
617                         else if (strcasecmp(key, "Frequency") == 0)  {
618                                 joined_info->channel = __frequency_to_channel(g_variant_get_uint16(val));
619                         }
620                 }
621
622                 /* Skip ignorable state */
623                 if (FALSE == valid_state) {
624                         g_free(joined_info->mesh_id);
625                         g_free(joined_info->bssid);
626                         continue;
627                 }
628
629                 MESH_LOGD("    Mesh ID : %s", joined_info->mesh_id);
630                 MESH_LOGD("    BSSID   : %s", joined_info->bssid);
631                 MESH_LOGD("    Channel : %d", joined_info->channel);
632                 service->joined_network = joined_info;
633
634                 g_variant_iter_free(property);
635
636                 /* If found, stop loop iteration */
637                 break;
638         }
639         g_variant_iter_free(peer);
640 }
641
642 static void _get_mesh_peers(mesh_service *service, GVariant *variant)
643 {
644         GVariantIter *peer = NULL;
645         GVariantIter *property = NULL;
646         gchar *key = NULL;
647         GVariant *val = NULL;
648         gsize len = 0;
649         GVariant *child;
650         const gchar* obj_path = NULL;
651
652         g_variant_get(variant, "(a(oa{sv}))", &peer);
653         while ((child = g_variant_iter_next_value(peer))) {
654                 mesh_scan_result_s *scan_info = NULL;
655
656                 scan_info = g_try_new0(mesh_scan_result_s, 1);
657                 if (NULL == scan_info) {
658                         MESH_LOGE("Failed to allocate !");
659                         return;
660                 }
661
662                 g_variant_get(child, "(oa{sv})", &obj_path, &property);
663                 if (NULL == obj_path) {
664                         MESH_LOGE("Null object");
665                         g_free(scan_info);
666                         continue;
667                 }
668                 MESH_LOGD("    Obj path : [%s]", obj_path);
669                 scan_info->object_path = g_strdup(obj_path);
670
671                 while (g_variant_iter_loop(property, "{sv}", &key, &val)) {
672                         if (strcasecmp(key, "Name") == 0)  {
673                                 const char *buf = g_variant_get_string(val, &len);
674                                 scan_info->mesh_id = g_strdup(buf);
675                                 MESH_LOGD("    Mesh ID : %s", scan_info->mesh_id);
676                         }
677                         else if (strcasecmp(key, "Address") == 0)  {
678                                 const char *buf = g_variant_get_string(val, &len);
679                                 scan_info->bssid = g_strdup(buf);
680                                 MESH_LOGD("    BSSID : %s", scan_info->bssid);
681                         }
682                         else if (strcasecmp(key, "State") == 0)  {
683                                 const char *buf = g_variant_get_string(val, &len);
684                                 MESH_LOGD("    State : %s", buf);
685
686                                 if (g_strcmp0(buf, "idle") == 0
687                                         || g_strcmp0(buf, "disconnect") == 0
688                                         || g_strcmp0(buf, "failure") == 0) {
689                                         scan_info->state = MESHD_CONNECTION_STATE_DISCONNECTED;
690                                 } else if (g_strcmp0(buf, "association") == 0) {
691                                         scan_info->state = MESHD_CONNECTION_STATE_ASSOCIATION;
692                                 } else if (g_strcmp0(buf, "configuration") == 0) {
693                                         scan_info->state = MESHD_CONNECTION_STATE_CONFIGURATION;
694                                 } else if (g_strcmp0(buf, "ready") == 0 || g_strcmp0(buf, "online") == 0) {
695                                         scan_info->state = MESHD_CONNECTION_STATE_CONNECTED;
696                                 }
697                         }
698                         else if (strcasecmp(key, "Security") == 0)  {
699                                 const char *buf = g_variant_get_string(val, &len);
700                                 MESH_LOGD("    Security : %s", buf);
701                         }
702                         else if (strcasecmp(key, "Frequency") == 0)  {
703                                 scan_info->channel = __frequency_to_channel(g_variant_get_uint16(val));
704                                 MESH_LOGD("    Channel : %d", scan_info->channel);
705                         }
706                         else if (strcasecmp(key, "Favorite") == 0)  {
707                                 const char *buf = g_variant_get_string(val, &len);
708                                 MESH_LOGD("    Favorite : %s", buf);
709                         }
710                         else if (strcasecmp(key, "Strength") == 0)  {
711                                 scan_info->rssi = (gint)g_variant_get_byte(val);
712                                 MESH_LOGD("    RSSI : %d", scan_info->rssi);
713                         }
714                 }
715                 /* Last element */
716                 service->scanned_mesh_network =
717                         g_list_prepend(service->scanned_mesh_network, scan_info);
718
719                 g_variant_iter_free(property);
720         }
721         g_variant_iter_free(peer);
722 }
723
724 static void _get_connected_mesh_peers(mesh_service *service, GVariant *variant)
725 {
726         GVariantIter *peer = NULL;
727         GVariantIter *property = NULL;
728         gchar *key = NULL;
729         GVariant *val = NULL;
730         gsize len = 0;
731         GVariant *child;
732
733         g_variant_get(variant, "(a(a{sv}))", &peer);
734         while ((child = g_variant_iter_next_value(peer))) {
735                 mesh_peer_info_s *peer_info = NULL;
736
737                 peer_info = g_try_new0(mesh_peer_info_s, 1);
738                 if (NULL == peer_info) {
739                         MESH_LOGE("Failed to allocate !");
740                         return;
741                 }
742
743                 g_variant_get(child, "(a{sv})", &property);
744                 while (g_variant_iter_loop(property, "{sv}", &key, &val)) {
745                         if (strcasecmp(key, "PeerAddress") == 0)  {
746                                 const char *buf = g_variant_get_string(val, &len);
747                                 peer_info->address = g_strdup(buf);
748                                 MESH_LOGD("    Address : %s", peer_info->address);
749                         }
750                 }
751                 /* Last element */
752                 service->connected_mesh_peers =
753                         g_list_prepend(service->connected_mesh_peers, peer_info);
754
755                 g_variant_iter_free(property);
756         }
757         g_variant_iter_free(peer);
758 }
759
760 int mesh_ipc_get_mesh_peers(mesh_service *service)
761 {
762         GVariant *variant = NULL;
763         GError *error = NULL;
764
765         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
766         meshd_check_null_ret_error("connection", service->connection,
767                         MESHD_ERROR_INVALID_PARAMETER);
768         meshd_check_null_ret_error("_gproxy_connman",
769                         _gproxy_connman, MESHD_ERROR_IO_ERROR);
770
771         variant = g_dbus_proxy_call_sync(_gproxy_connman, "GetMeshPeers",
772                                 NULL,
773                                 G_DBUS_CALL_FLAGS_NONE,
774                                 -1, NULL, &error);
775         if (variant) {
776                 MESH_LOGD("Successfully requested. [GetMeshPeers]");
777
778                 if (service->scanned_mesh_network) {
779                         g_list_free_full(service->scanned_mesh_network, _on_scan_result_destroy);
780                         service->scanned_mesh_network = NULL;
781                 }
782
783                 _get_mesh_peers(service, variant);
784
785                 /* List item is saved with reversed order for efficiency. */
786                 service->scanned_mesh_network =
787                                 g_list_reverse(service->scanned_mesh_network);
788         } else if (error) {
789                 MESH_LOGE("Failed DBus call [%s]", error->message);
790                 g_error_free(error);
791                 return MESHD_ERROR_IO_ERROR;
792         }
793
794         return MESHD_ERROR_NONE;
795 }
796
797 int mesh_ipc_get_connected_peers(mesh_service *service)
798 {
799         GVariant *variant = NULL;
800         GError *error = NULL;
801
802         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
803         meshd_check_null_ret_error("connection", service->connection,
804                         MESHD_ERROR_INVALID_PARAMETER);
805         meshd_check_null_ret_error("_gproxy_connman",
806                         _gproxy_connman, MESHD_ERROR_IO_ERROR);
807
808         variant = g_dbus_proxy_call_sync(_gproxy_connman, "GetConnectedMeshPeers",
809                                 NULL,
810                                 G_DBUS_CALL_FLAGS_NONE,
811                                 -1, NULL, &error);
812         if (variant) {
813                 MESH_LOGD("Successfully requested. [GetConnectedMeshPeers]");
814
815                 if (service->connected_mesh_peers) {
816                         g_list_free_full(service->connected_mesh_peers, _on_peer_info_destroy);
817                         service->connected_mesh_peers = NULL;
818                 }
819
820                 _get_connected_mesh_peers(service, variant);
821
822                 /* List item is saved with reversed order for efficiency. */
823                 service->connected_mesh_peers =
824                                 g_list_reverse(service->connected_mesh_peers);
825         } else if (error) {
826                 MESH_LOGE("Failed DBus call [%s]", error->message);
827                 g_error_free(error);
828                 return MESHD_ERROR_IO_ERROR;
829         }
830
831         return MESHD_ERROR_NONE;
832 }
833
834 int mesh_ipc_get_joined_mesh_network(mesh_service *service)
835 {
836         GVariant *variant = NULL;
837         GError *error = NULL;
838
839         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
840         meshd_check_null_ret_error("connection", service->connection,
841                         MESHD_ERROR_INVALID_PARAMETER);
842         meshd_check_null_ret_error("_gproxy_connman",
843                         _gproxy_connman, MESHD_ERROR_IO_ERROR);
844
845         variant = g_dbus_proxy_call_sync(_gproxy_connman, "GetMeshPeers",
846                                 NULL,
847                                 G_DBUS_CALL_FLAGS_NONE,
848                                 -1, NULL, &error);
849         if (variant) {
850                 MESH_LOGD("Successfully requested. [GetMeshPeers]");
851
852                 if (service->joined_network) {
853                         g_free(service->joined_network->mesh_id);
854                         g_free(service->joined_network->bssid);
855                         g_free(service->joined_network);
856                         service->joined_network = NULL;
857                 }
858
859                 _get_joined_network(service, variant);
860         } else if (error) {
861                 MESH_LOGE("Failed DBus call [%s]", error->message);
862                 g_error_free(error);
863                 return MESHD_ERROR_IO_ERROR;
864         }
865
866         return MESHD_ERROR_NONE;
867 }
868
869 static void _get_mesh_property(GVariant *variant, mesh_network_info_s *result)
870 {
871         GVariantIter *property = NULL;
872         gchar *key = NULL;
873         GVariant *val = NULL;
874         gsize len = 0;
875
876         MESH_LOGD("Type [%s]", g_variant_get_type_string(variant));
877
878         g_variant_get(variant, "(a{sv})", &property);
879
880         while (g_variant_iter_loop(property, "{sv}", &key, &val)) {
881                 if (strcasecmp(key, "Name") == 0)  {
882                         const char *buf = g_variant_get_string(val, &len);
883                         result->mesh_id = g_strdup(buf);
884                         MESH_LOGD("    Mesh ID : %s", result->mesh_id);
885                 }
886                 else if (strcasecmp(key, "Address") == 0)  {
887                         const char *buf = g_variant_get_string(val, &len);
888                         result->bssid = g_strdup(buf);
889                         MESH_LOGD("    BSSID : %s", result->bssid);
890                 }
891                 else if (strcasecmp(key, "State") == 0)  {
892                         const char *buf = g_variant_get_string(val, &len);
893                         MESH_LOGD("    State : %s", buf);
894
895                         if (g_strcmp0(buf, "idle") == 0
896                                 || g_strcmp0(buf, "disconnect") == 0
897                                 || g_strcmp0(buf, "failure") == 0) {
898                                 result->state = MESHD_CONNECTION_STATE_DISCONNECTED;
899                         } else if (g_strcmp0(buf, "association") == 0) {
900                                 result->state = MESHD_CONNECTION_STATE_ASSOCIATION;
901                         } else if (g_strcmp0(buf, "configuration") == 0) {
902                                 result->state = MESHD_CONNECTION_STATE_CONFIGURATION;
903                         } else if (g_strcmp0(buf, "ready") == 0 || g_strcmp0(buf, "online") == 0) {
904                                 result->state = MESHD_CONNECTION_STATE_CONNECTED;
905                         }
906                 }
907                 else if (strcasecmp(key, "Security") == 0)  {
908                         const char *buf = g_variant_get_string(val, &len);
909                         MESH_LOGD("    Security : %s", buf);
910                 }
911                 else if (strcasecmp(key, "Frequency") == 0)  {
912                         result->channel = __frequency_to_channel(g_variant_get_uint16(val));
913                         MESH_LOGD("    Channel : %d", result->channel);
914                 }
915                 else if (strcasecmp(key, "Favorite") == 0)  {
916                         const char *buf = g_variant_get_string(val, &len);
917                         MESH_LOGD("    Favorite : %s", buf);
918                 }
919                 else if (strcasecmp(key, "Strength") == 0)  {
920                         gint rssi = (gint)g_variant_get_byte(val);
921                         MESH_LOGD("    RSSI : %d", rssi);
922                 }
923         }
924         g_variant_iter_free(property);
925 }
926
927 static int _mesh_ipc_get_mesh_network_property(mesh_service *service,
928         const gchar* object_path, mesh_network_info_s *result)
929 {
930         GVariant *variant = NULL;
931         GError *error = NULL;
932
933         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
934         meshd_check_null_ret_error("connection", service->connection,
935                         MESHD_ERROR_INVALID_PARAMETER);
936         meshd_check_null_ret_error("result", result, MESHD_ERROR_INVALID_PARAMETER);
937
938         variant = g_dbus_connection_call_sync(service->connection,
939                         CONNMAN_SERVER_NAME,
940                         object_path,
941                         CONNMAN_INTERFACE_MESH,
942                         "GetProperties",
943                         NULL, NULL,
944                         G_DBUS_CALL_FLAGS_NONE,
945                         -1, NULL, &error);
946         if (variant) {
947                 MESH_LOGD("Successfully requested. [GetProperties]");
948
949                 /* Get properties */
950                 _get_mesh_property(variant, result);
951         } else if (error) {
952                 LOGE("Failed DBus call [%s]", error->message);
953                 g_error_free(error);
954                 return MESHD_ERROR_IO_ERROR;
955         }
956
957         return MESHD_ERROR_NONE;
958 }
959
960 int mesh_ipc_create_network(mesh_service *service, gchar *mesh_id, gint channel,
961         gint security)
962 {
963         GVariant *variant = NULL;
964         GError *error = NULL;
965         GVariant *var_dict = NULL;
966         GVariantBuilder builder;
967         const gchar* secu = (security == 0) ? "none" : "SAE";
968
969         enum nl80211_band band = (channel <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
970         gushort freq = __channel_to_frequency(channel, band);
971
972         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
973         meshd_check_null_ret_error("connection", service->connection,
974                         MESHD_ERROR_INVALID_PARAMETER);
975         meshd_check_null_ret_error("_gproxy_connman_technology",
976                         _gproxy_connman_technology, MESHD_ERROR_IO_ERROR);
977
978         g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
979         g_variant_builder_open(&builder, G_VARIANT_TYPE("a{sv}"));
980
981         g_variant_builder_open(&builder, G_VARIANT_TYPE("{sv}"));
982         g_variant_builder_add(&builder, "s", "Name");
983         g_variant_builder_add(&builder, "v", g_variant_new_string(mesh_id));
984         g_variant_builder_close(&builder); /* {sv} */
985
986         g_variant_builder_open(&builder, G_VARIANT_TYPE("{sv}"));
987         g_variant_builder_add(&builder, "s", "Frequency");
988         g_variant_builder_add(&builder, "v", g_variant_new_uint16(freq));
989         g_variant_builder_close(&builder); /* {sv} */
990
991         g_variant_builder_open(&builder, G_VARIANT_TYPE("{sv}"));
992         g_variant_builder_add(&builder, "s", "Security");
993         g_variant_builder_add(&builder, "v", g_variant_new_string(secu));
994         g_variant_builder_close(&builder); /* {sv} */
995
996         g_variant_builder_close(&builder); /* a{sv} */
997
998         var_dict = g_variant_builder_end(&builder);
999
1000         variant = g_dbus_proxy_call_sync(_gproxy_connman_technology, "MeshCommands",
1001                                 g_variant_new("(sv)", "MeshCreateNetwork", var_dict),
1002                                 G_DBUS_CALL_FLAGS_NONE,
1003                                 -1, NULL, &error);
1004         if (variant) {
1005                 MESH_LOGD("Successfully requested. [MeshCreateNetwork]");
1006         } else if (error) {
1007                 MESH_LOGE("Failed DBus call [%s]", error->message);
1008                 g_error_free(error);
1009                 return MESHD_ERROR_IO_ERROR;
1010         }
1011
1012         return MESHD_ERROR_NONE;
1013 }
1014 #if 0
1015 static void on_response_connect_network(GObject *source_object,
1016         GAsyncResult *res, gpointer user_data)
1017 {
1018         int ret = MESHD_ERROR_NONE;
1019         GError *error = NULL;
1020         GVariant *variant = NULL;
1021
1022         NOTUSED(user_data);
1023
1024         variant = g_dbus_connection_call_finish(
1025                         G_DBUS_CONNECTION(source_object), res, &error);
1026         if (variant) {
1027                 MESH_LOGD("Successfully requested. [Connect]");
1028
1029                 /* TODO: Unregister property change event */
1030         } else if (error) {
1031                 ret = MESHD_ERROR_IO_ERROR;
1032                 LOGE("Failed DBus call [%s]", error->message);
1033
1034                 if (g_strrstr(error->message, "Already exists"))
1035                         ret = MESHD_ERROR_ALREADY_REGISTERED;
1036                 else if (g_strrstr(error->message, "In progress"))
1037                         ret = MESHD_ERROR_IN_PROGRESS;
1038                 else
1039                         ret = MESHD_ERROR_IO_ERROR;
1040
1041                 g_error_free(error);
1042         }
1043 }
1044 #endif
1045
1046 int mesh_ipc_connect_network(mesh_service *service, mesh_scan_result_s *info)
1047 {
1048         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
1049         meshd_check_null_ret_error("info", info, MESHD_ERROR_INVALID_PARAMETER);
1050
1051         g_dbus_connection_call(service->connection,
1052                         CONNMAN_SERVER_NAME,
1053                         info->object_path,
1054                         CONNMAN_INTERFACE_MESH,
1055                         "Connect",
1056                         NULL, NULL,
1057                         G_DBUS_CALL_FLAGS_NONE,
1058                         -1,
1059                         NULL, /* G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED */
1060                         NULL, NULL);
1061         MESH_LOGD("Successfully requested. [Connect]");
1062
1063         return MESHD_ERROR_NONE;
1064 }
1065
1066 int mesh_ipc_disconnect_network(mesh_service *service, mesh_scan_result_s *info)
1067 {
1068         GVariant *variant = NULL;
1069         GError *error = NULL;
1070
1071         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
1072         meshd_check_null_ret_error("info", info, MESHD_ERROR_INVALID_PARAMETER);
1073
1074         variant = g_dbus_connection_call_sync(service->connection,
1075                         CONNMAN_SERVER_NAME,
1076                         info->object_path,
1077                         CONNMAN_INTERFACE_MESH,
1078                         "Disconnect",
1079                         NULL, NULL,
1080                         G_DBUS_CALL_FLAGS_NONE,
1081                         -1, NULL, &error);
1082         if (variant) {
1083                 MESH_LOGD("Successfully requested. [Disconnect]");
1084         } else if (error) {
1085                 LOGE("Failed DBus call [%s]", error->message);
1086                 g_error_free(error);
1087                 return MESHD_ERROR_IO_ERROR;
1088         }
1089
1090         return MESHD_ERROR_NONE;
1091 }
1092
1093 int mesh_ipc_remove_network(mesh_service *service, mesh_scan_result_s *info)
1094 {
1095         GVariant *variant = NULL;
1096         GError *error = NULL;
1097
1098         meshd_check_null_ret_error("service", service, MESHD_ERROR_INVALID_PARAMETER);
1099         meshd_check_null_ret_error("info", info, MESHD_ERROR_INVALID_PARAMETER);
1100
1101         variant = g_dbus_connection_call_sync(service->connection,
1102                         CONNMAN_SERVER_NAME,
1103                         info->object_path,
1104                         CONNMAN_INTERFACE_MESH,
1105                         "Remove",
1106                         NULL, NULL,
1107                         G_DBUS_CALL_FLAGS_NONE,
1108                         -1, NULL, &error);
1109         if (variant) {
1110                 MESH_LOGD("Successfully requested. [Remove]");
1111         } else if (error) {
1112                 LOGE("Failed DBus call [%s]", error->message);
1113                 g_error_free(error);
1114                 return MESHD_ERROR_IO_ERROR;
1115         }
1116
1117         return MESHD_ERROR_NONE;
1118 }