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