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