clientmanager: Do not check for running applications
[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(std::string appid, std::unordered_map<std::string, HS_Client*> client_list)
165 {
166     bool app_still_running = false;
167
168     std::string id(appid);
169     auto ip = client_list.find(id);
170
171     // this will always be case as the removeClientCtxt() is never called as
172     // clients do not handle the subscribe at all at this point. Not only that
173     // but is redudant but we keep at as it to highlight the fact that we're
174     // missing a feature to check if applications died or not (legitimate or not).
175     //
176     // Using ps (HS_AfmMainProxy::ps -- a sync version of checking if running
177     // applications) seem to block in afm-system-daemon (see SPEC-3902), and
178     // doing w/ it with an async version of ps doesn't work because we don't
179     // have a valid clientCtx (required, and only possible if the application
180     // subscribed themselves, which no longer happens)
181     //
182     // FIXME: We need another way of handling this would be necessary to correctly
183     // handle the case where the app died.
184     if (ip != client_list.end()) {
185         app_still_running = true;
186     }
187
188     // > 0 means err
189     return app_still_running != true;
190 }
191
192 /**
193  * handle homescreen request
194  *
195  * #### Parameters
196  *  - request : the request
197  *  - verb : the verb name
198  *  - appid : to which application
199  *
200  * #### Return
201  * 0 : success
202  * others : fail
203  *
204  */
205 int HS_ClientManager::handleRequest(afb_req_t request, const char *verb, const char *appid)
206 {
207     AFB_INFO("verb=[%s],appid=[%s].", verb, appid);
208     int ret = 0;
209     std::lock_guard<std::mutex> lock(this->mtx);
210     if(appid == nullptr) {
211         for(auto m : client_list) {
212             m.second->handleRequest(request, verb);
213         }
214     }
215     else {
216         std::string id(appid);
217         auto ip = client_list.find(id);
218         if(ip != client_list.end()) {
219             // for showWindow verb we need to verify if the app is (still)
220             // running, and return the appropriate value to attempt to start it
221             // again. This 'problem' is avoided if the application itself
222             // subscribes and with that process, to install a callback that
223             // automatically removes the application from client_list.
224             // That is exactly how "subscribe" verb is handled below.
225             if (strcasecmp(verb, "showWindow") == 0) {
226                 ret = is_application_running(id, client_list);
227                 if (ret == AFB_REQ_NOT_STARTED_APPLICATION) {
228                     AFB_INFO("%s is not running. Will attempt to start it", appid);
229                     return ret;
230                 }
231             }
232             AFB_INFO("%s found to be running. Forwarding request to the client", appid);
233             ret = ip->second->handleRequest(request, verb);
234         }
235         else {
236             if(!strcasecmp(verb, "subscribe")) {
237                 createClientCtxt(request, id);
238                 HS_Client* client = addClient(request, id);
239                 ret = client->handleRequest(request, "subscribe");
240             }
241             else {
242                 AFB_NOTICE("not exist session");
243                 ret = AFB_REQ_NOT_STARTED_APPLICATION;
244             }
245         }
246     }
247     return ret;
248 }
249
250 /**
251  * push event
252  *
253  * #### Parameters
254  *  - event : the event want to push
255  *  - param : the parameter contents of event
256  *  - appid : the destination application's id
257  *
258  * #### Return
259  * 0 : success
260  * others : fail
261  *
262  */
263 int HS_ClientManager::pushEvent(const char *event, struct json_object *param, std::string appid)
264 {
265     if(event == nullptr) {
266         AFB_WARNING("event name is null.");
267         return -1;
268     }
269
270     std::lock_guard<std::mutex> lock(this->mtx);
271     if(appid.empty()) { // broadcast event to clients who subscribed this event
272         for(auto m : client_list) {
273             m.second->pushEvent(event, param);
274         }
275     }
276     else {  // push event to specific client
277         auto ip = client_list.find(appid);
278         if(ip != client_list.end()) {
279             ip->second->pushEvent(event, param);
280         }
281     }
282
283     return 0;
284 }