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