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