Modify function argument from char to json
[src/libhomescreen.git] / src / libhomescreen.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
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 <stdarg.h>
18 #include <sys/socket.h>
19 #include <iostream>
20 #include <algorithm>
21 #include <errno.h>
22 #include <cassert>
23 #include <cctype>
24 #include <cerrno>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
28
29 #include <libhomescreen.hpp>
30 #include "hmi-debug.h"
31
32 using namespace std;
33
34 static bool has_verb(const string& verb);
35 static const char API[] = "homescreen";
36
37 const std::vector<std::string> LibHomeScreen::api_list {
38         std::string("ping"), // debug do not use
39         std::string("tap_shortcut"), // HomeScreen Application only
40         std::string("on_screen_message"),
41         std::string("subscribe"),
42         std::string("unsubscribe")
43 };
44
45 const std::vector<std::string> LibHomeScreen::event_list {
46         std::string("tap_shortcut"),
47         std::string("on_screen_message"),
48         std::string("none")
49 };
50
51
52 /**
53  * websocket
54  */
55
56 static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
57 {
58         static_cast<LibHomeScreen*>(closure)->on_hangup(NULL,wsj);
59 }
60
61 static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
62 {
63         /* LibHomeScreen is not called from other process */
64 }
65
66 static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
67 {
68         static_cast<LibHomeScreen*>(closure)->on_event(NULL,event,msg);
69 }
70
71 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
72 {
73         static_cast<LibHomeScreen*>(closure)->on_reply(NULL,msg);
74 }
75
76
77 /**
78  * constructor
79  */
80 LibHomeScreen::LibHomeScreen()
81 {
82 }
83
84 /**
85  * destructor
86  */
87 LibHomeScreen::~LibHomeScreen()
88 {
89         if(mploop)
90         {
91                 sd_event_unref(mploop);
92         }
93         if(sp_websock != NULL)
94         {
95                 afb_wsj1_unref(sp_websock);
96         }
97 }
98
99 /**
100  * This function is initializer
101  *
102  * #### Parameters
103  * - port  [in] : This argument should be specified to the port number to be used for websocket
104  * - token [in] : This argument should be specified to the token to be used for websocket
105  *
106  * #### Return
107  * Nothing
108  *
109  * #### Note
110  * Use this constructor
111  *
112  */
113 int LibHomeScreen::init(const int port, const string& token)
114 {
115         int ret = 0;
116         if(port > 0 && token.size() > 0)
117         {
118                 mport = port;
119                 mtoken = token;
120         }
121         else
122         {
123                 HMI_ERROR("libhomescreen","port and token should be > 0, Initial port and token uses.");
124         }
125
126         ret = initialize_websocket();
127         if(ret != 0 )
128         {
129                 HMI_ERROR("libhomescreen","Failed to initialize websocket");
130         }
131         else{
132                 HMI_DEBUG("libhomescreen","Initialized");
133         }
134
135         return ret;
136 }
137
138 /**
139  * This function register callback function for reply/event message from home screen
140  *
141  * #### Parameters
142  * - event_cb [in] : This argument should be specified to the callback for subscribed event
143  * - reply_cb [in] : This argument should be specified to the reply callback for call function
144  *
145  * #### Return
146  * Nothing
147  *
148  * #### Note
149  * Event callback is invoked by home screen for event you subscribed.
150  * If you would like to get event, please call subscribe function before/after this function
151  */
152 void LibHomeScreen::registerCallback(
153         void (*event_cb)(const std::string& event, struct json_object* event_contents),
154         void (*reply_cb)(struct json_object* reply_contents),
155         void (*hangup_cb)(void))
156 {
157         onEvent = event_cb;
158         onReply = reply_cb;
159         onHangup = hangup_cb;
160 }
161
162 int LibHomeScreen::initialize_websocket()
163 {
164         mploop = NULL;
165         onEvent = nullptr;
166         onReply = nullptr;
167         int ret = sd_event_default(&mploop);
168         if(ret < 0)
169         {
170                 HMI_ERROR("libhomescreen","Failed to create event loop");
171                 goto END;
172         }
173
174         /* Initialize interface from websocket */
175         minterface.on_hangup = _on_hangup_static;
176         minterface.on_call = _on_call_static; /* Is this necessary? */
177         minterface.on_event = _on_event_static;
178         muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
179         sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
180         if(sp_websock == NULL)
181         {
182                 HMI_ERROR("libhomescreen","Failed to create websocket connection");
183                 goto END;
184         }
185
186         /* creates the evsrc */
187         //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
188
189         return 0;
190 END:
191         if(mploop)
192         {
193                 sd_event_unref(mploop);
194         }
195         return -1;
196 }
197
198 /**
199  * Sending ShortCut Icon tapped event
200  *
201  * When HomeScreen shortcut area is tapped, sending a event
202  *
203  * #### Parameters
204  * - application_name [in] : Tapped application name (label)
205  *
206  * #### Return
207  * - Returns 0 on success or -1 in case of error.
208  */
209 int LibHomeScreen::tapShortcut(const char* application_name)
210 {
211         if(!sp_websock)
212         {
213                 return -1;
214         }
215
216         struct json_object* j_obj = json_object_new_object();
217         struct json_object* val = json_object_new_string(application_name);
218         json_object_object_add(j_obj, "application_name", val);
219         return this->call("tap_shortcut", j_obj);
220 }
221
222 /**
223  * Sending onScreen message event
224  *
225  * Sending OnScreen message event to HomeScreen from applications
226  *
227  * #### Parameters
228  * - display_message [in] : message for display
229  *
230  * #### Return
231  * - Returns 0 on success or -1 in case of error.
232  */
233 int LibHomeScreen::onScreenMessage(const char* display_message)
234 {
235         if(!sp_websock)
236         {
237                 return -1;
238         }
239
240         struct json_object* j_obj = json_object_new_object();
241         struct json_object* val = json_object_new_string(display_message);
242         json_object_object_add(j_obj, "display_message", val);
243         return this->call("on_screen_message", j_obj);
244 }
245
246 /**
247  * Setting Event Handler
248  *
249  * Setting event handler for Homescreen
250  *
251  * #### Parameters
252  * - et [in] : event name
253  * - f [in] : event handler
254  *
255  * #### Return
256  * Nothing
257  */
258 void LibHomeScreen::set_event_handler(enum EventType et, handler_func f)
259 {
260         if (et >= 1 && et <= 2) {
261                 switch (et) {
262                         case Event_TapShortcut:
263                                 this->subscribe(LibHomeScreen::event_list[0]);
264                                 break;
265                         case Event_OnScreenMessage:
266                                 this->subscribe(LibHomeScreen::event_list[1]);
267                                 break;
268                 }
269
270                 this->handlers[et] = std::move(f);
271         }
272 }
273
274 /**
275  * This function calls the API of HomeScreen via WebSocket
276  *
277  * #### Parameters
278  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
279  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
280  *
281  * #### Return
282  * - Returns 0 on success or -1 in case of error.
283  *
284  * #### Note
285  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
286  *
287  */
288 int LibHomeScreen::call(const string& verb, struct json_object* arg)
289 {
290         int ret;
291         if(!sp_websock)
292         {
293                 return -1;
294         }
295         if (!has_verb(verb))
296         {
297                 HMI_ERROR("libhomescreen","verb doesn't exit");
298                 return -1;
299         }
300         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
301         if (ret < 0) {
302                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb.c_str());
303         }
304         return ret;
305 }
306
307 /**
308  * This function calls the API of HomeScreen via WebSocket
309  * This function is overload function of "call"
310  *
311  * #### Parameters
312  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
313  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
314  *
315  * #### Return
316  * - Returns 0 on success or -1 in case of error.
317  *
318  * #### Note
319  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
320  *
321  */
322 int LibHomeScreen::call(const char* verb, struct json_object* arg)
323 {
324         int ret;
325         if(!sp_websock)
326         {
327                 return -1;
328         }
329         if (!has_verb(string(verb)))
330         {
331                 HMI_ERROR("libhomescreen","verb doesn't exit");
332                 return -1;
333         }
334         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
335         if (ret < 0) {
336                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb);
337         }
338         return ret;
339 }
340
341 /**
342  * Register callback function for each event
343  *
344  * #### Parameters
345  * - event_name [in] : This argument should be specified to the event name
346  *
347  * #### Return
348  * - Returns 0 on success or -1 in case of error.
349  *
350  * #### Note
351  * This function enables to get an event to your callback function.
352  *
353  */
354 int LibHomeScreen::subscribe(const string& event_name)
355 {
356         if(!sp_websock)
357         {
358                 return -1;
359         }
360         struct json_object* j_obj = json_object_new_object();
361         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
362
363         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
364         if (ret < 0) {
365                 HMI_ERROR("libhomescreen","Failed to call verb");
366         }
367         return ret;
368 }
369
370 /**
371  * Unregister callback function for each event
372  *
373  * #### Parameters
374  * - event_name [in] : This argument should be specified to the event name
375  *
376  * #### Return
377  * - Returns 0 on success or -1 in case of error.
378  *
379  * #### Note
380  * This function disables to get an event to your callback function.
381  *
382  */
383 int LibHomeScreen::unsubscribe(const string& event_name)
384 {
385         if(!sp_websock)
386         {
387                 return -1;
388         }
389         struct json_object* j_obj = json_object_new_object();
390         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
391
392         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
393         if (ret < 0) {
394                 HMI_ERROR("libhomescreen","Failed to call verb");
395         }
396         return ret;
397 }
398
399 /************* Callback Function *************/
400
401 void LibHomeScreen::on_hangup(void *closure, struct afb_wsj1 *wsj)
402 {
403         HMI_DEBUG("libhomescreen","called");
404         if(onHangup != nullptr)
405         {
406                 onHangup();
407         }
408 }
409
410 void LibHomeScreen::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
411 {
412 }
413
414 /*
415 * event is like "homescreen/tap_shortcut"
416 * msg is like {"event":"homescreen\/tap_shortcut","data":{"application_name":"hoge"},"jtype":"afb-event"}
417 * so you can get
418         event name : struct json_object obj = json_object_object_get(msg,"event")
419 */
420 void LibHomeScreen::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
421 {
422         HMI_DEBUG("libhomescreen","event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg));
423
424         if (strstr(event, API) == NULL) {
425                 return;
426         }
427
428         struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
429         struct json_object *json_data = json_object_object_get(ev_contents, "data");
430
431         if(onEvent != nullptr)
432         {
433                 const string ev(event);
434                 onEvent(ev, ev_contents);
435         }
436
437         const char* event_only = strchr(event, '/');
438         if (event_only != nullptr) {
439                 event_only = event_only + 1;
440         }
441
442         if (strcasecmp(event_only, LibHomeScreen::event_list[0].c_str()) == 0) {
443                 auto i = this->handlers.find(Event_TapShortcut);
444                 if ( i != this->handlers.end() ) {
445                         i->second(json_data);
446                 }
447         }
448         else if (strcasecmp(event_only, LibHomeScreen::event_list[1].c_str()) == 0) {
449                 auto i = this->handlers.find(Event_OnScreenMessage);
450                 if ( i != this->handlers.end() ) {
451                         i->second(json_data);
452                 }
453         }
454
455         json_object_put(ev_contents);
456 }
457
458 /**
459  * msg is like ({"response":{"verb":"subscribe","error":0},"jtype":"afb-reply","request":{"status":"success","info":"homescreen binder subscribe event name [on_screen_message]"}})
460  * msg is like ({"response":{"verb":"tap_shortcut","error":0},"jtype":"afb-reply","request":{"status":"success","info":"afb_event_push event [tap_shortcut]"}})
461  */
462 void LibHomeScreen::on_reply(void *closure, struct afb_wsj1_msg *msg)
463 {
464         HMI_DEBUG("libhomescreen","msg: (%s)", afb_wsj1_msg_object_s(msg));
465         if(onReply != nullptr)
466         {
467                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
468                 onReply(reply);
469
470                 json_object_put(reply);
471         }
472 }
473
474 static bool has_verb(const string& verb)
475 {
476         HMI_DEBUG("libhomescreen","verb is %s", verb.c_str());
477         if(find(LibHomeScreen::api_list.begin(), LibHomeScreen::api_list.end(), verb) != LibHomeScreen::api_list.end())
478                 return true;
479         else
480                 return false;
481 }