1d1d48c2e6ed50740ba43e4f66ae2baf6bfe9959
[apps/agl-service-homescreen.git] / src / hs-appinfo.cpp
1 /*
2  * Copyright (c) 2019 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 <unistd.h>
18 #include <cstring>
19 #include "hs-appinfo.h"
20 #include "hs-clientmanager.h"
21
22 #define RETRY_CNT 10
23
24 const char _keyName[] = "name";
25 const char _keyVersion[] = "version";
26 const char _keyInstall[] = "install";
27 const char _keyUninstall[] = "uninstall";
28 const char _keyOperation[] = "operation";
29 const char _keyRunnables[] = "runnables";
30 const char _keyStart[] = "start";
31 const char _keyApplistChanged[] = "application-list-changed";
32
33 HS_AppInfo* HS_AppInfo::me = nullptr;
34
35 /**
36  * get application property function
37  *
38  * #### Parameters
39  *  - key : retrieve keyword
40  *
41  * #### Return
42  * retrieved property
43  *
44  */
45 std::string AppDetail::getProperty(std::string key) const
46 {
47     struct json_object *j_obj;
48     struct json_object *j_detail = json_tokener_parse(this->detail.c_str());
49     if(json_object_object_get_ex(j_detail, key.c_str(), &j_obj) == 0) {
50         AFB_WARNING("can't find key=%s.", key.c_str());
51         return std::string();
52     }
53     return std::string(json_object_get_string(j_obj));
54 }
55
56 /**
57  * HS_AppInfo destruction function
58  *
59  * #### Parameters
60  *  - Nothing
61  *
62  * #### Return
63  * None
64  *
65  */
66 HS_AppInfo::~HS_AppInfo()
67 {
68     if(afmmain)
69         delete afmmain;
70 }
71
72 /**
73  * get instance
74  *
75  * #### Parameters
76  *  - Nothing
77  *
78  * #### Return
79  * HS_AppInfo instance pointer
80  *
81  */
82 HS_AppInfo* HS_AppInfo::instance(void)
83 {
84     if(me == nullptr)
85         me = new HS_AppInfo();
86
87     return me;
88 }
89
90 /**
91  * HS_AppInfo initialize function
92  *
93  * #### Parameters
94  *  - api : the api serving the request
95  *
96  * #### Return
97  * 0 : init success
98  * 1 : init fail
99  *
100  */
101 int HS_AppInfo::init(afb_api_t api)
102 {
103     afmmain = new HS_AfmMainProxy();
104     if(afmmain == nullptr) {
105         AFB_ERROR("Fatal Error:new HS_AfmMainProxy failed");
106         return -1;
107     }
108
109     struct json_object* j_runnable = nullptr;
110     int retry = 0;
111     do {
112         if(afmmain->runnables(api, &j_runnable) == 0) {
113             createAppDetailList(j_runnable);
114             json_object_put(j_runnable);
115             break;
116         }
117
118         ++retry;
119         if(retry == RETRY_CNT) {
120             AFB_ERROR("get runnables list failed");
121             json_object_put(j_runnable);
122             return -1;
123         }
124         AFB_DEBUG("retry to get runnables list %d", retry);
125         usleep(100000); // 100ms
126     } while(1);
127
128     return 0;
129 }
130
131 /**
132  * onEvent function
133  *
134  * #### Parameters
135  *  - api : the api serving the request
136  *  - event  : event name
137  *  - object : event json object
138  *
139  * #### Return
140  * None
141  *
142  */
143 void HS_AppInfo::onEvent(afb_api_t api, const char *event, struct json_object *object)
144 {
145     auto ip = concerned_event_list.find(std::string(event));
146     if(ip != concerned_event_list.end()) {
147         AFB_INFO("[%s] event received.", event);
148         (this->*(ip->second))(api, object);
149     }
150 }
151
152 /**
153  * create application detail list function
154  *
155  * #### Parameters
156  *  - object : the detail of all applications
157  *
158  * #### Return
159  * None
160  *
161  */
162 void HS_AppInfo::createAppDetailList(struct json_object *object)
163 {
164     AFB_DEBUG("applist:%s", json_object_to_json_string(object));
165
166     if(json_object_get_type(object) ==  json_type_array) {
167         int array_len = json_object_array_length(object);
168         for (int i = 0; i < array_len; ++i) {
169             struct json_object *obj = json_object_array_get_idx(object, i);
170             addAppDetail(obj);
171         }
172     }
173     else {
174         AFB_ERROR("Apps information input error.");
175     }
176 }
177
178 /**
179  * update application detail function
180  *
181  * #### Parameters
182  *  - object : the detail of all applications
183  *
184  * #### Return
185  * None
186  *
187  */
188 void HS_AppInfo::updateAppDetailList(afb_api_t api, struct json_object *object)
189 {
190     AFB_DEBUG("update:%s", json_object_to_json_string(object));
191     if(json_object_get_type(object) != json_type_object) {
192         AFB_ERROR("input detail object error.");
193         return;
194     }
195
196     struct json_object *obj_oper, *obj_data;
197     if(json_object_object_get_ex(object, _keyOperation, &obj_oper) == 0
198     ||  json_object_object_get_ex(object, _keyData, &obj_data) == 0) {
199         AFB_ERROR("can't find key=%s, %s.", _keyOperation, _keyData);
200         return;
201     }
202
203     std::string id = json_object_get_string(obj_data);
204     std::string appid = id2appid(id);
205     if(isPeripheryApp(appid.c_str())) {
206         AFB_INFO( "install/uninstall application is periphery.");
207         return;
208     }
209
210     std::string oper = json_object_get_string(obj_oper);
211     if(oper == _keyInstall) {
212         struct json_object* j_runnable = nullptr;
213         int ret = afmmain->runnables(api, &j_runnable);
214         if(!ret) {
215             struct json_object *j_found = retrieveRunnables(j_runnable, id);
216             if(j_found == nullptr) {
217                 AFB_INFO( "installed application isn't runnables.");
218                 json_object_put(j_runnable);
219                 return;
220             }
221             addAppDetail(j_found);
222             pushAppListChangedEvent(_keyInstall, j_found);
223         }
224         else {
225             AFB_ERROR("get runnalbes failed.");
226         }
227         json_object_put(j_runnable);
228     }
229     else if(oper == _keyUninstall) {
230         std::string appid_checked = checkAppId(appid);
231         if(appid_checked.empty()) {
232             AFB_INFO("uninstalled application isn't in runnables list, appid=%s.", appid.c_str());
233             return;
234         }
235         pushAppListChangedEvent(_keyUninstall, obj_data);
236         removeAppDetail(appid);
237     }
238     else {
239         AFB_ERROR("operation error.");
240     }
241 }
242
243 /**
244  * parse application detail function
245  *
246  * #### Parameters
247  *  - object : [IN] the detail of application
248  *  - info   : [OUT] parsed application detail
249  *
250  * #### Return
251  * the appid of application liked "dashboard"
252  *
253  */
254 std::string HS_AppInfo::parseAppDetail(struct json_object *object, AppDetail &info) const
255 {
256     struct json_object *name, *id;
257     if(json_object_object_get_ex(object, _keyName, &name) == 0
258     || json_object_object_get_ex(object, _keyId, &id) == 0) {
259         AFB_ERROR("can't find key=%s, %s.", _keyName, _keyId);
260         return std::string();
261     }
262     std::string appid = id2appid(json_object_get_string(id));
263     bool periphery = isPeripheryApp(appid.c_str());
264
265     info = { json_object_get_string(name),
266              json_object_get_string(id),
267              json_object_to_json_string(object),
268              periphery
269     };
270     return appid;
271 }
272
273 /**
274  * add application detail to list function
275  *
276  * #### Parameters
277  *  - object : application detail
278  *
279  * #### Return
280  * None
281  *
282  */
283 void HS_AppInfo::addAppDetail(struct json_object *object)
284 {
285     AppDetail info;
286     std::string appid = parseAppDetail(object, info);
287     if(appid.empty()) {
288         AFB_ERROR("application id error");
289         return;
290     }
291
292     std::lock_guard<std::mutex> lock(this->mtx);
293     appid2name[appid] = info.name;
294     name2appid[info.name] = appid;
295     app_detail_list[appid] = std::move(info);
296 }
297
298 /**
299  * remove application detail from list function
300  *
301  * #### Parameters
302  *  - appid : application id
303  *
304  * #### Return
305  * None
306  *
307  */
308 void HS_AppInfo::removeAppDetail(std::string appid)
309 {
310     std::lock_guard<std::mutex> lock(this->mtx);
311     auto it = app_detail_list.find(appid);
312     if(it != app_detail_list.end()) {
313         appid2name.erase(appid);
314         name2appid.erase(it->second.name);
315         app_detail_list.erase(it);
316     }
317     else {
318         AFB_WARNING("erase application(%s) wasn't in applist.", appid.c_str());
319     }
320 }
321
322 /**
323  * push app_list_changed event function
324  *
325  * #### Parameters
326  *  - oper: install/uninstall
327  *  - object: event data
328  *
329  * #### Return
330  * None
331  *
332  */
333 void HS_AppInfo::pushAppListChangedEvent(const char *oper, struct json_object *object)
334 {
335     AFB_DEBUG("called.");
336     struct json_object *push_obj = json_object_new_object();
337     json_object_object_add(push_obj, _keyOperation, json_object_new_string(oper));
338     json_object_object_add(push_obj, _keyData, object);
339
340     HS_ClientManager::instance()->pushEvent(_keyApplistChanged, push_obj);
341 }
342
343 /**
344  * retrieve runnables function
345  *
346  * #### Parameters
347  *  - obj_runnables: runnables array
348  *  - id: application id
349  *
350  * #### Return
351  * found application detail
352  *
353  */
354 struct json_object* HS_AppInfo::retrieveRunnables(struct json_object *obj_runnables, std::string id)
355 {
356     struct json_object *j_found = nullptr;
357     if(json_object_get_type(obj_runnables) ==  json_type_array) {
358         int array_len = json_object_array_length(obj_runnables);
359         for (int i = 0; i < array_len; ++i) {
360             struct json_object *obj = json_object_array_get_idx(obj_runnables, i);
361             struct json_object *j_id;
362             if(json_object_object_get_ex(obj, _keyId, &j_id) == 0) {
363                 AFB_WARNING("can't find id.");
364                 continue;
365             }
366             if(id == json_object_get_string(j_id)) {
367                 j_found = obj;
368                 break;
369             }
370         }
371     }
372     else {
373         AFB_ERROR("Apps information input error.");
374     }
375     return j_found;
376 }
377
378 /**
379  * convert id to appid function
380  *
381  * #### Parameters
382  *  - id : the id of application liked "dashboard@0.1"
383  *
384  * #### Return
385  * the appid of application liked "dashboard"
386  *
387  */
388 std::string HS_AppInfo::id2appid(const std::string &id) const
389 {
390     std::string appid;
391     std::size_t pos = id.find("@");
392     if(pos != std::string::npos) {
393         appid = id.substr(0,pos);
394     }
395     else {
396         AFB_WARNING("input id error.");
397     }
398     return appid;
399 }
400
401 /**
402  * get runnables list
403  *
404  * #### Parameters
405  *  - object : runnables list,json array
406  *
407  * #### Return
408  * None
409  *
410  */
411 void HS_AppInfo::getRunnables(struct json_object **object)
412 {
413     if(json_object_get_type(*object) !=  json_type_array) {
414         AFB_ERROR("json type error.");
415         return;
416     }
417
418     std::lock_guard<std::mutex> lock(this->mtx);
419     for(auto it : app_detail_list) {
420         if(!it.second.periphery)
421             json_object_array_add(*object, json_tokener_parse(it.second.detail.c_str()));
422     }
423 }
424
425 /**
426  * check appid function
427  *
428  * #### Parameters
429  *  - appid : appid liked "dashboard"
430  *
431  * #### Return
432  * success : the correct appid
433  * fail : empty string
434  *
435  */
436 std::string HS_AppInfo::checkAppId(const std::string &appid)
437 {
438     std::lock_guard<std::mutex> lock(this->mtx);
439     auto it_appid = appid2name.find(appid);
440     if(it_appid != appid2name.end())
441         return it_appid->first;
442
443     auto it_name = name2appid.find(appid);
444     if(it_name != name2appid.end())
445         return it_name->second;
446
447     return std::string();
448 }
449
450 /**
451  * check if application is a runnable periphery application function
452  *
453  * #### Parameters
454  *  - appid : appid liked "launcher"
455  *
456  * #### Return
457  * true : periphery
458  * false : not periphery
459  *
460  */
461 bool HS_AppInfo::isPeripheryApp(const char *appid) const
462 {
463     bool ret = false;
464     for(auto m : periphery_app_list) {
465         if(strcasecmp(appid, m) == 0) {
466             ret = true;
467             break;
468         }
469     }
470     return ret;
471 }
472
473 /**
474  * get application specific property
475  *
476  * #### Parameters
477  *  - appid : appid liked "launcher"
478  *  - key : the keyword
479  *
480  * #### Return
481  * application property
482  *
483  */
484 std::string HS_AppInfo::getAppProperty(const std::string appid, std::string key) const
485 {
486     std::string value = "";
487     auto it = app_detail_list.find(appid);
488     if(it != app_detail_list.end()) {
489         value = it->second.getProperty(key);
490     }
491     return value;
492 }