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