hs-clientmanager: Pass arguments to printf() wrappers
[apps/agl-service-homescreen.git] / src / hs-clientmanager.cpp
1 /*
2  * Copyright (c) 2018 TOYOTA MOTOR CORPORATION
3  * Copyright (C) 2020 Konsulko Group
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <cstring>
19 #include <algorithm>
20 #include <cassert>
21 #include "hs-proxy.h"
22 #include "hs-clientmanager.h"
23
24 static const char _homescreen[] = "homescreen";
25
26 HS_ClientManager* HS_ClientManager::me = nullptr;
27
28 static void cbRemoveClientCtxt(void *data)
29 {
30     HS_ClientManager::instance()->removeClientCtxt(data);
31 }
32
33 /**
34  * HS_ClientManager construction function
35  *
36  * #### Parameters
37  *  - Nothing
38  *
39  * #### Return
40  * None
41  *
42  */
43 HS_ClientManager::HS_ClientManager()
44 {
45 }
46
47 /**
48  * get instance
49  *
50  * #### Parameters
51  *  - Nothing
52  *
53  * #### Return
54  * HS_ClientManager instance pointer
55  *
56  */
57 HS_ClientManager* HS_ClientManager::instance(void)
58 {
59     if(me == nullptr)
60         me = new HS_ClientManager();
61
62     return me;
63 }
64
65 /**
66  * HS_ClientManager init function
67  *
68  * #### Parameters
69  *  - Nothing
70  *
71  * #### Return
72  * init result
73  *
74  */
75 int HS_ClientManager::init(void)
76 {
77     return 0;
78 }
79
80 /**
81  * create client's afb_req_context
82  *
83  * #### Parameters
84  *  - appid: app's id
85  *
86  * #### Return
87  * HS_ClientCtxt pointer
88  *
89  */
90 HS_ClientCtxt* HS_ClientManager::createClientCtxt(afb_req_t req, std::string appid)
91 {
92     HS_ClientCtxt *ctxt = (HS_ClientCtxt *)afb_req_context_get(req);
93     if (!ctxt)
94     {
95         AFB_INFO( "create new session for %s", appid.c_str());
96         ctxt = new HS_ClientCtxt(appid);
97         afb_req_session_set_LOA(req, 1);
98         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
99
100         appid2ctxt[appid] = ctxt;
101     }
102
103     return ctxt;
104 }
105
106 /**
107  * add Client
108  *
109  * #### Parameters
110  *  - ctxt: app's id
111  *
112  * #### Return
113  * HS_Client pointer
114  *
115  */
116 HS_Client* HS_ClientManager::addClient(afb_req_t req, std::string appid)
117 {
118     return (client_list[appid] = new HS_Client(req, appid));
119 }
120
121 /**
122  * remove Client
123  *
124  * #### Parameters
125  *  - appid: app's id
126  *
127  * #### Return
128  * None
129  *
130  */
131 void HS_ClientManager::removeClient(std::string appid)
132 {
133     delete client_list[appid];
134     client_list.erase(appid);
135 }
136
137 /**
138  * remove Client from list
139  *
140  * #### Parameters
141  *  - data: HS_ClientCtxt pointer
142  *
143  * #### Return
144  * None
145  *
146  */
147 void HS_ClientManager::removeClientCtxt(void *data)
148 {
149     HS_ClientCtxt *ctxt = (HS_ClientCtxt *)data;
150     if(ctxt == nullptr)
151     {
152         AFB_WARNING( "data is nullptr");
153         return;
154     }
155
156     AFB_INFO( "remove app %s", ctxt->id.c_str());
157     std::lock_guard<std::mutex> lock(this->mtx);
158     removeClient(ctxt->id);
159     delete appid2ctxt[ctxt->id];
160     appid2ctxt.erase(ctxt->id);
161 }
162
163 static int
164 is_application_running(afb_req_t request, std::string id)
165 {
166     bool app_still_running = false;
167     struct json_object *jobj = nullptr;
168
169     HS_AfmMainProxy afm_proxy;
170
171     // note this is sync, so this might block if afm-system-daemon is down
172     afm_proxy.ps(request->api, &jobj);
173
174     if (jobj) {
175             size_t len = json_object_array_length(jobj);
176             for (size_t i = 0; i < len; i++) {
177                 struct json_object *aid;
178                 struct json_object *item =
179                     json_object_array_get_idx(jobj, i);
180
181                 bool isFound = json_object_object_get_ex(item, "id", &aid);
182                 if (isFound) {
183                     const char *str_appid = json_object_get_string(aid);
184                     if (strcmp(str_appid, id.c_str()) == 0) {
185                         app_still_running = true;
186                         break;
187                     }
188                 }
189             }
190     }
191
192     if (!app_still_running) {
193         // we don't remove it from the context list as we're haven't really subscribed,
194         // and we just need to remove it from client_list, which happens here. We also
195         // return AFB_REQ_NOT_STARTED_APPLICATION which will attempt to start it (again).
196         HS_ClientManager::instance()->removeClient(id);
197         return AFB_REQ_NOT_STARTED_APPLICATION;
198     }
199
200     return 0;
201 }
202
203 /**
204  * handle homescreen request
205  *
206  * #### Parameters
207  *  - request : the request
208  *  - verb : the verb name
209  *  - appid : to which application
210  *
211  * #### Return
212  * 0 : success
213  * others : fail
214  *
215  */
216 int HS_ClientManager::handleRequest(afb_req_t request, const char *verb, const char *appid)
217 {
218     AFB_INFO("verb=[%s],appid=[%s].", verb, appid);
219     int ret = 0;
220     std::lock_guard<std::mutex> lock(this->mtx);
221     if(appid == nullptr) {
222         for(auto m : client_list) {
223             m.second->handleRequest(request, verb);
224         }
225     }
226     else {
227         std::string id(appid);
228         auto ip = client_list.find(id);
229         if(ip != client_list.end()) {
230             // for showWindow verb we need to verify if the app is (still)
231             // running, and return the appropriate value to attempt to start it
232             // again. This 'problem' is avoided if the application itself
233             // subscribes and with that process, to install a callback that
234             // automatically removes the application from client_list.
235             // That is exactly how "subscribe" verb is handled below.
236             if (strcasecmp(verb, "showWindow") == 0) {
237                 ret = is_application_running(request, id);
238                 if (ret == AFB_REQ_NOT_STARTED_APPLICATION) {
239                     AFB_INFO("%s is not running. Will attempt to start it", appid);
240                     return ret;
241                 }
242             }
243             AFB_INFO("%s found to be running. Forwarding request to the client", appid);
244             ret = ip->second->handleRequest(request, verb);
245         }
246         else {
247             if(!strcasecmp(verb, "subscribe")) {
248                 createClientCtxt(request, id);
249                 HS_Client* client = addClient(request, id);
250                 ret = client->handleRequest(request, "subscribe");
251             }
252             else {
253                 AFB_NOTICE("not exist session");
254                 ret = AFB_REQ_NOT_STARTED_APPLICATION;
255             }
256         }
257     }
258     return ret;
259 }
260
261 /**
262  * push event
263  *
264  * #### Parameters
265  *  - event : the event want to push
266  *  - param : the parameter contents of event
267  *  - appid : the destination application's id
268  *
269  * #### Return
270  * 0 : success
271  * others : fail
272  *
273  */
274 int HS_ClientManager::pushEvent(const char *event, struct json_object *param, std::string appid)
275 {
276     if(event == nullptr) {
277         AFB_WARNING("event name is null.");
278         return -1;
279     }
280
281     std::lock_guard<std::mutex> lock(this->mtx);
282     if(appid.empty()) { // broadcast event to clients who subscribed this event
283         for(auto m : client_list) {
284             m.second->pushEvent(event, param);
285         }
286     }
287     else {  // push event to specific client
288         auto ip = client_list.find(appid);
289         if(ip != client_list.end()) {
290             ip->second->pushEvent(event, param);
291         }
292     }
293
294     return 0;
295 }