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