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