Improve safety of link to event loop
[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 #include <thread>
29
30 #include <libhomescreen.hpp>
31 #include "hmi-debug.h"
32
33 using namespace std;
34
35 static bool has_verb(const string& verb);
36 static const char API[] = "homescreen";
37 static const char ApplicationId[] = "application_id";
38
39 const std::vector<std::string> LibHomeScreen::api_list {
40         std::string("ping"), // debug do not use
41         std::string("tap_shortcut"), // HomeScreen Application only
42         std::string("on_screen_message"),
43         std::string("on_screen_reply"),
44         std::string("subscribe"),
45         std::string("unsubscribe"),
46         std::string("showWindow"),
47         std::string("hideWindow"),
48         std::string("replyShowWindow"),
49         std::string("showNotification"),
50         std::string("showInformation")
51 };
52
53 const std::vector<std::string> LibHomeScreen::event_list {
54 //      std::string("tap_shortcut"),
55         std::string("showWindow"),
56         std::string("on_screen_message"),
57         std::string("on_screen_reply"),
58         std::string("hideWindow"),
59         std::string("replyShowWindow"),
60         std::string("showNotification"),
61         std::string("showInformation"),
62         std::string("none")
63 };
64
65
66 /**
67  * websocket
68  */
69
70 static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
71 {
72         static_cast<LibHomeScreen*>(closure)->on_hangup(NULL,wsj);
73 }
74
75 static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
76 {
77         /* LibHomeScreen is not called from other process */
78 }
79
80 static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
81 {
82         static_cast<LibHomeScreen*>(closure)->on_event(NULL,event,msg);
83 }
84
85 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
86 {
87         static_cast<LibHomeScreen*>(closure)->on_reply(NULL,msg);
88 }
89
90
91 static void event_loop_run(struct sd_event* loop){
92         sd_event_loop(loop);
93         sd_event_unref(loop);
94 }
95
96
97 /**
98  * constructor
99  */
100 LibHomeScreen::LibHomeScreen()
101 {
102 }
103
104 /**
105  * destructor
106  */
107 LibHomeScreen::~LibHomeScreen()
108 {
109         if(sp_websock != NULL)
110         {
111                 afb_wsj1_unref(sp_websock);
112         }
113         if(mploop)
114         {
115                 sd_event_exit(mploop, 0);
116         }
117 }
118
119 /**
120  * This function is initializer
121  *
122  * #### Parameters
123  * - port  [in] : This argument should be specified to the port number to be used for websocket
124  * - token [in] : This argument should be specified to the token to be used for websocket
125  *
126  * #### Return
127  * Nothing
128  *
129  * #### Note
130  * Use this constructor
131  *
132  */
133 int LibHomeScreen::init(const int port, const string& token)
134 {
135         int ret = 0;
136         if(port > 0 && token.size() > 0)
137         {
138                 mport = port;
139                 mtoken = token;
140         }
141         else
142         {
143                 HMI_ERROR("libhomescreen","port and token should be > 0, Initial port and token uses.");
144         }
145
146         ret = initialize_websocket();
147         if(ret != 0 )
148         {
149                 HMI_ERROR("libhomescreen","Failed to initialize websocket");
150         }
151         else{
152                 HMI_DEBUG("libhomescreen","Initialized");
153         }
154
155         return ret;
156 }
157
158 /**
159  * This function register callback function for reply/event message from home screen
160  *
161  * #### Parameters
162  * - event_cb [in] : This argument should be specified to the callback for subscribed event
163  * - reply_cb [in] : This argument should be specified to the reply callback for call function
164  *
165  * #### Return
166  * Nothing
167  *
168  * #### Note
169  * Event callback is invoked by home screen for event you subscribed.
170  * If you would like to get event, please call subscribe function before/after this function
171  */
172 void LibHomeScreen::registerCallback(
173         void (*event_cb)(const std::string& event, struct json_object* event_contents),
174         void (*reply_cb)(struct json_object* reply_contents),
175         void (*hangup_cb)(void))
176 {
177         onEvent = event_cb;
178         onReply = reply_cb;
179         onHangup = hangup_cb;
180 }
181
182 int LibHomeScreen::initialize_websocket()
183 {
184         mploop = NULL;
185         onEvent = nullptr;
186         onReply = nullptr;
187         int ret = sd_event_new(&mploop);
188         if(ret < 0)
189         {
190                 HMI_ERROR("libhomescreen","Failed to create event loop");
191                 goto END;
192         }
193
194         {
195                 // enforce context to avoid initialization/goto error
196                 std::thread th(event_loop_run, mploop);
197                 th.detach();
198         }
199
200         /* Initialize interface from websocket */
201         minterface.on_hangup = _on_hangup_static;
202         minterface.on_call = _on_call_static;
203         minterface.on_event = _on_event_static;
204         muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
205         sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
206         if(sp_websock == NULL)
207         {
208                 HMI_ERROR("libhomescreen","Failed to create websocket connection");
209                 goto END;
210         }
211
212         /* creates the evsrc */
213         //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
214
215         return 0;
216 END:
217         return -1;
218 }
219
220 /**
221  * Sending ShortCut Icon tapped event
222  *
223  * When HomeScreen shortcut area is tapped, sending a event
224  *
225  * #### Parameters
226  * - application_id [in] : Tapped application id (label)
227  *
228  * #### Return
229  * - Returns 0 on success or -1 in case of error.
230  */
231 int LibHomeScreen::tapShortcut(const char* application_id)
232 {
233         struct json_object* obj = json_object_new_object();
234         struct json_object* val = json_object_new_string("normal");
235         json_object_object_add(obj, "area", val);
236
237         return showWindow(application_id, obj);
238 }
239
240 /**
241  * Sending onScreen message event
242  *
243  * Sending OnScreen message event to HomeScreen from applications
244  *
245  * #### Parameters
246  * - display_message [in] : message for display
247  *
248  * #### Return
249  * - Returns 0 on success or -1 in case of error.
250  */
251 int LibHomeScreen::onScreenMessage(const char* display_message)
252 {
253         if(!sp_websock)
254         {
255                 return -1;
256         }
257
258         struct json_object* j_obj = json_object_new_object();
259         struct json_object* val = json_object_new_string(display_message);
260         json_object_object_add(j_obj, "display_message", val);
261         return this->call("on_screen_message", j_obj);
262 }
263
264 /**
265  * Sending onScreen reply event
266  *
267  * Sending OnScreen reply event to applications from HomeScreen
268  *
269  * #### Parameters
270  * - reply_message [in] : message for reply
271  *
272  * #### Return
273  * - Returns 0 on success or -1 in case of error.
274  */
275 int LibHomeScreen::onScreenReply(const char* reply_message)
276 {
277         if(!sp_websock)
278         {
279                 return -1;
280         }
281
282         struct json_object* j_obj = json_object_new_object();
283         struct json_object* val = json_object_new_string(reply_message);
284         json_object_object_add(j_obj, "reply_message", val);
285         return this->call("on_screen_reply", j_obj);
286 }
287
288 /**
289  * Setting Event Handler
290  *
291  * Setting event handler for Homescreen
292  *
293  * #### Parameters
294  * - et [in] : event name
295  * - f [in] : event handler
296  *
297  * #### Return
298  * Nothing
299  *
300  * #### Note
301  * Don't release json_object by json_object_put in handler_func.
302  * The resource is released by libafbwsc library.
303  */
304 void LibHomeScreen::set_event_handler(enum EventType et, handler_func f)
305 {
306         if (et >= 1 && et <= 7) {
307                 switch (et) {
308                         case Event_ShowWindow:
309                                 this->subscribe(LibHomeScreen::event_list[0]);
310                                 break;
311                         case Event_OnScreenMessage:
312                                 this->subscribe(LibHomeScreen::event_list[1]);
313                                 break;
314                         case Event_OnScreenReply:
315                                 this->subscribe(LibHomeScreen::event_list[2]);
316                                 break;
317                         case Event_HideWindow:
318                                 this->subscribe(LibHomeScreen::event_list[3]);
319                                 break;
320                         case Event_ReplyShowWindow:
321                                 this->subscribe(LibHomeScreen::event_list[4]);
322                                 break;
323                         case Event_ShowNotification:
324                                 this->subscribe(LibHomeScreen::event_list[5]);
325                                 break;
326                         case Event_ShowInformation:
327                                 this->subscribe(LibHomeScreen::event_list[6]);
328                                 break;
329                 }
330
331                 this->handlers[et] = std::move(f);
332         }
333 }
334
335 /**
336  * This function calls the API of HomeScreen via WebSocket
337  *
338  * #### Parameters
339  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
340  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
341  *
342  * #### Return
343  * - Returns 0 on success or -1 in case of error.
344  *
345  * #### Note
346  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
347  *
348  */
349 int LibHomeScreen::call(const string& verb, struct json_object* arg)
350 {
351         int ret;
352         if(!sp_websock)
353         {
354                 return -1;
355         }
356         if (!has_verb(verb))
357         {
358                 HMI_ERROR("libhomescreen","verb doesn't exit");
359                 return -1;
360         }
361         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
362         if (ret < 0) {
363                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb.c_str());
364         }
365         return ret;
366 }
367
368 /**
369  * This function calls the API of HomeScreen via WebSocket
370  * This function is overload function of "call"
371  *
372  * #### Parameters
373  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
374  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
375  *
376  * #### Return
377  * - Returns 0 on success or -1 in case of error.
378  *
379  * #### Note
380  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
381  *
382  */
383 int LibHomeScreen::call(const char* verb, struct json_object* arg)
384 {
385         int ret;
386         if(!sp_websock)
387         {
388                 return -1;
389         }
390         if (!has_verb(string(verb)))
391         {
392                 HMI_ERROR("libhomescreen","verb doesn't exit");
393                 return -1;
394         }
395         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
396         if (ret < 0) {
397                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb);
398         }
399         return ret;
400 }
401
402 /**
403  * Register callback function for each event
404  *
405  * #### Parameters
406  * - event_name [in] : This argument should be specified to the event name
407  *
408  * #### Return
409  * - Returns 0 on success or -1 in case of error.
410  *
411  * #### Note
412  * This function enables to get an event to your callback function.
413  *
414  */
415 int LibHomeScreen::subscribe(const string& event_name)
416 {
417         if(!sp_websock)
418         {
419                 return -1;
420         }
421         struct json_object* j_obj = json_object_new_object();
422         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
423
424         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
425         if (ret < 0) {
426                 HMI_ERROR("libhomescreen","Failed to call verb");
427         }
428         return ret;
429 }
430
431 /**
432  * Unregister callback function for each event
433  *
434  * #### Parameters
435  * - event_name [in] : This argument should be specified to the event name
436  *
437  * #### Return
438  * - Returns 0 on success or -1 in case of error.
439  *
440  * #### Note
441  * This function disables to get an event to your callback function.
442  *
443  */
444 int LibHomeScreen::unsubscribe(const string& event_name)
445 {
446         if(!sp_websock)
447         {
448                 return -1;
449         }
450         struct json_object* j_obj = json_object_new_object();
451         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
452
453         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
454         if (ret < 0) {
455                 HMI_ERROR("libhomescreen","Failed to call verb");
456         }
457         return ret;
458 }
459
460 /**
461  * Sending show window event
462  *
463  * Call HomeScreen Service's showWindow verb to request display id's screen.
464  *
465  * #### Parameters
466  * - application_id [in] : This argument should be specified to the application's id.
467  * - json [in] : This argument should be specified to the json parameters.
468  *
469  * #### Return
470  * - Returns 0 on success or -1 in case of error.
471  *
472  */
473 int LibHomeScreen::showWindow(const char* application_id, json_object* json)
474 {
475         if(!sp_websock)
476         {
477                 return -1;
478         }
479
480         struct json_object* j_obj = json_object_new_object();
481         struct json_object* val = json_object_new_string(application_id);
482         json_object_object_add(j_obj, ApplicationId, val);
483
484         if (json == nullptr) {
485                 struct json_object* j_json = json_object_new_object();
486                 struct json_object* value = json_object_new_string("normal");
487                 json_object_object_add(j_json, "area", value);
488                 json_object_object_add(j_obj, "parameter", j_json);
489         }
490         else {
491                 json_object_object_add(j_obj, "parameter", json);
492         }
493
494         return this->call("showWindow", j_obj);
495 }
496
497 /**
498  * Sending hide window event
499  *
500  * Call HomeScreen Service's hideWindow verb to release id's screen.
501  *
502  * #### Parameters
503  * - application_id [in] : This argument should be specified to the application's id.
504  *
505  * #### Return
506  * - Returns 0 on success or -1 in case of error.
507  *
508  */
509 int LibHomeScreen::hideWindow(const char* application_id)
510 {
511         if(!sp_websock)
512         {
513                 return -1;
514         }
515
516         struct json_object* j_obj = json_object_new_object();
517         struct json_object* val = json_object_new_string(application_id);
518         json_object_object_add(j_obj, ApplicationId, val);
519
520         return this->call("hideWindow", j_obj);
521 }
522
523 /**
524  * Sending reply onscreen message event
525  *
526  * Call HomeScreen Service's replyShowWindow verb to reply onscreen message.
527  *
528  * #### Parameters
529  * - application_id [in] : This argument should be specified to the onscreen reply to applilcation id.
530  * - json [in] : This argument should be specified to the json parameters.
531  *
532  * #### Return
533  * - Returns 0 on success or -1 in case of error.
534  *
535  */
536 int LibHomeScreen::replyShowWindow(const char* application_id, json_object* json)
537 {
538         if(!sp_websock)
539         {
540                 return -1;
541         }
542
543         if (json == nullptr) {
544                 HMI_WARNING("libhomescreen", "replyShowWindow`s parameter is null");
545                 return -1;
546         }
547
548         struct json_object* j_obj = json_object_new_object();
549         struct json_object* val = json_object_new_string(application_id);
550         json_object_object_add(j_obj, ApplicationId, val);
551         json_object_object_add(j_obj, "parameter", json);
552
553         return this->call("replyShowWindow", j_obj);
554 }
555
556 /**
557  * Sending show notification event
558  *
559  * Call HomeScreen Service's notification verb to show notification on Status Bar.
560  *
561  * #### Parameters
562  * - json [in] : This argument should be specified to the json parameters.
563  *
564  * #### Return
565  * - Returns 0 on success or -1 in case of error.
566  *
567  */
568 int LibHomeScreen::showNotification(json_object* json)
569 {
570         if(!sp_websock)
571         {
572                 return -1;
573         }
574
575         return this->call("showNotification", json);
576 }
577
578 /**
579  * Sending show information event
580  *
581  * Call HomeScreen Service's information verb to show notification on Information Bar.
582  *
583  * #### Parameters
584  * - json [in] : This argument should be specified to the json parameters.
585  *
586  * #### Return
587  * - Returns 0 on success or -1 in case of error.
588  *
589  */
590 int LibHomeScreen::showInformation(json_object* json)
591 {
592         if(!sp_websock)
593         {
594                 return -1;
595         }
596
597         return this->call("showInformation", json);
598 }
599
600
601 /************* Callback Function *************/
602
603 void LibHomeScreen::on_hangup(void *closure, struct afb_wsj1 *wsj)
604 {
605         HMI_DEBUG("libhomescreen","called");
606         if(onHangup != nullptr)
607         {
608                 onHangup();
609         }
610 }
611
612 void LibHomeScreen::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
613 {
614 }
615
616 /*
617 * event is like "homescreen/hvac"
618 * msg is like {"event":"homescreen\/hvac","data":{"type":"tap_shortcut"},"jtype":"afb-event"}
619 * so you can get
620         event name : struct json_object obj = json_object_object_get(msg,"event")
621 */
622 void LibHomeScreen::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
623 {
624         HMI_DEBUG("libhomescreen","event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg));
625
626         if (strstr(event, API) == NULL) {
627                 return;
628         }
629
630         struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
631         struct json_object *json_data;
632         if(!json_object_object_get_ex(ev_contents, "data", &json_data)) {
633                 HMI_ERROR("libhomescreen", "got ev_contents error.");
634                 return;
635         }
636
637         if(onEvent != nullptr)
638         {
639                 const string ev(event);
640                 onEvent(ev, ev_contents);
641         }
642
643         const char* event_type = nullptr;
644         struct json_object *json_event_type;
645         if(json_object_object_get_ex(json_data, "type", &json_event_type)) {
646                 event_type = json_object_get_string(json_event_type);
647         }
648         else {
649                 HMI_WARNING("libhomescreen","event_type is null.");
650                 return;
651         }
652
653         if (strcasecmp(event_type, LibHomeScreen::event_list[0].c_str()) == 0) {
654                 auto i = this->handlers.find(Event_ShowWindow);
655                 if ( i != this->handlers.end() ) {
656                         i->second(json_data);
657                 }
658         }
659         else if (strcasecmp(event_type, LibHomeScreen::event_list[1].c_str()) == 0) {
660                 auto i = this->handlers.find(Event_OnScreenMessage);
661                 if ( i != this->handlers.end() ) {
662                         i->second(json_data);
663                 }
664         }
665         else if (strcasecmp(event_type, LibHomeScreen::event_list[2].c_str()) == 0) {
666                 auto i = this->handlers.find(Event_OnScreenReply);
667                 if ( i != this->handlers.end() ) {
668                         i->second(json_data);
669                 }
670         }
671         else if (strcasecmp(event_type, LibHomeScreen::event_list[3].c_str()) == 0) {
672                 auto i = this->handlers.find(Event_HideWindow);
673                 if ( i != this->handlers.end() ) {
674                         i->second(json_data);
675                 }
676         }
677         else if (strcasecmp(event_type, LibHomeScreen::event_list[4].c_str()) == 0) {
678                 auto i = this->handlers.find(Event_ReplyShowWindow);
679                 if ( i != this->handlers.end() ) {
680                         i->second(json_data);
681                 }
682         }
683         else if (strcasecmp(event_type, LibHomeScreen::event_list[5].c_str()) == 0) {
684                 auto i = this->handlers.find(Event_ShowNotification);
685                 if ( i != this->handlers.end() ) {
686                         i->second(json_data);
687                 }
688         }
689         else if (strcasecmp(event_type, LibHomeScreen::event_list[6].c_str()) == 0) {
690                 auto i = this->handlers.find(Event_ShowInformation);
691                 if ( i != this->handlers.end() ) {
692                         i->second(json_data);
693                 }
694         }
695 }
696
697 /**
698  * msg is like ({"response":{"verb":"subscribe","error":0},"jtype":"afb-reply","request":{"status":"success","info":"homescreen binder subscribe event name [on_screen_message]"}})
699  * msg is like ({"response":{"verb":"tap_shortcut","error":0},"jtype":"afb-reply","request":{"status":"success","info":"afb_event_push event [tap_shortcut]"}})
700  */
701 void LibHomeScreen::on_reply(void *closure, struct afb_wsj1_msg *msg)
702 {
703         HMI_DEBUG("libhomescreen","msg: (%s)", afb_wsj1_msg_object_s(msg));
704         if(onReply != nullptr)
705         {
706                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
707                 onReply(reply);
708         }
709 }
710
711 static bool has_verb(const string& verb)
712 {
713         HMI_DEBUG("libhomescreen","verb is %s", verb.c_str());
714         if(find(LibHomeScreen::api_list.begin(), LibHomeScreen::api_list.end(), verb) != LibHomeScreen::api_list.end())
715                 return true;
716         else
717                 return false;
718 }