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