71afa6626f2949661c0ed9f151320214531086b5
[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                 this->handlers[et] = std::move(f);
311         }
312 }
313
314 /**
315  * This function subscribe HomeScreen event
316  *
317  * #### Parameters
318  * None
319  *
320  * #### Return
321  * - Nothing
322  *
323  * #### Note
324  * To call HomeScreen's subscribe APIs.
325  *
326  */
327 void LibHomeScreen::publishSubscription(void)
328 {
329         for(auto &it : handlers) {
330                 this->subscribe(LibHomeScreen::event_list[it.first - 1]);
331         }
332 }
333
334 /**
335  * This function calls the API of HomeScreen via WebSocket
336  *
337  * #### Parameters
338  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
339  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
340  *
341  * #### Return
342  * - Returns 0 on success or -1 in case of error.
343  *
344  * #### Note
345  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
346  *
347  */
348 int LibHomeScreen::call(const string& verb, struct json_object* arg)
349 {
350         int ret;
351         if(!sp_websock)
352         {
353                 return -1;
354         }
355         if (!has_verb(verb))
356         {
357                 HMI_ERROR("libhomescreen","verb doesn't exit");
358                 return -1;
359         }
360         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
361         if (ret < 0) {
362                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb.c_str());
363         }
364         return ret;
365 }
366
367 /**
368  * This function calls the API of HomeScreen via WebSocket
369  * This function is overload function of "call"
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 char* verb, struct json_object* arg)
383 {
384         int ret;
385         if(!sp_websock)
386         {
387                 return -1;
388         }
389         if (!has_verb(string(verb)))
390         {
391                 HMI_ERROR("libhomescreen","verb doesn't exit");
392                 return -1;
393         }
394         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
395         if (ret < 0) {
396                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb);
397         }
398         return ret;
399 }
400
401 /**
402  * Register callback function for each event
403  *
404  * #### Parameters
405  * - event_name [in] : This argument should be specified to the event name
406  *
407  * #### Return
408  * - Returns 0 on success or -1 in case of error.
409  *
410  * #### Note
411  * This function enables to get an event to your callback function.
412  *
413  */
414 int LibHomeScreen::subscribe(const string& event_name)
415 {
416         if(!sp_websock)
417         {
418                 return -1;
419         }
420         struct json_object* j_obj = json_object_new_object();
421         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
422
423         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
424         if (ret < 0) {
425                 HMI_ERROR("libhomescreen","Failed to call verb");
426         }
427         return ret;
428 }
429
430 /**
431  * Unregister callback function for each event
432  *
433  * #### Parameters
434  * - event_name [in] : This argument should be specified to the event name
435  *
436  * #### Return
437  * - Returns 0 on success or -1 in case of error.
438  *
439  * #### Note
440  * This function disables to get an event to your callback function.
441  *
442  */
443 int LibHomeScreen::unsubscribe(const string& event_name)
444 {
445         if(!sp_websock)
446         {
447                 return -1;
448         }
449         struct json_object* j_obj = json_object_new_object();
450         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
451
452         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
453         if (ret < 0) {
454                 HMI_ERROR("libhomescreen","Failed to call verb");
455         }
456         return ret;
457 }
458
459 /**
460  * Sending show window event
461  *
462  * Call HomeScreen Service's showWindow verb to request display id's screen.
463  *
464  * #### Parameters
465  * - application_id [in] : This argument should be specified to the application's id.
466  * - json [in] : This argument should be specified to the json parameters.
467  *
468  * #### Return
469  * - Returns 0 on success or -1 in case of error.
470  *
471  */
472 int LibHomeScreen::showWindow(const char* application_id, json_object* json)
473 {
474         if(!sp_websock)
475         {
476                 return -1;
477         }
478
479         struct json_object* j_obj = json_object_new_object();
480         struct json_object* val = json_object_new_string(application_id);
481         json_object_object_add(j_obj, ApplicationId, val);
482
483         if (json == nullptr) {
484                 struct json_object* j_json = json_object_new_object();
485                 struct json_object* value = json_object_new_string("normal");
486                 json_object_object_add(j_json, "area", value);
487                 json_object_object_add(j_obj, "parameter", j_json);
488         }
489         else {
490                 json_object_object_add(j_obj, "parameter", json);
491         }
492
493         return this->call("showWindow", j_obj);
494 }
495
496 /**
497  * Sending hide window event
498  *
499  * Call HomeScreen Service's hideWindow verb to release id's screen.
500  *
501  * #### Parameters
502  * - application_id [in] : This argument should be specified to the application's id.
503  *
504  * #### Return
505  * - Returns 0 on success or -1 in case of error.
506  *
507  */
508 int LibHomeScreen::hideWindow(const char* application_id)
509 {
510         if(!sp_websock)
511         {
512                 return -1;
513         }
514
515         struct json_object* j_obj = json_object_new_object();
516         struct json_object* val = json_object_new_string(application_id);
517         json_object_object_add(j_obj, ApplicationId, val);
518
519         return this->call("hideWindow", j_obj);
520 }
521
522 /**
523  * Sending reply onscreen message event
524  *
525  * Call HomeScreen Service's replyShowWindow verb to reply onscreen message.
526  *
527  * #### Parameters
528  * - application_id [in] : This argument should be specified to the onscreen reply to applilcation id.
529  * - json [in] : This argument should be specified to the json parameters.
530  *
531  * #### Return
532  * - Returns 0 on success or -1 in case of error.
533  *
534  */
535 int LibHomeScreen::replyShowWindow(const char* application_id, json_object* json)
536 {
537         if(!sp_websock)
538         {
539                 return -1;
540         }
541
542         if (json == nullptr) {
543                 HMI_WARNING("libhomescreen", "replyShowWindow`s parameter is null");
544                 return -1;
545         }
546
547         struct json_object* j_obj = json_object_new_object();
548         struct json_object* val = json_object_new_string(application_id);
549         json_object_object_add(j_obj, ApplicationId, val);
550         json_object_object_add(j_obj, "parameter", json);
551
552         return this->call("replyShowWindow", j_obj);
553 }
554
555 /**
556  * Sending show notification event
557  *
558  * Call HomeScreen Service's notification verb to show notification on Status Bar.
559  *
560  * #### Parameters
561  * - json [in] : This argument should be specified to the json parameters.
562  *
563  * #### Return
564  * - Returns 0 on success or -1 in case of error.
565  *
566  */
567 int LibHomeScreen::showNotification(json_object* json)
568 {
569         if(!sp_websock)
570         {
571                 return -1;
572         }
573
574         return this->call("showNotification", json);
575 }
576
577 /**
578  * Sending show information event
579  *
580  * Call HomeScreen Service's information verb to show notification on Information Bar.
581  *
582  * #### Parameters
583  * - json [in] : This argument should be specified to the json parameters.
584  *
585  * #### Return
586  * - Returns 0 on success or -1 in case of error.
587  *
588  */
589 int LibHomeScreen::showInformation(json_object* json)
590 {
591         if(!sp_websock)
592         {
593                 return -1;
594         }
595
596         return this->call("showInformation", json);
597 }
598
599 /**
600  * get runnables list
601  *
602  * Call HomeScreen Service's getRunnables verb to get runnalbes list.
603  *
604  * #### Parameters
605  * - Nothing
606  *
607  * #### Return
608  * - Returns 0 on success or -1 in case of error.
609  *
610  */
611 int LibHomeScreen::getRunnables(void)
612 {
613         return this->call("getRunnables", nullptr);
614 }
615
616
617 /************* Callback Function *************/
618
619 void LibHomeScreen::on_hangup(void *closure, struct afb_wsj1 *wsj)
620 {
621         HMI_DEBUG("libhomescreen","called");
622         if(onHangup != nullptr)
623         {
624                 onHangup();
625         }
626 }
627
628 void LibHomeScreen::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
629 {
630 }
631
632 /*
633 * event is like "homescreen/hvac"
634 * msg is like {"event":"homescreen\/hvac","data":{"type":"tap_shortcut"},"jtype":"afb-event"}
635 * so you can get
636         event name : struct json_object obj = json_object_object_get(msg,"event")
637 */
638 void LibHomeScreen::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
639 {
640         HMI_DEBUG("libhomescreen","event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg));
641
642         if (strstr(event, API) == NULL) {
643                 return;
644         }
645
646         struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
647         struct json_object *json_data;
648         if(!json_object_object_get_ex(ev_contents, "data", &json_data)) {
649                 HMI_ERROR("libhomescreen", "got ev_contents error.");
650                 return;
651         }
652
653         if(onEvent != nullptr)
654         {
655                 const string ev(event);
656                 onEvent(ev, ev_contents);
657         }
658
659         const char* event_type = nullptr;
660         struct json_object *json_event_type;
661         if(json_object_object_get_ex(json_data, "type", &json_event_type)) {
662                 event_type = json_object_get_string(json_event_type);
663         }
664         else {
665                 HMI_WARNING("libhomescreen","event_type is null.");
666                 return;
667         }
668
669         if (strcasecmp(event_type, LibHomeScreen::event_list[0].c_str()) == 0) {
670                 auto i = this->handlers.find(Event_ShowWindow);
671                 if ( i != this->handlers.end() ) {
672                         i->second(json_data);
673                 }
674         }
675         else if (strcasecmp(event_type, LibHomeScreen::event_list[1].c_str()) == 0) {
676                 auto i = this->handlers.find(Event_OnScreenMessage);
677                 if ( i != this->handlers.end() ) {
678                         i->second(json_data);
679                 }
680         }
681         else if (strcasecmp(event_type, LibHomeScreen::event_list[2].c_str()) == 0) {
682                 auto i = this->handlers.find(Event_OnScreenReply);
683                 if ( i != this->handlers.end() ) {
684                         i->second(json_data);
685                 }
686         }
687         else if (strcasecmp(event_type, LibHomeScreen::event_list[3].c_str()) == 0) {
688                 auto i = this->handlers.find(Event_HideWindow);
689                 if ( i != this->handlers.end() ) {
690                         i->second(json_data);
691                 }
692         }
693         else if (strcasecmp(event_type, LibHomeScreen::event_list[4].c_str()) == 0) {
694                 auto i = this->handlers.find(Event_ReplyShowWindow);
695                 if ( i != this->handlers.end() ) {
696                         i->second(json_data);
697                 }
698         }
699         else if (strcasecmp(event_type, LibHomeScreen::event_list[5].c_str()) == 0) {
700                 auto i = this->handlers.find(Event_ShowNotification);
701                 if ( i != this->handlers.end() ) {
702                         i->second(json_data);
703                 }
704         }
705         else if (strcasecmp(event_type, LibHomeScreen::event_list[6].c_str()) == 0) {
706                 auto i = this->handlers.find(Event_ShowInformation);
707                 if ( i != this->handlers.end() ) {
708                         i->second(json_data);
709                 }
710         }
711         else if (strcasecmp(event_type, LibHomeScreen::event_list[7].c_str()) == 0) {
712                 auto i = this->handlers.find(Event_AppListChanged);
713                 if ( i != this->handlers.end() ) {
714                         i->second(json_data);
715                 }
716         }
717 }
718
719 /**
720  * msg is like ({"response":{"verb":"subscribe","error":0},"jtype":"afb-reply","request":{"status":"success","info":"homescreen binder subscribe event name [on_screen_message]"}})
721  * msg is like ({"response":{"verb":"tap_shortcut","error":0},"jtype":"afb-reply","request":{"status":"success","info":"afb_event_push event [tap_shortcut]"}})
722  */
723 void LibHomeScreen::on_reply(void *closure, struct afb_wsj1_msg *msg)
724 {
725         HMI_DEBUG("libhomescreen","msg: (%s)", afb_wsj1_msg_object_s(msg));
726         if(onReply != nullptr)
727         {
728                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
729                 onReply(reply);
730         }
731 }
732
733 static bool has_verb(const string& verb)
734 {
735         HMI_DEBUG("libhomescreen","verb is %s", verb.c_str());
736         if(find(LibHomeScreen::api_list.begin(), LibHomeScreen::api_list.end(), verb) != LibHomeScreen::api_list.end())
737                 return true;
738         else
739                 return false;
740 }