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