Add cache support 41/291341/4 accepted/tizen/unified/20230420.041540
authorJaehyun Kim <jeik01.kim@samsung.com>
Thu, 13 Apr 2023 13:56:04 +0000 (22:56 +0900)
committerJaehyun Kim <jeik01.kim@samsung.com>
Wed, 19 Apr 2023 02:19:44 +0000 (11:19 +0900)
Cache Agent and related DBs have been added to support cache
and logic using them has been implemented.

Change-Id: Ieaa8773959d9c3c6b0b0b2c8c90da243452a677d
Signed-off-by: Jaehyun Kim <jeik01.kim@samsung.com>
42 files changed:
CMakeLists.txt
agent/download-agent-client-mgr.c
agent/download-agent-dl-info.c
agent/download-agent-http-mgr.c
agent/download-agent-http-msg-handler.c
agent/download-agent-interface.c
agent/include/download-agent-dl-info.h
agent/include/download-agent-http-msg-handler.h
agent/include/download-agent-interface.h
cache-agent.pc.in [new file with mode: 0644]
cache/CMakeLists.txt [new file with mode: 0755]
cache/cache-agent-config.c [new file with mode: 0755]
cache/cache-agent-interface.c [new file with mode: 0755]
cache/cache-agent-storage.c [new file with mode: 0755]
cache/include/cache-agent-config.h [new file with mode: 0755]
cache/include/cache-agent-debug.h [new file with mode: 0755]
cache/include/cache-agent-defs.h [new file with mode: 0755]
cache/include/cache-agent-info.h [new file with mode: 0755]
cache/include/cache-agent-interface.h [new file with mode: 0755]
cache/include/cache-agent-storage.h [new file with mode: 0755]
packaging/download-provider.spec
provider-interface/download-provider-interface.c
provider-interface/include/download-provider-interface.h
provider/CMakeLists.txt
provider/download-provider-cache-manager.c [new file with mode: 0644]
provider/download-provider-cache.c [new file with mode: 0644]
provider/download-provider-client-manager.c
provider/download-provider-client.c
provider/download-provider-db.c
provider/download-provider-plugin-cache-agent.c [new file with mode: 0755]
provider/download-provider-plugin-download-agent.c
provider/download-provider-queue-manager.c
provider/download-provider-utils.c
provider/include/download-provider-cache-manager.h [new file with mode: 0644]
provider/include/download-provider-cache.h [new file with mode: 0644]
provider/include/download-provider-client.h
provider/include/download-provider-db-defs.h
provider/include/download-provider-db.h
provider/include/download-provider-plugin-cache-agent.h [new file with mode: 0644]
provider/include/download-provider-utils.h
provider/include/download-provider.h
res/var/lib/download-provider/settings [new file with mode: 0644]

index 5bc41c2..33d2472 100755 (executable)
@@ -46,6 +46,7 @@ IF(DEFINED MAX_CONCURRENT_DOWNLOADS)
 ENDIF(DEFINED MAX_CONCURRENT_DOWNLOADS)
 
 ADD_DEFINITIONS(-DLIB_AGENT_PATH=\"${LIB_AGENT_PATH}\")
+ADD_DEFINITIONS(-DLIB_CACHE_AGENT_PATH=\"${LIB_CACHE_AGENT_PATH}\")
 IF(BUILD_GCOV)
        ADD_DEFINITIONS(-DBUILD_GCOV)
 ENDIF(BUILD_GCOV)
@@ -59,6 +60,7 @@ ENDIF(BUILD_GTESTS)
 # BUILD
 
 ADD_SUBDIRECTORY(agent)
+ADD_SUBDIRECTORY(cache)
 ADD_SUBDIRECTORY(provider-interface)
 ADD_SUBDIRECTORY(provider)
 # i18n
@@ -71,9 +73,11 @@ SET(PACKAGE_DESCRIPTION "Defines for ${PROJECT_NAME}")
 
 CONFIGURE_FILE(download-provider.pc.in download-provider.pc @ONLY)
 CONFIGURE_FILE(download-agent.pc.in download-agent.pc @ONLY)
+CONFIGURE_FILE(cache-agent.pc.in cache-agent.pc @ONLY)
 
 INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-provider.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
 INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-agent.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/cache-agent.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
 
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.service DESTINATION /lib/systemd/system)
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.socket DESTINATION /lib/systemd/system)
index ba6e2c2..4e0a532 100755 (executable)
@@ -129,8 +129,24 @@ da_ret_t send_client_finished_info(da_info_t *da_info, int err)
                        DA_LOGE("http_msg_response is NULL");
                if (file_info->file_path)
                        info->saved_path = strdup(file_info->file_path);
+
+               if (file_info->pure_file_name) {
+                       if (file_info->extension) {
+                               char file_name[DA_MAX_FILE_NAME_LEN] = { 0, };
+                               snprintf(file_name, DA_MAX_FILE_NAME_LEN, "%s.%s", file_info->pure_file_name, file_info->extension);
+                               info->ori_file = strdup(file_name);
+                       } else {
+                               info->ori_file = strdup(file_info->pure_file_name);
+                       }
+               }
+
                if (http_info->etag_from_header)
                        info->etag = strdup(http_info->etag_from_header);
+               if (http_info->cache_control_from_header)
+                       info->cache_control = strdup(http_info->cache_control_from_header);
+               if (http_info->last_modified_from_header)
+                       info->last_modified = strdup(http_info->last_modified_from_header);
+
                info->err = err;
                da_info->cb_info.finished_cb(info,
                                req_info->user_req_data, req_info->user_client_data);
index edaddd7..568b304 100644 (file)
@@ -290,6 +290,7 @@ da_ret_t copy_user_input_data(da_info_t *da_info, const char *url,
 
                req_info->network_bonding = ext_data->network_bonding;
                req_info->disable_verify_host = ext_data->disable_verify_host;
+               req_info->cache = ext_data->cache;
 
                da_info->req_info = req_info;
        }
@@ -368,6 +369,8 @@ void destroy_http_info(http_info_t *http_info)
                NULL_CHECK_AND_FREE(http_info->location_url);
                NULL_CHECK_AND_FREE(http_info->content_type_from_header);
                NULL_CHECK_AND_FREE(http_info->etag_from_header);
+               NULL_CHECK_AND_FREE(http_info->cache_control_from_header);
+               NULL_CHECK_AND_FREE(http_info->last_modified_from_header);
                NULL_CHECK_AND_FREE(http_info->file_name_from_header);
                if (http_info->proxy_info) {
                        __destroy_proxy_info(http_info->proxy_info);
index bfe5905..0fdd565 100755 (executable)
@@ -1098,7 +1098,8 @@ da_ret_t __handle_http_status_code(http_info_t *http_info,
                DA_LOGV("HTTP Status is %d - 204 server got the request, \
                                but no content to reply back, \
                                304 means not modified!", http_status);
-               ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT;
+               if (!req_info->cache)
+                       ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT;
                break;
 
        case 416: // Requested range not satisfiable
@@ -1254,6 +1255,8 @@ da_ret_t __handle_event_http_header(http_raw_data_t *raw_data, da_info_t *da_inf
        da_size_t size = 0;
        char *mime_type = DA_NULL;
        char *etag = DA_NULL;
+       char *cache_control = DA_NULL;
+       char *last_modified = DA_NULL;
        char *file_name = DA_NULL;
 
        NULL_CHECK_RET(da_info);
@@ -1288,6 +1291,12 @@ da_ret_t __handle_event_http_header(http_raw_data_t *raw_data, da_info_t *da_inf
                        file_info->mime_type = strdup(mime_type);
                http_msg_response_get_ETag(http_msg_response, &etag);
                http_info->etag_from_header = etag;
+
+               http_msg_response_get_cache_control(http_msg_response, &cache_control);
+               http_info->cache_control_from_header = cache_control;
+               http_msg_response_get_last_modified(http_msg_response, &last_modified);
+               http_info->last_modified_from_header = last_modified;
+
                http_msg_response_get_content_disposition(
                                http_msg_response, http_msg, DA_NULL, &file_name);
                http_info->file_name_from_header = file_name;
index a1dc55a..4686809 100755 (executable)
@@ -843,8 +843,7 @@ da_bool_t http_msg_response_get_content_disposition(
        }
 }
 
-da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response,
-               char **out_value)
+da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value)
 {
        da_bool_t b_ret = DA_FALSE;
        http_header_t *header = NULL;
@@ -863,6 +862,55 @@ da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response,
        return DA_TRUE;
 }
 
+da_bool_t http_msg_response_get_cache_control(http_msg_response_t *http_msg_response, char **out_value)
+{
+       da_bool_t b_ret = DA_FALSE;
+       http_header_t *header = NULL;
+
+       if (!out_value) {
+               DA_LOGE("[NULL CHECK] out_value");
+               return DA_FALSE;
+       }
+
+       b_ret = __get_http_header_for_field(http_msg_response, HTTP_FIELD_CACHE_CONTROL,
+                       &header);
+       if (!b_ret) {
+               DA_LOGV("no cache-control");
+               return DA_FALSE;
+       }
+
+       if (header->value)
+               *out_value = strdup(header->value);
+       else
+               return DA_FALSE;
+
+       return DA_TRUE;
+}
+
+da_bool_t http_msg_response_get_last_modified(http_msg_response_t *http_msg_response, char **out_value)
+{
+       da_bool_t b_ret = DA_FALSE;
+       http_header_t *header = NULL;
+
+       if (!out_value) {
+               DA_LOGE("[NULL CHECK] out_value");
+               return DA_FALSE;
+       }
+
+       b_ret = __get_http_header_for_field(http_msg_response, HTTP_FIELD_LAST_MODIFIED,
+                       &header);
+       if (!b_ret) {
+               DA_LOGV("no last-modified");
+               return DA_FALSE;
+       }
+       if (header->value)
+               *out_value = strdup(header->value);
+       else
+               return DA_FALSE;
+
+       return DA_TRUE;
+}
+
 #ifdef _RAF_SUPPORT
 da_bool_t http_msg_response_get_RAF_mode(http_msg_response_t *http_msg_response,
                char **out_value)
index a01e328..212df53 100755 (executable)
@@ -84,6 +84,8 @@ int da_start_download(const char *url, req_data_t *ext_data,
                DA_SECURE_LOGI("pkg_name[%s]", ext_data->pkg_name);
        if (ext_data->network_bonding)
                DA_LOGD("network bonding option[%d]", ext_data->network_bonding);
+       if (ext_data->cache)
+               DA_LOGD("cache option[%d]", ext_data->cache);
        if (ext_data->user_req_data)
                DA_LOGI("user_req_data[%p]", ext_data->user_req_data);
        if (ext_data->user_client_data)
index fc35efb..4823018 100644 (file)
@@ -66,6 +66,7 @@ typedef struct {
        char *pkg_name;
        int network_bonding;
        int disable_verify_host;
+       int cache;
        void *user_req_data;
        void *user_client_data;
 } req_info_t;
@@ -141,6 +142,8 @@ typedef struct {
        char *file_name_from_header;
        da_size_t content_len_from_header;
        char *etag_from_header;
+       char *cache_control_from_header;
+       char *last_modified_from_header;
        int error_code; // for error value for http abort.
        da_size_t total_size;
 #ifdef _RAF_SUPPORT
index e37e183..5dd5ae8 100755 (executable)
@@ -40,6 +40,8 @@
 #define HTTP_FIELD_LOCATION "Location"
 #define HTTP_FIELD_DATA "Date"
 #define HTTP_FIELD_ETAG "ETag"
+#define HTTP_FIELD_CACHE_CONTROL "cache-control"
+#define HTTP_FIELD_LAST_MODIFIED "last-modified"
 #ifdef _RAF_SUPPORT
 #define HTTP_FIELD_RAF_MODE "x-direct-write"
 #endif
@@ -62,6 +64,8 @@ void http_msg_response_set_content_type(http_msg_response_t *http_msg_response,
 da_bool_t http_msg_response_get_content_length(http_msg_response_t *http_msg_response, da_size_t *out_length);
 da_bool_t http_msg_response_get_content_disposition(http_msg_response_t *http_msg_response, http_msg_t *http_msg, char **out_disposition, char **out_file_name);
 da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value);
+da_bool_t http_msg_response_get_cache_control(http_msg_response_t *http_msg_response, char **out_value);
+da_bool_t http_msg_response_get_last_modified(http_msg_response_t *http_msg_response, char **out_value);
 da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, char **out_value);
 da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, char **out_value);
 da_bool_t http_msg_response_get_transfer_encoding(http_msg_response_t *http_msg_response, char **out_value);
index 2e3e4b2..b40bc46 100755 (executable)
@@ -41,7 +41,10 @@ typedef struct {
 typedef struct {
        int download_id;
        char *saved_path;
+       char *ori_file;
        char *etag;
+       char *cache_control;
+       char *last_modified;
        int err;
        int http_status;
 } finished_info_t;
@@ -57,6 +60,7 @@ typedef struct {
        const char *pkg_name;
        int network_bonding;
        int disable_verify_host;
+       int cache;
        void *user_req_data;
        void *user_client_data;
 } req_data_t;
diff --git a/cache-agent.pc.in b/cache-agent.pc.in
new file mode 100644 (file)
index 0000000..5e82ec1
--- /dev/null
@@ -0,0 +1,6 @@
+# Package Information
+
+Name: @PROJECT_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Cflags: -I/usr/include/cacheagent
diff --git a/cache/CMakeLists.txt b/cache/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..887233e
--- /dev/null
@@ -0,0 +1,81 @@
+PROJECT(cacheagent C)
+
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+       SET(CMAKE_BUILD_TYPE "Release")
+ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
+
+SET(VERSION "0.1.0")
+
+FIND_PROGRAM(UNAME NAMES uname)
+EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "ARCH")
+IF("${ARCH}" MATCHES "^arm.*")
+       ADD_DEFINITIONS("-D_TARGET")
+       SET(CMAKE_C_FLAGS_RELEASE "-mabi=aapcs-linux -msoft-float -O2")
+ENDIF("${ARCH}" MATCHES "^arm.*")
+
+#CA Engine Include Directory
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/provider/include)
+
+SET(SRCS_PATH ".")
+SET(SRCS_CA
+        ${SRCS_PATH}/cache-agent-interface.c
+        ${SRCS_PATH}/cache-agent-storage.c
+        ${SRCS_PATH}/cache-agent-config.c
+)
+
+SET(HEADERS
+       include/cache-agent-defs.h
+       include/cache-agent-interface.h
+)
+
+INCLUDE(FindPkgConfig)
+
+pkg_check_modules(subpkgs REQUIRED
+       xdgmime
+       vconf
+       dlog
+       storage
+)
+
+IF(SUPPORT_LARGE_FILE)
+       MESSAGE("Large file:On")
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -D_FILE_OFFSET_BITS=64")
+ENDIF(SUPPORT_LARGE_FILE)
+
+FOREACH(flag ${subpkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+IF (SUPPORT_OMA_DRM)
+       FOREACH(flag ${drmpkgs_CFLAGS})
+               SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+       ENDFOREACH(flag)
+       #This is request of DRM Team.
+       ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64")
+ENDIF (SUPPORT_OMA_DRM)
+
+IF (USE_SSL_THREAD_LOCKING)
+       MESSAGE("USE_SSL_THREAD_LOCKING(openssl<=1.0):On")
+       ADD_DEFINITIONS("-DUSE_SSL_THREAD_LOCKING")
+ENDIF (USE_SSL_THREAD_LOCKING)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fpic -Wall -Werror -Werror-implicit-function-declaration")
+IF (BUILD_GTESTS)
+       SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fvisibility=default")
+ELSE (BUILD_GTESTS)
+       SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fvisibility=hidden")
+ENDIF (BUILD_GTESTS)
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -fPIE")
+SET(CMAKE_C_FLAGS_RELEASE "-O2 -fPIE")
+
+ADD_DEFINITIONS("-D_ENABLE_DLOG")
+#This should be removed when release a target
+
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS_CA})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${subpkgs_LDFLAGS} ${drmpkgs_LDFLAGS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/cache-agent-interface.h DESTINATION ${INCLUDE_INSTALL_DIR}/${PKG_NAME})
diff --git a/cache/cache-agent-config.c b/cache/cache-agent-config.c
new file mode 100755 (executable)
index 0000000..84b927c
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include "include/cache-agent-config.h"
+
+static pthread_mutex_t mutex_ca_config_info = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_config_capa_cb = PTHREAD_MUTEX_INITIALIZER;
+static cache_config_info_t config_info = { 0, 0, CA_NULL, 0, 0, CA_NULL };
+
+
+static GKeyFile *__ca_config_keyfile_load(const char *pathname)
+{
+       GKeyFile *keyfile = NULL;
+       GError *error = NULL;
+
+       keyfile = g_key_file_new();
+       if (g_key_file_load_from_file(keyfile, pathname, 0, &error) != TRUE) {
+               CA_LOGE("Unable to open %s, error %s", pathname, error->message);
+               g_error_free(error);
+
+               g_key_file_free(keyfile);
+               keyfile = NULL;
+       }
+
+       CA_LOGD("loaded keyfile %s", pathname);
+       return keyfile;
+}
+
+static int __ca_config_keyfile_save(GKeyFile *keyfile, char *pathname)
+{
+       int ret = CA_RESULT_OK;
+       gchar *data = NULL;
+       gsize length = 0;
+       GError *error = NULL;
+
+       data = g_key_file_to_data(keyfile, &length, NULL);
+
+       if (!g_file_set_contents(pathname, data, length, &error)) {
+               CA_LOGE("Failed to store configurations: %s", error->message);
+               g_error_free(error);
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+       }
+
+       g_free(data);
+       return ret;
+}
+
+static int __load_configurations(void)
+{
+       int ret = CA_RESULT_OK;
+       GKeyFile *keyfile = NULL;
+       char *str;
+       int value;
+       GError *error = NULL;
+
+       config_info.max_cache_size = CA_MAX_CACHE_SIZE;
+       config_info.max_lifecycle = CA_MAX_LIFECYCLE;
+
+       keyfile = __ca_config_keyfile_load(CA_CONFIG_FILE_PATH);
+       if (keyfile == NULL) {
+               CA_LOGE("keyfile[%s] is NULL", CA_CONFIG_FILE_PATH);
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+               goto done;
+       }
+
+       str = g_key_file_get_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, &error);
+       if (!str || *str == '\0') {
+               CA_LOGE("Failed to get string value from config file: %s, use default value: %s",
+                               error->message, CA_STORAGE_PATH);
+               free(str);
+               g_error_free(error);
+               error = NULL;
+
+               config_info.cache_storage_path = strdup(CA_STORAGE_PATH);
+       } else {
+               g_strchomp(str);
+               CA_LOGD("Cache path: %s", str);
+               config_info.cache_storage_path = str;
+       }
+
+       if (!config_info.cache_storage_path) {
+               ret = CA_ERR_FAIL_TO_MEMALLOC;
+               goto done;
+       }
+
+       value = g_key_file_get_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, &error);
+       if (error) {
+               CA_LOGE("Failed to get max cache size from config file: %s, use default value: %d",
+                               error->message, CA_MAX_CACHE_SIZE);
+               g_error_free(error);
+               error = NULL;
+       } else {
+               CA_LOGD("Max cache size: %d", value);
+               config_info.max_cache_size = value;
+       }
+
+       value = g_key_file_get_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, &error);
+       if (error) {
+               CA_LOGE("Failed to get max lifecycle from config file: %s, use default value: %d",
+                               error->message, CA_MAX_LIFECYCLE);
+               g_error_free(error);
+       } else {
+               CA_LOGD("Max lifecycle: %d", value);
+               config_info.max_lifecycle = value;
+       }
+
+done:
+       if (keyfile)
+               g_key_file_free(keyfile);
+
+       return ret;
+}
+
+static int __save_configurations(void)
+{
+       int ret = CA_RESULT_OK;
+       GKeyFile *keyfile = NULL;
+
+       keyfile = __ca_config_keyfile_load(CA_CONFIG_FILE_PATH);
+       if (keyfile == NULL) {
+               CA_LOGE("keyfile[%s] is NULL, create new file", CA_CONFIG_FILE_PATH);
+               keyfile = g_key_file_new();
+       }
+
+       if (config_info.cache_storage_path)
+               g_key_file_set_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, config_info.cache_storage_path);
+       else
+               g_key_file_set_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, CA_STORAGE_PATH);
+
+       if (config_info.max_cache_size > 0)
+               g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, config_info.max_cache_size);
+       else
+               g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, CA_MAX_CACHE_SIZE);
+
+       if (config_info.max_lifecycle > 0)
+               g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, config_info.max_lifecycle);
+       else
+               g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, CA_MAX_LIFECYCLE);
+
+       __ca_config_keyfile_save(keyfile, CA_CONFIG_FILE_PATH);
+
+       if (keyfile)
+               g_key_file_free(keyfile);
+
+       return ret;
+}
+
+int ca_config_init()
+{
+       int ret = CA_RESULT_OK;
+
+       CA_LOGI("");
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       ret = __load_configurations();
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_deinit()
+{
+       int ret = CA_RESULT_OK;
+
+       CA_LOGI("");
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       free(config_info.cache_storage_path);
+       config_info.cache_storage_path = CA_NULL;
+       config_info.max_cache_size = 0;
+       config_info.max_lifecycle = 0;
+       config_info.cache_size = 0;
+       config_info.oldest_time = 0;
+       config_info.capa_cb = CA_NULL;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_set_cache_path(const char *cache_path)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       if (config_info.cache_storage_path)
+               free(config_info.cache_storage_path);
+
+       config_info.cache_storage_path = strdup(cache_path);
+       if (!config_info.cache_storage_path)
+               ret = CA_ERR_FAIL_TO_MEMALLOC;
+       else
+               __save_configurations();
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+char *ca_config_get_cache_path(void)
+{
+       char *cache_path;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       if (config_info.cache_storage_path)
+               cache_path = strdup(config_info.cache_storage_path);
+       else
+               cache_path = strdup(CA_STORAGE_PATH);
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return cache_path;
+}
+
+int ca_config_set_max_cache_size(unsigned int max_cache_size)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       config_info.max_cache_size = max_cache_size;
+       __save_configurations();
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_get_max_cache_size(unsigned int *max_cache_size)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       *max_cache_size = (config_info.max_cache_size > 0) ? config_info.max_cache_size : CA_MAX_CACHE_SIZE;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_set_max_lifecycle(unsigned int max_lifecycle)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       config_info.max_lifecycle = max_lifecycle;
+       __save_configurations();
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_get_max_lifecycle(unsigned int *max_lifecycle)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       *max_lifecycle = (config_info.max_lifecycle > 0) ? config_info.max_lifecycle : CA_MAX_LIFECYCLE;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_set_oldest_time(ca_time_t oldest_time)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       config_info.oldest_time = oldest_time;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_get_oldest_time(ca_time_t *oldest_time)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       *oldest_time = (config_info.oldest_time > 0) ? config_info.oldest_time : 0;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_set_cache_size(ca_size_t cache_size)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       config_info.cache_size = cache_size;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_get_cache_size(ca_size_t *cache_size)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+       *cache_size = (config_info.cache_size > 0) ? config_info.cache_size : 0;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+       return ret;
+}
+
+int ca_config_set_capacity_cb(ca_capacity_event_cb capa_cb)
+{
+       int ret = CA_RESULT_OK;
+       CA_MUTEX_LOCK(&(mutex_ca_config_capa_cb));
+
+       config_info.capa_cb = capa_cb;
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_capa_cb));
+       return ret;
+}
+
+GSList *ca_config_call_capacity_cb(ca_capacity_event_type type)
+{
+       GSList *file_list = CA_NULL;
+
+       CA_MUTEX_LOCK(&(mutex_ca_config_capa_cb));
+
+       if (config_info.capa_cb)
+               file_list = config_info.capa_cb(type);
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_config_capa_cb));
+
+       return file_list;
+}
diff --git a/cache/cache-agent-interface.c b/cache/cache-agent-interface.c
new file mode 100755 (executable)
index 0000000..40dd21d
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "include/cache-agent-interface.h"
+
+static pthread_mutex_t mutex_ca_block_request = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_capa_handling = PTHREAD_MUTEX_INITIALIZER;
+static ca_bool_t block_request = CA_TRUE;
+static pthread_t g_capa_tid = 0;
+
+static void __set_block_request(ca_bool_t update)
+{
+       CA_MUTEX_LOCK(&mutex_ca_block_request);
+       block_request = update;
+       CA_MUTEX_UNLOCK(&mutex_ca_block_request);
+}
+
+static int __cancel_cache_download(int req_id, ca_bool_t update)
+{
+       int ret = CA_RESULT_OK;
+       ca_info_t *ca_info = CA_NULL;
+
+       CA_LOGV("cache_id[%d]", req_id);
+       ret = ca_get_info_with_ca_id(req_id, &ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       ca_info->is_cb_update = update;
+
+       ret = ca_request_to_cancel_cache_download(ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       CA_LOGI("Cache-download is canceled for cache id[%d]", req_id);
+
+done:
+       CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+       return ret;
+}
+
+static int __suspend_cache_download(int req_id, ca_bool_t update)
+{
+       int ret = CA_RESULT_OK;
+       ca_info_t *ca_info = CA_NULL;
+
+       CA_LOGV("cache_id[%d]", req_id);
+       ret = ca_get_info_with_ca_id(req_id, &ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       ca_info->is_cb_update = update;
+
+       ret = ca_request_to_suspend_cache_download(ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       CA_LOGI("Cache-download is paused for cache id[%d]", req_id);
+
+done:
+       CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+       return ret;
+}
+
+static int __check_capacity_exceeded(const char *file_path, ca_bool_t *is_allowed)
+{
+       int ret = CA_RESULT_OK;
+       ca_size_t cache_size = 0;
+       unsigned int max_cache_size = 0;
+       ca_size_t src_file_size = 0;
+       *is_allowed = CA_FALSE;
+
+       if (file_path)
+               ca_storage_get_file_size(file_path, &src_file_size);
+
+       if (src_file_size < 0) {
+               CA_LOGE("Unable to get the file size!");
+               return CA_ERR_FAIL_TO_ACCESS_FILE;
+       }
+
+       ca_config_get_cache_size(&cache_size);
+       ca_config_get_max_cache_size(&max_cache_size);
+       cache_size /= CA_MB_BYTE;
+       src_file_size /= CA_MB_BYTE;
+
+       if (cache_size + src_file_size >= max_cache_size * CA_CAPA_THRESHOLD_RATIO) {
+               CA_LOGD("Capacity is exceeded!, cache_size: %lld, src_file_size: %lld, max_cache_size * %f: %f",
+                               cache_size, src_file_size, CA_CAPA_THRESHOLD_RATIO,
+                               max_cache_size * CA_CAPA_THRESHOLD_RATIO);
+               ret = CA_ERR_DISK_FULL;
+       }
+
+       if (cache_size + src_file_size < max_cache_size)
+               *is_allowed = CA_TRUE;
+
+       return ret;
+}
+
+static ca_bool_t __check_lifecycle_exceeded(void)
+{
+       ca_time_t oldest_time;
+       unsigned int max_lifecycle;
+
+       ca_config_get_oldest_time(&oldest_time);
+       if (oldest_time == 0) {
+               ca_config_set_oldest_time(time(CA_NULL));
+               return CA_FALSE;
+       }
+
+       ca_config_get_max_lifecycle(&max_lifecycle);
+
+       if (difftime(time(CA_NULL), oldest_time) >= max_lifecycle * CA_CAPA_THRESHOLD_RATIO) {
+               CA_LOGD("Lifecycle expired!, oldest_time: %ld, max_lifecycle: %u, difftime: %f, max_lifecycle * %f: %f",
+                                       oldest_time, max_lifecycle, difftime(time(CA_NULL), oldest_time),
+                                       CA_CAPA_THRESHOLD_RATIO, max_lifecycle * CA_CAPA_THRESHOLD_RATIO);
+               return CA_TRUE;
+       }
+
+       return CA_FALSE;
+}
+
+static char **__convert_gslist_to_array(GSList *file_list, unsigned int *file_count)
+{
+       char **file_array = CA_NULL;
+       int index = 0;
+
+       *file_count = g_slist_length(file_list);
+       if (*file_count == 0)
+               return CA_NULL;
+
+       file_array = (char **)calloc(*file_count, sizeof(char *));
+       if (!file_array)
+               return CA_NULL;
+
+       for (GSList* node = file_list; node != NULL; node = node->next) {
+               file_array[index] = (char *)node->data;
+               index++;
+       }
+
+       return file_array;
+}
+
+static void *__thread_clear_exceeded_files(void *data)
+{
+       GSList *file_list = CA_NULL;
+       char **file_array = CA_NULL;
+       unsigned int file_count = 0;
+       ca_capacity_event_type type = (ca_capacity_event_type)data;
+
+       if (type != CA_CAPACITY_MAX_SIZE && type != CA_CAPACITY_LIFECYCLE)
+               goto done;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, CA_NULL);
+
+       __set_block_request(CA_TRUE);
+
+       file_list = ca_config_call_capacity_cb(type);
+       if (!file_list)
+               goto done;
+
+       file_array = __convert_gslist_to_array(file_list, &file_count);
+       if (!file_array) {
+               g_slist_free_full(file_list, g_free);
+               goto done;
+       }
+
+       ca_storage_remove_files((int)file_count, (const char **) file_array);
+
+       g_slist_free_full(file_list, g_free);
+       free(file_array);
+
+done:
+       __set_block_request(CA_FALSE);
+
+       CA_LOGI("=====EXIT thread : ca_id[%lu]=====", g_capa_tid);
+
+       CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+       g_capa_tid = 0;
+       CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+       pthread_exit((void *)CA_NULL);
+       return CA_NULL;
+}
+
+static void __create_capacity_thread(ca_capacity_event_type type)
+{
+       pthread_attr_t thread_attr;
+
+       if (pthread_attr_init(&thread_attr) != 0)
+               return;
+
+       if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0)
+               return;
+
+       if (pthread_create(&g_capa_tid, &thread_attr, __thread_clear_exceeded_files, (void *)type) != 0) {
+               CA_LOGE("Fail to make thread capacity managing: type[%d]", type);
+               return;
+       }
+
+       CA_LOGI("Thread is created:thread id[%lu]", g_capa_tid);
+}
+
+int ca_init(ca_capacity_event_cb capa_cb)
+{
+       int ret = CA_RESULT_OK;
+       CA_LOGI("");
+
+       ret = ca_storage_init();
+       if (ret != CA_RESULT_OK)
+               return ret;
+
+       ret = ca_config_init();
+       if (ret != CA_RESULT_OK) {
+               ca_storage_deinit();
+               return ret;
+       }
+
+       ca_config_set_capacity_cb(capa_cb);
+       __set_block_request(CA_FALSE);
+
+       return CA_RESULT_OK;
+}
+
+int ca_deinit()
+{
+       int ret = CA_RESULT_OK;
+       CA_LOGI("====== ca_deint EXIT =====");
+
+       __set_block_request(CA_TRUE);
+
+       ca_storage_deinit();
+       ca_config_deinit();
+
+       return ret;
+}
+
+char *ca_store_file(const char *file_path)
+{
+       int ret = CA_RESULT_OK;
+       ca_bool_t is_allowed = CA_TRUE;
+
+       REQ_BLOCK_RET_PTR(&mutex_ca_block_request, block_request);
+
+       if (!file_path)
+               return CA_NULL;
+
+       CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+       if (g_capa_tid != 0) {
+               CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+               return CA_NULL;
+       }
+
+       ret = __check_capacity_exceeded(file_path, &is_allowed);
+       if (ret == CA_ERR_DISK_FULL)
+               __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
+
+       if (!is_allowed) {
+               CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+               return CA_NULL;
+       }
+
+       if (g_capa_tid == 0 && __check_lifecycle_exceeded())
+               __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+       return ca_storage_store_file(file_path);
+}
+
+int ca_remove_files(int file_count, const char *cache_files[])
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       if (file_count <= 0)
+               return CA_ERR_INVALID_ARGUMENT;
+
+       return ca_storage_remove_files(file_count, cache_files);
+}
+
+int ca_is_file_available(const char *cache_file)
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       if (!cache_file)
+               return CA_ERR_INVALID_ARGUMENT;
+
+       return ca_storage_is_file_available(cache_file);
+}
+
+int ca_get_used_cache_size(ca_size_t *size)
+{
+       if (!size)
+               return CA_ERR_INVALID_ARGUMENT;
+
+       return ca_storage_get_used_cache_size(size);
+}
+
+int ca_start_cache_download(const char *cache_file,
+               ca_req_data_t *ext_data, ca_cb_t *ca_cb_data, int *cache_id)
+{
+       int ret = CA_RESULT_OK;
+       int ca_id = CA_INVALID_ID;
+       ca_info_t *ca_info = CA_NULL;
+
+       if (!cache_file || !ext_data || !ca_cb_data || !cache_id) {
+               CA_LOGE("Null Check: cache_file, ext_data, ca_cb_data, cache_id");
+               return CA_ERR_INVALID_ARGUMENT;
+       }
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       ret = ca_get_available_ca_id(&ca_id);
+       if (ret != CA_RESULT_OK) {
+               CA_LOGE("No available cache id");
+               return CA_ERR_ALREADY_MAX_COPY;
+       }
+
+       ca_info = ca_get_ca_info(ca_id);
+       if (ca_info == CA_NULL) {
+               CA_LOGE("No available cache info, ca_id[%d]", ca_id);
+               return CA_ERR_INVALID_STATE;
+       }
+
+       ca_info->ca_id = ca_id;
+
+       if (ext_data->install_path)
+               CA_SECURE_LOGI("install path[%s]", ext_data->install_path);
+       if (ext_data->file_name)
+               CA_SECURE_LOGI("file_name[%s]", ext_data->file_name);
+       if (ext_data->pkg_name)
+               CA_SECURE_LOGI("pkg_name[%s]", ext_data->pkg_name);
+       if (ext_data->user_req_data)
+               CA_LOGI("user_req_data[%p]", ext_data->user_req_data);
+       if (ext_data->user_client_data)
+               CA_LOGI("user_client_data[%p]", ext_data->user_client_data);
+
+       ret = ca_init_cache_download_data(ca_info, cache_file, ext_data);
+       if (ret != CA_RESULT_OK) {
+               ca_destroy_ca_info(ca_info->ca_id);
+               CA_LOGE("Unable to init cache download info");
+               return CA_ERR_INVALID_STATE;
+       }
+
+       ca_info->is_cb_update = CA_TRUE;
+       memcpy(&(ca_info->cb_info), ca_cb_data, sizeof(ca_cb_t));
+
+       ret = ca_storage_copy_file(ca_info);
+
+       if (ret != CA_RESULT_OK) {
+               if (ca_info->dst_fp && ca_info->dst_file) {
+                       fclose(ca_info->dst_fp);
+                       unlink(ca_info->dst_file);
+                       ca_info->dst_fp = CA_NULL;
+               }
+
+               ca_destroy_ca_info(ca_info->ca_id);
+       }
+
+       *cache_id = ca_id;
+       CA_LOGI("Return:id[%d], ret[%d]", *cache_id, ret);
+
+       return ret;
+}
+
+int ca_pause_cache_download(int req_id)
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+       return __suspend_cache_download(req_id, CA_TRUE);
+}
+
+int ca_resume_cache_download(int req_id)
+{
+       int ret = CA_RESULT_OK;
+       ca_info_t *ca_info = CA_NULL;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       CA_LOGV("cache_id[%d]", req_id);
+       ret = ca_get_info_with_ca_id(req_id, &ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       ca_info->is_cb_update = CA_TRUE;
+
+       ret = ca_request_to_resume_cache_download(ca_info);
+       if (ret != CA_RESULT_OK)
+               goto done;
+
+       CA_LOGI("Cache-download is resumed for cache id[%d]", req_id);
+
+done:
+       CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+       return ret;
+}
+
+int ca_cancel_cache_download(int req_id)
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+       return __cancel_cache_download(req_id, CA_TRUE);
+}
+
+int ca_pause_cache_download_without_update(int req_id)
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+       return __suspend_cache_download(req_id, CA_FALSE);
+}
+
+int ca_cancel_cache_download_without_update(int req_id)
+{
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+       return __cancel_cache_download(req_id, CA_FALSE);
+}
+
+int ca_is_alive_cache_download(int req_id)
+{
+       int ret = CA_RESULT_OK;
+       ca_info_t *ca_info = CA_NULL;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       ca_info = ca_get_ca_info(req_id);
+       if (ca_info == CA_NULL) {
+               CA_LOGE("No available cache info, ca_id[%d]", req_id);
+               ret = CA_ERR_INVALID_ARGUMENT;
+               goto done;
+       }
+
+       if (CA_RESULT_OK != ca_check_ca_id(ca_info, req_id)) {
+               CA_LOGE("Invalid ca_id[%d]", ca_info->ca_id);
+               ret = CA_ERR_INVALID_ARGUMENT;
+       }
+
+done:
+       CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+       return ret;
+}
+
+int ca_get_max_cache_size(unsigned int *size)
+{
+       return ca_config_get_max_cache_size(size);
+}
+
+int ca_set_max_cache_size(unsigned int size)
+{
+       int ret = CA_RESULT_OK;
+       ca_bool_t is_allowed;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       ret = ca_config_set_max_cache_size(size);
+       if (ret != CA_RESULT_OK)
+               return ret;
+
+       CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+       if (g_capa_tid != 0) {
+               CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+               return ret;
+       }
+
+       ret = __check_capacity_exceeded(CA_NULL, &is_allowed);
+       if (ret == CA_ERR_DISK_FULL)
+               __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+       return CA_RESULT_OK;
+}
+
+int ca_get_cache_path(char **path)
+{
+       int ret = CA_RESULT_OK;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       *path = (char *)ca_config_get_cache_path();
+       if (!*path)
+               ret = CA_ERR_INVALID_STATE;
+
+       return ret;
+}
+
+int ca_set_cache_path(const char *path)
+{
+       int ret = CA_RESULT_OK;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       __set_block_request(CA_TRUE);
+
+       ca_cancel_all_cache_operations();
+
+       ret = ca_clear_all_cache_files();
+       if (ret != CA_RESULT_OK) {
+               __set_block_request(CA_FALSE);
+               return ret;
+       }
+
+       ca_config_set_cache_path(path);
+
+       __set_block_request(CA_FALSE);
+
+       return ret;
+}
+
+int ca_get_cache_lifecycle(unsigned int *time)
+{
+       return ca_config_get_max_lifecycle(time);
+}
+
+int ca_set_cache_lifecycle(unsigned int time)
+{
+       int ret = CA_RESULT_OK;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       ret = ca_config_set_max_lifecycle(time);
+       if (ret != CA_RESULT_OK)
+               return ret;
+
+       CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+       if (g_capa_tid == 0 && __check_lifecycle_exceeded())
+               __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
+
+       CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+       return ret;
+}
+
+int ca_clear_all_files(void)
+{
+       int ret = CA_RESULT_OK;
+
+       REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+       __set_block_request(CA_TRUE);
+
+       ca_cancel_all_cache_operations();
+       ret = ca_clear_all_cache_files();
+
+       ca_config_set_cache_size(0);
+       ca_config_set_oldest_time(time(CA_NULL));
+
+       __set_block_request(CA_FALSE);
+
+       return ret;
+}
+
+int ca_set_oldest_file_time(ca_time_t oldest_time)
+{
+       return ca_config_set_oldest_time(oldest_time);
+}
diff --git a/cache/cache-agent-storage.c b/cache/cache-agent-storage.c
new file mode 100755 (executable)
index 0000000..820e145
--- /dev/null
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <pthread.h>
+#include <ftw.h>
+#include "include/cache-agent-interface.h"
+
+static pthread_mutex_t mutex_ca_info_list = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_file_io = PTHREAD_MUTEX_INITIALIZER;
+static ca_info_t *ca_info_list[CA_MAX_ID];
+static ca_size_t cache_dir_size;
+
+static char *__get_file_name(const char *file_path)
+{
+       char *file_name = CA_NULL;
+
+       file_name = strrchr(file_path, '/');
+       if (file_name)
+               ++file_name;
+       else
+               return strdup(file_path);
+
+       return strdup(file_name);
+}
+
+static void __ca_progress_cb(int cache_id,
+               unsigned long long copied_size, void *user_req_data, void *user_client_data)
+{
+       CA_LOGI("__ca_progress_cb id: %d, copied size: %llu", cache_id, copied_size);
+}
+
+static int __sum(const char *fpath, const struct stat *sb, int typeflag) {
+       cache_dir_size += sb->st_size;
+       return 0;
+}
+
+static void __destroy_ca_req_info(ca_req_info_t *req_info)
+{
+       if (req_info) {
+               if (req_info->install_path)
+                       free(req_info->install_path);
+               if (req_info->file_name)
+                       free(req_info->file_name);
+               if (req_info->pkg_name)
+                       free(req_info->pkg_name);
+
+               req_info->user_req_data = CA_NULL;
+               req_info->user_client_data = CA_NULL;
+       }
+}
+
+static void __init_ca_info(int id)
+{
+       ca_info_t *ca_info = CA_NULL;
+       ca_req_info_t *req_info = CA_NULL;
+
+       ca_info = (ca_info_t *)calloc(1, sizeof(ca_info_t));
+       if (!ca_info) {
+               CA_LOGE("Fail to calloc. id[%d]", id);
+               ca_info_list[id] = CA_NULL;
+               return;
+       }
+
+       req_info = (ca_req_info_t *)calloc(1, sizeof(ca_req_info_t));
+       if (!req_info) {
+               CA_LOGE("Fail to calloc. id[%d]", id);
+               free(ca_info);
+               return;
+       }
+
+       CA_MUTEX_INIT(&(ca_info->mutex_state), CA_NULL);
+       CA_COND_INIT(&(ca_info->cond), CA_NULL);
+
+       ca_info->state = CA_STATE_NONE;
+       ca_info->ca_id = CA_INVALID_ID;
+       ca_info->req_info = req_info;
+       ca_info_list[id] = ca_info;
+}
+
+static int __remove_func(const char *filename,
+                       const struct stat *statptr, int fileflags)
+{
+       int ret = 0;
+       char *cache_path = ca_config_get_cache_path();
+
+       if (strlen(filename) == strlen(cache_path)) {
+               free(cache_path);
+               return ret;
+       }
+
+       ret = unlink(filename);
+       if (ret < 0) {
+               char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+               strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+               CA_LOGE("Unable to remove the file: %s, error [%d/%s]", filename, errno, err_buf);
+       }
+
+       free(cache_path);
+       return ret;
+}
+
+void ca_destroy_ca_info(int id)
+{
+       ca_info_t *ca_info = CA_NULL;
+
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+       ca_info = ca_info_list[id];
+       if (ca_info) {
+               if (ca_info->dst_fp)
+                       fclose(ca_info->dst_fp);
+
+               if (ca_info->src_file)
+                       free(ca_info->src_file);
+
+               if (ca_info->dst_file)
+                       free(ca_info->dst_file);
+
+               if (ca_info->file_name)
+                       free(ca_info->file_name);
+
+               if (ca_info->req_info) {
+                       __destroy_ca_req_info(ca_info->req_info);
+                       free(ca_info->req_info);
+               }
+
+               CA_MUTEX_DESTROY(&(ca_info->mutex_state));
+               CA_COND_DESTROY(&(ca_info->cond));
+
+               free(ca_info);
+               ca_info_list[id] = CA_NULL;
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+}
+
+ca_info_t *ca_get_ca_info(int ca_id)
+{
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+       ca_info_t *ca_info = ca_info_list[ca_id];
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+       return ca_info;
+}
+
+int ca_check_ca_id(ca_info_t *ca_info, int ca_id)
+{
+       int ret = CA_ERR_INVALID_ARGUMENT;
+
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+       if (ca_info && ca_info->ca_id == ca_id)
+               ret = CA_RESULT_OK;
+
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+       return ret;
+}
+
+int ca_get_info_with_ca_id(int id, ca_info_t **out_info)
+{
+       int ret = CA_ERR_INVALID_ARGUMENT;
+
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+       for (int i = 0; i < CA_MAX_ID; i++) {
+               if (CA_NULL != ca_info_list[i] && ca_info_list[i]->ca_id == id) {
+                       *out_info = ca_info_list[i];
+                       ret = CA_RESULT_OK;
+                       break;
+               }
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+       return ret;
+}
+
+void ca_cancel_all_cache_operations(void)
+{
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+       for (int i = 0; i < CA_MAX_ID; i++) {
+               if (CA_NULL != ca_info_list[i])
+                       ca_request_to_cancel_cache_download(ca_info_list[i]);
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+}
+
+int ca_clear_all_cache_files(void)
+{
+       int ret = CA_RESULT_OK;
+       char *cache_path = ca_config_get_cache_path();
+
+       if (ftw(cache_path, __remove_func, 1) == -1)
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+
+       free(cache_path);
+       return ret;
+}
+
+int ca_request_to_cancel_cache_download(ca_info_t *ca_info)
+{
+       int ret = CA_RESULT_OK;
+
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       CA_LOGD("state [%d]", ca_info->state);
+
+       switch (ca_info->state) {
+       case CA_STATE_PAUSED:
+               CA_COND_SIGNAL(&(ca_info->cond));
+               ca_info->state = CA_STATE_CANCELED;
+               break;
+       case CA_STATE_COPYING:
+               ca_info->state = CA_STATE_CANCELED;
+               break;
+       case CA_STATE_CANCELED:
+               ret = CA_ERR_ALREADY_CANCELED;
+               break;
+       case CA_STATE_NONE:
+       case CA_STATE_COMPLETED:
+       case CA_STATE_FAILED:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       default:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       }
+
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+       return ret;
+}
+
+int ca_request_to_suspend_cache_download(ca_info_t *ca_info)
+{
+       int ret = CA_RESULT_OK;
+
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       CA_LOGD("state [%d]", ca_info->state);
+
+       switch (ca_info->state) {
+       case CA_STATE_PAUSED:
+               ret = CA_ERR_ALREADY_SUSPENDED;
+               break;
+       case CA_STATE_COPYING:
+               ca_info->state = CA_STATE_PAUSED;
+               break;
+       case CA_STATE_NONE:
+       case CA_STATE_COMPLETED:
+       case CA_STATE_CANCELED:
+       case CA_STATE_FAILED:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       default:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       }
+
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+       return ret;
+}
+
+int ca_request_to_resume_cache_download(ca_info_t *ca_info)
+{
+       int ret = CA_RESULT_OK;
+
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       CA_LOGD("state [%d]", ca_info->state);
+
+       switch (ca_info->state) {
+       case CA_STATE_PAUSED:
+               CA_COND_SIGNAL(&(ca_info->cond));
+               ca_info->state = CA_STATE_COPYING;
+               break;
+       case CA_STATE_COPYING:
+       case CA_STATE_NONE:
+       case CA_STATE_COMPLETED:
+       case CA_STATE_CANCELED:
+       case CA_STATE_FAILED:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       default:
+               ret = CA_ERR_INVALID_STATE;
+               break;
+       }
+
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+       return ret;
+}
+
+int ca_get_available_ca_id(int *available_id)
+{
+       int ret = CA_ERR_ALREADY_MAX_COPY;
+       int i = 0;
+
+       CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+       for (i = 0; i < CA_MAX_ID; i++) {
+               if (ca_info_list[i] == CA_NULL) {
+                       *available_id = i;
+                       CA_LOGV("available cache id[%d]", *available_id);
+
+                       __init_ca_info(i);
+                       ret = CA_RESULT_OK;
+
+                       break;
+               }
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+       return ret;
+}
+
+void ca_storage_get_file_size(const char *file_path, ca_size_t *file_size)
+{
+       struct stat dir_state;
+       int stat_ret;
+
+       *file_size = -1;
+
+       if (!file_path) {
+               CA_LOGE("NULL CHECK!: file path");
+               return;
+       }
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+       stat_ret = stat(file_path, &dir_state);
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       if (stat_ret != 0)
+               return;
+
+       if (dir_state.st_mode & S_IFREG) {
+               CA_LOGV("file size = %lu", dir_state.st_size);
+               *file_size = dir_state.st_size;
+       }
+}
+
+static ca_file_state_e __get_file_state(const char *file_path)
+{
+       struct stat dir_state;
+       int stat_ret;
+
+       if (file_path == CA_NULL) {
+               CA_LOGE("file path is CA_NULL");
+               return CA_NOT_EXIST_FILE;
+       }
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+       stat_ret = stat(file_path, &dir_state);
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       if (stat_ret == 0) {
+               if (dir_state.st_mode & S_IFREG)
+                       return CA_REGULAR_FILE;
+
+               return CA_OTHER_FILE;
+       }
+       return CA_NOT_EXIST_FILE;
+}
+
+static int __extract_file_name_and_extension(const char *in_file_name, char **pure_file_name, char **extension)
+{
+       char *file_name = CA_NULL;
+       char *tmp_ptr = CA_NULL;
+       char temp_file[CA_MAX_FILE_NAME_LEN + 1] = {0,};
+       char tmp_ext[CA_MAX_FILE_NAME_LEN] = {0,};
+       int len = 0;
+
+       if (!in_file_name || !pure_file_name)
+               return CA_ERR_INVALID_ARGUMENT;
+
+       file_name = (char *)in_file_name;
+       tmp_ptr = strrchr(file_name, '.');
+
+       if (!tmp_ptr || tmp_ptr == file_name)
+               tmp_ptr = CA_NULL;
+       else
+               tmp_ptr++;
+
+       if (tmp_ptr && extension) {
+               strncpy((char*)tmp_ext, tmp_ptr, sizeof(tmp_ext) - 1);
+               *extension = strdup((const char *)tmp_ext);
+               CA_LOGD("extension [%s]", *extension);
+       }
+
+       if (tmp_ptr)
+               len = tmp_ptr - file_name - 1;
+       else
+               len = strlen(file_name);
+
+       if (len >= CA_MAX_FILE_NAME_LEN) {
+               strncpy((char*)temp_file, file_name, CA_MAX_FILE_NAME_LEN);
+               temp_file[CA_MAX_FILE_NAME_LEN] = '\0';
+       } else {
+               strncpy((char*) temp_file, file_name, len);
+               temp_file[len] = '\0';
+       }
+
+       if (strlen(temp_file) >= 1)
+               *pure_file_name = strdup((const char*)temp_file);
+
+       CA_LOGD("pure file name [%s]", *pure_file_name ? *pure_file_name : "NoName");
+       return CA_RESULT_OK;
+}
+
+static char *__generate_cache_file_name(char *dst_path, char *file_name)
+{
+       int i;
+       char *cache_path;
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+
+       cache_path = ca_config_get_cache_path();
+
+       for (i = 1; i <= CA_MAX_DUP_FILE_COUNT; i++) {
+               if (access(dst_path, F_OK) != 0)
+                       break;
+
+               snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s.%d", cache_path, file_name, i);
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       free(cache_path);
+
+       if (i > CA_MAX_DUP_FILE_COUNT)
+               return CA_NULL;
+
+       return __get_file_name(dst_path);
+}
+
+static char *__generate_dst_file_name(char *dst_path, const char *dir_path, const char *file_name)
+{
+       int i;
+       char *pure_file_name = CA_NULL;
+       char *extension = CA_NULL;
+
+       __extract_file_name_and_extension(file_name, &pure_file_name, &extension);
+       if (!pure_file_name)
+               return CA_NULL;
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+
+       for (i = 1; i <= CA_MAX_DUP_FILE_COUNT; i++) {
+               if (access(dst_path, F_OK) != 0)
+                       break;
+
+               if (extension)
+                       snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s(%d).%s", dir_path, pure_file_name, i, extension);
+               else
+                       snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s(%d)", dir_path, pure_file_name, i);
+       }
+
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       free(pure_file_name);
+       free(extension);
+
+       if (i > CA_MAX_DUP_FILE_COUNT)
+               return CA_NULL;
+
+       return __get_file_name(dst_path);
+}
+
+static int __is_realpath(const char *file_path)
+{
+       char *resolved_path = CA_NULL;
+
+       if (!file_path)
+               return CA_ERR_INVALID_ARGUMENT;
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+       resolved_path = realpath(file_path, NULL);
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       if (resolved_path) {
+               /* Check if actual_file_path is symbolic file or not */
+               if (strcmp(resolved_path, file_path) != 0) {
+                       free(resolved_path);
+                       return CA_ERR_INVALID_ARGUMENT;
+               }
+       } else if (errno != ENOENT) {
+               return CA_ERR_INVALID_ARGUMENT;
+       }
+
+       free(resolved_path);
+       return CA_RESULT_OK;
+}
+
+static void *__open_dst_file(const char *dst_file)
+{
+       void *fp = CA_NULL;
+
+       if (!dst_file) {
+               CA_LOGE("NULL CHECK!: dst_file");
+               return CA_NULL;
+       }
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+       fp = fopen(dst_file, "wb");
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       if (fp == CA_NULL) {
+               char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+               strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+               CA_LOGE("File open failed [%d/%s]", errno, err_buf);
+
+               if (errno == ENOSPC)
+                       CA_LOGE("Disk full!");
+               else
+                       CA_LOGE("Fail to access file!");
+       }
+
+       return fp;
+}
+
+static void *__open_src_file(const char *src_file)
+{
+       void *fp = CA_NULL;
+
+       if (!src_file) {
+               CA_LOGE("NULL CHECK!: src_file");
+               return CA_NULL;
+       }
+
+       CA_MUTEX_LOCK(&mutex_ca_file_io);
+       fp = fopen(src_file, "rb");
+       CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+       if (fp == CA_NULL) {
+               char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+               strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+               CA_LOGE("File open failed [%d/%s]", errno, err_buf);
+       }
+
+       return fp;
+}
+
+static void __call_started_cb(int error, ca_info_t *ca_info, ca_size_t src_file_size)
+{
+       cache_info_t cache_info = { 0, CA_RESULT_OK, 0, CA_NULL, CA_NULL };
+
+       if (!ca_info)
+               return;
+
+       cache_info.cache_id = ca_info->ca_id;
+       cache_info.error = error;
+       cache_info.file_size = src_file_size;
+       cache_info.content_name = ca_info->file_name;
+       cache_info.saved_path = ca_info->dst_file;
+
+       if (ca_info->cb_info.cache_info_cb)
+               ca_info->cb_info.cache_info_cb(&cache_info,
+                               ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+
+static void __call_finidhed_cb(int error, int http_status, ca_info_t *ca_info, ca_size_t src_file_size)
+{
+       ca_finished_info_t finished_info = { 0, CA_RESULT_OK, 0, CA_NULL, 0};
+
+       if (!ca_info || !ca_info->is_cb_update)
+               return;
+
+       finished_info.cache_id = ca_info->ca_id;
+       finished_info.error = error;
+       finished_info.file_size = src_file_size;
+
+       if (error == CA_RESULT_OK)
+               finished_info.saved_path = ca_info->dst_file;
+
+       finished_info.http_status = http_status;
+
+       if (ca_info->cb_info.finished_cb)
+               ca_info->cb_info.finished_cb(&finished_info,
+                               ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+static void __call_paused_cb(ca_info_t *ca_info)
+{
+       if (!ca_info || !ca_info->is_cb_update)
+               return;
+
+       if (ca_info->cb_info.paused_cb)
+               ca_info->cb_info.paused_cb(ca_info->ca_id,
+                               ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+static void __set_cache_state(int state, ca_info_t *ca_info)
+{
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+
+       ca_info->state = state;
+
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+}
+
+static ca_bool_t __check_cancel_state(ca_info_t *ca_info)
+{
+       ca_bool_t ret = CA_TRUE;
+
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       if (ca_info->state != CA_STATE_CANCELED)
+               ret = CA_FALSE;
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+
+       return ret;
+}
+
+static void __handle_suspend_state(ca_info_t *ca_info)
+{
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       if (ca_info->state != CA_STATE_PAUSED) {
+               CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+               return;
+       }
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+
+       __call_paused_cb(ca_info);
+
+       CA_MUTEX_LOCK(&(ca_info->mutex_state));
+       CA_COND_WAIT(&(ca_info->cond), &(ca_info->mutex_state));
+       CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+}
+
+static int __copy_content(ca_info_t *ca_info)
+{
+       int ret = CA_RESULT_OK;
+       FILE *src_fp = CA_NULL;
+       FILE *dst_fp = CA_NULL;
+       void *file_buff = CA_NULL;
+       size_t r_size = 0;
+       size_t w_size = 0;
+       ca_size_t src_file_size = 0;
+       ca_size_t dst_file_size = 0;
+       struct timespec curr_time = {0};
+       __time_t prev_time = 0;
+
+       if (!ca_info || !ca_info->src_file || !ca_info->dst_file) {
+               CA_LOGE("NULL CHECK!: ca_info, src_file, dst_file");
+               ret = CA_ERR_INVALID_ARGUMENT;
+               goto done;
+       }
+
+       CA_LOGI("=====START thread : ca_id[%d]=====", ca_info->ca_id);
+
+       file_buff = calloc(CA_MAX_FILE_BUFFER_SIZE, sizeof(char));
+       if (!file_buff) {
+               CA_LOGE("Out of memory");
+               ret = CA_ERR_FAIL_TO_MEMALLOC;
+               goto done;
+       }
+
+       ca_storage_get_file_size(ca_info->src_file, &src_file_size);
+       if (src_file_size < 0) {
+               CA_LOGE("Unable to get dst file size!");
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+               goto done;
+       }
+
+       if (ca_info->dst_fp) {
+               dst_fp = ca_info->dst_fp;
+       } else {
+               dst_fp = __open_dst_file(ca_info->dst_file);
+               if (!dst_fp) {
+                       CA_LOGE("Unable to open dst file!");
+                       ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+                       goto done;
+               }
+               ca_info->dst_fp = dst_fp;
+       }
+
+       src_fp = (FILE *)__open_src_file(ca_info->src_file);
+       if (!src_fp) {
+               CA_LOGE("Unable to open src file!");
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+               goto done;
+       }
+
+       if (ca_info->is_cb_update)
+               __call_started_cb(CA_RESULT_OK, ca_info, src_file_size);
+
+       while (0 < (r_size = fread(file_buff, 1, CA_MAX_FILE_BUFFER_SIZE, src_fp)) ) {
+               w_size = fwrite(file_buff, 1, r_size, dst_fp);
+               if (w_size != r_size)
+                       break;
+
+               dst_file_size += w_size;
+
+               __handle_suspend_state(ca_info);
+
+               if (__check_cancel_state(ca_info)) {
+                       dst_file_size = src_file_size;
+                       ret = CA_RESULT_USER_CANCELED;
+                       break;
+               }
+
+               if (!ca_info->is_cb_update)
+                       continue;
+
+               clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_time);
+               if (curr_time.tv_sec - prev_time >= 1) {
+                       if (ca_info->cb_info.progress_cb)
+                               ca_info->cb_info.progress_cb(ca_info->ca_id, dst_file_size,
+                                               ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+
+                       prev_time = curr_time.tv_sec;
+               }
+       }
+
+       if (dst_file_size != src_file_size) {
+               ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+               CA_LOGE("The file sizes of the src and dst do not match!");
+       }
+
+       fclose(src_fp);
+       fclose(dst_fp);
+       ca_info->dst_fp = CA_NULL;
+
+done:
+       if (file_buff)
+               free(file_buff);
+
+       if (ret == CA_RESULT_OK)
+               __call_finidhed_cb(CA_RESULT_OK, 200, ca_info, src_file_size);
+       else
+               __call_finidhed_cb(ret, 0, ca_info, src_file_size);
+
+       if (ca_info && ca_info->is_cache_adding && ret == CA_RESULT_OK) {
+               ca_size_t cache_size;
+
+               ca_config_get_cache_size(&cache_size);
+               cache_size += dst_file_size;
+               ca_config_set_cache_size(cache_size);
+       }
+
+       return ret;
+}
+
+static void *__thread_start_copy(void *data)
+{
+       ca_info_t *ca_info = CA_NULL;
+       int ca_id = CA_INVALID_ID;
+       int ret = CA_RESULT_OK;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, CA_NULL);
+
+       ca_info = (ca_info_t *)data;
+       if (!ca_info) {
+               CA_LOGE("[NULL Check] ca_info");
+               goto done;
+       }
+
+       __set_cache_state(CA_STATE_COPYING, ca_info);
+
+       ca_id = ca_info->ca_id;
+
+       ret = __copy_content(ca_info);
+       if (ret != CA_RESULT_OK) {
+               CA_LOGE("Failed to copy file");
+
+               if (ca_info->dst_file)
+                       unlink(ca_info->dst_file);
+       }
+
+       ca_destroy_ca_info(ca_id);
+done:
+       CA_LOGI("=====EXIT thread : ca_id[%d]=====", ca_id);
+       pthread_exit((void *)CA_NULL);
+
+       return CA_NULL;
+}
+
+static void __reduce_cache_size(ca_size_t total_file_size)
+{
+       ca_size_t cache_size;
+
+       if (total_file_size <= 0)
+               return;
+
+       ca_config_get_cache_size(&cache_size);
+       cache_size -= total_file_size;
+       ca_config_set_cache_size(cache_size);
+}
+
+int ca_storage_init()
+{
+       ca_size_t size;
+       CA_LOGI("");
+
+       if (CA_RESULT_OK != ca_storage_get_used_cache_size(&size))
+               return CA_ERR_FAIL_TO_ACCESS_FILE;
+
+       return ca_config_set_cache_size(size);
+}
+
+int ca_storage_deinit()
+{
+       int ret = CA_RESULT_OK;
+       CA_LOGI("");
+       ca_cancel_all_cache_operations();
+
+       return ret;
+}
+
+char *ca_storage_store_file(const char *file_path)
+{
+       int ret = CA_RESULT_OK;
+       int ca_id = CA_INVALID_ID;
+       char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+       char *file_name = CA_NULL;
+       char *dst_file_name = CA_NULL;
+       ca_info_t *ca_info = CA_NULL;
+       void *dst_fp = CA_NULL;
+       char *cache_path = ca_config_get_cache_path();
+
+       if (__get_file_state(file_path) != CA_REGULAR_FILE) {
+               CA_LOGE("Not a regular file!");
+               free(cache_path);
+               return CA_NULL;
+       }
+
+       if (__is_realpath(file_path) != CA_RESULT_OK) {
+               CA_LOGE("Not a real file!");
+               free(cache_path);
+               return CA_NULL;
+       }
+
+       ret = ca_get_available_ca_id(&ca_id);
+       if (ret != CA_RESULT_OK) {
+               CA_LOGD("No available cache id");
+               free(cache_path);
+               return CA_NULL;
+       }
+
+       ca_cb_t ca_cb = {
+                       CA_NULL,
+                       __ca_progress_cb,
+                       CA_NULL,
+                       CA_NULL
+       };
+
+       ca_info = ca_info_list[ca_id];
+       ca_info->ca_id = ca_id;
+
+       file_name = __get_file_name(file_path);
+       snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, file_name);
+       dst_file_name = __generate_cache_file_name(dst_path, file_name);
+       free(cache_path);
+       free(file_name);
+
+       if (dst_file_name == CA_NULL) {
+               CA_LOGE("Fail to generate dst file name!");
+               ca_destroy_ca_info(ca_info->ca_id);
+               return CA_NULL;
+       }
+
+       dst_fp = __open_dst_file(dst_path);
+       if (dst_fp == CA_NULL) {
+               CA_LOGE("Unable to open dst file!");
+               free(dst_file_name);
+               ca_destroy_ca_info(ca_info->ca_id);
+               return CA_NULL;
+       }
+
+       ca_info->dst_fp = (FILE *)dst_fp;
+       ca_info->src_file = strdup(file_path);
+       ca_info->dst_file = strdup(dst_path);
+       ca_info->file_name = strdup(dst_file_name);
+
+       ca_info->is_cb_update = CA_TRUE;
+       ca_info->is_cache_adding = CA_TRUE;
+       memcpy(&(ca_info->cb_info), &ca_cb, sizeof(ca_cb_t));
+
+       ret = ca_storage_copy_file(ca_info);
+
+       if (ret != CA_RESULT_OK) {
+               free(dst_file_name);
+               dst_file_name = CA_NULL;
+
+               if (ca_info->dst_fp) {
+                       fclose(ca_info->dst_fp);
+                       ca_info->dst_fp = CA_NULL;
+                       unlink(dst_path);
+               }
+
+               ca_destroy_ca_info(ca_info->ca_id);
+       }
+
+       CA_LOGI("file name: %s, id: %d, ret: %d", dst_file_name, ca_id, ret);
+
+       return dst_file_name;
+}
+
+int ca_storage_remove_files(int file_count, const char *cache_files[])
+{
+       int ret = CA_RESULT_OK;
+       char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+       char *cache_path = ca_config_get_cache_path();
+       ca_size_t total_file_size = 0;
+       ca_size_t file_size = 0;
+
+       for (int i = 0; i < file_count; i++) {
+               if (!cache_files[i]) {
+                       CA_LOGE("[NULL-CHECK] cache_files[%d]", i);
+
+                       ret = CA_ERR_INVALID_ARGUMENT;
+                       break;
+               }
+
+               snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_files[i]);
+               ca_storage_get_file_size(dst_path, &file_size);
+
+               if (unlink(dst_path) < 0) {
+                       char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+                       strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+                       CA_LOGE("Unable to remove the file: %s, error [%d/%s]", dst_path, errno, err_buf);
+
+                       ret = CA_ERR_INVALID_PATH;
+                       break;
+               }
+
+               if (file_size > 0)
+                       total_file_size += file_size;
+       }
+
+       free(cache_path);
+       __reduce_cache_size(total_file_size);
+
+       return ret;
+}
+
+int ca_storage_is_file_available(const char *cache_file)
+{
+       int ret = CA_RESULT_OK;
+       char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+       char *cache_path = ca_config_get_cache_path();
+
+       snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_file);
+
+       if (__get_file_state(dst_path) != CA_REGULAR_FILE) {
+               CA_LOGE("Not a regular file!");
+               ret = CA_ERR_INVALID_ARGUMENT;
+               goto done;
+       }
+
+       if (__is_realpath(dst_path) != CA_RESULT_OK) {
+               CA_LOGE("Not a real file!");
+               ret = CA_ERR_INVALID_ARGUMENT;
+       }
+
+done:
+       free(cache_path);
+       return ret;
+}
+
+int ca_storage_get_used_cache_size(ca_size_t *size)
+{
+       cache_dir_size = 0;
+       char *cache_path = ca_config_get_cache_path();
+
+       if (ftw(cache_path, __sum, 1) != 0) {
+               CA_LOGE("Failed to get cache directory size!");
+               free(cache_path);
+               return CA_ERR_INVALID_ARGUMENT;
+       }
+
+       *size = cache_dir_size;
+
+       CA_LOGD("size(Bytes): %lld", *size);
+       free(cache_path);
+
+       return CA_RESULT_OK;
+}
+
+int ca_storage_copy_file(ca_info_t *ca_info)
+{
+       pthread_attr_t thread_attr;
+       pthread_t tid;
+
+       if (pthread_attr_init(&thread_attr) != 0)
+               return CA_ERR_FAIL_TO_CREATE_THREAD;
+
+       if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0)
+               return CA_ERR_FAIL_TO_CREATE_THREAD;
+
+       if (pthread_create(&(tid), &thread_attr, __thread_start_copy, ca_info) != 0) {
+               CA_LOGE("Fail to make thread:id[%d]", ca_info->ca_id);
+               return CA_ERR_FAIL_TO_CREATE_THREAD;
+       }
+
+       ca_info->thread_id = tid;
+       CA_LOGI("Thread is created:thread id[%lu]", ca_info->thread_id);
+
+       return CA_RESULT_OK;
+}
+
+int ca_init_cache_download_data(ca_info_t *ca_info,
+               const char *cache_file, ca_req_data_t *ext_data)
+{
+       char src_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+       char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+       char *gen_dst_file_name = CA_NULL;
+       void *dst_fp = CA_NULL;
+       const char *dst_file_name = ext_data->file_name;
+       const char *dst_file_path = ext_data->install_path;
+       char *cache_path = ca_config_get_cache_path();
+
+       if (!dst_file_name || !dst_file_path) {
+               CA_LOGE("Invalid dst file path or dst file name!");
+               free(cache_path);
+               return CA_ERR_INVALID_ARGUMENT;
+       }
+
+       snprintf(src_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_file);
+       snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", dst_file_path, dst_file_name);
+
+       free(cache_path);
+
+       if (__get_file_state(src_path) != CA_REGULAR_FILE) {
+               CA_LOGE("No src file exist!");
+               return CA_ERR_INVALID_PATH;
+       }
+
+       gen_dst_file_name = __generate_dst_file_name(dst_path, dst_file_path, dst_file_name);
+
+       if (gen_dst_file_name == CA_NULL) {
+               CA_LOGE("Fail to generate dst file name!");
+               return CA_ERR_INVALID_STATE;
+       }
+
+       dst_fp = __open_dst_file(dst_path);
+       if (dst_fp == CA_NULL) {
+               CA_LOGE("Unable to open dst file!");
+               free(gen_dst_file_name);
+               return CA_ERR_FAIL_TO_ACCESS_FILE;
+       }
+
+       ca_info->dst_fp = (FILE *)dst_fp;
+       ca_info->src_file = strdup(src_path);
+       ca_info->dst_file = strdup(dst_path);
+       ca_info->file_name = gen_dst_file_name;
+
+       ca_req_info_t *req_info = ca_info->req_info;
+
+       if (ext_data->install_path)
+               req_info->install_path = strdup(ext_data->install_path);
+       if (ext_data->file_name)
+               req_info->file_name = strdup(ext_data->file_name);
+       if (ext_data->pkg_name)
+               req_info->pkg_name = strdup(ext_data->pkg_name);
+       if (ext_data->user_req_data)
+               req_info->user_req_data = ext_data->user_req_data;
+       if (ext_data->user_client_data)
+               req_info->user_client_data = ext_data->user_client_data;
+
+       return CA_RESULT_OK;
+}
+
diff --git a/cache/include/cache-agent-config.h b/cache/include/cache-agent-config.h
new file mode 100755 (executable)
index 0000000..43c33ce
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_CONFIG_H
+#define _CACHE_AGENT_CONFIG_H
+
+#include "cache-agent-defs.h"
+#include "cache-agent-debug.h"
+#include "cache-agent-info.h"
+
+#define CA_CONFIG_FILE_PATH "/var/lib/download-provider/settings"
+#define CA_STORAGE_PATH "/opt/usr/data/download_cache"
+#define CA_MAX_CACHE_SIZE 1000 /* 1000 MByte */
+#define CA_MAX_LIFECYCLE 172800 /* Seconds, 48 hours */
+
+#define CA_SETTING_GROUP_NAME "CACHE"
+#define CA_STORAGE_PATH_KEY "CACHE_PATH"
+#define CA_MAX_CACHE_SIZE_KEY "CACHE_SIZE"
+#define CA_MAX_LIFECYCLE_KEY "CACHE_LIFECYCLE"
+
+typedef struct {
+       int max_cache_size;
+       int max_lifecycle;
+       char *cache_storage_path;
+       ca_time_t oldest_time;
+       ca_size_t cache_size;
+       ca_capacity_event_cb capa_cb;
+} cache_config_info_t;
+
+int ca_config_init();
+int ca_config_deinit();
+int ca_config_set_cache_path(const char *cache_path);
+char *ca_config_get_cache_path(void);
+int ca_config_set_max_cache_size(unsigned int max_cache_size);
+int ca_config_get_max_cache_size(unsigned int *max_cache_size);
+int ca_config_set_max_lifecycle(unsigned int max_lifecycle);
+int ca_config_get_max_lifecycle(unsigned int *max_lifecycle);
+int ca_config_set_oldest_time(ca_time_t oldest_time);
+int ca_config_get_oldest_time(ca_time_t *oldest_time);
+int ca_config_set_cache_size(ca_size_t cache_size);
+int ca_config_get_cache_size(ca_size_t *cache_size);
+int ca_config_set_capacity_cb(ca_capacity_event_cb capa_cb);
+GSList *ca_config_call_capacity_cb(ca_capacity_event_type type);
+
+#endif //_CACHE_AGENT_CONFIG_H
+
+
diff --git a/cache/include/cache-agent-debug.h b/cache/include/cache-agent-debug.h
new file mode 100755 (executable)
index 0000000..1b2f77d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_DEBUG_H
+#define _CACHE_AGENT_DEBUG_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <pthread.h>
+
+// ansi color
+#define COLOR_RED              "\033[0;31m"
+#define COLOR_GREEN            "\033[0;32m"
+#define COLOR_BROWN            "\033[0;33m"
+#define COLOR_LIGHTBLUE        "\033[0;37m"
+#define COLOR_END              "\033[0;m"
+
+#ifdef _ENABLE_DLOG
+#include <unistd.h>
+#include <syscall.h>
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif /*  LOG_TAG */
+
+#define LOG_TAG "DP_CA"
+#define CA_LOGV(format, ...) ((void)0)
+#define CA_LOGD(format, ...) LOGD(COLOR_LIGHTBLUE "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_LOGI(format, ...) LOGI(COLOR_BROWN "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_LOGE(format, ...) LOGE(COLOR_RED "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_SECURE_LOGD(format, ...) SECURE_LOGD(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#define CA_SECURE_LOGI(format, ...) SECURE_LOGI(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#define CA_SECURE_LOGE(format, ...) SECURE_LOGE(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#else
+
+#include <unistd.h>
+#include <syscall.h>
+
+#define CA_LOGD(format, ...) do {\
+       fprintf(stderr, "[CA][%ld][%s():%d] "format"\n", syscall(__NR_gettid), __FUNCTION__, __LINE__, ##__VA_ARGS__);\
+} while (0)
+#define CA_LOGE(format, ...) do {\
+       fprintf(stderr, "[CA][%ld][ERR][%s():%d]\n", syscall(__NR_gettid), __FUNCTION__, __LINE__, ##__VA_ARGS__);\
+} while (0)
+#define CA_LOGV CA_LOGD
+#define CA_LOGI CA_LOGD
+#define CA_SECURE_LOGD(format, ...) ((void)0)
+#define CA_SECURE_LOGI(format, ...) ((void)0)
+#define CA_SECURE_LOGE(format, ...) ((void)0)
+#endif /* _ENABLE_DLOG */
+
+#endif /* _CACHE_AGENT_DEBUG_H */
diff --git a/cache/include/cache-agent-defs.h b/cache/include/cache-agent-defs.h
new file mode 100755 (executable)
index 0000000..829e963
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_DEFS_H
+#define _CACHE_AGENT_DEFS_H
+
+#include <pthread.h>
+#include <time.h>
+
+typedef long long ca_size_t;
+typedef time_t ca_time_t;
+typedef int ca_bool_t;
+
+#define CA_DEFAULT_INSTALL_PATH "/opt/usr/home/owner/media/Downloads"
+
+// Maximum number of copy operations
+#define CA_MAX_ID 32
+
+#define CA_RESULT_OK 0
+
+#define CA_TRUE 1
+#define CA_FALSE 0
+#define CA_NULL (void *)0
+#define CA_INVALID_ID -1
+
+#define CA_RESULT_USER_CANCELED -10
+
+// InputError Input error (-100 ~ -199)
+// Client passed wrong parameter
+#define CA_ERR_INVALID_ARGUMENT -100
+#define CA_ERR_INVALID_URL -101
+#define CA_ERR_INVALID_PATH -102
+#define CA_ERR_INVALID_MIME_TYPE -103
+
+// Client passed correct parameter, but Cache Agent rejects the request because of internal policy.
+#define CA_ERR_ALREADY_CANCELED -160
+#define CA_ERR_ALREADY_SUSPENDED -161
+#define CA_ERR_ALREADY_RESUMED -162
+#define CA_ERR_CANNOT_SUSPEND -170
+#define CA_ERR_CANNOT_RESUME -171
+#define CA_ERR_INVALID_STATE -190
+#define CA_ERR_ALREADY_MAX_COPY -191
+#define CA_ERR_UNSUPPORTED_PROTOCAL -192
+
+// System error (-200 ~ -299)
+#define CA_ERR_FAIL_TO_MEMALLOC -200
+#define CA_ERR_FAIL_TO_CREATE_THREAD -210
+#define CA_ERR_FAIL_TO_ACCESS_FILE -230
+#define CA_ERR_DISK_FULL -240
+
+// DRM error - not conforming with DRM spec (-600 ~ -699)
+#define CA_ERR_DRM_FAIL                        -600
+
+#define CA_CAPA_THRESHOLD_RATIO 0.9
+
+#define NULL_CHECK_RET_OPT(DATA, RET_DATA) {\
+       if (!DATA) {\
+               CA_LOGE("NULL CHECK!:%s", (#DATA));\
+               return RET_DATA;\
+       } \
+}
+
+#define CA_MUTEX_INIT(mutex_add, attr) {\
+       int ret = 0;\
+       do {\
+               ret = pthread_mutex_init(mutex_add, attr);\
+               if (0 == ret) {\
+                       break;\
+               } else if (EINVAL == ret) {\
+                       CA_LOGE("pthread_mutex_init FAIL with EINVAL.");\
+                       break;\
+               } else if (ENOMEM == ret) {\
+                       CA_LOGE("pthread_mutex_init FAIL with ENOMEM.");\
+                       break;\
+               } else {\
+                       CA_LOGE("pthread_mutex_init FAIL with %d.", ret);\
+                       break;\
+               } \
+       } while (1);\
+}
+
+#define CA_MUTEX_LOCK(mutex_add) {\
+       int ret = 0;\
+       do {\
+               ret = pthread_mutex_lock(mutex_add);\
+               if (0 == ret) {\
+                       break;\
+               } else if (EINVAL == ret) {\
+                       CA_LOGE("pthread_mutex_lock FAIL with EINVAL.");\
+                       break;\
+               } else if (EDEADLK == ret) {\
+                       CA_LOGE("pthread_mutex_lock FAIL with EDEADLK.");\
+                       break;\
+               } else {\
+                       CA_LOGE("pthread_mutex_lock FAIL with %d.", ret);\
+                       break;\
+               } \
+       } while (1);\
+}
+
+#define CA_MUTEX_UNLOCK(mutex_add) {\
+       int ret = 0;\
+       do {\
+               ret = pthread_mutex_unlock(mutex_add);\
+               if (0 == ret) {\
+                       break;\
+               } else if (EINVAL == ret) {\
+                       CA_LOGE("pthread_mutex_unlock FAIL with EINVAL.");\
+                       break;\
+               } else if (EPERM == ret) {\
+                       CA_LOGE("pthread_mutex_unlock FAIL with EPERM.");\
+                       break;\
+               } else {\
+                       CA_LOGE("pthread_mutex_unlock FAIL with %d.", ret);\
+                       break;\
+               } \
+       } while (1);\
+}
+
+#define CA_MUTEX_DESTROY(mutex_add) {\
+       int ret = 0;\
+       do {\
+               ret = pthread_mutex_destroy(mutex_add);\
+               if (0 == ret) {\
+                       break;\
+               } else if (EINVAL == ret) {\
+                       CA_LOGE("pthread_mutex_destroy FAIL with EINVAL.");\
+                       break;\
+               } else if (EBUSY == ret) {\
+                       CA_LOGE("pthread_mutex_destroy FAIL with EBUSY.");\
+                       break;\
+               } else {\
+                       CA_LOGE("pthread_mutex_destroy FAIL with %d.", ret);\
+                       break;\
+               } \
+       } while (1);\
+}
+
+#define CA_COND_INIT(cond_add, attr) do {\
+               if (0 != pthread_cond_init(cond_add, attr)) {\
+                       CA_LOGE("pthread_cond_init FAIL");\
+               } \
+       } while (0)
+
+#define CA_COND_SIGNAL(cond_add) do {\
+               if (0 != pthread_cond_signal(cond_add)) {\
+                       CA_LOGE("pthread_cond_signal FAIL");\
+               } \
+       } while (0)
+
+#define CA_COND_WAIT(cond_add, mutex_add) do {\
+               if (0 != pthread_cond_wait(cond_add, mutex_add)) {\
+                       CA_LOGE("pthread_cond_wait FAIL");\
+               } \
+       } while (0)
+
+#define CA_COND_TIMED_WAIT(cond_add, mutex_add, time) do {\
+               if (0 != pthread_cond_timedwait(cond_add, mutex_add, time)) {\
+                       CA_LOGE("pthread_cond_wait FAIL");\
+               } \
+       } while (0)
+
+#define CA_COND_DESTROY(cond_add)      do {\
+               if (0 != pthread_cond_destroy(cond_add)) {\
+                       CA_LOGE("pthread_cond_destroy FAIL");\
+               } \
+       } while (0)
+
+#endif
+
diff --git a/cache/include/cache-agent-info.h b/cache/include/cache-agent-info.h
new file mode 100755 (executable)
index 0000000..aa5e4e8
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_INFO_H
+#define _CACHE_AGENT_INFO_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+
+typedef enum {
+       CA_STATE_NONE,
+       CA_STATE_COPYING,
+       CA_STATE_PAUSED,
+       CA_STATE_COMPLETED,
+       CA_STATE_CANCELED,
+       CA_STATE_FAILED,
+} ca_state_type;
+
+typedef enum {
+       CA_CAPACITY_MAX_SIZE,
+       CA_CAPACITY_LIFECYCLE,
+} ca_capacity_event_type;
+
+typedef struct {
+       int cache_id;
+       int error;
+       long long file_size;
+       char *saved_path;
+       char *content_name;
+} cache_info_t;
+
+typedef struct {
+       int cache_id;
+       int error;
+       long long file_size;
+       char *saved_path;
+       int http_status;
+} ca_finished_info_t;
+
+typedef struct {
+       char *install_path;
+       char *file_name;
+       char *pkg_name;
+       void *user_req_data;
+       void *user_client_data;
+} ca_req_info_t;
+
+typedef GSList* (*ca_capacity_event_cb) (ca_capacity_event_type capa_type);
+
+typedef void (*ca_paused_cb) (int cache_id,
+               void *user_param1, void *user_param2);
+typedef void (*ca_progress_cb) (int cache_id,
+               unsigned long long received_size,
+               void *user_param1, void *user_param2);
+typedef void (*ca_started_cb) (cache_info_t *cache_info,
+               void *user_param1, void *user_param2);
+typedef void (*ca_finished_cb) (ca_finished_info_t *finished_info,
+               void *user_param1, void *user_param2);
+
+typedef struct {
+       ca_started_cb cache_info_cb;
+       ca_progress_cb progress_cb;
+       ca_finished_cb finished_cb;
+       ca_paused_cb paused_cb;
+} ca_cb_t;
+
+typedef struct {
+       int ca_id;
+       pthread_t thread_id;
+       pthread_mutex_t mutex_state;
+       pthread_cond_t cond;
+       ca_state_type state;
+       char *src_file;
+       char *dst_file;
+       char *file_name;
+       FILE *dst_fp;
+       ca_req_info_t *req_info;
+       ca_cb_t cb_info;
+       ca_bool_t is_cb_update;
+       int update_time;
+       ca_bool_t is_cache_adding;
+} ca_info_t;
+
+typedef struct {
+       const char *install_path;
+       const char *file_name;
+       const char *pkg_name;
+       void *user_req_data;
+       void *user_client_data;
+} ca_req_data_t;
+
+#endif //_CACHE_AGENT_INFO_H
+
+
diff --git a/cache/include/cache-agent-interface.h b/cache/include/cache-agent-interface.h
new file mode 100755 (executable)
index 0000000..3ff744d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_INTERFACE_H
+#define _CACHE_AGENT_INTERFACE_H
+
+#ifndef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "cache-agent-storage.h"
+
+#define REQ_BLOCK_RET(mutex_add, DATA) do {\
+               pthread_mutex_lock(mutex_add);\
+               if (DATA) {\
+                       pthread_mutex_unlock(mutex_add);\
+                       CA_LOGD("Request is blocked!");\
+                       return CA_ERR_INVALID_STATE;\
+               } \
+               pthread_mutex_unlock(mutex_add);\
+       } while (0)
+
+#define REQ_BLOCK_RET_PTR(mutex_add, DATA) do {\
+               pthread_mutex_lock(mutex_add);\
+               if (DATA) {\
+                       pthread_mutex_unlock(mutex_add);\
+                       CA_LOGD("Request is blocked!");\
+                       return NULL;\
+               } \
+               pthread_mutex_unlock(mutex_add);\
+       } while (0)
+
+EXPORT_API int ca_init(ca_capacity_event_cb capa_cb);
+EXPORT_API int ca_deinit();
+
+EXPORT_API char *ca_store_file(const char *file_path);
+EXPORT_API int ca_remove_files(int file_count, const char *cache_files[]);
+EXPORT_API int ca_is_file_available(const char *cache_file);
+EXPORT_API int ca_get_used_cache_size(ca_size_t *size);
+
+EXPORT_API int ca_start_cache_download(const char *cache_file,
+                                       ca_req_data_t *ext_data, ca_cb_t *ca_cb_data,   int *cache_id);
+EXPORT_API int ca_pause_cache_download(int req_id);
+EXPORT_API int ca_resume_cache_download(int req_id);
+EXPORT_API int ca_cancel_cache_download(int req_id);
+EXPORT_API int ca_pause_cache_download_without_update(int req_id);
+EXPORT_API int ca_cancel_cache_download_without_update(int req_id);
+EXPORT_API int ca_is_alive_cache_download(int req_id);
+
+EXPORT_API int ca_get_max_cache_size(unsigned int *size);
+EXPORT_API int ca_set_max_cache_size(unsigned int size);
+EXPORT_API int ca_get_cache_path(char **path);
+EXPORT_API int ca_set_cache_path(const char *path);
+EXPORT_API int ca_get_cache_lifecycle(unsigned int *time);
+EXPORT_API int ca_set_cache_lifecycle(unsigned int time);
+EXPORT_API int ca_clear_all_files(void);
+EXPORT_API int ca_set_oldest_file_time(ca_time_t oldest_time);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_CACHE_AGENT_INTERFACE_H
+
+
diff --git a/cache/include/cache-agent-storage.h b/cache/include/cache-agent-storage.h
new file mode 100755 (executable)
index 0000000..bafe0d3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef _CACHE_AGENT_STORAGE_H
+#define _CACHE_AGENT_STORAGE_H
+
+#include <linux/limits.h>
+#include "cache-agent-defs.h"
+#include "cache-agent-debug.h"
+#include "cache-agent-info.h"
+#include "cache-agent-config.h"
+
+#define CA_MAX_FILE_PATH_LEN PATH_MAX
+#define CA_MAX_FILE_NAME_LEN NAME_MAX
+#define CA_MAX_ERROR_STR_LEN 256
+#define CA_MAX_DUP_FILE_COUNT 2048
+#define CA_MAX_FILE_BUFFER_SIZE 65536
+#define CA_MB_BYTE 1048576
+
+typedef enum {
+       CA_NOT_EXIST_FILE,
+       CA_REGULAR_FILE,
+       CA_OTHER_FILE
+} ca_file_state_e;
+
+ca_info_t *ca_get_ca_info(int ca_id);
+int ca_check_ca_id(ca_info_t *ca_info, int ca_id);
+int ca_get_info_with_ca_id(int id, ca_info_t **out_info);
+void ca_cancel_all_cache_operations(void);
+int ca_clear_all_cache_files(void);
+int ca_request_to_cancel_cache_download(ca_info_t *ca_info);
+int ca_request_to_suspend_cache_download(ca_info_t *ca_info);
+int ca_request_to_resume_cache_download(ca_info_t *ca_info);
+void ca_destroy_ca_info(int id);
+int ca_get_available_ca_id(int *available_id);
+void ca_storage_get_file_size(const char *file_path, ca_size_t *file_size);
+
+int ca_storage_init();
+int ca_storage_deinit();
+
+char *ca_storage_store_file(const char *file_path);
+int ca_storage_remove_files(int file_count, const char *cache_files[]);
+int ca_storage_is_file_available(const char *cache_file);
+int ca_storage_get_used_cache_size(ca_size_t *size);
+
+int ca_storage_copy_file(ca_info_t *ca_info);
+int ca_init_cache_download_data(ca_info_t *ca_info,
+               const char *cache_file, ca_req_data_t *ext_data);
+
+#endif //_CACHE_AGENT_STORAGE_H
+
+
index 856e101..1f93da3 100755 (executable)
@@ -1,6 +1,6 @@
 Name:       download-provider
 Summary:    Download the contents in background
-Version:    2.3.10
+Version:    2.3.11
 Release:    0
 Group:      Development/Libraries
 License:    Apache-2.0
@@ -89,6 +89,7 @@ export LDFLAGS+=" -lgcov"
        -DBIN_INSTALL_DIR:PATH=%{_bindir} \
        -DLIB_INSTALL_DIR:PATH=%{_libdir} \
        -DLIB_AGENT_PATH="/usr/%{?_lib}/libdownloadagent2.so.0.1.0" \
+       -DLIB_CACHE_AGENT_PATH="/usr/%{?_lib}/libcacheagent.so.0.1.0" \
        -DINCLUDE_INSTALL_DIR:PATH=%{_includedir} \
        -DPKG_NAME=%{name} \
        -DPKG_VERSION=%{version} \
@@ -142,6 +143,9 @@ mkdir -p %{buildroot}/etc/notstrip/
 install -m 644 packaging/download-provider.notstrip %{buildroot}/etc/notstrip/download-provider.notstrip
 %endif
 
+mkdir -p %{buildroot}/var/lib/download-provider/
+install -m 600 res/var/lib/download-provider/settings %{buildroot}/var/lib/download-provider/settings
+
 ## container_enable
 mkdir -p %{buildroot}/etc/vasum/vsmzone.resource/
 mv %{buildroot}/usr/share/download-provider/download-provider.res %{buildroot}/etc/vasum/vsmzone.resource/
@@ -163,6 +167,9 @@ mkdir -p --mode=0700 %{_database_client_dir}
 chsmack -a 'System' %{_database_client_dir}
 chown -R web_fw:web_fw %{_database_client_dir}
 chown -R web_fw:web_fw %{_data_install_path}
+chown -R web_fw:web_fw /var/lib/download-provider/
+mkdir -p /opt/usr/data/download_cache
+chown -R web_fw:web_fw /opt/usr/data/download_cache
 
 %files
 %defattr(-,root,root,-)
@@ -170,6 +177,7 @@ chown -R web_fw:web_fw %{_data_install_path}
 %{_imagedir}/*.png
 %{_localedir}/*/*/download-provider.mo
 %{_libdir}/*.so.*
+%attr(0600,web_fw,web_fw) /var/lib/download-provider/settings
 /lib/systemd/system/download-provider.service
 /lib/systemd/system/download-provider.socket
 /lib/systemd/system/sockets.target.wants/download-provider.socket
@@ -189,9 +197,11 @@ chown -R web_fw:web_fw %{_data_install_path}
 %{_libdir}/pkgconfig/download-provider.pc
 %{_libdir}/pkgconfig/download-provider-interface.pc
 %{_libdir}/pkgconfig/download-agent.pc
+%{_libdir}/pkgconfig/cache-agent.pc
 %{_includedir}/download-provider/download-provider.h
 %{_includedir}/download-provider/download-provider-interface.h
 %{_includedir}/download-provider/download-agent-interface.h
+%{_includedir}/download-provider/cache-agent-interface.h
 
 %if 0%{?gtests:1}
 %{_bindir}/gtest*
index a421ad8..40c23de 100755 (executable)
@@ -250,6 +250,10 @@ static int __create_socket()
                        TRACE_ERROR("check permission");
                        return -DP_ERROR_PERMISSION_DENIED;
                }
+
+               char err_buf[DOWNLOAD_FILENAME_MAX] = { 0, };
+               strerror_r(errno, err_buf, DOWNLOAD_FILENAME_MAX);
+               TRACE_ERROR("DP Connection error [%d/%s]", errno, err_buf);
                return -1;
        }
        TRACE_DEBUG("sockfd [%d]", sockfd);
@@ -1721,3 +1725,76 @@ int dp_interface_set_verify_host(const int id, int enable)
        return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_VERIFY_HOST,
                enable, __FUNCTION__);
 }
+
+int dp_interface_set_cache(const int id, int enable)
+{
+       return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_CACHE,
+               enable, __FUNCTION__);
+}
+
+int dp_interface_get_cache(const int id, int *enable)
+{
+       if (enable == NULL) {
+               TRACE_ERROR("check buffer");
+               return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+       }
+       return __dp_ipc_get_int(id, DP_PROP_CACHE, enable, __FUNCTION__);
+}
+
+int dp_interface_reset_cache(const int id)
+{
+       return __dp_ipc_echo(id, DP_SEC_CONTROL, DP_PROP_RESET_CACHE, __FUNCTION__);
+}
+
+int dp_interface_set_cache_max_size(const int id, const unsigned int size)
+{
+       return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_SET_CACHE_MAX_SIZE,
+               size, __FUNCTION__);
+}
+
+int dp_interface_get_cache_max_size(const int id, unsigned int *size)
+{
+       if (size == NULL) {
+               TRACE_ERROR("check buffer");
+               return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+       }
+       return __dp_ipc_get_int(id, DP_PROP_GET_CACHE_MAX_SIZE, (int *)size, __FUNCTION__);
+}
+
+int dp_interface_reset_all_cache(const int id)
+{
+       return __dp_ipc_echo(id, DP_SEC_CONTROL, DP_PROP_RESET_ALL_CACHE, __FUNCTION__);
+}
+
+int dp_interface_set_cache_path(const int id, const char *path)
+{
+       if (path == NULL) {
+               TRACE_ERROR("check buffer");
+               return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+       }
+       return __dp_ipc_set_string(id, DP_SEC_SET, DP_PROP_SET_CACHE_PATH, path, __FUNCTION__);
+}
+
+int dp_interface_get_cache_path(const int id, char **path)
+{
+       if (path == NULL) {
+               TRACE_ERROR("check buffer");
+               return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+       }
+       return __dp_ipc_get_string(id, DP_PROP_GET_CACHE_PATH, path, __FUNCTION__);
+}
+
+int dp_interface_set_cache_lifecycle(const int id, const unsigned int time)
+{
+       return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_SET_CACHE_LIFECYCLE,
+               time, __FUNCTION__);
+}
+
+int dp_interface_get_cache_lifecycle(const int id, unsigned int *time)
+{
+       if (time == NULL) {
+               TRACE_ERROR("check buffer");
+               return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+       }
+       return __dp_ipc_get_int(id, DP_PROP_GET_CACHE_LIFECYCLE, (int *)time, __FUNCTION__);
+}
index cb8b247..6f5045d 100755 (executable)
@@ -137,6 +137,19 @@ EXPORT_API int dp_interface_set_notification_type(const int id, int type);
 EXPORT_API int dp_interface_get_notification_type(const int id, int *type);
 
 EXPORT_API int dp_interface_set_verify_host(const int id, int enable);
+
+EXPORT_API int dp_interface_set_cache(const int id, int enable);
+EXPORT_API int dp_interface_get_cache(const int id, int *enable);
+EXPORT_API int dp_interface_reset_cache(const int id);
+
+EXPORT_API int dp_interface_set_cache_max_size(const int id, const unsigned int size);
+EXPORT_API int dp_interface_get_cache_max_size(const int id, unsigned int *size);
+EXPORT_API int dp_interface_reset_all_cache(const int id);
+EXPORT_API int dp_interface_set_cache_path(const int id, const char *path);
+EXPORT_API int dp_interface_get_cache_path(const int id, char **path);
+EXPORT_API int dp_interface_set_cache_lifecycle(const int id, const unsigned int time);
+EXPORT_API int dp_interface_get_cache_lifecycle(const int id, unsigned int *time);
+
 #ifdef __cplusplus
 }
 #endif
index 20482a1..7eeb98c 100755 (executable)
@@ -73,7 +73,9 @@ IF(SUPPORT_LARGE_FILE)
 ENDIF(SUPPORT_LARGE_FILE)
 
 ## INCLUDES
-INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/agent/include)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include
+       ${CMAKE_SOURCE_DIR}/cache/include
+       ${CMAKE_SOURCE_DIR}/agent/include)
 
 set(DP2_LINK_LIBRARIES ${GLIB-2_LIBRARIES}
                ${GOBJECT-2_LIBRARIES}
@@ -123,12 +125,15 @@ ADD_EXECUTABLE(${PROJECT_NAME}
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-network.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-db.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-plugin-download-agent.c
+       ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-plugin-cache-agent.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-queue.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-queue-manager.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-client.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-client-manager.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification-manager.c
+       ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-cache.c
+       ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-cache-manager.c
        ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-main.c )
 TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dp2_pkgs_LDFLAGS} ${DP2_LINK_LIBRARIES} ${dp2_noti_pkgs_LDFLAGS} ${dp2_companion_pkgs_LDFLAGS} ${dp2_wifi_direct_pkgs_LDFLAGS} ${dp2_security_privilege_pkgs_LDFLAGS} -ldl -pie)
 INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BIN_INSTALL_DIR})
diff --git a/provider/download-provider-cache-manager.c b/provider/download-provider-cache-manager.c
new file mode 100644 (file)
index 0000000..6460295
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <stdlib.h>
+#include <download-provider-cache.h>
+#include <download-provider-cache-manager.h>
+#include <download-provider-client-manager.h>
+#include <download-provider-ipc.h>
+#include <download-provider-log.h>
+
+static char *__dp_cache_print_request_type(unsigned req_type)
+{
+       switch (req_type) {
+       case DP_CACHE_REQUEST_RESET_CACHE:
+               return "RESET_CACHE";
+       case DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE:
+               return "SET_CACHE_MAX_SIZE";
+       case DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE:
+               return "GET_CACHE_MAX_SIZE";
+       case DP_CACHE_REQUEST_SET_CACHE_PATH:
+               return "SET_CACHE_PATH";
+       case DP_CACHE_REQUEST_GET_CACHE_PATH:
+               return "GET_CACHE_PATH";
+       case DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE:
+               return "SET_CACHE_LIFECYCLE";
+       case DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE:
+               return "GET_CACHE_LIFECYCLE";
+       case DP_CACHE_REQUEST_RESET_ALL_CACHE:
+               return "RESET_ALL_CACHE";
+       default:
+               break;
+       }
+       return "UNKNOWN";
+}
+
+static int __dp_cache_request_feedback_int(int sock, dp_ipc_fmt *ipc_info, void *value, int errorvalue, size_t extra_size)
+{
+       int errorcode = DP_ERROR_NONE;
+       if (errorvalue != DP_ERROR_NONE)
+               extra_size = 0;
+       if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, extra_size) < 0) {
+               errorcode = DP_ERROR_IO_ERROR;
+               TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size);
+       }
+       if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) {
+               if (dp_ipc_write(sock, value, extra_size) < 0) {
+                       errorcode = DP_ERROR_IO_ERROR;
+                       TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size);
+               }
+       }
+       return errorcode;
+}
+
+static int __dp_cache_request_feedback_string(int sock, dp_ipc_fmt *ipc_info, void *string, size_t length, int errorvalue)
+{
+       int errorcode = DP_ERROR_NONE;
+
+       if (length == 0 && errorvalue == DP_ERROR_NONE)
+               errorvalue = DP_ERROR_NO_DATA;
+
+       if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, length * sizeof(char)) < 0) {
+               errorcode = DP_ERROR_IO_ERROR;
+               TRACE_ERROR("sock:%d check ipc length:%zd", sock, length);
+       }
+       if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) {
+               if (dp_ipc_write(sock, string, sizeof(char) * length) < 0) {
+                       errorcode = DP_ERROR_IO_ERROR;
+                       TRACE_ERROR("sock:%d check ipc length:%zd", sock, length);
+               }
+       }
+       return errorcode;
+}
+
+static int __dp_cache_request_read_int(int sock, dp_ipc_fmt *ipc_info, int *value)
+{
+       int errorcode = DP_ERROR_NONE;
+       if (ipc_info->size == sizeof(int)) {
+               if (dp_ipc_read(sock, value, ipc_info->size, __FUNCTION__) < 0) {
+                       TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size);
+                       errorcode = DP_ERROR_IO_ERROR;
+               }
+       } else
+               errorcode = DP_ERROR_IO_ERROR;
+
+       return errorcode;
+}
+
+static int __dp_cache_request_read_string(int sock, dp_ipc_fmt *ipc_info, char **string)
+{
+       int errorcode = DP_ERROR_NONE;
+       if (ipc_info->size > 0) {
+               char *recv_str = (char *)calloc((ipc_info->size + (size_t)1), sizeof(char));
+               if (recv_str == NULL) {
+                       TRACE_ERROR("sock:%d check memory length:%zd", sock, ipc_info->size);
+                       errorcode = DP_ERROR_OUT_OF_MEMORY;
+               } else {
+                       if (dp_ipc_read(sock, recv_str, ipc_info->size, __FUNCTION__) <= 0) {
+                               TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size);
+                               errorcode = DP_ERROR_IO_ERROR;
+                               free(recv_str);
+                       } else {
+                               recv_str[ipc_info->size] = '\0';
+                               TRACE_DEBUG("sock:%d length:%zd string:%s", sock, ipc_info->size, recv_str);
+                               *string = recv_str;
+                       }
+               }
+       } else
+               errorcode = DP_ERROR_IO_ERROR;
+
+       return errorcode;
+}
+
+int dp_cache_manager_handle_request(void *req_slot, void *req_ipc_info, const int req_type)
+{
+       if (req_type < 0) {
+               TRACE_ERROR("check req_type");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (req_slot == NULL) {
+               TRACE_ERROR("check req_slot");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       dp_client_slots_fmt *slot = req_slot;
+       dp_client_fmt *client = &slot->client;
+       dp_ipc_fmt *ipc_info = req_ipc_info;
+       if (client == NULL || ipc_info == NULL) {
+               TRACE_ERROR("check client or ipc_info");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       int errorcode = DP_ERROR_NONE;
+       switch (req_type) {
+       case DP_CACHE_REQUEST_RESET_CACHE:
+               {
+                       if (dp_cache_reset_cache(slot->pkgname, &errorcode) < 0)
+                               TRACE_ERROR("failed to control %s", __dp_cache_print_request_type(req_type));
+                       // feedback
+                       if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_CONTROL,
+                                               ipc_info->property, errorcode, 0) < 0)
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       break;
+               }
+       case DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE:
+               {
+                       int recv_int = -1;
+                       errorcode = __dp_cache_request_read_int(client->channel, ipc_info, &recv_int);
+                       if (errorcode == DP_ERROR_NONE) {
+                               if (recv_int < 0)
+                                       errorcode = DP_ERROR_INVALID_PARAMETER;
+                               else {
+                                       if (dp_cache_set_cache_max_size((unsigned int)recv_int, &errorcode) < 0)
+                                               TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+                               }
+                       }
+                       // feedback
+                       if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+                                               ipc_info->property, errorcode, 0) < 0)
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       break;
+               }
+       case DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE:
+               {
+                       int errorcode = DP_ERROR_NONE;
+                       size_t size = sizeof(int);
+                       unsigned int value = 0;
+                       if (dp_cache_get_cache_max_size(&value, &errorcode) < 0)
+                               TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+
+                       // feedback
+                       if (__dp_cache_request_feedback_int(client->channel, ipc_info,
+                                               (void *)&value, errorcode, size) == DP_ERROR_IO_ERROR) {
+                               errorcode = DP_ERROR_IO_ERROR;
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       }
+                       break;
+               }
+       case DP_CACHE_REQUEST_RESET_ALL_CACHE:
+               {
+                       if (dp_cache_reset_all_cache(&errorcode) < 0)
+                               TRACE_ERROR("failed to control %s", __dp_cache_print_request_type(req_type));
+                       // feedback
+                       if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_CONTROL,
+                                               ipc_info->property, errorcode, 0) < 0)
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       break;
+               }
+       case DP_CACHE_REQUEST_SET_CACHE_PATH:
+               {
+                       char *recv_str = NULL;
+                       errorcode = __dp_cache_request_read_string(client->channel, ipc_info, &recv_str);
+                       if (errorcode == DP_ERROR_NONE) {
+                               if (recv_str == NULL) {
+                                       errorcode = DP_ERROR_INVALID_PARAMETER;
+                               } else {
+                                       if (dp_cache_set_cache_path((const char *)recv_str, &errorcode) < 0)
+                                               TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+                                       free(recv_str);
+                               }
+                       }
+                       // feedback
+                       if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+                                               ipc_info->property, errorcode, 0) < 0)
+                       TRACE_ERROR("check ipc sock:%d", client->channel);
+                       break;
+               }
+       case DP_CACHE_REQUEST_GET_CACHE_PATH:
+               {
+                       int errorcode = DP_ERROR_NONE;
+                       char *string = NULL;
+                       unsigned length = 0;
+                       if (dp_cache_get_cache_path(&string, &errorcode) < 0)
+                               TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+                       if (string)
+                               length = strlen(string);
+
+                       // feedback
+                       if (__dp_cache_request_feedback_string(client->channel, ipc_info,
+                                               string, length, errorcode) == DP_ERROR_IO_ERROR) {
+                               errorcode = DP_ERROR_IO_ERROR;
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       }
+
+                       free(string);
+                       break;
+               }
+       case DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE:
+               {
+                       int recv_int = -1;
+                       errorcode = __dp_cache_request_read_int(client->channel, ipc_info, &recv_int);
+                       if (errorcode == DP_ERROR_NONE) {
+                               if (recv_int < 0)
+                                       errorcode = DP_ERROR_INVALID_PARAMETER;
+                               else {
+                                       if (dp_cache_set_cache_lifecycle((unsigned int)recv_int, &errorcode) < 0)
+                                               TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+                               }
+                       }
+                       // feedback
+                       if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+                                               ipc_info->property, errorcode, 0) < 0)
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       break;
+               }
+       case DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE:
+               {
+                       int errorcode = DP_ERROR_NONE;
+                       size_t size = sizeof(int);
+                       unsigned int value = 0;
+                       if (dp_cache_get_cache_lifecycle(&value, &errorcode) < 0)
+                               TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+
+                       // feedback
+                       if (__dp_cache_request_feedback_int(client->channel, ipc_info,
+                                               (void *)&value, errorcode, size) == DP_ERROR_IO_ERROR) {
+                               errorcode = DP_ERROR_IO_ERROR;
+                               TRACE_ERROR("check ipc sock:%d", client->channel);
+                       }
+                       break;
+               }
+       default:
+               errorcode = DP_ERROR_INVALID_PARAMETER;
+               break;
+       }
+
+       return errorcode;
+}
diff --git a/provider/download-provider-cache.c b/provider/download-provider-cache.c
new file mode 100644 (file)
index 0000000..3517ac7
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <stdio.h>
+#include <download-provider.h>
+#include <download-provider-cache.h>
+#include <download-provider-db.h>
+#include <download-provider-db-defs.h>
+#include <download-provider-log.h>
+#include <download-provider-plugin-cache-agent.h>
+
+extern void *g_db_handle;
+/*
+void __print_data(gpointer data, gpointer user_data)
+{
+       TRACE_DEBUG("%s", (char *)data);
+}
+*/
+static char **__convert_list_to_array(const int file_count, GSList *file_list)
+{
+       char **file_array = (char **)malloc(sizeof(char *) * file_count);
+       if (!file_array)
+               return NULL;
+
+       int index = 0;
+       for (GSList* node = file_list; node != NULL; node = node->next) {
+               file_array[index] = (char *)node->data;
+               index++;
+       }
+
+       return file_array;
+}
+
+static void __clear_file_array( const int file_count, char **file_array)
+{
+    if (file_array != NULL) {
+        for (int i = 0; i < file_count; i++)
+            free((void *)(file_array[i]));
+    }
+    free(file_array);
+}
+
+static int __verify_directory(const char *dir)
+{
+       int ret = DP_ERROR_NONE;
+
+       if (!dir) {
+               TRACE_ERROR("NULL CHECK!: directory path");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (access(dir, R_OK | W_OK | X_OK) != 0) {
+               TRACE_ERROR("Invalid path [%s]", dir);
+               ret = DP_ERROR_INVALID_PARAMETER;
+       }
+
+       return ret;
+}
+
+int dp_cache_delete_all_from_db(int *error)
+{
+       if (dp_db_delete_all(g_db_handle, DP_TABLE_CACHE, error) < 0) {
+               TRACE_ERROR("failed to delete all from cache table");
+               return -1;
+       }
+
+       if (dp_db_delete_all(g_db_handle, DP_TABLE_CACHE_FILE, error) < 0) {
+               TRACE_ERROR("failed to delete all from cache_file table");
+               return -1;
+       }
+
+       return 0;
+}
+
+int dp_cache_reset_cache(const char *package, int *error)
+{
+       int ret = 0;
+       GSList *file_list = NULL;
+
+       int file_count = dp_db_clear_app_cache(g_db_handle, package, &file_list, error);
+       if (file_count < 0) {
+               TRACE_ERROR("failed to delete app cache. package:%s", package);
+               return -1;
+       }
+       if (file_count == 0 || file_list == NULL)
+               return 0;
+
+       // g_slist_foreach(file_list, __print_data, NULL);
+
+       char **file_array = __convert_list_to_array(file_count, file_list);
+       if (!file_array) {
+               TRACE_ERROR("failed to allocate the file_array");
+               *error = DP_ERROR_OUT_OF_MEMORY;
+               g_slist_free_full(file_list, g_free);
+               return -1;
+       }
+
+       ret = dp_remove_files(file_count, (const char **)file_array);
+
+       __clear_file_array(file_count, file_array);
+       g_slist_free(file_list);
+
+    return ret;
+}
+
+int dp_cache_set_cache_max_size(unsigned int size, int *error)
+{
+       *error = dp_set_max_cache_size(size);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_get_cache_max_size(unsigned int *size, int *error)
+{
+       *error = dp_get_max_cache_size(size);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_set_cache_path(const char *path, int *error)
+{
+       if (__verify_directory(path) != DP_ERROR_NONE)
+               return -1;
+
+       if (dp_cache_delete_all_from_db(error) < 0)
+               return -1;
+
+       *error = dp_set_cache_path(path);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_get_cache_path(char **path, int *error)
+{
+       *error = dp_get_cache_path(path);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_set_cache_lifecycle(unsigned int time, int *error)
+{
+       *error = dp_set_cache_lifecycle(time);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_get_cache_lifecycle(unsigned int *time, int *error)
+{
+       *error = dp_get_cache_lifecycle(time);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_cache_reset_all_cache(int *error)
+{
+       if (dp_cache_delete_all_from_db(error) < 0)
+               return -1;
+
+       // delete from cache.
+       if (dp_clear_all_cache_files() != DP_ERROR_NONE) {
+               TRACE_ERROR("failed to remove files");
+               *error = DP_ERROR_INVALID_PARAMETER;
+               return -1;
+       }
+
+    return 0;
+}
+
+GSList *dp_cache_clear_exceeded_files(long long max_val, int type)
+{
+       GSList *file_list = NULL;
+       int error;
+
+       dp_db_clear_exceeded_cache(g_db_handle, &file_list, max_val, type, &error);
+
+       for (GSList* node = file_list; node != NULL; node = node->next) {
+               const char *file_name = (char *)node->data;
+               dp_db_clear_app_cache_by_filename(g_db_handle, file_name, &error);
+       }
+
+       return file_list;
+}
index 15f28bd..f28f40e 100755 (executable)
 #include <download-provider-queue-manager.h>
 #include <download-provider-client-manager.h>
 #include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
 #include <download-provider-network.h>
 
 static int g_dp_sock = -1;
 static dp_client_slots_fmt *g_dp_client_slots = NULL;
-static void *g_db_handle = 0;
-static pthread_mutex_t g_db_mutex = PTHREAD_MUTEX_INITIALIZER;
+void *g_db_handle = 0;
+pthread_mutex_t g_db_mutex = PTHREAD_MUTEX_INITIALIZER;
 extern pthread_t g_client_manager_tid;
 
 void dp_terminate(int signo)
@@ -685,6 +686,9 @@ void *dp_client_manager(void *arg)
        FD_SET(g_dp_sock, &listen_fdset);
        FD_SET(g_dp_sock, &except_fdset);
 
+       if (dp_init_cache_agent() != DP_ERROR_NONE)
+               TRACE_ERROR("failed to init cache agent");
+
        int maxfd = g_dp_sock;
        while (g_dp_sock >= 0) {
                // initialize timeout structure for calling timeout exactly
@@ -740,6 +744,7 @@ void *dp_client_manager(void *arg)
        free(g_dp_client_slots);
        // free all resources
 
+       dp_deinit_cache_agent();
        TRACE_INFO("client-manager's working is done");
 
 ERR:
index 3ce1785..04be60d 100755 (executable)
 #include <download-provider-notification-manager.h>
 #include <download-provider-queue-manager.h>
 #include <download-provider-client-manager.h>
+#include <download-provider-cache-manager.h>
 #include <download-provider-db-defs.h>
 #include <download-provider-db.h>
 #include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
 #include <download-provider-security.h>
 
 #ifndef SIZE_MAX
@@ -67,6 +69,7 @@ static const char *dp_db_column[] = {
        [DP_PROP_NOTIFICATION_SUBJECT] = DP_DB_COL_NOTI_SUBJECT,
        [DP_PROP_NOTIFICATION_DESCRIPTION] = DP_DB_COL_NOTI_DESCRIPTION,
        [DP_PROP_NOTIFICATION_TYPE] = DP_DB_COL_NOTI_TYPE,
+       [DP_PROP_CACHE] = DP_DB_COL_CACHE,
        [DP_PROP_CREATE] = NULL,
        [DP_PROP_START] = NULL,
        [DP_PROP_PAUSE] = NULL,
@@ -252,6 +255,24 @@ char *dp_print_property(unsigned property)
                return "HTTP_HEADER";
        case DP_PROP_VERIFY_HOST:
                return "VERIFY_HOST";
+       case DP_PROP_CACHE:
+               return "CACHE";
+       case DP_PROP_RESET_CACHE:
+               return "RESET_CACHE";
+       case DP_PROP_SET_CACHE_MAX_SIZE:
+               return "SET_CACHE_MAX_SIZE";
+       case DP_PROP_GET_CACHE_MAX_SIZE:
+               return "GET_CACHE_MAX_SIZE";
+       case DP_PROP_SET_CACHE_PATH:
+               return "SET_CACHE_PATH";
+       case DP_PROP_GET_CACHE_PATH:
+               return "GET_CACHE_PATH";
+       case DP_PROP_SET_CACHE_LIFECYCLE:
+               return "SET_CACHE_LIFECYCLE";
+       case DP_PROP_GET_CACHE_LIFECYCLE:
+               return "GET_CACHE_LIFECYCLE";
+       case DP_PROP_RESET_ALL_CACHE:
+               return "RESET_ALL_CACHE";
        default:
                break;
        }
@@ -269,6 +290,7 @@ static const char *__dp_get_db_table_name(unsigned property)
        case DP_PROP_PROGRESS_CALLBACK:
        case DP_PROP_NETWORK_TYPE:
        case DP_PROP_NETWORK_BONDING:
+       case DP_PROP_CACHE:
                return DP_TABLE_REQUEST;
        case DP_PROP_AUTO_DOWNLOAD:
        case DP_PROP_STATE:
@@ -303,6 +325,31 @@ static const char *__dp_get_db_table_name(unsigned property)
        return NULL;
 }
 
+static const int __dp_get_cache_req_type(unsigned property)
+{
+       switch (property) {
+       case DP_PROP_RESET_CACHE:
+               return DP_CACHE_REQUEST_RESET_CACHE;
+       case DP_PROP_SET_CACHE_MAX_SIZE:
+               return DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE;
+       case DP_PROP_GET_CACHE_MAX_SIZE:
+               return DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE;
+       case DP_PROP_RESET_ALL_CACHE:
+               return DP_CACHE_REQUEST_RESET_ALL_CACHE;
+       case DP_PROP_SET_CACHE_PATH:
+               return DP_CACHE_REQUEST_SET_CACHE_PATH;
+       case DP_PROP_GET_CACHE_PATH:
+               return DP_CACHE_REQUEST_GET_CACHE_PATH;
+       case DP_PROP_SET_CACHE_LIFECYCLE:
+               return DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE;
+       case DP_PROP_GET_CACHE_LIFECYCLE:
+               return DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE;
+       default:
+               break;
+       }
+       return -1;
+}
+
 static int __dp_get_download_id(dp_client_fmt *client)
 {
        int download_id = -1;
@@ -402,6 +449,7 @@ static int __dp_request_create(dp_client_fmt *client, dp_ipc_fmt *ipc_info)
 
        request->id = download_id;
        request->agent_id = -1;
+       request->cache_agent_id = -1;
        request->state = DP_STATE_READY;
        request->error = DP_ERROR_NONE;
        request->network_type = DP_NETWORK_ALL;
@@ -415,6 +463,7 @@ static int __dp_request_create(dp_client_fmt *client, dp_ipc_fmt *ipc_info)
        request->content_type = DP_CONTENT_UNKNOWN;
        request->file_size = 0;
        request->noti_priv_id = -1;
+       request->file_name = NULL;
 
        dp_request_create(client, request);
        ipc_info->id = download_id;
@@ -425,6 +474,11 @@ void dp_request_free(dp_request_fmt *request)
 {
        // free notification handle here
        TRACE_DEBUG("destory id:%d", request->id);
+       if (request->file_name) {
+               free(request->file_name);
+               request->file_name = NULL;
+       }
+
        free(request);
 }
 
@@ -444,11 +498,17 @@ static int _dp_client_can_remove_request(dp_request_fmt *request)
                        return 1;
        }
 
-       if (request->state == DP_STATE_PAUSED &&
-                       dp_is_alive_download(request->agent_id) == 0) {
-               // paused & agent_id not exist.... unload from memory.
-               TRACE_ERROR("id:%d hanged as paused (%d/%d)",request->id, request->access_time, now_time);
-               return 1;
+       if (request->state == DP_STATE_PAUSED) {
+               int ret;
+               if (request->cache_agent_id >= 0)
+                       ret = dp_is_alive_cache_download(request->cache_agent_id);
+               else
+                       ret = dp_is_alive_download(request->agent_id);
+               if (ret == 0) {
+                       // paused & agent_id not exist.... unload from memory.
+                       TRACE_ERROR("id:%d hanged as paused (%d/%d)",request->id, request->access_time, now_time);
+                       return 1;
+               }
        }
 
        return 0;
@@ -492,8 +552,16 @@ void dp_client_clear_requests(void *slotp)
 
                if (is_zombie) {
                        TRACE_ERROR("id:%d is zombie request.", tailp->id);
-                       if (dp_cancel_agent_download_without_update(tailp->agent_id) < 0)
-                               TRACE_ERROR("failed to cancel download(%d) id:%d", tailp->agent_id, tailp->id);
+                       int ret;
+                       if (tailp->cache_agent_id >= 0) {
+                               ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id);
+                       } else {
+                               ret = dp_cancel_agent_download_without_update(tailp->agent_id);
+                       }
+                       if (ret < 0) {
+                               TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+                                               tailp->agent_id, tailp->cache_agent_id);
+                       }
                        tailp->state = DP_STATE_FAILED;
                        tailp->error = DP_ERROR_CONNECTION_TIMED_OUT;
                        if (tailp->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY ||
@@ -909,6 +977,7 @@ static int __dp_request_get_info(dp_client_fmt *client, dp_ipc_fmt *ipc_info, dp
        case DP_PROP_NETWORK_BONDING:
        case DP_PROP_AUTO_DOWNLOAD:
        case DP_PROP_HTTP_STATUS:
+       case DP_PROP_CACHE:
                errorcode = __dp_request_get_info_int_from_db(client, ipc_info);
                break;
        case DP_PROP_HTTP_HEADERS:
@@ -1111,18 +1180,25 @@ static int __dp_request_set_info(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info
                                                if (requestp->state == DP_STATE_CONNECTING ||
                                                                requestp->state == DP_STATE_DOWNLOADING) {
                                                        // pause & push queue
-                                                       if (dp_pause_agent_download_without_update(requestp->agent_id) < 0) {
-                                                               TRACE_ERROR("failed to pause download(%d) id:%d", requestp->agent_id, ipc_info->id);
+                                                       int ret;
+                                                       if (requestp->cache_agent_id >= 0) {
+                                                               ret = dp_pause_cache_download_without_update(requestp->cache_agent_id);
                                                        } else {
-                                                               requestp->state = DP_STATE_PAUSED;
-                                                               requestp->error = DP_ERROR_NONE;
-                                                               if (dp_queue_manager_push_queue(slot, requestp) < 0) {
-                                                                       if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0)
-                                                                               TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id);
-                                                                       requestp->state = DP_STATE_FAILED;
-                                                                       requestp->error = DP_ERROR_QUEUE_FULL;
-                                                                       errorcode = DP_ERROR_QUEUE_FULL;
-                                                               }
+                                                               ret = dp_pause_agent_download_without_update(requestp->agent_id);
+                                                       }
+                                                       if (ret < 0) {
+                                                               TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id,
+                                                                               requestp->agent_id, requestp->cache_agent_id);
+                                                       } else {
+                                                                       requestp->state = DP_STATE_PAUSED;
+                                                                       requestp->error = DP_ERROR_NONE;
+                                                                       if (dp_queue_manager_push_queue(slot, requestp) < 0) {
+                                                                               if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0)
+                                                                                       TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id);
+                                                                               requestp->state = DP_STATE_FAILED;
+                                                                               requestp->error = DP_ERROR_QUEUE_FULL;
+                                                                               errorcode = DP_ERROR_QUEUE_FULL;
+                                                                       }
                                                        }
                                                }
                                        }
@@ -1349,6 +1425,18 @@ static int __dp_request_set_info(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info
                }
                break;
        }
+       case DP_PROP_CACHE:
+       {
+               int recv_int = -1;
+               errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int);
+               if (errorcode == DP_ERROR_NONE) {
+                       if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (void *)&recv_int, ipc_info->size, 0, &errorcode) < 0) {
+                               TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property));
+                               errorcode = DP_ERROR_DISK_BUSY;
+                       }
+               }
+               break;
+       }
        default:
                errorcode = DP_ERROR_INVALID_PARAMETER;
                break;
@@ -1522,13 +1610,20 @@ static int __dp_call_cancel_agent(dp_request_fmt *request)
 {
        int ret = -1;
        if (request != NULL) {
-               if (request->agent_id >= 0) {
-                       TRACE_INFO("cancel download(%d) id: %d state:%s", request->agent_id,
-                                       request->id, dp_print_state(request->state));
+               if (request->cache_agent_id >= 0) {
+                       TRACE_INFO("cancel download for id(%d). cache-id:%d state:%s", request->id,
+                                       request->cache_agent_id, dp_print_state(request->state));
+                       if (dp_cancel_cache_download_without_update(request->cache_agent_id) == 0)
+                               ret = 0;
+               }
+               else if (request->agent_id >= 0) {
+                       TRACE_INFO("cancel download for id(%d). agent-id:%d state:%s", request->id,
+                                       request->agent_id, dp_print_state(request->state));
                        if (dp_cancel_agent_download_without_update(request->agent_id) == 0)
                                ret = 0;
                } else {
-                       TRACE_ERROR("invalid agent-id:%d id:%d", request->agent_id,     request->id);
+                       TRACE_ERROR("invalid agent-id:%d cache-id:%d id:%d", request->agent_id,
+                                       request->cache_agent_id, request->id);
                }
        }
        return ret;
@@ -1636,8 +1731,16 @@ static int __dp_request_controls(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info
                                                        dp_queue_manager_clear_queue(requestp);
                                                } else if (requestp->state == DP_STATE_CONNECTING ||
                                                                requestp->state == DP_STATE_DOWNLOADING) {
-                                                       if (dp_pause_agent_download_without_update(requestp->agent_id) < 0)
-                                                               TRACE_ERROR("failed to pause download(%d) id:%d", requestp->agent_id, ipc_info->id);
+                                                       int ret;
+                                                       if (requestp->cache_agent_id >= 0) {
+                                                               ret = dp_pause_cache_download_without_update(requestp->cache_agent_id);
+                                                       } else {
+                                                               ret = dp_pause_agent_download_without_update(requestp->agent_id);
+                                                       }
+                                                       if (ret < 0) {
+                                                               TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id,
+                                                                               requestp->agent_id, requestp->cache_agent_id);
+                                                       }
                                                }
                                                requestp->state = DP_STATE_PAUSED;
                                                requestp->error = DP_ERROR_NONE;
@@ -1665,6 +1768,7 @@ static int __dp_request_controls(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info
                                                        if (__dp_call_cancel_agent(requestp) < 0)
                                                                TRACE_ERROR("failed to cancel download(%d) id:%d", requestp->agent_id, ipc_info->id);
                                                }
+
                                                requestp->agent_id = -1;
                                                requestp->state = DP_STATE_CANCELED;
                                                requestp->error = DP_ERROR_NONE;
@@ -1800,6 +1904,9 @@ static int __dp_client_requests(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info)
                return errorcode;
        }
 
+       if (ipc_info->property >= DP_PROP_RESET_CACHE && ipc_info->property <= DP_PROP_RESET_ALL_CACHE)
+               return dp_cache_manager_handle_request(slot, ipc_info, __dp_get_cache_req_type(ipc_info->property));
+
        switch (ipc_info->section) {
        case DP_SEC_CONTROL:
                errorcode = __dp_request_controls(slot, ipc_info, requestp);
@@ -1830,11 +1937,27 @@ static void __dp_client_stop_all_requests(dp_client_slots_fmt *slot)
                TRACE_DEBUG("request %d stop id:%d state:%s", i, tailp->id, dp_print_state(tailp->state));
                int state = tailp->state;
                if (state == DP_STATE_CONNECTING) {
-                       if (dp_cancel_agent_download_without_update(tailp->agent_id) < 0)
-                               TRACE_ERROR("failed to cancel download(%d) id:%d", tailp->agent_id, tailp->id);
+                       int ret;
+                       if (tailp->cache_agent_id >= 0) {
+                               ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id);
+                       } else {
+                               ret = dp_cancel_agent_download_without_update(tailp->agent_id);
+                       }
+                       if (ret < 0) {
+                               TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+                                               tailp->agent_id, tailp->cache_agent_id);
+                       }
                } else if (state == DP_STATE_DOWNLOADING) {
-                       if (dp_pause_agent_download(tailp->agent_id) < 0)
-                               TRACE_ERROR("failed to pause download(%d) id:%d", tailp->agent_id, tailp->id);
+                       int ret;
+                       if (tailp->cache_agent_id >= 0) {
+                               ret = dp_pause_cache_download(tailp->cache_agent_id);
+                       } else {
+                               ret = dp_pause_agent_download(tailp->agent_id);
+                       }
+                       if (ret < 0) {
+                               TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+                                               tailp->agent_id, tailp->cache_agent_id);
+                       }
                }
                if (state == DP_STATE_DOWNLOADING || state == DP_STATE_CONNECTING) {
                        tailp->state = DP_STATE_QUEUED;
@@ -1881,17 +2004,14 @@ void *dp_client_request_thread(void *arg)
        sigaddset(&newmask, SIGUSR1);
        act.sa_handler = dp_client_sig_handler;
        sigaction(SIGUSR1, &act, NULL);
-
        fd_set imask, emask;
        int errorcode = DP_ERROR_NONE;
        dp_client_fmt *client = &slot->client;
        int client_sock = client->channel;
        struct timeval timeout; // for timeout of select
-
        CLIENT_MUTEX_UNLOCK(&slot->mutex);
 
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
-
        while (slot != NULL && client_sock >= 0 &&
                        client_sock == slot->client.channel) {
                memset(&timeout, 0x00, sizeof(struct timeval));
index 5e047ac..d2078ea 100755 (executable)
@@ -19,6 +19,7 @@
 #include <stdio.h>
 #include <stdlib.h> // alloc
 //#include <unistd.h> // unlink
+#include <string.h>
 
 #include <sqlite3.h>
 
 #include "download-provider-db.h"
 #include "download-provider-log.h"
 #include "download-provider-utils.h"
+#include "download-provider-cache.h"
+#include "download-provider-plugin-cache-agent.h"
 
+#define DP_DB_PARAM_NULL_CHECK do {\
+       if (handle == 0) {\
+               TRACE_ERROR("check connection handle");\
+               return -1;\
+       } \
+} while (0)
+
+#define DP_DB_BUFFER_NULL_CHECK(buffer) do {\
+       if (buffer == NULL) {\
+               TRACE_ERROR("check available memory");\
+               return -1;\
+       } \
+} while (0)
+
+#define DP_DB_BASIC_EXCEPTION_CHECK do {\
+       if (errorcode != SQLITE_OK) {\
+               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
+               *error = DP_ERROR_INVALID_PARAMETER;\
+               __dp_finalize(stmt);\
+               return -1;\
+       } \
+} while (0)
+
+#define DP_DB_WRITE_STEP_EXCEPTION_CHECK do {\
+       errorcode = sqlite3_step(stmt);\
+       __dp_finalize(stmt);\
+       if (errorcode != SQLITE_DONE) {\
+               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
+               *error = DP_ERROR_INVALID_PARAMETER;\
+               return -1;\
+       } \
+} while (0)
 
 static void __basic_property(sqlite3 *handle)
 {
@@ -130,15 +165,128 @@ static int __rebuild_client_tables(sqlite3 *handle)
        return 0;
 }
 
+static bool __check_cache_directory_empty(void)
+{
+       int entry_count = 0;
+       bool ret = false;
+       char *cache_path = NULL;
+       struct dirent *entry;
+       DIR *dir;
+
+       if (dp_get_cache_path(&cache_path) != DP_ERROR_NONE)
+               return ret;
+
+       if (!cache_path)
+               return ret;
+
+       dir = opendir(cache_path);
+       if (!dir) {
+               free(cache_path);
+               return ret;
+       }
+
+       while ((entry = readdir(dir)) != NULL) {
+               if (++entry_count > 2)
+                       break;
+       }
+
+       if (entry_count <= 2) {
+               TRACE_DEBUG("Cache directory is empty: %s, entry count: %d",
+                               cache_path, entry_count);
+               ret = true;
+       }
+
+       free(cache_path);
+       closedir(dir);
+
+       return ret;
+}
+
+static time_t __dp_get_oldest_cache_file_time_from_db(void *handle, int *error)
+{
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+       time_t result = 0;
+       *error = DP_ERROR_INVALID_PARAMETER;
+
+       DP_DB_PARAM_NULL_CHECK;
+
+       char *query = sqlite3_mprintf("SELECT MIN(%s) FROM %s",
+                       DP_DB_COL_CACHE_LAST_UPDATE, DP_TABLE_CACHE_FILE);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+
+       errorcode = sqlite3_step(stmt);
+       if (errorcode == SQLITE_ROW) {
+               int data_type = sqlite3_column_type(stmt, 0);
+               if (data_type == SQLITE_INTEGER) {
+                       long long last_update;
+                       last_update = sqlite3_column_int64(stmt, 0);
+                       if (last_update < 0)
+                               result = 0;
+                       else
+                               result = (time_t)last_update;
+               } else {
+                       TRACE_ERROR("check column type:%d", data_type);
+                       *error = DP_ERROR_NO_DATA;
+               }
+       } else if (errorcode == SQLITE_DONE) {
+               TRACE_DEBUG("no data");
+               *error = DP_ERROR_NO_DATA;
+       } else {
+               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+                       *error = DP_ERROR_ID_NOT_FOUND;
+       }
+
+       __dp_finalize(stmt);
+
+       return result;
+}
+
 static int __rebuild_client_manager_tables(sqlite3 *handle)
 {
        int ret = SQLITE_OK;
+       int errorcode = DP_ERROR_NONE;
+       static int cache_path_initial_check = DP_ERROR_UNKNOWN;
+
        if (__check_table(handle, DP_TABLE_CLIENTS) < 0)
                ret = sqlite3_exec(handle, DP_SCHEMA_CLIENTS, 0, 0, 0);
        if (ret != SQLITE_OK) {
-               TRACE_ERROR("create tables:%d error:%s", ret, sqlite3_errmsg(handle));
+               TRACE_ERROR("create clients table:%d error:%s", ret, sqlite3_errmsg(handle));
+               return -1;
+       }
+       if (__check_table(handle, DP_TABLE_CACHE) < 0)
+               ret = sqlite3_exec(handle, DP_SCHEMA_CACHE, 0, 0, 0);
+       if (ret != SQLITE_OK) {
+               TRACE_ERROR("create cache table:%d error:%s", ret, sqlite3_errmsg(handle));
                return -1;
        }
+       if (__check_table(handle, DP_TABLE_CACHE_FILE) < 0)
+               ret = sqlite3_exec(handle, DP_SCHEMA_CACHE_FILE, 0, 0, 0);
+       if (ret != SQLITE_OK) {
+               TRACE_ERROR("create cache_file table:%d error:%s", ret, sqlite3_errmsg(handle));
+               return -1;
+       }
+
+       if (cache_path_initial_check == DP_ERROR_UNKNOWN) {
+               if (__check_cache_directory_empty()) {
+                       dp_cache_delete_all_from_db(&errorcode);
+               } else {
+                       time_t oldest_time = 0;
+                       int error;
+
+                       oldest_time = __dp_get_oldest_cache_file_time_from_db(handle, &error);
+                       dp_set_oldest_cache_file_time(oldest_time);
+               }
+               cache_path_initial_check = DP_ERROR_ALREADY_COMPLETED;
+       }
+
        return 0;
 }
 
@@ -362,40 +510,6 @@ int dp_db_get_errorcode(void *handle)
        return DP_ERROR_NONE;
 }
 
-
-#define DP_DB_PARAM_NULL_CHECK do {\
-       if (handle == 0) {\
-               TRACE_ERROR("check connection handle");\
-               return -1;\
-       } \
-} while (0)
-
-#define DP_DB_BUFFER_NULL_CHECK(buffer) do {\
-       if (buffer == NULL) {\
-               TRACE_ERROR("check available memory");\
-               return -1;\
-       } \
-} while (0)
-
-#define DP_DB_BASIC_EXCEPTION_CHECK do {\
-       if (errorcode != SQLITE_OK) {\
-               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
-               *error = DP_ERROR_INVALID_PARAMETER;\
-               __dp_finalize(stmt);\
-               return -1;\
-       } \
-} while (0)
-
-#define DP_DB_WRITE_STEP_EXCEPTION_CHECK do {\
-       errorcode = sqlite3_step(stmt);\
-       __dp_finalize(stmt);\
-       if (errorcode != SQLITE_DONE) {\
-               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
-               *error = DP_ERROR_INVALID_PARAMETER;\
-               return -1;\
-       } \
-} while (0)
-
 int dp_db_get_ids(void *handle, const char *table, char *idcolumn, int *ids, const char *where, const int limit, char *ordercolumn, char *ordering, int *error)
 {
        *error = DP_ERROR_INVALID_PARAMETER;
@@ -965,6 +1079,30 @@ int dp_db_delete(void *handle, const int id, const char *table, int *error)
        return 0;
 }
 
+int dp_db_delete_all(void *handle, const char *table, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       DP_DB_PARAM_NULL_CHECK;
+       if (table == NULL) {
+               TRACE_ERROR("check materials for query");
+               return -1;
+       }
+
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+       char *query = sqlite3_mprintf("DELETE FROM %s", table);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+       return 0;
+}
+
 int dp_db_new_header(void *handle, const int id, const char *field, const char *value, int *error)
 {
        *error = DP_ERROR_INVALID_PARAMETER;
@@ -1416,4 +1554,768 @@ int dp_db_get_max_download_id(void *handle, const char *table, int *pvalue, int
        return 0;
 }
 
+static int __dp_db_increase_file_refcount(void *handle, const int id, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (id <= 0) {
+               TRACE_ERROR("check id:%d", id);
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "UPDATE %s SET %s=%s+1 WHERE %s IS ?",
+                       DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_ID);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int(stmt, 1, id);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       int refcount = 0;
+       if (dp_db_get_property_int(handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, &refcount, &errorcode) < 0) {
+               TRACE_ERROR("failed to get cache refcount");
+               *error = DP_ERROR_NO_DATA;
+               return -1;
+       }
+
+       return refcount;
+}
+
+static int __dp_db_decrease_file_refcount(void *handle, const int id, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (id <= 0) {
+               TRACE_ERROR("check id:%d", id);
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "UPDATE %s SET %s=%s-1 WHERE %s IS ?",
+                       DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_ID);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int(stmt, 1, id);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       int refcount = -1;
+       if (dp_db_get_property_int(handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, &refcount, error) < 0
+                       || refcount < 0) {
+               TRACE_ERROR("failed to get cache refcount");
+               return -1;
+       }
+
+       if (refcount == 0) {
+               if (dp_db_delete(handle, id, DP_TABLE_CACHE_FILE, error) < 0) {
+                       TRACE_ERROR("failed to delete cache file");
+                       return -1;
+               }
+       }
+
+       return refcount;
+}
+
+static int __dp_db_add_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!pkgname || !url || !file_name) {
+               TRACE_ERROR("NULL CHECK!: pkgname, url or file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                               "INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, ?)",
+                               DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_PACKAGE);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(url);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, url, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 3, file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 4, pkgname, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
+
+int dp_db_new_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, bool new_file, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       DP_DB_PARAM_NULL_CHECK;
+       if (!pkgname || !url || !file_name) {
+               TRACE_ERROR("NULL CHECK!: pkgname, url or file_name");
+               return -1;
+       }
+
+       if (__dp_db_add_app_cache(handle, pkgname, url, file_name, error) < 0) {
+               TRACE_ERROR("failed to add new cache data");
+               return -1;
+       }
+
+       if (!new_file) {
+               int id = dp_db_get_cache_file_id(handle, file_name, error);
+               if (id < 0) {
+                       TRACE_ERROR("failed to get cache file id");
+                       return -1;
+               }
+               if (__dp_db_increase_file_refcount(handle, id, error) < 0) {
+                       TRACE_ERROR("failed to increase file refcount");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+int dp_db_new_file_cache(void *handle, const char *file_name, const char *content_name, const char *etag,
+               const long max_age, const unsigned long long file_size, const long now, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!file_name || !content_name) {
+               TRACE_ERROR("NULL CHECK!: file_name or content_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, 1, ?, ?)",
+                       DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_FILENAME, DP_DB_COL_ETAG,
+                       DP_DB_COL_CACHE_FILE_SIZE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_MAX_AGE, DP_DB_COL_CACHE_LAST_UPDATE);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(file_name);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 3, content_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 4, etag, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 5, (sqlite_int64)file_size);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 6, (sqlite_int64)max_age);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 7, (sqlite_int64)now);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
+
+int dp_db_clear_app_cache(void *handle, const char *pkgname, GSList **files, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+       GSList *file_list = NULL;
+       int file_count = 0;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!pkgname || !files) {
+               TRACE_ERROR("NULL CHECK!: pkgname or files");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "SELECT %s, %s FROM %s WHERE %s IS ?",
+                       DP_DB_COL_ID, DP_DB_COL_CACHE_FILE_NAME, DP_TABLE_CACHE, DP_DB_COL_PACKAGE);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       errorcode = sqlite3_bind_text(stmt, 1, (char *)pkgname, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+               int get_id = sqlite3_column_int(stmt, 0);
+               const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+               if (dp_db_delete(handle, get_id, DP_TABLE_CACHE, &errorcode) < 0) {
+                       TRACE_DEBUG("failed to delete app cache from cache table");
+                       *error = DP_ERROR_NONE;
+                       continue;
+               }
+               int id = dp_db_get_cache_file_id(handle, get_file, error);
+               if (id < 0) {
+                       TRACE_DEBUG("failed to get cache file id");
+                       *error = DP_ERROR_NONE;
+                       continue;
+               }
+               int refcount = __dp_db_decrease_file_refcount(handle, id, error);
+               if (refcount < 0) {
+                       TRACE_DEBUG("failed to decrease file refcount");
+                       *error = DP_ERROR_NONE;
+                       continue;
+               }
+               if (refcount == 0) {
+                       int get_byte = sqlite3_column_bytes(stmt, 1);
+                       if (get_byte > 0) {
+                               char *get_str = (char *)g_try_malloc0((get_byte + 1) * sizeof(char));
+                               if (!get_str) {
+                                       TRACE_ERROR("check available system memory");
+                                       *error = DP_ERROR_OUT_OF_MEMORY;
+                                       break;
+                               }
+                               memcpy(get_str, get_file, get_byte * sizeof(char));
+                               get_str[get_byte] = '\0';
+                               file_list = g_slist_append(file_list, get_str);
+                               file_count++;
+                       }
+               }
+       }
+       __dp_finalize(stmt);
+
+       if (*error != DP_ERROR_NONE) {
+               if (file_list)
+                       g_slist_free_full(file_list, g_free);
+               return -1;
+       }
+       *files = file_list;
+       return file_count;
+}
+
+static int __dp_db_update_cache_file_name(void *handle, const int id, const char *file_name, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (id <= 0) {
+               TRACE_ERROR("check id:%d", id);
+               return -1;
+       }
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf("UPDATE %s SET %s = ? WHERE %s IS ?",
+                       DP_TABLE_CACHE, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_ID);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       errorcode = sqlite3_bind_text(stmt, 1, (char *)file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int(stmt, 2, id);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
+
+int dp_db_update_app_cache(void *handle, const char *url, const char *file_name, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!url || !file_name) {
+               TRACE_ERROR("NULL CHECK!: url or file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "SELECT %s FROM %s WHERE %s IS ? AND %s IS ?",
+                       DP_DB_COL_ID, DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(url);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, (char *)url, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+               int get_id = sqlite3_column_int(stmt, 0);
+               if (__dp_db_update_cache_file_name(handle, get_id, file_name, error) < 0) {
+                       TRACE_ERROR("failed to update app cache file");
+                       break;
+               }
+       }
+
+       __dp_finalize(stmt);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+int dp_db_update_file_cache(void *handle, const int id, const char *file_name, const char *etag,
+               const long max_age, const unsigned long long file_size, const long now, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (id <= 0) {
+               TRACE_ERROR("check id:%d", id);
+               return -1;
+       }
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf("UPDATE %s SET %s = ?, %s = ?, %s = ?, %s = ?, %s = ?, %s = ? WHERE %s IS ?",
+                       DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_CACHE_KEY, DP_DB_COL_ETAG,
+                       DP_DB_COL_CACHE_MAX_AGE, DP_DB_COL_CACHE_FILE_SIZE, DP_DB_COL_CACHE_LAST_UPDATE, DP_DB_COL_ID);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       errorcode = sqlite3_bind_text(stmt, 1, (char *)file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(file_name);
+       errorcode = sqlite3_bind_int64(stmt, 2, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 3, (char *)etag, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 4, (sqlite_int64)max_age);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 5, (sqlite_int64)file_size);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int64(stmt, 6, (sqlite_int64)now);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_int(stmt, 7, id);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
+
+// type 1:max_age or last_update 2:etag
+int dp_db_update_file_cache_validator(void *handle, const int id, const char *column, const int type, void *value, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (id <= 0) {
+               TRACE_ERROR("check id:%d", id);
+               return -1;
+       }
+       if (type != 1 && type != 2) {
+               TRACE_ERROR("check type:%d", type);
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf("UPDATE %s SET %s = ? WHERE %s IS ?", DP_TABLE_CACHE_FILE, column, DP_DB_COL_ID);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       if (type == 1) {
+               long *val_ptr = (long *)value;
+               errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)*val_ptr);
+               DP_DB_BASIC_EXCEPTION_CHECK;
+       } else if (type == 2) {
+               char *val_ptr = (char *)value;
+               errorcode = sqlite3_bind_text(stmt, 1, (char *)val_ptr, -1, SQLITE_STATIC);
+               DP_DB_BASIC_EXCEPTION_CHECK;
+       }
+
+       errorcode = sqlite3_bind_int(stmt, 2, id);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
+
+int dp_db_get_file_cache_string(void *handle, const char *file_name, const char *column, unsigned char **string, unsigned *length, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+       int found = 0;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!file_name || !column || !string || !length) {
+               TRACE_ERROR("NULL CHECK!: file_name, column, string or length");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ? AND %s IS ?", column, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME);
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(file_name);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, (char *)file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       errorcode = sqlite3_step(stmt);
+       if (errorcode == SQLITE_ROW) {
+               const char *get_col = (const char *)sqlite3_column_text(stmt, 0);
+               int get_byte = sqlite3_column_bytes(stmt, 0);
+               if (get_byte > 0) {
+                       unsigned char *get_str = (unsigned char *)calloc(get_byte + 1, sizeof(unsigned char));
+                       if (!get_str) {
+                               TRACE_ERROR("check available system memory");
+                               *error = DP_ERROR_OUT_OF_MEMORY;
+                               __dp_finalize(stmt);
+                               return -1;
+                       }
+
+                       memcpy(get_str, get_col, get_byte * sizeof(unsigned char));
+                       get_str[get_byte] = '\0';
+                       *string = get_str;
+                       *length = get_byte;
+                       found = 1;
+               } else {
+                       TRACE_DEBUG("no data");
+                       *error = DP_ERROR_NO_DATA;
+               }
+       } else if (errorcode == SQLITE_DONE) {
+               TRACE_DEBUG("no data");
+               *error = DP_ERROR_NO_DATA;
+       } else {
+               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+                       *error = DP_ERROR_ID_NOT_FOUND;
+       }
+       __dp_finalize(stmt);
+
+       if (*error != DP_ERROR_NO_DATA && *error != DP_ERROR_NONE)
+               return -1;
+
+       return found;
+}
+
+int dp_db_get_cache_file_name(void *handle, const char *pkgname, const char *url, char **file_name, unsigned *length, int *pkg_exist, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+       int found = 0;
+       *pkg_exist = 0;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!pkgname || !url || !file_name || !length) {
+               TRACE_ERROR("NULL CHECK!: pkgname, url, file_name or length");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "SELECT %s, %s FROM %s WHERE %s IS ? AND %s IS ?",
+                       DP_DB_COL_PACKAGE, DP_DB_COL_CACHE_FILE_NAME, DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(url);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, (char *)url, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+               const char *get_pkg = (const char *)sqlite3_column_text(stmt, 0);
+               const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+               if (found == 0) {
+                       int get_byte = sqlite3_column_bytes(stmt, 1);
+                       if (get_byte > 0) {
+                               char *get_str = (char *)calloc(get_byte + 1, sizeof(char));
+                               if (!get_str) {
+                                       TRACE_ERROR("check available system memory");
+                                       *error = DP_ERROR_OUT_OF_MEMORY;
+                                       break;
+                               }
+                               memcpy(get_str, get_file, get_byte * sizeof(char));
+                               get_str[get_byte] = '\0';
+                               *file_name = get_str;
+                               *length = get_byte;
+                               found = 1;
+                       }
+               }
+               if (strcmp(get_pkg, pkgname) == 0)
+                       *pkg_exist = 1;
+       }
+       __dp_finalize(stmt);
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       if (found == 0)
+               *error = DP_ERROR_NO_DATA;
+
+       return found;
+}
+
+int dp_db_get_cache_file_id(void *handle, const char *file_name, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       int errorcode = SQLITE_OK;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "SELECT %s FROM %s WHERE %s IS ? AND %s IS ?",
+                       DP_DB_COL_ID, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       //TRACE_DEBUG("debug query:%s", query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       unsigned long key = dp_get_hash_key(file_name);
+       errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 2, (char *)file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       errorcode = sqlite3_step(stmt);
+       if (errorcode == SQLITE_ROW) {
+               int get_id = sqlite3_column_int(stmt, 0);
+               __dp_finalize(stmt);
+               return get_id;
+       }
+
+       if (errorcode == SQLITE_DONE) {
+               TRACE_ERROR("no data");
+               *error = DP_ERROR_NO_DATA;
+       } else {
+               if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+                       *error = DP_ERROR_ID_NOT_FOUND;
+       }
+
+       __dp_finalize(stmt);
+       return -1;
+}
+
+static long long __get_capacity_size_threshold(long long max_size)
+{
+       long long deletion_threshold = 0;
+       long long used_cache_size;
+
+       if (dp_get_used_cache_size(&used_cache_size)) {
+               TRACE_ERROR("dp_get_used_cache_size failed!!");
+               return -1;
+       }
+
+       deletion_threshold = max_size - max_size * CACHE_CONTROL_DELETION_RATIO;
+
+       if (deletion_threshold > used_cache_size) {
+               TRACE_ERROR("No need to clear!!");
+               return -1;
+       }
+
+       deletion_threshold = used_cache_size - deletion_threshold;
+
+       return deletion_threshold;
+}
+
+int dp_db_clear_exceeded_cache(void *handle, GSList **files, long long max_val, int type, int *error)
+{
+       int errorcode = DP_ERROR_NONE;
+       sqlite3_stmt *stmt = NULL;
+       GSList *file_list = NULL;
+       time_t now_time = 0;
+       const char *col_str = NULL;
+       long long deletion_threshold = 0;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!files) {
+               TRACE_ERROR("NULL CHECK!: files");
+               return -1;
+       }
+
+       if (type ==  DP_CACHE_CAPACITY_MAX_SIZE) {
+               col_str = DP_DB_COL_CACHE_FILE_SIZE;
+
+               deletion_threshold = __get_capacity_size_threshold(max_val);
+               if (deletion_threshold < 0)
+                       return -1;
+
+       } else if (type ==  DP_CACHE_CAPACITY_LIFECYCLE) {
+               now_time = time(NULL);
+               col_str = DP_DB_COL_CACHE_LAST_UPDATE;
+               deletion_threshold = max_val - max_val * CACHE_CONTROL_DELETION_RATIO;
+       } else {
+               TRACE_ERROR("Invalid type: %d", type);
+               return -1;
+       }
+
+       TRACE_DEBUG("max_val : %lld, deletion_threshold: %lld, type: %d",
+                       max_val, deletion_threshold, type);
+
+       char *query = sqlite3_mprintf(
+                       "SELECT %s, %s, %s FROM %s ORDER BY %s",
+                       DP_DB_COL_ID, DP_DB_COL_CACHE_FILE_NAME, col_str,
+                       DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_LAST_UPDATE);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       *error = DP_ERROR_NONE;
+       while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+               int get_id = sqlite3_column_int(stmt, 0);
+               const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+               long long get_value = sqlite3_column_int64(stmt, 2);
+
+               if (get_value < 0)
+                       get_value = 0;
+
+               if (type ==  DP_CACHE_CAPACITY_MAX_SIZE) {
+                       deletion_threshold -= get_value;
+                       if (0 > deletion_threshold)
+                               break;
+               } else {
+                       if (difftime(now_time, (time_t)get_value) < deletion_threshold) {
+                               dp_set_oldest_cache_file_time((time_t)get_value);
+                               break;
+                       }
+               }
+
+               if (dp_db_delete(handle, get_id, DP_TABLE_CACHE_FILE, &errorcode) < 0) {
+                       TRACE_DEBUG("failed to delete cache data from cache_file table");
+                       *error = DP_ERROR_NONE;
+                       continue;
+               }
+
+               int get_byte = sqlite3_column_bytes(stmt, 1);
+               if (get_byte > 0) {
+                       char *get_str = (char *)g_try_malloc0((get_byte + 1) * sizeof(char));
+                       if (!get_str) {
+                               TRACE_ERROR("check available system memory");
+                               *error = DP_ERROR_OUT_OF_MEMORY;
+                               break;
+                       }
+                       memcpy(get_str, get_file, get_byte * sizeof(char));
+                       get_str[get_byte] = '\0';
+                       file_list = g_slist_append(file_list, get_str);
+               }
+
+       }
+       __dp_finalize(stmt);
+
+       TRACE_DEBUG("file_list count: %u", g_slist_length(file_list));
+
+       if (*error != DP_ERROR_NONE) {
+               if (file_list)
+                       g_slist_free_full(file_list, g_free);
+               return -1;
+       }
+
+       *files = file_list;
+       return 0;
+}
+
+int dp_db_clear_app_cache_by_filename(void *handle, const char *file_name, int *error)
+{
+       int errorcode = DP_ERROR_NONE;
+       sqlite3_stmt *stmt = NULL;
+
+       DP_DB_PARAM_NULL_CHECK;
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return -1;
+       }
+
+       char *query = sqlite3_mprintf(
+                       "DELETE FROM %s WHERE %s IS ?",
+                       DP_TABLE_CACHE, DP_DB_COL_CACHE_FILE_NAME);
+
+       DP_DB_BUFFER_NULL_CHECK(query);
+       errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+       sqlite3_free(query);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+       errorcode = sqlite3_bind_text(stmt, 1, file_name, -1, SQLITE_STATIC);
+       DP_DB_BASIC_EXCEPTION_CHECK;
+
+       DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+       return 0;
+}
 
diff --git a/provider/download-provider-plugin-cache-agent.c b/provider/download-provider-plugin-cache-agent.c
new file mode 100755 (executable)
index 0000000..5fac1cc
--- /dev/null
@@ -0,0 +1,1129 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+
+#include <download-provider.h>
+#include <download-provider-log.h>
+#include <download-provider-pthread.h>
+#include <download-provider-ipc.h>
+#include <download-provider-db-defs.h>
+#include <download-provider-db.h>
+#include <download-provider-utils.h>
+#include <download-provider-notify.h>
+#include <download-provider-security.h>
+#include <download-provider-client.h>
+#include <download-provider-client-manager.h>
+#include <download-provider-plugin-cache-agent.h>
+#include <download-provider-notification-manager.h>
+#include <download-provider-queue-manager.h>
+#include <download-provider-cache.h>
+
+#include <cache-agent-defs.h>
+#include <cache-agent-interface.h>
+#include <tzplatform_config.h>
+
+#include "xdgmime.h"
+#include "content/mime_type.h"
+
+#define DP_SDCARD_MNT_POINT tzplatform_mkpath(TZ_SYS_STORAGE, "sdcard")
+#define DP_EXTERNAL_STORAGE "/opt/media/"
+#define DP_MAX_FILE_PATH_LEN 256
+#define DP_MAX_MIME_TABLE_NUM 15
+
+static pthread_mutex_t mutex_capacity_max = PTHREAD_MUTEX_INITIALIZER;
+static bool g_capacity_max = false;
+
+static void *g_ca_handle = NULL;
+
+static int (*cache_agent_init)(ca_capacity_event_cb) = NULL;
+static int (*cache_agent_deinit)(void) = NULL;
+
+static char *(*cache_agent_store_file)(const char *) = NULL;
+static int (*cache_agent_remove_files)(int, const char **) = NULL;
+static int (*cache_agent_is_file_available)(const char *) = NULL;
+static int (*cache_agent_get_used_cache_size)(ca_size_t *) = NULL;
+
+static int (*cache_agent_start_download)(const char *, ca_req_data_t *, ca_cb_t *, int *) = NULL;
+static int (*cache_agent_pause_download)(int) = NULL;
+static int (*cache_agent_resume_download)(int) = NULL;
+static int (*cache_agent_cancel_download)(int) = NULL;
+static int (*cache_agent_pause_download_without_update)(int) = NULL;
+static int (*cache_agent_cancel_download_without_update)(int) = NULL;
+static int (*cache_agent_is_alive_download)(int) = NULL;
+
+static int (*cache_agent_get_max_cache_size)(unsigned int *) = NULL;
+static int (*cache_agent_set_max_cache_size)(unsigned int) = NULL;
+static int (*cache_agent_get_cache_path)(char **) = NULL;
+static int (*cache_agent_set_cache_path)(const char *) = NULL;
+static int (*cache_agent_get_cache_lifecycle)(unsigned int *) = NULL;
+static int (*cache_agent_set_cache_lifecycle)(unsigned int) = NULL;
+static int (*cache_agent_clear_all_files)(void) = NULL;
+static int (*cache_agent_set_oldest_file_time)(ca_time_t) = NULL;
+
+static int __ca_change_error(int err)
+{
+       int ret = DP_ERROR_NONE;
+       switch (err) {
+       case CA_RESULT_OK:
+               ret = DP_ERROR_NONE;
+               break;
+       case CA_ERR_INVALID_ARGUMENT:
+               ret = DP_ERROR_INVALID_PARAMETER;
+               break;
+       case CA_ERR_FAIL_TO_MEMALLOC:
+               ret = DP_ERROR_OUT_OF_MEMORY;
+               break;
+       case CA_ERR_DISK_FULL:
+               ret = DP_ERROR_NO_SPACE;
+               break;
+       case CA_ERR_ALREADY_MAX_COPY:
+               ret = DP_ERROR_TOO_MANY_DOWNLOADS;
+               break;
+       case CA_ERR_INVALID_STATE:
+               ret = DP_ERROR_INVALID_STATE;
+               break;
+       case CA_ERR_FAIL_TO_CREATE_THREAD:
+       case CA_ERR_FAIL_TO_ACCESS_FILE:
+       default:
+               ret = DP_ERROR_IO_ERROR;
+               break;
+       }
+       return ret;
+}
+
+static void __remove_trailing_slash(char *dir)
+{
+       int dir_path_len = 0;
+
+       dir_path_len = strlen(dir);
+       if (dir[dir_path_len - 1] == '/')
+               dir[dir_path_len - 1] = '\0';
+}
+
+static int __set_file_permission_to_client(dp_client_slots_fmt *slot, dp_request_fmt *request, char *saved_path)
+{
+       if (slot == NULL || request == NULL || saved_path == NULL) {
+               TRACE_ERROR("slot:%p request:%p id:%d cache agent id:%d saved_path:%s",
+                               slot, request, (request == NULL ? 0 : request->id),
+                               (request == NULL ? 0 : request->cache_agent_id), saved_path ? saved_path : "");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (strncmp(saved_path, DP_EXTERNAL_STORAGE, strlen(DP_EXTERNAL_STORAGE)) == 0
+                       || strncmp(DP_SDCARD_MNT_POINT,
+                               saved_path, strlen(DP_SDCARD_MNT_POINT)) == 0) {
+               TRACE_INFO("Do not change permission for %s", saved_path);
+               return DP_ERROR_NONE;
+       }
+
+       struct stat lstat_info;
+       int fd;
+       dp_credential cred = slot->credential;
+
+       if (lstat(saved_path, &lstat_info) == -1) {
+               TRACE_ERROR("Cannot access to %s", saved_path);
+               return DP_ERROR_PERMISSION_DENIED;
+       }
+
+       if ((lstat_info.st_mode & S_IFMT) == S_IFLNK) {
+               TRACE_ERROR("%s is a symbolic link.", saved_path);
+               return DP_ERROR_PERMISSION_DENIED;
+       }
+
+       fd = open(saved_path, O_RDONLY);
+       if (fd == -1) {
+               TRACE_SECURE_ERROR("open failed for file : %s", saved_path);
+               return DP_ERROR_IO_ERROR;
+       }
+
+       // Change the owner to client's uid.
+       if (fchown(fd, cred.uid, lstat_info.st_gid) != 0) {
+               TRACE_ERROR("[ERROR][%d] permission user:%d group:%d",
+                               request->id, cred.uid, lstat_info.st_gid);
+               close(fd);
+               return DP_ERROR_PERMISSION_DENIED;
+       }
+
+       TRACE_INFO("file owner has been changed to %d:%d", cred.uid, lstat_info.st_gid);
+
+       close(fd);
+       return DP_ERROR_NONE;
+}
+
+static int __ca_precheck_request(dp_client_slots_fmt* slot, dp_request_fmt *request, int agentid)
+{
+       if (request == NULL) {
+               TRACE_ERROR("null-check req_id:%d", agentid);
+               return -1;
+       }
+
+       dp_client_fmt *client = &slot->client;
+
+       for (dp_request_fmt *req = client->requests; req; req = req->next) {
+               if (req == request) {
+                       if (request->id < 0 || (request->cache_agent_id != agentid)) {
+                               TRACE_ERROR("id-check request_id:%d cache_id:%d req_id:%d",
+                                               request->id, request->cache_agent_id, agentid);
+                               return -1;
+                       }
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int __dp_ca_state_feedback(dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+       if (slot == NULL || request == NULL) {
+               TRACE_ERROR("check address");
+               return -1; // try cancel
+       }
+
+       TRACE_INFO("[INFO][%d] state:%s error:%s", request->id,
+                       dp_print_state(request->state), dp_print_errorcode(request->error));
+
+       int errorcode = DP_ERROR_NONE;
+       if (dp_db_update_logging(slot->client.dbhandle, request->id,
+                               request->state, request->error, &errorcode) < 0) {
+               TRACE_ERROR("logging failure id:%d error:%d", request->id, errorcode);
+               return -1; // try cancel
+       }
+
+       request->access_time = (int)time(NULL);
+
+       if (request->state_cb == 1) {
+               if (slot->client.notify < 0 ||
+                               dp_notify_feedback(slot->client.notify, slot, request->id, request->state, request->error, 0) < 0) {
+                       TRACE_ERROR("id:%d disable state callback by IO_ERROR", request->id);
+                       request->state_cb = 0;
+               }
+       }
+
+       return 0;
+}
+
+static GSList *__capacity_event_cb(ca_capacity_event_type capa_type)
+{
+       GSList *file_list = NULL;
+       unsigned int ref_val;
+       long long max_cache_size;
+
+       pthread_mutex_lock(&(mutex_capacity_max));
+       g_capacity_max = true;
+       pthread_mutex_unlock(&(mutex_capacity_max));
+
+       if (capa_type == CA_CAPACITY_MAX_SIZE) {
+               if (DP_ERROR_NONE != dp_get_max_cache_size(&ref_val))
+                       goto done;
+
+               max_cache_size = (long long)ref_val * CA_MB_BYTE;
+               file_list = dp_cache_clear_exceeded_files(max_cache_size, DP_CACHE_CAPACITY_MAX_SIZE);
+
+       } else if (capa_type == CA_CAPACITY_LIFECYCLE) {
+               if (DP_ERROR_NONE != dp_get_cache_lifecycle(&ref_val))
+                       goto done;
+
+               file_list = dp_cache_clear_exceeded_files((long long)ref_val, DP_CACHE_CAPACITY_LIFECYCLE);
+       }
+
+done:
+       pthread_mutex_lock(&(mutex_capacity_max));
+       g_capacity_max = false;
+       pthread_mutex_unlock(&(mutex_capacity_max));
+
+       return file_list;
+}
+
+static void __cache_finished_cb(ca_finished_info_t *info,
+                                       void *user_req_data, void *user_client_data)
+{
+       if (info == NULL) {
+               TRACE_ERROR("check cache info address");
+               return;
+       }
+
+       dp_client_slots_fmt *slot = user_client_data;
+       dp_request_fmt *request = user_req_data;
+       if (slot == NULL || request == NULL) {
+               TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+                               slot, request, (request == NULL ? 0 : request->id), info->cache_id);
+               return;
+       }
+
+       CLIENT_MUTEX_LOCK(&slot->mutex);
+       if (__ca_precheck_request(slot, request, info->cache_id) < 0) {
+               TRACE_ERROR("error request agent_id:%d", info->cache_id);
+               if (dp_cancel_cache_download(info->cache_id) < 0)
+                       TRACE_ERROR("failed to call cancel_cache_download(%d)", info->cache_id);
+
+               CLIENT_MUTEX_UNLOCK(&slot->mutex);
+               return ;
+       }
+
+       int state = DP_STATE_NONE;
+       int errorcode = DP_ERROR_NONE;
+
+       if (info->http_status > 0) {
+               if (dp_db_replace_property(slot->client.dbhandle, request->id,
+                               DP_TABLE_DOWNLOAD, DP_DB_COL_HTTP_STATUS, (void *)&info->http_status, 0, 0, &errorcode) < 0)
+                       TRACE_ERROR("id:%d failed to set http_status(%d)", request->id, info->http_status);
+       }
+
+       TRACE_SECURE_DEBUG("[FINISH][%d][%s]", request->id, info->saved_path);
+
+       if (info->error == CA_RESULT_OK) {
+               if (info->saved_path != NULL) {
+                       errorcode = __set_file_permission_to_client(slot,
+                                       request, info->saved_path);
+               } else {
+                       TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
+                       errorcode = DP_ERROR_INVALID_DESTINATION;
+               }
+               if (errorcode == DP_ERROR_NONE)
+                       state = DP_STATE_COMPLETED;
+               else
+                       state = DP_STATE_FAILED;
+       } else {
+               if (info->error == CA_RESULT_USER_CANCELED) {
+
+                       TRACE_INFO("[CANCELED][%d]", request->id);
+
+                       if (request->error == DP_ERROR_IO_EAGAIN) {
+                               request->error = DP_ERROR_NONE;
+                       } else {
+                               state = DP_STATE_CANCELED;
+                               errorcode = request->error;
+                       }
+               } else {
+                       state = DP_STATE_FAILED;
+                       errorcode = __ca_change_error(info->error);
+                       TRACE_ERROR("[FAILED][%d][%s] agent error:%d", request->id,
+                                       dp_print_errorcode(errorcode), info->error);
+               }
+       }
+
+       if (errorcode == DP_ERROR_NONE && info->saved_path != NULL) {
+
+               char *content_name = NULL;
+               char *str = NULL;
+               str = strrchr(info->saved_path, '/');
+               if (str != NULL) {
+                       str++;
+                       content_name = dp_strdup(str);
+               }
+               if (request->file_size == 0) {// missed in download_cb
+                       request->file_size = info->file_size;
+                       if (dp_db_replace_property(slot->client.dbhandle, request->id,
+                                       DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&request->file_size, 0, 1, &errorcode) < 0)
+                               TRACE_ERROR("id:%d failed to set content_size(%llu)", request->id, request->file_size);
+               }
+
+               if (content_name != NULL) {
+                       if (dp_db_replace_property(slot->client.dbhandle, request->id,
+                                       DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)content_name, 0, 2, &errorcode) < 0)
+                               TRACE_ERROR("id:%d failed to set content_name", request->id);
+               }
+
+               if (dp_db_replace_property(slot->client.dbhandle, request->id,
+                               DP_TABLE_DOWNLOAD, DP_DB_COL_SAVED_PATH, (void *)info->saved_path, 0, 2, &errorcode) < 0)
+                       TRACE_ERROR("id:%d failed to set saved_path", request->id);
+
+               free(content_name);
+
+               /* update the received file size.
+                * The last received file size cannot update
+                * because of reducing update algorithm */
+               request->received_size = info->file_size;
+               if (request->progress_cb == 1) {
+                       if (slot->client.notify < 0 ||
+                                       dp_notify_feedback(slot->client.notify, slot, request->id,
+                                                       DP_STATE_DOWNLOADING, DP_ERROR_NONE, request->received_size) < 0) {
+                               TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id);
+                               request->progress_cb = 0;
+                       }
+               }
+       }
+
+       request->state = state;
+       request->error = errorcode;
+
+       if (__dp_ca_state_feedback(slot, request) < 0) {
+               TRACE_ERROR("id:%d check notify channel", request->id);
+               if (dp_cancel_cache_download_without_update(request->cache_agent_id) < 0)
+                       TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+       }
+       if (request->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY ||
+                       request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+               if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION) < 0)
+                       TRACE_ERROR("failed to register notification for id:%d", request->id);
+       }
+
+       CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+static void __cache_paused_cb(int cache_id, void *user_req_data, void *user_client_data)
+{
+       dp_client_slots_fmt *slot = user_client_data;
+       dp_request_fmt *request = user_req_data;
+       if (slot == NULL || request == NULL) {
+               TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+                               slot, request, (request == NULL ? 0 : request->id), cache_id);
+               return ;
+       }
+
+       TRACE_DEBUG("[PAUSED] id:%d agent id:%d", request->id, cache_id);
+       dp_queue_manager_wake_up();
+}
+
+static void __cache_download_info_cb(cache_info_t *info, void *user_req_data, void *user_client_data)
+{
+       if (info == NULL) {
+               TRACE_ERROR("check download info address");
+               return ;
+       }
+
+       dp_client_slots_fmt *slot = user_client_data;
+       dp_request_fmt *request = user_req_data;
+
+       if (slot == NULL || request == NULL) {
+               TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+                               slot, request, (request == NULL ? 0 : request->id), info->cache_id);
+               return ;
+       }
+
+       CLIENT_MUTEX_LOCK(&slot->mutex);
+
+       if (__ca_precheck_request(slot, request, info->cache_id) < 0) {
+               TRACE_ERROR("error request agent_id:%d", info->cache_id);
+
+               if (dp_cancel_cache_download(info->cache_id) < 0)
+                       TRACE_ERROR("failed to call cancel_download(%d)", info->cache_id);
+
+               CLIENT_MUTEX_UNLOCK(&slot->mutex);
+               return ;
+       }
+
+       if (info->error != CA_RESULT_OK) {
+               request->error = DP_ERROR_IO_ERROR;
+       } else {
+               int errorcode = DP_ERROR_NONE;
+
+               if (dp_db_replace_property(slot->client.dbhandle, request->id,
+                               DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)info->content_name, 0, 2, &errorcode) < 0)
+                       TRACE_ERROR("id:%d failed to set contentname", request->id);
+
+               if (info->file_size > 0 && dp_db_replace_property(slot->client.dbhandle, request->id,
+                               DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&(info->file_size), 0, 1, &errorcode) < 0)
+                       TRACE_ERROR("id:%d failed to set file size", request->id);
+
+               errorcode = __set_file_permission_to_client(slot, request, info->saved_path);
+               request->error = errorcode;
+       }
+
+       if (request->error != DP_ERROR_NONE) {
+               request->state = DP_STATE_FAILED;
+               TRACE_ERROR("id:%d try to cancel(%d)", request->id, info->cache_id);
+
+               if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+                       TRACE_ERROR("[fail][%d] cancel_agent:%d", request->id, info->cache_id);
+
+       } else {
+               request->state = DP_STATE_DOWNLOADING;
+               request->file_size = info->file_size;
+               TRACE_DEBUG("[STARTED] id:%d agent id:%d", request->id, info->cache_id);
+       }
+
+       if (__dp_ca_state_feedback(slot, request) < 0) {
+               TRACE_ERROR("id:%d check notify channel", request->id);
+               if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+                       TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+       }
+
+       if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+               if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_UPDATE) < 0)
+                       TRACE_ERROR("failed to register notification for id:%d", request->id);
+       }
+
+       CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+static void __cache_progress_cb(int cache_id, unsigned long long received_size,
+               void *user_req_data, void *user_client_data)
+{
+       dp_client_slots_fmt *slot = user_client_data;
+       dp_request_fmt *request = user_req_data;
+       if (slot == NULL || request == NULL) {
+               TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+                               slot, request, (request == NULL ? 0 : request->id), cache_id);
+               return ;
+       }
+       CLIENT_MUTEX_LOCK(&slot->mutex);
+
+       if (__ca_precheck_request(slot, request, cache_id) < 0) {
+               TRACE_ERROR("error request agent_id:%d", cache_id);
+               if (dp_cancel_cache_download(cache_id) < 0)
+                       TRACE_ERROR("failed to call cancel_download(%d)", cache_id);
+               CLIENT_MUTEX_UNLOCK(&slot->mutex);
+               return ;
+       }
+
+       TRACE_DEBUG("state: %d, progress %llu", request->state, received_size);
+
+       // For resume case after pause, it change state from connecting to downloading
+       if (request->state == DP_STATE_CONNECTING) {
+               request->state = DP_STATE_DOWNLOADING;
+               if (__dp_ca_state_feedback(slot, request) < 0) {
+                       TRACE_ERROR("id:%d check notify channel", request->id);
+                       if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+                               TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+               }
+       }
+
+       if (request->state == DP_STATE_DOWNLOADING) {
+               request->received_size = received_size;
+
+               if (request->progress_cb == 1) {
+                       if (slot->client.notify < 0 ||
+                                       dp_notify_feedback(slot->client.notify, slot,
+                                                       request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, received_size) < 0) {
+                               // failed to read from socket // ignore this status
+                               TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id);
+                               request->progress_cb = 0;
+                       }
+               }
+
+               if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+                       if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_PROGRESS) < 0)
+                               TRACE_ERROR("failed to register notification for id:%d", request->id);
+               }
+       }
+       CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+int dp_init_cache_agent()
+{
+       if (g_ca_handle)
+               return DP_ERROR_NONE;
+
+       g_ca_handle = dlopen(LIB_CACHE_AGENT_PATH, RTLD_LAZY | RTLD_GLOBAL);
+
+       if (!g_ca_handle) {
+               TRACE_ERROR("[dlopen] %s", dlerror());
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+       dlerror();    /* Clear any existing error */
+
+       *(void **) (&cache_agent_init) = dlsym(g_ca_handle, "ca_init");
+       if (cache_agent_init == NULL) {
+               TRACE_ERROR("[dlsym] ca_init:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_deinit) = dlsym(g_ca_handle, "ca_deinit");
+       if (cache_agent_deinit == NULL) {
+               TRACE_ERROR("[dlsym] ca_deinit:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_store_file) = dlsym(g_ca_handle, "ca_store_file");
+       if (cache_agent_store_file == NULL) {
+               TRACE_ERROR("[dlsym] ca_store_file:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_remove_files) = dlsym(g_ca_handle, "ca_remove_files");
+       if (cache_agent_remove_files == NULL) {
+               TRACE_ERROR("[dlsym] ca_remove_files:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_is_file_available) = dlsym(g_ca_handle, "ca_is_file_available");
+       if (cache_agent_is_file_available == NULL) {
+               TRACE_ERROR("[dlsym] ca_is_file_available:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_get_used_cache_size) = dlsym(g_ca_handle, "ca_get_used_cache_size");
+       if (cache_agent_get_used_cache_size == NULL) {
+               TRACE_ERROR("[dlsym] ca_get_used_cache_size:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_start_download) = dlsym(g_ca_handle, "ca_start_cache_download");
+       if (cache_agent_start_download == NULL) {
+               TRACE_ERROR("[dlsym] ca_start_cache_download:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_pause_download) = dlsym(g_ca_handle, "ca_pause_cache_download");
+       if (cache_agent_pause_download == NULL) {
+               TRACE_ERROR("[dlsym] ca_pause_cache_download:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_resume_download) = dlsym(g_ca_handle, "ca_resume_cache_download");
+       if (cache_agent_resume_download == NULL) {
+               TRACE_ERROR("[dlsym] ca_resume_cache_download:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_cancel_download) = dlsym(g_ca_handle, "ca_cancel_cache_download");
+       if (cache_agent_cancel_download == NULL) {
+               TRACE_ERROR("[dlsym] ca_cancel_cache_download:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_pause_download_without_update) = dlsym(g_ca_handle, "ca_pause_cache_download_without_update");
+       if (cache_agent_pause_download_without_update == NULL) {
+               TRACE_ERROR("[dlsym] ca_pause_cache_download_without_update:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_cancel_download_without_update) = dlsym(g_ca_handle, "ca_cancel_cache_download_without_update");
+       if (cache_agent_cancel_download_without_update == NULL) {
+               TRACE_ERROR("[dlsym] ca_cancel_cache_download_without_update:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_is_alive_download) = dlsym(g_ca_handle, "ca_is_alive_cache_download");
+       if (cache_agent_is_alive_download == NULL) {
+               TRACE_ERROR("[dlsym] ca_is_alive_cache_download:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_get_max_cache_size) = dlsym(g_ca_handle, "ca_get_max_cache_size");
+       if (cache_agent_get_max_cache_size == NULL) {
+               TRACE_ERROR("[dlsym] ca_get_max_cache_size:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_set_max_cache_size) = dlsym(g_ca_handle, "ca_set_max_cache_size");
+       if (cache_agent_set_max_cache_size == NULL) {
+               TRACE_ERROR("[dlsym] ca_set_max_cache_size:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_get_cache_path) = dlsym(g_ca_handle, "ca_get_cache_path");
+       if (cache_agent_get_cache_path == NULL) {
+               TRACE_ERROR("[dlsym] ca_get_cache_path:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_set_cache_path) = dlsym(g_ca_handle, "ca_set_cache_path");
+       if (cache_agent_set_cache_path == NULL) {
+               TRACE_ERROR("[dlsym] ca_set_cache_path:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_get_cache_lifecycle) = dlsym(g_ca_handle, "ca_get_cache_lifecycle");
+       if (cache_agent_get_cache_lifecycle == NULL) {
+               TRACE_ERROR("[dlsym] ca_get_cache_lifecycle:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_set_cache_lifecycle) = dlsym(g_ca_handle, "ca_set_cache_lifecycle");
+       if (cache_agent_set_cache_lifecycle == NULL) {
+               TRACE_ERROR("[dlsym] ca_set_cache_lifecycle:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_clear_all_files) = dlsym(g_ca_handle, "ca_clear_all_files");
+       if (cache_agent_clear_all_files == NULL) {
+               TRACE_ERROR("[dlsym] ca_clear_all_files:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       *(void **) (&cache_agent_set_oldest_file_time) = dlsym(g_ca_handle, "ca_set_oldest_file_time");
+       if (cache_agent_clear_all_files == NULL) {
+               TRACE_ERROR("[dlsym] ca_set_oldest_file_time:%s", dlerror());
+               dlclose(g_ca_handle);
+               g_ca_handle = NULL;
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       int ca_ret = -1;
+       ca_ret = (*cache_agent_init)(__capacity_event_cb);
+       if (ca_ret != CA_RESULT_OK)
+               return DP_ERROR_OUT_OF_MEMORY;
+       return DP_ERROR_NONE;
+}
+
+void dp_deinit_cache_agent()
+{
+       if (g_ca_handle) {
+               if (cache_agent_deinit) {
+                       (*cache_agent_deinit)();
+                       g_ca_handle = NULL;
+                       // Do not unload a symbol file here.
+               }
+       }
+}
+
+// NULL : failed
+char *dp_store_file(const char *file_path)
+{
+       TRACE_DEBUG("dp_store_file called, file path: %s", file_path);
+
+       if (!file_path) {
+               TRACE_ERROR("[NULL-CHECK] file_path");
+               return NULL;
+       }
+
+       if (cache_agent_store_file)
+               return (*cache_agent_store_file)(file_path);
+
+       return NULL;
+}
+
+// 0 : success
+// -1 : failed
+int dp_remove_files(int file_count, const char *cache_files[])
+{
+       TRACE_DEBUG("dp_remove_files called, file_count: %d", file_count);
+
+       if (file_count <= 0) {
+               TRACE_ERROR("[PARAM-CHECK] file_count");
+               return -1;
+       }
+
+       for (int i = 0; i < file_count; i++) {
+               if (!cache_files[i]) {
+                       TRACE_ERROR("[NULL-CHECK] cache_files[%d]", i);
+                       return -1;
+               }
+               TRACE_DEBUG("file name[%d]: %s", i, cache_files[i]);
+       }
+
+       if (cache_agent_remove_files) {
+               if ((*cache_agent_remove_files)(file_count, cache_files) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+// 0 : available
+// -1 : not available
+int dp_is_file_available(const char *cache_file)
+{
+       TRACE_DEBUG("dp_is_file_available called, cache_file: %s", cache_file);
+
+       if (!cache_file) {
+               TRACE_ERROR("[NULL-CHECK] cache_file");
+               return -1;
+       }
+
+       if (cache_agent_is_file_available) {
+               if ((*cache_agent_is_file_available)(cache_file) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+// 0 : success
+// -1 : failed
+int dp_get_used_cache_size(long long *size)
+{
+       TRACE_DEBUG("dp_get_used_cache_size called");
+
+       if (!size) {
+               TRACE_ERROR("[NULL-CHECK] size");
+               return -1;
+       }
+
+       if (cache_agent_get_used_cache_size) {
+               if ((*cache_agent_get_used_cache_size)(size) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+int dp_start_cache_download(const char *cache_file, void *slot, void *request)
+{
+       TRACE_DEBUG("dp_start_cache_download called, cache_file: %s", cache_file);
+
+       int ret = -1;
+       int req_cache_id = -1;
+       int errorcode = DP_ERROR_NONE;
+       unsigned length = 0;
+       char *destination = NULL;
+       char *filename = NULL;
+       ca_req_data_t *req_data = NULL;
+
+       dp_client_slots_fmt *base_slot = slot;
+       dp_request_fmt *base_req = request;
+
+       if (cache_file == NULL || slot == NULL || request == NULL) {
+               TRACE_ERROR("check cache_file, slot or request address");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       ca_cb_t ca_cb = {
+               __cache_download_info_cb,
+               __cache_progress_cb,
+               __cache_finished_cb,
+               __cache_paused_cb
+       };
+
+       req_data = (ca_req_data_t *)calloc(1, sizeof(ca_req_data_t));
+       if (req_data == NULL) {
+               TRACE_ERROR("[ERROR] calloc");
+               return DP_ERROR_OUT_OF_MEMORY;
+       }
+
+       if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id,
+                       DP_TABLE_REQUEST, DP_DB_COL_FILENAME, (unsigned char **)&filename, &length, &errorcode) < 0 ||
+                       filename == NULL) {
+               TRACE_DEBUG("filename id:%d NO_DATA, use request->file_name: %s", base_req->id, base_req->file_name);
+               req_data->file_name = base_req->file_name;
+       } else {
+               req_data->file_name = filename;
+       }
+
+       if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id,
+                       DP_TABLE_REQUEST, DP_DB_COL_DESTINATION, (unsigned char **)&destination, &length, &errorcode) < 0 ||
+                       destination == NULL) {
+               TRACE_DEBUG("destination id:%d NO_DATA, use default install path: %s", base_req->id, CA_DEFAULT_INSTALL_PATH);
+               req_data->install_path = CA_DEFAULT_INSTALL_PATH;
+       } else {
+               __remove_trailing_slash(destination);
+               req_data->install_path = destination;
+       }
+
+       req_data->pkg_name = base_slot->pkgname;
+
+       req_data->user_client_data = (void *)slot;
+       req_data->user_req_data = (void *)request;
+
+       // call start API of cache agent lib
+       if (cache_agent_start_download != NULL)
+               ret = (*cache_agent_start_download)(cache_file, req_data, &ca_cb, &req_cache_id);
+
+       free(destination);
+       free(filename);
+       free(req_data);
+
+       if (ret == CA_RESULT_OK) {
+               TRACE_DEBUG("request id :%d SUCCESS cache_id:%d", base_req->id, req_cache_id);
+               base_req->cache_agent_id = req_cache_id;
+       }
+       return __ca_change_error(ret);
+}
+
+int dp_pause_cache_download(int req_id)
+{
+       TRACE_DEBUG("dp_pause_cache_download called, req_id %d",req_id);
+
+       if (req_id < 0) {
+               TRACE_ERROR("[PARAM-CHECK] req_id");
+               return -1;
+       }
+
+       if (dp_is_alive_cache_download(req_id) == 0) {
+               TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+               return -1;
+       }
+
+       if (cache_agent_pause_download != NULL) {
+               if ((*cache_agent_pause_download)(req_id) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+int dp_resume_cache_download(int req_id)
+{
+       TRACE_DEBUG("dp_resume_cache_download called, req_id %d",req_id);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (req_id < 0) {
+               TRACE_ERROR("[PARAM-CHECK] req_id");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_resume_download)
+               ca_ret = (*cache_agent_resume_download)(req_id);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_cancel_cache_download(int req_id)
+{
+       TRACE_DEBUG("dp_cancel_cache_download called, req_id %d",req_id);
+
+       if (req_id < 0) {
+               TRACE_ERROR("[NULL-CHECK] req_id");
+               return -1;
+       }
+
+       if (dp_is_alive_cache_download(req_id) == 0) {
+               TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+               return -1;
+       }
+
+       if (cache_agent_cancel_download != NULL) {
+               if ((*cache_agent_cancel_download)(req_id) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+int dp_pause_cache_download_without_update(int req_id)
+{
+       TRACE_DEBUG("dp_pause_cache_download_without_update called, req_id %d",req_id);
+
+       if (req_id < 0) {
+               TRACE_ERROR("[PARAM-CHECK] req_id");
+               return -1;
+       }
+
+       if (dp_is_alive_cache_download(req_id) == 0) {
+               TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+               return -1;
+       }
+
+       if (cache_agent_pause_download_without_update != NULL) {
+               if ((*cache_agent_pause_download_without_update)(req_id) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+int dp_cancel_cache_download_without_update(int req_id)
+{
+       TRACE_DEBUG("dp_cancel_cache_download_without_update called, req_id %d",req_id);
+
+       if (req_id < 0) {
+               TRACE_ERROR("[NULL-CHECK] req_id");
+               return -1;
+       }
+
+       if (dp_is_alive_cache_download(req_id) == 0) {
+               TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+               return -1;
+       }
+
+       if (cache_agent_cancel_download_without_update != NULL) {
+               if ((*cache_agent_cancel_download_without_update)(req_id) == CA_RESULT_OK)
+                       return 0;
+       }
+
+       return -1;
+}
+
+int dp_is_alive_cache_download(int req_id)
+{
+       TRACE_DEBUG("dp_is_alive_cache_download called, req_id %d",req_id);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (req_id < 0)
+               return 0;
+
+       if (cache_agent_is_alive_download)
+               ca_ret = (*cache_agent_is_alive_download)(req_id);
+
+       return ca_ret == CA_RESULT_OK ? 1 : 0;
+}
+
+int dp_get_max_cache_size(unsigned int *size)
+{
+       TRACE_DEBUG("dp_get_max_cache_size called");
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (!size) {
+               TRACE_ERROR("[PARAM-CHECK] size");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_get_max_cache_size)
+               ca_ret = (*cache_agent_get_max_cache_size)(size);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_set_max_cache_size(unsigned int size)
+{
+       TRACE_DEBUG("dp_set_max_cache_size called, size: %d", size);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (cache_agent_set_max_cache_size)
+               ca_ret = (*cache_agent_set_max_cache_size)(size);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_get_cache_path(char **path)
+{
+       TRACE_DEBUG("dp_get_cache_path called");
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (!path) {
+               TRACE_ERROR("[PARAM-CHECK] path");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_get_cache_path)
+               ca_ret = (*cache_agent_get_cache_path)(path);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_set_cache_path(const char *path)
+{
+       TRACE_DEBUG("dp_set_cache_path called, path: %s", path);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (!path) {
+               TRACE_ERROR("[PARAM-CHECK] path");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_set_cache_path)
+               ca_ret = (*cache_agent_set_cache_path)(path);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_get_cache_lifecycle(unsigned int *time)
+{
+       TRACE_DEBUG("dp_get_cache_lifecycle called");
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (!time) {
+               TRACE_ERROR("[PARAM-CHECK] time");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_get_cache_lifecycle)
+               ca_ret = (*cache_agent_get_cache_lifecycle)(time);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_set_cache_lifecycle(unsigned int time)
+{
+       TRACE_DEBUG("dp_set_cache_lifecycle called, time: %u", time);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (cache_agent_set_cache_lifecycle)
+               ca_ret = (*cache_agent_set_cache_lifecycle)(time);
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_clear_all_cache_files(void)
+{
+       TRACE_DEBUG("dp_clear_all_files called");
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (cache_agent_clear_all_files)
+               ca_ret = (*cache_agent_clear_all_files)();
+
+       return __ca_change_error(ca_ret);
+}
+
+int dp_set_oldest_cache_file_time(time_t time)
+{
+       TRACE_DEBUG("dp_set_oldest_cache_file_time called, time: %ld", time);
+
+       int ca_ret = CA_RESULT_OK;
+
+       if (time < 0) {
+               TRACE_ERROR("[PARAM-CHECK] time");
+               return DP_ERROR_INVALID_PARAMETER;
+       }
+
+       if (cache_agent_set_oldest_file_time)
+               ca_ret = (*cache_agent_set_oldest_file_time)(time);
+
+       return __ca_change_error(ca_ret);
+}
+
+bool dp_is_cache_storage_busy(void)
+{
+       bool ret;
+
+       pthread_mutex_lock(&(mutex_capacity_max));
+       ret = g_capacity_max;
+       pthread_mutex_unlock(&(mutex_capacity_max));
+
+       return ret;
+}
index f32e2e9..bff38a4 100755 (executable)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -36,6 +37,7 @@
 #include <download-provider-security.h>
 #include <download-provider-client.h>
 #include <download-provider-client-manager.h>
+#include <download-provider-plugin-cache-agent.h>
 #include <download-provider-plugin-download-agent.h>
 #include <download-provider-notification-manager.h>
 #include <download-provider-queue-manager.h>
@@ -87,6 +89,7 @@ static mime_table_type mime_table[] = {
        {"text/calendar", DP_CONTENT_VCAL},
 };
 
+extern void *g_db_handle;
 static void *g_da_handle = NULL;
 static int (*download_agent_init)(void) = NULL; // int da_init(da_client_cb_t *da_client_callback);
 static int (*download_agent_deinit)(void) = NULL; //  int da_deinit();
@@ -251,6 +254,324 @@ static int __set_file_permission_to_client(dp_client_slots_fmt *slot, dp_request
        return DP_ERROR_NONE;
 }
 
+static char *__dp_get_cache_directive_string(const char *cache_control, const char *directive)
+{
+       if (!cache_control || !directive)
+               return NULL;
+
+       char *str = strstr(cache_control, directive);
+       return str;
+}
+
+static long __calculate_heuristic_freshness(const char *last_modified)
+{
+       if (!last_modified)
+               return 0;
+
+       struct tm tm = {0};
+       strptime(last_modified, "%a, %d %b %Y %H:%M:%S %Z", &tm);
+       time_t now_time = time(NULL);
+       time_t last_time = timegm(&tm);
+       long freshness_time = (long)difftime(now_time, last_time);
+
+       // calculate the heuristic freshness as 10% of the freshness time.
+       long heuristic_time = freshness_time / 10.0;
+       return heuristic_time;
+}
+
+static long __dp_determine_cache_max_age(const char *cache_control, const char *last_modified)
+{
+       if (cache_control && !__dp_get_cache_directive_string(cache_control, CACHE_CONTROL_NO_CACHE)) {
+               char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE);
+               if (str)
+                       return strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10);
+       }
+
+       return __calculate_heuristic_freshness(last_modified);
+}
+
+static int __dp_store_cache_to_db(const char *package, const char *url, const char *ori_file_name, const char *file_name, const char *etag,
+               const long max_age, const unsigned long long file_size, int *error)
+{
+       *error = DP_ERROR_NONE;
+       if (dp_db_new_app_cache(g_db_handle, package, url, file_name, true, error) < 0) {
+               TRACE_ERROR("failed to add new app cache");
+               return -1;
+       }
+
+       if (dp_db_new_file_cache(g_db_handle, file_name, ori_file_name, etag,
+                       max_age, file_size, (const long)time(NULL), error) < 0) {
+               TRACE_ERROR("failed to add new file cache");
+       }
+
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+static int __dp_update_cache_from_db(const char *url, const char *file_name, const char *new_file_name, const char *etag,
+               const long max_age, const unsigned long long file_size, int *error)
+{
+       *error = DP_ERROR_NONE;
+       if (strcmp(file_name, new_file_name) != 0) {
+               if (dp_db_update_app_cache(g_db_handle, url, new_file_name, error) < 0) {
+                       TRACE_ERROR("failed to update app cache");
+                       return -1;
+               }
+       }
+
+       int id = dp_db_get_cache_file_id(g_db_handle, file_name, error);
+       if (id < 0) {
+               TRACE_ERROR("failed to get cache file id");
+               return -1;
+       }
+
+       if (dp_db_update_file_cache(g_db_handle, id, new_file_name, etag,
+                       max_age, file_size, (const long)time(NULL), error) < 0) {
+               TRACE_ERROR("failed to update file cache");
+       }
+
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+static int __dp_remove_old_file(char *file_name)
+{
+       char **file_ptr = (char **)malloc(sizeof(char *));
+       if (!file_ptr) {
+               TRACE_ERROR("failed to allocate the file_array");
+               return -1;
+       }
+       *file_ptr = file_name;
+
+       if (dp_remove_files(1, (const char **)file_ptr) < 0) {
+               TRACE_ERROR("failed to remove cache file");
+               free(file_ptr);
+               return -1;
+       }
+
+       free(file_ptr);
+       return 0;
+}
+
+static int __dp_store_to_cache(const char *package, const char *url, const char *ori_file, const char *cache_control, const char *etag,
+               const char *last_modified, const char *saved_path, const unsigned long long file_size, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       if (!package || !url || !ori_file || !saved_path) {
+               TRACE_ERROR("NULL CHECK!: package, url, ori_file or saved_path");
+               return -1;
+       }
+
+       *error = DP_ERROR_NONE;
+       char *file_name = NULL;
+       unsigned length = 0;
+       int pkg_exist = 0;
+       int found = dp_db_get_cache_file_name(g_db_handle, package, url, &file_name, &length, &pkg_exist, error);
+       if (found < 0) {
+               TRACE_ERROR("failed to get cache_file_name");
+               return -1;
+       }
+       if (found > 0) {
+               if (__dp_remove_old_file(file_name) < 0) {
+                       TRACE_ERROR("failed to remove old files");
+                       free(file_name);
+                       return -1;
+               }
+       }
+
+       char *new_file_name = dp_store_file(saved_path);
+       if (!new_file_name) {
+               TRACE_ERROR("failed to store file");
+               *error = DP_ERROR_DISK_BUSY;
+               free(file_name);
+               return -1;
+       }
+
+       long max_age = __dp_determine_cache_max_age(cache_control, last_modified);
+
+       if (found == 0) {
+               if (__dp_store_cache_to_db(package, url, ori_file, new_file_name, etag, (const long)max_age, file_size, error) < 0)
+                       TRACE_ERROR("failed to store new cache data to db");
+       } else {
+               if (__dp_update_cache_from_db(url, file_name, new_file_name, etag, (const long)max_age, file_size, error) < 0)
+                       TRACE_ERROR("failed to update cache data from db");
+       }
+
+       free(file_name);
+       free(new_file_name);
+
+       if (*error != DP_ERROR_NONE)
+               return -1;
+
+       return 0;
+}
+
+// return url only when cache feature is enabled.
+static char *__dp_get_cache_key_url(void *client_db_handle, const int req_id, int *error)
+{
+       *error = DP_ERROR_INVALID_PARAMETER;
+       if (!client_db_handle) {
+               TRACE_ERROR("NULL CHECK!: client_db_handle");
+               return NULL;
+       }
+
+       if (req_id < 0) {
+               TRACE_ERROR("check req_id");
+               return NULL;
+       }
+
+       int cache = 0;
+       unsigned length = 0;
+       char *url = NULL;
+
+       *error = DP_ERROR_NONE;
+       if (dp_db_get_property_int(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, error) < 0 ||
+                       cache <= 0) {
+               TRACE_ERROR("failed to get cache value or cache is not enabled for id:%d", req_id);
+               return NULL;
+       }
+
+       if (dp_db_get_property_string(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, error) < 0 ||
+                       !url) {
+               TRACE_ERROR("failed to get url. id:%d", req_id);
+               return NULL;
+       }
+
+       return url;
+}
+
+static char *__get_ori_file_name(const char *file_name)
+{
+       int errorcode = DP_ERROR_INVALID_PARAMETER;
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return NULL;
+       }
+
+       char *ori_file = NULL;
+       unsigned len = 0;
+       if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_FILENAME, (unsigned char **)&ori_file, &len, &errorcode) < 0 ||
+                       !ori_file) {
+               return NULL;
+       }
+
+       return ori_file;
+}
+
+static int __dp_try_cache_download(void *slot, void *request)
+{
+       int errorcode = DP_ERROR_INVALID_PARAMETER;
+       if (!slot || !request) {
+               TRACE_ERROR("NULL CHECK!: slot or request");
+               return errorcode;
+       }
+
+       dp_client_slots_fmt *req_slot = slot;
+       dp_request_fmt *req = request;
+
+       char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode);
+       if (!url) {
+               TRACE_ERROR("failed to get cache key url");
+               return errorcode;
+       }
+
+       char *file_name = NULL;
+       unsigned length = 0;
+       int pkg_exist = 0;
+       int ret = dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode);
+       if (ret <= 0 || !file_name) {
+               TRACE_ERROR("failed to get cache_file_name");
+               free(url);
+               return errorcode;
+       }
+
+       char *ori_file = __get_ori_file_name(file_name);
+       if (!ori_file) {
+               TRACE_ERROR("failed to get original file name");
+               errorcode = DP_ERROR_NO_DATA;
+               return errorcode;
+       }
+       req->file_name = ori_file;
+
+       errorcode = dp_start_cache_download(file_name, req_slot, req);
+
+       free(url);
+       free(file_name);
+
+       return errorcode;
+}
+
+static int __dp_update_cache_validator(void *slot, void *request, const char *cache_control, const char *etag)
+{
+       int errorcode = DP_ERROR_INVALID_PARAMETER;
+       if (!slot || !request) {
+               TRACE_ERROR("NULL CHECK!: slot or request");
+               return errorcode;
+       }
+
+       dp_client_slots_fmt *req_slot = slot;
+       dp_request_fmt *req = request;
+
+       char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode);
+       if (!url) {
+               TRACE_ERROR("failed to get cache key url for id:%d", req->id);
+               return errorcode;
+       }
+
+       char *file_name = NULL;
+       unsigned length = 0;
+       int pkg_exist = 0;
+       if (dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode) < 0
+                       || !file_name) {
+               TRACE_ERROR("failed to get cache file name");
+               free(url);
+               return errorcode;
+       }
+
+       int id = dp_db_get_cache_file_id(g_db_handle, file_name, &errorcode);
+       if (id < 0) {
+               TRACE_ERROR("failed to get cache file id");
+               goto done;
+       }
+
+       long max_age = 0;
+       if (cache_control) {
+               char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE);
+               if (str)
+                       max_age = strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10);
+       }
+
+       if (max_age > 0) {
+               if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_MAX_AGE, 1, (void *)&max_age, &errorcode) < 0) {
+                       TRACE_ERROR("failed to update max_age");
+                       goto done;
+               }
+       }
+
+       if (etag) {
+               if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_ETAG, 2, (void *)etag, &errorcode) < 0) {
+                       TRACE_ERROR("failed to update etag");
+                       goto done;
+               }
+       }
+
+       long current_time = (long)time(NULL);
+       if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_LAST_UPDATE, 1, (void *)&current_time, &errorcode) < 0) {
+               TRACE_ERROR("failed to update last_update");
+               goto done;
+       }
+
+done:
+       free(url);
+       free(file_name);
+
+       return errorcode;
+}
+
 static void __finished_cb(finished_info_t *info, void *user_req_data,
                void *user_client_data)
 {
@@ -263,7 +584,10 @@ static void __finished_cb(finished_info_t *info, void *user_req_data,
        if (slot == NULL || request == NULL) {
                TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), info->download_id);
                free(info->etag);
+               free(info->cache_control);
+               free(info->last_modified);
                free(info->saved_path);
+               free(info->ori_file);
                free(info);
                return ;
        }
@@ -273,7 +597,10 @@ static void __finished_cb(finished_info_t *info, void *user_req_data,
                if (dp_cancel_agent_download(info->download_id) < 0)
                        TRACE_ERROR("failed to call cancel_download(%d)", info->download_id);
                free(info->etag);
+               free(info->cache_control);
+               free(info->last_modified);
                free(info->saved_path);
+               free(info->ori_file);
                free(info);
                CLIENT_MUTEX_UNLOCK(&slot->mutex);
                return ;
@@ -287,14 +614,35 @@ static void __finished_cb(finished_info_t *info, void *user_req_data,
                        TRACE_ERROR("id:%d failed to set http_status(%d)", request->id, info->http_status);
        }
 
+       TRACE_DEBUG("cache_control: %s, last_modified: %s", info->cache_control, info->last_modified);
        TRACE_SECURE_DEBUG("[FINISH][%d][%s]", request->id, info->saved_path);
        if (info->err == DA_RESULT_OK) {
+               if (info->http_status == 304) {
+                       errorcode = __dp_update_cache_validator(slot, request, info->cache_control, info->etag);
+                       if (errorcode != DP_ERROR_NONE) {
+                               state = DP_STATE_FAILED;
+                       } else {
+                               errorcode = __dp_try_cache_download(slot, request);
+                               state = (errorcode == DP_ERROR_NONE) ? DP_STATE_COMPLETED : DP_STATE_FAILED;
+                               free(info->etag);
+                               free(info->cache_control);
+                               free(info->last_modified);
+                               free(info->saved_path);
+                               free(info->ori_file);
+                               free(info);
+                               CLIENT_MUTEX_UNLOCK(&slot->mutex);
+                               return;
+                       }
+               }
+
                if (info->saved_path != NULL) {
                        errorcode = __set_file_permission_to_client(slot,
                                        request, info->saved_path);
                } else {
-                       TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
-                       errorcode = DP_ERROR_INVALID_DESTINATION;
+                       if (state != DP_STATE_COMPLETED) {
+                               TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
+                               errorcode = DP_ERROR_INVALID_DESTINATION;
+                       }
                }
                if (errorcode == DP_ERROR_NONE)
                        state = DP_STATE_COMPLETED;
@@ -344,6 +692,22 @@ static void __finished_cb(finished_info_t *info, void *user_req_data,
                if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_SAVED_PATH, (void *)info->saved_path, 0, 2, &errorcode) < 0)
                        TRACE_ERROR("id:%d failed to set saved_path", request->id);
                free(content_name);
+
+               // if cache is enabled, store to cache.
+               int cache = 0;
+               if (dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, &errorcode) < 0)
+                       TRACE_DEBUG("unable to get cache value for id:%d", request->id);
+
+               if (cache && !__dp_get_cache_directive_string(info->cache_control, CACHE_CONTROL_NO_STORE)) {
+                       char *url = __dp_get_cache_key_url(slot->client.dbhandle, request->id, &errorcode);
+                       if (url) {
+                               if (__dp_store_to_cache(slot->pkgname, url, info->ori_file, info->cache_control, info->etag,
+                                               info->last_modified, info->saved_path, request->file_size, &errorcode) < 0)
+                                       TRACE_ERROR("failed to store cache for id:%d", request->id);
+                               free(url);
+                       }
+               }
+
                /* update the received file size.
                 * The last received file size cannot update
                 * because of reducing update algorithm*/
@@ -370,7 +734,10 @@ static void __finished_cb(finished_info_t *info, void *user_req_data,
                        TRACE_ERROR("failed to register notification for id:%d", request->id);
        }
        free(info->etag);
+       free(info->cache_control);
+       free(info->last_modified);
        free(info->saved_path);
+       free(info->ori_file);
        free(info);
        CLIENT_MUTEX_UNLOCK(&slot->mutex);
 }
@@ -497,6 +864,8 @@ static void __progress_cb(int download_id, unsigned long long received_size,
                return ;
        }
 
+       TRACE_DEBUG("state: %d, progress %llu", request->state, received_size);
+
        // For resume case after pause, it change state from connecting to downloading
        if (request->state == DP_STATE_CONNECTING) {
                request->state = DP_STATE_DOWNLOADING;
@@ -848,6 +1217,7 @@ int dp_start_agent_download(void *slot, void *request)
        char *filename = NULL;
        char *etag = NULL;
        int user_network_bonding = 0;
+       int user_cache = 0;
 
        if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, &errorcode) < 0 ||
                        url == NULL) {
@@ -917,6 +1287,13 @@ int dp_start_agent_download(void *slot, void *request)
        } else
                req_data->network_bonding = user_network_bonding;
 
+       if (dp_db_get_property_int(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&user_cache, &errorcode) < 0 ||
+                       user_cache < 0) {
+               TRACE_DEBUG("unable to get cache value for id:%d", base_req->id);
+       } else {
+               req_data->cache = user_cache;
+       }
+
        req_data->disable_verify_host = base_req->disable_verify_host;
        req_data->pkg_name = base_slot->pkgname;
 
@@ -977,4 +1354,3 @@ int dp_resume_agent_download(int req_id)
                return DP_ERROR_INVALID_STATE;
        return __change_error(da_ret);
 }
-
index 73ebc45..0b7e1a5 100644 (file)
  * limitations under the License.
  */
 
+#define _GNU_SOURCE
+#include <stdbool.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <signal.h> // pthread_kill
 #include <errno.h> // ESRCH
+#include <time.h>
 #include <sys/time.h>
 
 #include <download-provider-log.h>
@@ -30,6 +34,9 @@
 #include <download-provider-client.h>
 #include <download-provider-client-manager.h>
 #include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
+
+extern void *g_db_handle;
 
 static pthread_mutex_t g_dp_queue_manager_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t g_dp_queue_manager_cond = PTHREAD_COND_INITIALIZER;
@@ -92,6 +99,156 @@ void dp_queue_manager_clear_queue(void *request)
        dp_queue_clear(queue, request);
 }
 
+// return 0:stale 1:fresh -1:error
+static int __dp_queue_manager_check_cache_freshness(const char *file_name)
+{
+       int errorcode = DP_ERROR_NONE;
+       int id = dp_db_get_cache_file_id(g_db_handle, file_name, &errorcode);
+       if (id < 0) {
+               TRACE_ERROR("failed to get cache file id");
+               return -1;
+       }
+
+       long max_age = 0;
+       if (dp_db_get_property_int(g_db_handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_MAX_AGE, (long *)&max_age, &errorcode) < 0 ||
+                       max_age == 0) {
+               TRACE_DEBUG("failed to get max_age");
+               return 0;
+       }
+
+       long last_update = 0;
+       if (dp_db_get_property_int(g_db_handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_LAST_UPDATE, (long *)&last_update, &errorcode) < 0 ||
+                       last_update == 0) {
+               TRACE_DEBUG("failed to get last_update");
+               return 0;
+       }
+
+       long current_age = difftime(time(NULL), last_update);
+       if (current_age >= max_age)
+               return 0;
+
+       return 1;
+}
+
+static int __dp_queue_send_request_for_cache_validation(const char *file_name, dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+       int errorcode = DP_ERROR_INVALID_PARAMETER;
+       if (!file_name || !slot || !request) {
+               TRACE_ERROR("NULL CHECK!: file_name, slot or request");
+               return errorcode;
+  }
+
+       errorcode = DP_ERROR_NONE;
+       char *etag = NULL;
+       unsigned length = 0;
+       if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_ETAG, (unsigned char **)&etag, &length, &errorcode) < 0 ||
+                       !etag) {
+               TRACE_DEBUG("failed to get etag");
+               return errorcode;
+       }
+
+       const char *header_field = "If-None-Match";
+       const char *header_value = etag;
+
+       int check_field = dp_db_check_duplicated_string(slot->client.dbhandle, request->id, DP_TABLE_HEADERS, DP_DB_COL_HEADER_FIELD, 0, header_field, &errorcode);
+       if (check_field < 0) {
+               errorcode = DP_ERROR_DISK_BUSY;
+       } else {
+               if (check_field == 0) { // insert
+                       if (dp_db_new_header(slot->client.dbhandle, request->id, header_field, header_value, &errorcode) < 0) {
+                               TRACE_ERROR("failed to set header");
+                               errorcode = DP_ERROR_DISK_BUSY;
+                       }
+               } else { // update
+                       if (dp_db_update_header(slot->client.dbhandle, request->id, header_field, header_value, &errorcode) < 0) {
+                               TRACE_ERROR("failed to set header");
+                               errorcode = DP_ERROR_DISK_BUSY;
+
+                       }
+               }
+               if (errorcode == DP_ERROR_NONE)
+                       errorcode = dp_start_agent_download(slot, request);
+       }
+
+       if (etag)
+               free(etag);
+
+       return errorcode;
+}
+
+static char *__get_ori_file_name(const char *file_name)
+{
+       int errorcode = DP_ERROR_INVALID_PARAMETER;
+       if (!file_name) {
+               TRACE_ERROR("NULL CHECK!: file_name");
+               return NULL;
+       }
+
+       char *ori_file = NULL;
+       unsigned len = 0;
+       if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_FILENAME, (unsigned char **)&ori_file, &len, &errorcode) < 0 ||
+                       !ori_file) {
+               return NULL;
+       }
+
+       return ori_file;
+}
+
+static int __dp_queue_manager_try_cache_download(dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+       int errorcode = DP_ERROR_NONE;
+       char *url = NULL;
+       unsigned length = 0;
+       if (dp_db_get_property_string(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, &errorcode) < 0 ||
+                       !url) {
+               TRACE_ERROR("faild to get url. id:%d", request->id);
+               return errorcode;
+       }
+
+       char *file_name = NULL;
+       unsigned len = 0;
+       int pkg_exist = 0;
+       int ret = dp_db_get_cache_file_name(g_db_handle, slot->pkgname, url, &file_name, &len, &pkg_exist, &errorcode);
+       if (ret <= 0) {
+               TRACE_DEBUG("no cache file name. id:%d", request->id);
+               free(url);
+               return errorcode;
+       } else {
+               if (pkg_exist == 0) {
+                       if (dp_db_new_app_cache(g_db_handle, slot->pkgname, url, (const char *)file_name, false, &errorcode) < 0) {
+                               TRACE_ERROR("failed to add new app cache. id:%d", request->id);
+                               goto done;
+                       }
+               }
+               int fresh = __dp_queue_manager_check_cache_freshness(file_name);
+               if (fresh < 0) {
+                       TRACE_ERROR("failed to check cache freshness. id:%d", request->id);
+                       errorcode = DP_ERROR_INVALID_PARAMETER;
+                       goto done;
+               }
+               if (fresh == 1) {
+                       char *ori_file = __get_ori_file_name(file_name);
+                       if (!ori_file) {
+                               TRACE_ERROR("failed to get pure file name. id:%d", request->id);
+                               errorcode = DP_ERROR_NO_DATA;
+                               goto done;
+                       }
+                       request->file_name = ori_file;
+
+                       // call cache agent start function
+                       errorcode = dp_start_cache_download(file_name, slot, request);
+               } else { // stale
+                       errorcode = __dp_queue_send_request_for_cache_validation(file_name, slot, request);
+               }
+       }
+
+done:
+       free(url);
+       free(file_name);
+
+       return errorcode;
+}
+
 // if return negative, queue-manager try again.
 static int __dp_queue_manager_try_download(dp_client_slots_fmt *slot, dp_request_fmt *request)
 {
@@ -124,11 +281,25 @@ static int __dp_queue_manager_try_download(dp_client_slots_fmt *slot, dp_request
 
        errorcode = DP_ERROR_NONE;
 
-       if (dp_is_alive_download(request->agent_id) > 0)
+       int cache = 0;
+       if (dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, &errorcode) < 0 ||
+                       cache < 0) {
+               TRACE_ERROR("unable to get cache value for id:%d", request->id);
+               cache = 0;
+       }
+       if (cache && dp_is_alive_cache_download(request->cache_agent_id) > 0) {
+               errorcode = dp_resume_cache_download(request->cache_agent_id);
+       } else if (dp_is_alive_download(request->agent_id) > 0) {
                errorcode = dp_resume_agent_download(request->agent_id);
-       else
-               // call agent start function
-               errorcode = dp_start_agent_download(slot, request);
+       } else {
+               if (cache) {
+                       if (__dp_queue_manager_try_cache_download(slot, request) != DP_ERROR_NONE)
+                               errorcode = dp_start_agent_download(slot, request);
+               } else {
+                       // call agent start function
+                       errorcode = dp_start_agent_download(slot, request);
+               }
+       }
 
        if (errorcode == DP_ERROR_NONE) {
                request->state = DP_STATE_CONNECTING;
@@ -168,13 +339,13 @@ static int __dp_queue_manager_try_download(dp_client_slots_fmt *slot, dp_request
        }
 
        return result;
-
 }
 
 static int __dp_queue_manager_check_queue(dp_queue_fmt **queue)
 {
        dp_client_slots_fmt *slot = NULL;
        dp_request_fmt *request = NULL;
+
        while (dp_queue_pop(queue, (void *)&slot, (void *)&request) == 0) { // pop a request from queue.
                TRACE_DEBUG("queue-manager pop a request");
                if (slot == NULL || request == NULL) {
index ba8d05d..8ad0c22 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/time.h>
 #include <sys/statfs.h>
 #include <unistd.h>
+#include <glib.h>
 
 #include "download-provider-log.h"
 
@@ -101,3 +102,8 @@ int dp_remove_file(const char *file_path)
        }
        return -1;
 }
+
+unsigned long dp_get_hash_key(const char *str)
+{
+       return g_str_hash(str);
+}
diff --git a/provider/include/download-provider-cache-manager.h b/provider/include/download-provider-cache-manager.h
new file mode 100644 (file)
index 0000000..d626604
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_CACHE_MANAGER_H
+#define DOWNLOAD_PROVIDER_CACHE_MANAGER_H
+
+typedef enum {
+       DP_CACHE_REQUEST_RESET_CACHE = 0,
+       DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE,
+       DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE,
+       DP_CACHE_REQUEST_SET_CACHE_PATH,
+       DP_CACHE_REQUEST_GET_CACHE_PATH,
+       DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE,
+       DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE,
+       DP_CACHE_REQUEST_RESET_ALL_CACHE,
+} dp_cache_req_type_defs;
+
+int dp_cache_manager_handle_request(void *req_slot, void *req_ipc_info, const int req_type);
+
+#endif
diff --git a/provider/include/download-provider-cache.h b/provider/include/download-provider-cache.h
new file mode 100644 (file)
index 0000000..7943671
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_CACHE_H
+#define DOWNLOAD_PROVIDER_CACHE_H
+
+#include <glib.h>
+
+int dp_cache_delete_all_from_db(int *error);
+int dp_cache_reset_cache(const char *package, int *error);
+int dp_cache_set_cache_max_size(unsigned int size, int *error);
+int dp_cache_get_cache_max_size(unsigned int *size, int *error);
+int dp_cache_set_cache_path(const char *path, int *error);
+int dp_cache_get_cache_path(char **path, int *error);
+int dp_cache_set_cache_lifecycle(unsigned int time, int *error);
+int dp_cache_get_cache_lifecycle(unsigned int *time, int *error);
+int dp_cache_reset_all_cache(int *error);
+GSList *dp_cache_clear_exceeded_files(long long cache_max, int type);
+
+#endif
index 31ed0cb..e155fde 100644 (file)
@@ -24,6 +24,7 @@ typedef struct {
        int state; // downloading state, to prevent the crash, placed at the head of structure.
        int id; // ID created in create request in requests thread.
        int agent_id;
+       int cache_agent_id;
        int error;
        int network_type;
        int access_time;
@@ -37,6 +38,7 @@ typedef struct {
        int noti_type;
        int noti_priv_id;
        int disable_verify_host; // tv only, internal API
+       char *file_name;
        void *next;
 } dp_request_fmt;
 
index ae22f6d..071edfd 100644 (file)
@@ -21,6 +21,8 @@
 
 // provider have a groups database file.
 #define DP_TABLE_CLIENTS "clients"
+#define DP_TABLE_CACHE "cache"
+#define DP_TABLE_CACHE_FILE "cache_file"
 // each client has a database file with below tables. file is named as pkgname.
 #define DP_TABLE_LOGGING "logging"
 #define DP_TABLE_REQUEST "request"
 #define DP_DB_COL_CREATE_TIME "createtime"
 #define DP_DB_COL_ACCESS_TIME "accesstime"
 
+// cache and cache_file table
+#define DP_DB_COL_CACHE_KEY "key"
+#define DP_DB_COL_CACHE_FILE_NAME "cache_file_name"
+#define DP_DB_COL_CACHE_FILE_SIZE "file_size"
+#define DP_DB_COL_CACHE_REFCOUNT "ref_count"
+#define DP_DB_COL_CACHE_MAX_AGE "max_age"
+#define DP_DB_COL_CACHE_LAST_UPDATE "last_update"
+
 // clients table
 #define DP_DB_COL_SMACK_LABEL "smack_label"
 #define DP_DB_COL_PACKAGE "package"
@@ -57,6 +67,7 @@
 #define DP_DB_COL_NETWORK_TYPE "network_type"
 #define DP_DB_COL_NETWORK_BONDING "network_bonding"
 #define DP_DB_COL_TEMP_FILE_PATH "temp_file_path"
+#define DP_DB_COL_CACHE "cache"
 
 // download table
 #define DP_DB_COL_SAVED_PATH "saved_path"
@@ -81,7 +92,6 @@
 #define DP_DB_COL_HEADER_DATA "header_data"
 
 
-
 // when a client is accepted, add
 // when disconnected with no request, clear
 // if exist, it's possible to be remain some requests
@@ -95,6 +105,26 @@ createtime DATE,\
 accesstime DATE\
 )"
 
+#define DP_SCHEMA_CACHE "CREATE TABLE IF NOT EXISTS cache(\
+id INTEGER UNIQUE PRIMARY KEY,\
+key UNSIGNED BIG INT DEFAULT 0,\
+url TEXT DEFAULT NULL,\
+cache_file_name TEXT DEFAULT NULL,\
+package TEXT DEFAULT NULL\
+)"
+
+#define DP_SCHEMA_CACHE_FILE "CREATE TABLE IF NOT EXISTS cache_file(\
+id INTEGER UNIQUE PRIMARY KEY,\
+filename TEXT DEFAULT NULL,\
+key UNSIGNED BIG INT DEFAULT 0,\
+cache_file_name TEXT UNIQUE NOT NULL,\
+etag TEXT DEFAULT NULL,\
+file_size UNSIGNED BIG INT DEFAULT 0,\
+ref_count INTEGER DEFAULT 0,\
+max_age BIG INT DEFAULT 0,\
+last_update BIG INT DEFAULT 0\
+)"
+
 // limitation : 1000 rows, 48 hours standard by createtime
 #define DP_SCHEMA_LOGGING "CREATE TABLE IF NOT EXISTS logging(\
 id INTEGER UNIQUE PRIMARY KEY DESC NOT NULL,\
@@ -117,6 +147,7 @@ url TEXT DEFAULT NULL,\
 proxy TEXT DEFAULT NULL,\
 temp_file_path TEXT DEFAULT NULL,\
 network_bonding BOOLEAN DEFAULT 0,\
+cache BOOLEAN DEFAULT 0,\
 FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE\
 )"
 
index 7d1c824..3f80bfa 100755 (executable)
@@ -17,6 +17,9 @@
 #ifndef DOWNLOAD_PROVIDER_DB_H
 #define DOWNLOAD_PROVIDER_DB_H
 
+#include <glib.h>
+#include <stdbool.h>
+
 int dp_db_check_connection(void *handle);
 int dp_db_open_client_manager(void **handle, int *errorcode);
 int dp_db_open_client(void **handle, char *pkgname, int *errorcode);
@@ -40,6 +43,7 @@ int dp_db_get_property_string(void *handle, const int id, const char *table, con
 int dp_db_get_property_int(void *handle, const int id, const char *table, const char *column, void *value, int *error);
 int dp_db_unset_property_string(void *handle, const int id, const char *table, const char *column, int *error);
 int dp_db_delete(void *handle, const int id, const char *table, int *error);
+int dp_db_delete_all(void *handle, const char *table, int *error);
 
 
 int dp_db_new_header(void *handle, const int id, const char *field, const char *value, int *error);
@@ -53,4 +57,18 @@ int dp_db_limit_time(void *handle, const char *table, int hours, int *error);
 int dp_db_get_http_headers_list(void *handle, int id, char **headers, int *error);
 int dp_db_get_max_download_id(void *handle, const char *table, int *pvalue, int *error);
 
+
+int dp_db_new_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, bool new_file, int *error);
+int dp_db_new_file_cache(void *handle, const char *file_name, const char *content_name, const char *etag,
+               const long max_age, const unsigned long long file_size, const long now, int *error);
+int dp_db_clear_app_cache(void *handle, const char *pkgname, GSList **files, int *error);
+int dp_db_update_app_cache(void *handle, const char *url, const char *file_name, int *error);
+int dp_db_update_file_cache(void *handle, const int id, const char *file_name, const char *etag,
+               const long max_age, const unsigned long long file_size, const long now, int *error);
+int dp_db_update_file_cache_validator(void *handle, const int id, const char *column, const int type, void *value, int *error);
+int dp_db_get_file_cache_string(void *handle, const char *file_name, const char *column, unsigned char **string, unsigned *length, int *error);
+int dp_db_get_cache_file_name(void *handle, const char *pkgname, const char *url, char **file_name, unsigned *length, int *pkg_exist, int *error);
+int dp_db_get_cache_file_id(void *handle, const char *file_name, int *error);
+int dp_db_clear_exceeded_cache(void *handle, GSList **files, long long cache_max, int type, int *error);
+int dp_db_clear_app_cache_by_filename(void *handle, const char *file_name, int *error);
 #endif
diff --git a/provider/include/download-provider-plugin-cache-agent.h b/provider/include/download-provider-plugin-cache-agent.h
new file mode 100644 (file)
index 0000000..c1a966e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_PLUGIN_CACHE_AGENT_H
+#define DOWNLOAD_PROVIDER_PLUGIN_CACHE_AGENT_H
+
+#define CACHE_CONTROL_MAX_AGE "max-age="
+#define CACHE_CONTROL_NO_STORE "no-store"
+#define CACHE_CONTROL_NO_CACHE "no-cache"
+#define CACHE_CONTROL_DELETION_RATIO 0.3
+
+typedef enum {
+       DP_CACHE_CAPACITY_MAX_SIZE,
+       DP_CACHE_CAPACITY_LIFECYCLE,
+} dp_cache_capacity_event_type;
+
+int dp_init_cache_agent();
+void dp_deinit_cache_agent();
+
+char *dp_store_file(const char *file_path);
+int dp_remove_files(int file_count, const char *cache_files[]);
+int dp_is_file_available(const char *cache_file);
+int dp_get_used_cache_size(long long *size);
+
+int dp_start_cache_download(const char *cache_file, void *slot, void *request);
+int dp_pause_cache_download(int req_id);
+int dp_resume_cache_download(int req_id);
+int dp_cancel_cache_download(int req_id);
+int dp_pause_cache_download_without_update(int req_id);
+int dp_cancel_cache_download_without_update(int req_id);
+int dp_is_alive_cache_download(int req_id);
+
+int dp_get_max_cache_size(unsigned int *size);
+int dp_set_max_cache_size(unsigned int size);
+int dp_get_cache_path(char **path);
+int dp_set_cache_path(const char *path);
+int dp_get_cache_lifecycle(unsigned int *time);
+int dp_set_cache_lifecycle(unsigned int time);
+int dp_clear_all_cache_files(void);
+int dp_set_oldest_cache_file_time(time_t time);
+bool dp_is_cache_storage_busy(void);
+
+#endif
index 0698cd1..b89aad4 100644 (file)
@@ -21,5 +21,6 @@ char *dp_strdup(char *src);
 int dp_is_file_exist(const char *file_path);
 long long dp_get_file_modified_time(const char *file_path);
 int dp_remove_file(const char *file_path);
+unsigned long dp_get_hash_key(const char *str);
 
 #endif
index 879c80c..674ce8b 100755 (executable)
@@ -117,7 +117,7 @@ typedef enum {
        DP_SEC_CONTROL,
        DP_SEC_GET,
        DP_SEC_SET,
-       DP_SEC_UNSET
+       DP_SEC_UNSET,
 } dp_ipc_section_defs;
 
 typedef enum {
@@ -147,12 +147,21 @@ typedef enum {
        DP_PROP_NOTIFICATION_SUBJECT,
        DP_PROP_NOTIFICATION_DESCRIPTION,
        DP_PROP_NOTIFICATION_TYPE,
+       DP_PROP_CACHE,
+       DP_PROP_RESET_CACHE,
+       DP_PROP_SET_CACHE_MAX_SIZE,
+       DP_PROP_GET_CACHE_MAX_SIZE,
+       DP_PROP_SET_CACHE_PATH,
+       DP_PROP_GET_CACHE_PATH,
+       DP_PROP_SET_CACHE_LIFECYCLE,
+       DP_PROP_GET_CACHE_LIFECYCLE,
+       DP_PROP_RESET_ALL_CACHE,
        DP_PROP_CREATE,
        DP_PROP_START,
        DP_PROP_PAUSE,
        DP_PROP_CANCEL,
        DP_PROP_DESTROY,
-       DP_PROP_VERIFY_HOST
+       DP_PROP_VERIFY_HOST,
 } dp_ipc_property_defs;
 
 typedef enum {
@@ -173,7 +182,6 @@ typedef enum {
        DP_CONTENT_VCAL, //13
 } dp_content_type;
 
-
 typedef struct {
        short section;
        unsigned property;
diff --git a/res/var/lib/download-provider/settings b/res/var/lib/download-provider/settings
new file mode 100644 (file)
index 0000000..e69de29