6c6076e1c88b6d3b98e9f333dcb01b382e079820
[apps/agl-service-homescreen.git] / libhomescreen / 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 <thread>
22 #include <errno.h>
23 #include <cassert>
24 #include <cctype>
25 #include <cerrno>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cstring>
29
30 #include <libhomescreen.hpp>
31
32 #define ELOG(args,...) _ELOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
33 #define DLOG(args,...) _DLOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
34
35 using namespace std;
36
37 static void _DLOG(const char* func, const int line, const char* log, ...);
38 static void _ELOG(const char* func, const int line, const char* log, ...);
39 static bool has_verb(const string& verb);
40 static const char API[] = "homescreen";
41
42 const std::vector<std::string> LibHomeScreen::api_list {
43         std::string("ping"), // debug do not use
44         std::string("tap_shortcut"), // HomeScreen Application only
45         std::string("on_screen_message"),
46         std::string("subscribe"),
47         std::string("unsubscribe")
48 };
49
50 const std::vector<std::string> LibHomeScreen::event_list {
51         std::string("tap_shortcut"),
52         std::string("on_screen_message"),
53         std::string("none")
54 };
55
56
57 /**
58  * websocket
59  */
60
61 static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
62 {
63         static_cast<LibHomeScreen*>(closure)->on_hangup(NULL,wsj);
64 }
65
66 static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
67 {
68         /* LibHomeScreen is not called from other process */
69 }
70
71 static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
72 {
73         static_cast<LibHomeScreen*>(closure)->on_event(NULL,event,msg);
74 }
75
76 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
77 {
78         static_cast<LibHomeScreen*>(closure)->on_reply(NULL,msg);
79 }
80
81
82 /**
83  * constructor
84  */
85 LibHomeScreen::LibHomeScreen()
86 {
87
88 }
89
90 /**
91  * destructor
92  */
93 LibHomeScreen::~LibHomeScreen()
94 {
95         if(mploop)
96         {
97                 sd_event_unref(mploop);
98         }
99         if(sp_websock != NULL)
100         {
101                 afb_wsj1_unref(sp_websock);
102         }
103 }
104
105 /**
106  * This function is initializer
107  *
108  * #### Parameters
109  * - port  [in] : This argument should be specified to the port number to be used for websocket
110  * - token [in] : This argument should be specified to the token to be used for websocket
111  *
112  * #### Return
113  * Nothing
114  *
115  * #### Note
116  * Use this constructor
117  *
118  */
119 int LibHomeScreen::init(const int port, const string& token)
120 {
121         int ret = 0;
122         if(port > 0 && token.size() > 0)
123         {
124                 mport = port;
125                 mtoken = token;
126         }
127         else
128         {
129                 ELOG("port and token should be > 0, Initial port and token uses.");
130         }
131
132         ret = initialize_websocket();
133         if(ret != 0 )
134         {
135                 ELOG("Failed to initialize websocket");
136         }
137         else{
138                 DLOG("Initialized");
139         }
140
141         this->runEventloop();
142
143         return ret;
144 }
145
146 /**
147  * This function register callback function for reply/event message from home screen
148  *
149  * #### Parameters
150  * - event_cb [in] : This argument should be specified to the callback for subscribed event
151  * - reply_cb [in] : This argument should be specified to the reply callback for call function
152  *
153  * #### Return
154  * Nothing
155  *
156  * #### Note
157  * Event callback is invoked by home screen for event you subscribed.
158  * If you would like to get event, please call subscribe function before/after this function
159  */
160 void LibHomeScreen::registerCallback(
161         void (*event_cb)(const std::string& event, struct json_object* event_contents),
162         void (*reply_cb)(struct json_object* reply_contents),
163         void (*hangup_cb)(void))
164 {
165         onEvent = event_cb;
166         onReply = reply_cb;
167         onHangup = hangup_cb;
168 }
169
170 int LibHomeScreen::initialize_websocket()
171 {
172         mploop = NULL;
173         onEvent = nullptr;
174         onReply = nullptr;
175         int ret = sd_event_default(&mploop);
176         if(ret < 0)
177         {
178                 ELOG("Failed to create event loop");
179                 goto END;
180         }
181
182         /* Initialize interface from websocket */
183         minterface.on_hangup = _on_hangup_static;
184         minterface.on_call = _on_call_static; /* Is this necessary? */
185         minterface.on_event = _on_event_static;
186         muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
187         sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
188         if(sp_websock == NULL)
189         {
190                 ELOG("Failed to create websocket connection");
191                 goto END;
192         }
193
194         /* creates the evsrc */
195         //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
196
197         return 0;
198 END:
199         if(mploop)
200         {
201                 sd_event_unref(mploop);
202         }
203         return -1;
204 }
205
206 static void *event_loop_run(void *args)
207 {
208         struct sd_event* loop = (struct sd_event*)(args);
209         DLOG("start eventloop");
210         for(;;)
211                 sd_event_run(loop, 30000000);
212 }
213
214 /**
215  * This function start receiving the reply/event message from home screen
216  *
217  * #### Parameters
218  * Nothing
219  *
220  * #### Return
221  * - Returns thread_id on success or -1 in case of error.
222  *
223  * #### Note
224  *
225  */
226 int LibHomeScreen::runEventloop()
227 {
228         if(mploop && sp_websock)
229         {
230                 pthread_t thread_id;
231             int ret = pthread_create(&thread_id, NULL, event_loop_run, mploop);
232                 if(ret != 0)
233                 {
234                         ELOG("Cannot run eventloop due to error:%d", errno);
235                         return -1;
236                 }
237                 else
238                         return thread_id;
239         }
240         else
241         {
242                 ELOG("Connecting is not established yet");
243                 return -1;
244         }
245 }
246
247 /**
248  * Sending ShortCut Icon tapped event
249  *
250  * When HomeScreen shortcut area is tapped, sending a event
251  *
252  * #### Parameters
253  * - application_name [in] : Tapped application name (label)
254  *
255  * #### Return
256  * - Returns 0 on success or -1 in case of error.
257  */
258 int LibHomeScreen::tapShortcut(const char* application_name)
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(application_name);
267         json_object_object_add(j_obj, "application_name", val);
268         return this->call("tap_shortcut", j_obj);
269 }
270
271 /**
272  * Sending onScreen message event
273  *
274  * Sending OnScreen message event to HomeScreen from applications
275  *
276  * #### Parameters
277  * - display_message [in] : message for display
278  *
279  * #### Return
280  * - Returns 0 on success or -1 in case of error.
281  */
282 int LibHomeScreen::onScreenMessage(const char* display_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(display_message);
291         json_object_object_add(j_obj, "display_message", val);
292         return this->call("on_screen_message", 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 void LibHomeScreen::set_event_handler(enum EventType et, handler_func f)
308 {
309         if (et >= 1 && et <= 2) {
310                 switch (et) {
311                         case Event_TapShortcut:
312                                 this->subscribe(LibHomeScreen::event_list[0]);
313                                 break;
314                         case Event_OnScreenMessage:
315                                 this->subscribe(LibHomeScreen::event_list[1]);
316                                 break;
317                 }
318
319                 this->handlers[et] = std::move(f);
320         }
321 }
322
323 /**
324  * This function calls the API of HomeScreen via WebSocket
325  *
326  * #### Parameters
327  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
328  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
329  *
330  * #### Return
331  * - Returns 0 on success or -1 in case of error.
332  *
333  * #### Note
334  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
335  *
336  */
337 int LibHomeScreen::call(const string& verb, struct json_object* arg)
338 {
339         int ret;
340         if(!sp_websock)
341         {
342                 return -1;
343         }
344         if (!has_verb(verb))
345         {
346                 ELOG("verb doesn't exit");
347                 return -1;
348         }
349         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
350         if (ret < 0) {
351                 ELOG("Failed to call verb:%s",verb.c_str());
352         }
353         return ret;
354 }
355
356 /**
357  * This function calls the API of HomeScreen via WebSocket
358  * This function is overload function of "call"
359  *
360  * #### Parameters
361  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
362  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
363  *
364  * #### Return
365  * - Returns 0 on success or -1 in case of error.
366  *
367  * #### Note
368  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
369  *
370  */
371 int LibHomeScreen::call(const char* verb, struct json_object* arg)
372 {
373         int ret;
374         if(!sp_websock)
375         {
376                 return -1;
377         }
378         if (!has_verb(string(verb)))
379         {
380                 ELOG("verb doesn't exit");
381                 return -1;
382         }
383         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
384         if (ret < 0) {
385                 ELOG("Failed to call verb:%s",verb);
386         }
387         return ret;
388 }
389
390 /**
391  * Register callback function for each event
392  *
393  * #### Parameters
394  * - event_name [in] : This argument should be specified to the event name
395  *
396  * #### Return
397  * - Returns 0 on success or -1 in case of error.
398  *
399  * #### Note
400  * This function enables to get an event to your callback function.
401  *
402  */
403 int LibHomeScreen::subscribe(const string& event_name)
404 {
405         if(!sp_websock)
406         {
407                 return -1;
408         }
409         struct json_object* j_obj = json_object_new_object();
410         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
411
412         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
413         if (ret < 0) {
414                 ELOG("Failed to call verb:%s",__FUNCTION__);
415         }
416         return ret;
417 }
418
419 /**
420  * Unregister callback function for each event
421  *
422  * #### Parameters
423  * - event_name [in] : This argument should be specified to the event name
424  *
425  * #### Return
426  * - Returns 0 on success or -1 in case of error.
427  *
428  * #### Note
429  * This function disables to get an event to your callback function.
430  *
431  */
432 int LibHomeScreen::unsubscribe(const string& event_name)
433 {
434         if(!sp_websock)
435         {
436                 return -1;
437         }
438         struct json_object* j_obj = json_object_new_object();
439         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
440
441         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
442         if (ret < 0) {
443                 ELOG("Failed to call verb:%s",__FUNCTION__);
444         }
445         return ret;
446 }
447
448 /************* Callback Function *************/
449
450 void LibHomeScreen::on_hangup(void *closure, struct afb_wsj1 *wsj)
451 {
452         DLOG("%s called", __FUNCTION__);
453         if(onHangup != nullptr)
454         {
455                 onHangup();
456         }
457 }
458
459 void LibHomeScreen::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
460 {
461 }
462
463 /*
464 * event is like "homescreen/tap_shortcut"
465 * msg is like {"event":"homescreen\/tap_shortcut","data":{"application_name":"hoge"},"jtype":"afb-event"}
466 * so you can get
467         event name : struct json_object obj = json_object_object_get(msg,"event")
468 */
469 void LibHomeScreen::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
470 {
471         cout << "[libhomescreen on_event]: " << event << " (" << afb_wsj1_msg_object_s(msg) << ")" << endl;
472
473   if (strstr(event, API) == NULL) {
474                 return;
475         }
476
477         struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
478         struct json_object *json_data = json_object_object_get(ev_contents, "data");
479
480         if(onEvent != nullptr)
481         {
482                 const string ev(event);
483                 onEvent(ev, ev_contents);
484         }
485
486         const char* event_only = strchr(event, '/');
487         if (event_only != nullptr) {
488                 event_only = event_only + 1;
489         }
490
491         if (strcasecmp(event_only, LibHomeScreen::event_list[0].c_str()) == 0) {
492                 auto i = this->handlers.find(Event_TapShortcut);
493
494                 struct json_object *json_application_name = json_object_object_get(json_data, "application_name");
495                 const char* application_name = json_object_get_string(json_application_name);
496
497                 if ( i != this->handlers.end() ) {
498                         i->second(application_name);
499                 }
500         }
501         else if (strcasecmp(event_only, LibHomeScreen::event_list[1].c_str()) == 0) {
502
503                 auto i = this->handlers.find(Event_OnScreenMessage);
504
505                 struct json_object *json_display_message = json_object_object_get(json_data, "display_message");
506                 const char* display_message = json_object_get_string(json_display_message);
507
508                 if ( i != this->handlers.end() ) {
509                         i->second(display_message);
510                 }
511
512         }
513
514         json_object_put(ev_contents);
515 }
516
517 /**
518  * msg is like ({"response":{"verb":"subscribe","error":0},"jtype":"afb-reply","request":{"status":"success","info":"homescreen binder subscribe event name [on_screen_message]"}})
519  * msg is like ({"response":{"verb":"tap_shortcut","error":0},"jtype":"afb-reply","request":{"status":"success","info":"afb_event_push event [tap_shortcut]"}})
520  */
521 void LibHomeScreen::on_reply(void *closure, struct afb_wsj1_msg *msg)
522 {
523         cout << "[libhomescreen on_reply]: " <<  " (" << afb_wsj1_msg_object_s(msg) << ")" << endl;
524         if(onReply != nullptr)
525         {
526                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
527                 onReply(reply);
528
529                 json_object_put(reply);
530         }
531 }
532
533 /* Internal Function in libhomescreen */
534
535 static void _ELOG(const char* func, const int line, const char* log, ...)
536 {
537         char *message;
538         va_list args;
539         va_start(args, log);
540         if (log == NULL || vasprintf(&message, log, args) < 0)
541                 message = NULL;
542         cout << "[libhomescreen ERROR]" << func << "(" << line << "):" << message << endl;
543         va_end(args);
544         free(message);
545 }
546
547 static void _DLOG(const char* func, const int line, const char* log, ...)
548 {
549         char *message;
550         va_list args;
551         va_start(args, log);
552         if (log == NULL || vasprintf(&message, log, args) < 0)
553                 message = NULL;
554         cout << "[libhomescreen DEBUG]" << func << "(" << line << "):" << message << endl;
555         va_end(args);
556         free(message);
557 }
558
559 static bool has_verb(const string& verb)
560 {
561         DLOG("verb is %s", verb.c_str());
562         if(find(LibHomeScreen::api_list.begin(), LibHomeScreen::api_list.end(), verb) != LibHomeScreen::api_list.end())
563                 return true;
564         else
565                 return false;
566 }