66fd259bd8a6f47d32749c1396a8bdd72cda44f9
[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; /* Is this necessary? */
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 void LibHomeScreen::set_event_handler(enum EventType et, handler_func f)
285 {
286         if (et >= 1 && et <= 3) {
287                 switch (et) {
288                         case Event_TapShortcut:
289                                 this->subscribe(LibHomeScreen::event_list[0]);
290                                 break;
291                         case Event_OnScreenMessage:
292                                 this->subscribe(LibHomeScreen::event_list[1]);
293                                 break;
294                         case Event_OnScreenReply:
295                                 this->subscribe(LibHomeScreen::event_list[2]);
296                                 break;
297                 }
298
299                 this->handlers[et] = std::move(f);
300         }
301 }
302
303 /**
304  * This function calls the API of HomeScreen via WebSocket
305  *
306  * #### Parameters
307  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
308  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
309  *
310  * #### Return
311  * - Returns 0 on success or -1 in case of error.
312  *
313  * #### Note
314  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
315  *
316  */
317 int LibHomeScreen::call(const string& verb, struct json_object* arg)
318 {
319         int ret;
320         if(!sp_websock)
321         {
322                 return -1;
323         }
324         if (!has_verb(verb))
325         {
326                 HMI_ERROR("libhomescreen","verb doesn't exit");
327                 return -1;
328         }
329         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
330         if (ret < 0) {
331                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb.c_str());
332         }
333         return ret;
334 }
335
336 /**
337  * This function calls the API of HomeScreen via WebSocket
338  * This function is overload function of "call"
339  *
340  * #### Parameters
341  * - verb [in] : This argument should be specified to the API name (e.g. "tap_shortcut")
342  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
343  *
344  * #### Return
345  * - Returns 0 on success or -1 in case of error.
346  *
347  * #### Note
348  * To call HomeScreen's APIs, the application should set its function name, arguments to JSON format.
349  *
350  */
351 int LibHomeScreen::call(const char* verb, struct json_object* arg)
352 {
353         int ret;
354         if(!sp_websock)
355         {
356                 return -1;
357         }
358         if (!has_verb(string(verb)))
359         {
360                 HMI_ERROR("libhomescreen","verb doesn't exit");
361                 return -1;
362         }
363         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
364         if (ret < 0) {
365                 HMI_ERROR("libhomescreen","Failed to call verb:%s",verb);
366         }
367         return ret;
368 }
369
370 /**
371  * Register callback function for each event
372  *
373  * #### Parameters
374  * - event_name [in] : This argument should be specified to the event name
375  *
376  * #### Return
377  * - Returns 0 on success or -1 in case of error.
378  *
379  * #### Note
380  * This function enables to get an event to your callback function.
381  *
382  */
383 int LibHomeScreen::subscribe(const string& event_name)
384 {
385         if(!sp_websock)
386         {
387                 return -1;
388         }
389         struct json_object* j_obj = json_object_new_object();
390         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
391
392         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
393         if (ret < 0) {
394                 HMI_ERROR("libhomescreen","Failed to call verb");
395         }
396         return ret;
397 }
398
399 /**
400  * Unregister callback function for each event
401  *
402  * #### Parameters
403  * - event_name [in] : This argument should be specified to the event name
404  *
405  * #### Return
406  * - Returns 0 on success or -1 in case of error.
407  *
408  * #### Note
409  * This function disables to get an event to your callback function.
410  *
411  */
412 int LibHomeScreen::unsubscribe(const string& event_name)
413 {
414         if(!sp_websock)
415         {
416                 return -1;
417         }
418         struct json_object* j_obj = json_object_new_object();
419         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
420
421         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
422         if (ret < 0) {
423                 HMI_ERROR("libhomescreen","Failed to call verb");
424         }
425         return ret;
426 }
427
428 /************* Callback Function *************/
429
430 void LibHomeScreen::on_hangup(void *closure, struct afb_wsj1 *wsj)
431 {
432         HMI_DEBUG("libhomescreen","called");
433         if(onHangup != nullptr)
434         {
435                 onHangup();
436         }
437 }
438
439 void LibHomeScreen::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
440 {
441 }
442
443 /*
444 * event is like "homescreen/tap_shortcut"
445 * msg is like {"event":"homescreen\/tap_shortcut","data":{"application_name":"hoge"},"jtype":"afb-event"}
446 * so you can get
447         event name : struct json_object obj = json_object_object_get(msg,"event")
448 */
449 void LibHomeScreen::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
450 {
451         HMI_DEBUG("libhomescreen","event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg));
452
453         if (strstr(event, API) == NULL) {
454                 return;
455         }
456
457         struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
458         struct json_object *json_data = json_object_object_get(ev_contents, "data");
459
460         if(onEvent != nullptr)
461         {
462                 const string ev(event);
463                 onEvent(ev, ev_contents);
464         }
465
466         const char* event_only = strchr(event, '/');
467         if (event_only != nullptr) {
468                 event_only = event_only + 1;
469         }
470
471         if (strcasecmp(event_only, LibHomeScreen::event_list[0].c_str()) == 0) {
472                 auto i = this->handlers.find(Event_TapShortcut);
473                 if ( i != this->handlers.end() ) {
474                         i->second(json_data);
475                 }
476         }
477         else if (strcasecmp(event_only, LibHomeScreen::event_list[1].c_str()) == 0) {
478                 auto i = this->handlers.find(Event_OnScreenMessage);
479                 if ( i != this->handlers.end() ) {
480                         i->second(json_data);
481                 }
482         }
483         else if (strcasecmp(event_only, LibHomeScreen::event_list[2].c_str()) == 0) {
484                 auto i = this->handlers.find(Event_OnScreenReply);
485                 if ( i != this->handlers.end() ) {
486                         i->second(json_data);
487                 }
488         }
489
490         json_object_put(ev_contents);
491 }
492
493 /**
494  * msg is like ({"response":{"verb":"subscribe","error":0},"jtype":"afb-reply","request":{"status":"success","info":"homescreen binder subscribe event name [on_screen_message]"}})
495  * msg is like ({"response":{"verb":"tap_shortcut","error":0},"jtype":"afb-reply","request":{"status":"success","info":"afb_event_push event [tap_shortcut]"}})
496  */
497 void LibHomeScreen::on_reply(void *closure, struct afb_wsj1_msg *msg)
498 {
499         HMI_DEBUG("libhomescreen","msg: (%s)", afb_wsj1_msg_object_s(msg));
500         if(onReply != nullptr)
501         {
502                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
503                 onReply(reply);
504
505                 json_object_put(reply);
506         }
507 }
508
509 static bool has_verb(const string& verb)
510 {
511         HMI_DEBUG("libhomescreen","verb is %s", verb.c_str());
512         if(find(LibHomeScreen::api_list.begin(), LibHomeScreen::api_list.end(), verb) != LibHomeScreen::api_list.end())
513                 return true;
514         else
515                 return false;
516 }