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