+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include "hs-appinfo.h"
+#include "hmi-debug.h"
+#include "hs-clientmanager.h"
+
+#define RETRY_CNT 10
+
+const char _keyName[] = "name";
+const char _keyVersion[] = "version";
+const char _keyInstall[] = "install";
+const char _keyUninstall[] = "uninstall";
+const char _keyOperation[] = "operation";
+const char _keyRunnables[] = "runnables";
+const char _keyStart[] = "start";
+const char _keyApplistChanged[] = "application-list-changed";
+
+HS_AppInfo* HS_AppInfo::me = nullptr;
+
+/**
+ * get application property function
+ *
+ * #### Parameters
+ * - key : retrieve keyword
+ *
+ * #### Return
+ * retrieved property
+ *
+ */
+std::string AppDetail::getProperty(std::string key) const
+{
+ struct json_object *j_obj;
+ struct json_object *j_detail = json_tokener_parse(this->detail.c_str());
+ if(json_object_object_get_ex(j_detail, key.c_str(), &j_obj) == 0) {
+ HMI_ERROR("homescreen-service","can't find key=%s.", key.c_str());
+ return std::string();
+ }
+ return std::string(json_object_get_string(j_obj));
+}
+
+/**
+ * HS_AppInfo destruction function
+ *
+ * #### Parameters
+ * - Nothing
+ *
+ * #### Return
+ * None
+ *
+ */
+HS_AppInfo::~HS_AppInfo()
+{
+ if(afmmain)
+ delete afmmain;
+}
+
+/**
+ * get instance
+ *
+ * #### Parameters
+ * - Nothing
+ *
+ * #### Return
+ * HS_AppInfo instance pointer
+ *
+ */
+HS_AppInfo* HS_AppInfo::instance(void)
+{
+ if(me == nullptr)
+ me = new HS_AppInfo();
+
+ return me;
+}
+
+/**
+ * HS_AppInfo initialize function
+ *
+ * #### Parameters
+ * - api : the api serving the request
+ *
+ * #### Return
+ * 0 : init success
+ * 1 : init fail
+ *
+ */
+int HS_AppInfo::init(afb_api_t api)
+{
+ afmmain = new HS_AfmMainProxy();
+ if(afmmain == nullptr) {
+ HMI_ERROR("homescreen-service","Fatal Error:new HS_AfmMainProxy failed");
+ return -1;
+ }
+
+ struct json_object* j_runnable = nullptr;
+ int retry = 0;
+ do {
+ if(afmmain->runnables(api, &j_runnable) == 0) {
+ createAppDetailList(j_runnable);
+ json_object_put(j_runnable);
+ break;
+ }
+
+ ++retry;
+ if(retry == RETRY_CNT) {
+ HMI_ERROR("homescreen-service","get runnables list failed");
+ json_object_put(j_runnable);
+ return -1;
+ }
+ HMI_NOTICE("homescreen-service","retry to get runnables list %d", retry);
+ usleep(100000); // 100ms
+ } while(1);
+
+ return 0;
+}
+
+/**
+ * onEvent function
+ *
+ * #### Parameters
+ * - api : the api serving the request
+ * - event : event name
+ * - object : event json object
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::onEvent(afb_api_t api, const char *event, struct json_object *object)
+{
+ auto ip = concerned_event_list.find(std::string(event));
+ if(ip != concerned_event_list.end()) {
+ HMI_NOTICE("homescreen-service","[%s] event received.", event);
+ (this->*(ip->second))(api, object);
+ }
+}
+
+/**
+ * create application detail list function
+ *
+ * #### Parameters
+ * - object : the detail of all applications
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::createAppDetailList(struct json_object *object)
+{
+ HMI_NOTICE("homescreen-service","applist:%s", json_object_to_json_string(object));
+
+ if(json_object_get_type(object) == json_type_array) {
+ int array_len = json_object_array_length(object);
+ for (int i = 0; i < array_len; ++i) {
+ struct json_object *obj = json_object_array_get_idx(object, i);
+ addAppDetail(obj);
+ }
+ }
+ else {
+ HMI_ERROR("homescreen-service","Apps information input error.");
+ }
+}
+
+/**
+ * update application detail function
+ *
+ * #### Parameters
+ * - object : the detail of all applications
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::updateAppDetailList(afb_api_t api, struct json_object *object)
+{
+ HMI_NOTICE("homescreen-service","update:%s", json_object_to_json_string(object));
+ if(json_object_get_type(object) != json_type_object) {
+ HMI_ERROR("homescreen-service","input detail object error.");
+ return;
+ }
+
+ struct json_object *obj_oper, *obj_data;
+ if(json_object_object_get_ex(object, _keyOperation, &obj_oper) == 0
+ || json_object_object_get_ex(object, _keyData, &obj_data) == 0) {
+ HMI_ERROR("homescreen-service","can't find key=%s, %s.", _keyOperation, _keyData);
+ return;
+ }
+
+ std::string id = json_object_get_string(obj_data);
+ std::string appid = id2appid(id);
+ if(isPeripheryApp(appid.c_str())) {
+ HMI_NOTICE("homescreen-service", "install/uninstall application is periphery.");
+ return;
+ }
+
+ std::string oper = json_object_get_string(obj_oper);
+ if(oper == _keyInstall) {
+ struct json_object* j_runnable = nullptr;
+ int ret = afmmain->runnables(api, &j_runnable);
+ if(!ret) {
+ struct json_object *j_found = retrieveRunnables(j_runnable, id);
+ if(j_found == nullptr) {
+ HMI_NOTICE("homescreen-service", "installed application isn't runnables.");
+ json_object_put(j_runnable);
+ return;
+ }
+ addAppDetail(j_found);
+ pushAppListChangedEvent(_keyInstall, j_found);
+ }
+ else {
+ HMI_ERROR("homescreen-service","get runnalbes failed.");
+ }
+ json_object_put(j_runnable);
+ }
+ else if(oper == _keyUninstall) {
+ std::string appid_checked = checkAppId(appid);
+ if(appid_checked.empty()) {
+ HMI_NOTICE("homescreen-service","uninstalled application isn't in runnables list, appid=%s.", appid.c_str());
+ return;
+ }
+ pushAppListChangedEvent(_keyUninstall, obj_data);
+ removeAppDetail(appid);
+ }
+ else {
+ HMI_ERROR("homescreen-service","operation error.");
+ }
+}
+
+/**
+ * parse application detail function
+ *
+ * #### Parameters
+ * - object : [IN] the detail of application
+ * - info : [OUT] parsed application detail
+ *
+ * #### Return
+ * the appid of application liked "dashboard"
+ *
+ */
+std::string HS_AppInfo::parseAppDetail(struct json_object *object, AppDetail &info) const
+{
+ struct json_object *name, *id;
+ if(json_object_object_get_ex(object, _keyName, &name) == 0
+ || json_object_object_get_ex(object, _keyId, &id) == 0) {
+ HMI_ERROR("homescreen-service","can't find key=%s, %s.", _keyName, _keyId);
+ return std::string();
+ }
+ std::string appid = id2appid(json_object_get_string(id));
+ bool periphery = isPeripheryApp(appid.c_str());
+
+ info = { json_object_get_string(name),
+ json_object_get_string(id),
+ json_object_to_json_string(object),
+ periphery
+ };
+ return appid;
+}
+
+/**
+ * add application detail to list function
+ *
+ * #### Parameters
+ * - object : application detail
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::addAppDetail(struct json_object *object)
+{
+ AppDetail info;
+ std::string appid = parseAppDetail(object, info);
+ if(appid.empty()) {
+ HMI_ERROR("homescreen-service","application id error");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(this->mtx);
+ appid2name[appid] = info.name;
+ name2appid[info.name] = appid;
+ app_detail_list[appid] = std::move(info);
+}
+
+/**
+ * remove application detail from list function
+ *
+ * #### Parameters
+ * - appid : application id
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::removeAppDetail(std::string appid)
+{
+ std::lock_guard<std::mutex> lock(this->mtx);
+ auto it = app_detail_list.find(appid);
+ if(it != app_detail_list.end()) {
+ appid2name.erase(appid);
+ name2appid.erase(it->second.name);
+ app_detail_list.erase(it);
+ }
+ else {
+ HMI_WARNING("homescreen-service","erase application(%s) wasn't in applist.", appid.c_str());
+ }
+}
+
+/**
+ * push app_list_changed event function
+ *
+ * #### Parameters
+ * - oper: install/uninstall
+ * - object: event data
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::pushAppListChangedEvent(const char *oper, struct json_object *object)
+{
+ HMI_NOTICE("homescreen-service","called.");
+ struct json_object *push_obj = json_object_new_object();
+ json_object_object_add(push_obj, _keyOperation, json_object_new_string(oper));
+ json_object_object_add(push_obj, _keyData, object);
+
+ HS_ClientManager::instance()->pushEvent(_keyApplistChanged, push_obj);
+}
+
+/**
+ * retrieve runnables function
+ *
+ * #### Parameters
+ * - obj_runnables: runnables array
+ * - id: application id
+ *
+ * #### Return
+ * found application detail
+ *
+ */
+struct json_object* HS_AppInfo::retrieveRunnables(struct json_object *obj_runnables, std::string id)
+{
+ struct json_object *j_found = nullptr;
+ if(json_object_get_type(obj_runnables) == json_type_array) {
+ int array_len = json_object_array_length(obj_runnables);
+ for (int i = 0; i < array_len; ++i) {
+ struct json_object *obj = json_object_array_get_idx(obj_runnables, i);
+ struct json_object *j_id;
+ if(json_object_object_get_ex(obj, _keyId, &j_id) == 0) {
+ HMI_WARNING("homescreen-service","can't find id.");
+ continue;
+ }
+ if(id == json_object_get_string(j_id)) {
+ j_found = obj;
+ break;
+ }
+ }
+ }
+ else {
+ HMI_ERROR("homescreen-service","Apps information input error.");
+ }
+ return j_found;
+}
+
+/**
+ * convert id to appid function
+ *
+ * #### Parameters
+ * - id : the id of application liked "dashboard@0.1"
+ *
+ * #### Return
+ * the appid of application liked "dashboard"
+ *
+ */
+std::string HS_AppInfo::id2appid(const std::string &id) const
+{
+ std::string appid;
+ std::size_t pos = id.find("@");
+ if(pos != std::string::npos) {
+ appid = id.substr(0,pos);
+ }
+ else {
+ HMI_ERROR("homescreen-service","input id error.");
+ }
+ return appid;
+}
+
+/**
+ * get runnables list
+ *
+ * #### Parameters
+ * - object : runnables list,json array
+ *
+ * #### Return
+ * None
+ *
+ */
+void HS_AppInfo::getRunnables(struct json_object **object)
+{
+ if(json_object_get_type(*object) != json_type_array) {
+ HMI_ERROR("homescreen-service","json type error.");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(this->mtx);
+ for(auto it : app_detail_list) {
+ if(!it.second.periphery)
+ json_object_array_add(*object, json_tokener_parse(it.second.detail.c_str()));
+ }
+}
+
+/**
+ * check appid function
+ *
+ * #### Parameters
+ * - appid : appid liked "dashboard"
+ *
+ * #### Return
+ * success : the correct appid
+ * fail : empty string
+ *
+ */
+std::string HS_AppInfo::checkAppId(const std::string &appid)
+{
+ std::lock_guard<std::mutex> lock(this->mtx);
+ auto it_appid = appid2name.find(appid);
+ if(it_appid != appid2name.end())
+ return it_appid->first;
+
+ auto it_name = name2appid.find(appid);
+ if(it_name != name2appid.end())
+ return it_name->second;
+
+ return std::string();
+}
+
+/**
+ * check if application is a runnable periphery application function
+ *
+ * #### Parameters
+ * - appid : appid liked "launcher"
+ *
+ * #### Return
+ * true : periphery
+ * false : not periphery
+ *
+ */
+bool HS_AppInfo::isPeripheryApp(const char *appid) const
+{
+ bool ret = false;
+ for(auto m : periphery_app_list) {
+ if(strcasecmp(appid, m) == 0) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * get application specific property
+ *
+ * #### Parameters
+ * - appid : appid liked "launcher"
+ * - key : the keyword
+ *
+ * #### Return
+ * application property
+ *
+ */
+std::string HS_AppInfo::getAppProperty(const std::string appid, std::string key) const
+{
+ std::string value = "";
+ auto it = app_detail_list.find(appid);
+ if(it != app_detail_list.end()) {
+ value = it->second.getProperty(key);
+ }
+ return value;
+}
\ No newline at end of file