Extract package update monitor class and add test cases 02/260602/2
authorJi-hoon Lee <dalton.lee@samsung.com>
Fri, 25 Jun 2021 10:48:34 +0000 (19:48 +0900)
committerJi-hoon Lee <dalton.lee@samsung.com>
Wed, 30 Jun 2021 04:53:44 +0000 (13:53 +0900)
Since the Package Update Monitor feature can be isolated
in a separate class, first copied the existing code
into a new class and added tests for verifying the
behavior of the new class.

While extracting, it was required to extract client info
struct into a new class also to avoid circular dependency.
At the moment, the newly extracted CClientInfo is not
well-organized, merely a struct revealing internal member
variable. This should be refactored in the future also.

Change-Id: I179540b5ba2890d40d4e1440a8e88861863dc416

inc/client_info.h [new file with mode: 0644]
inc/package_update_monitor.h [new file with mode: 0644]
inc/service_main.h
src/package_update_monitor.cpp [new file with mode: 0644]
src/service_main.cpp
tests/utc/CMakeLists.txt
tests/utc/package-update-monitor/CMakeLists.txt [new file with mode: 0644]
tests/utc/package-update-monitor/test_package_update_monitor.cpp [new file with mode: 0644]

diff --git a/inc/client_info.h b/inc/client_info.h
new file mode 100644 (file)
index 0000000..d2de5a8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 __CLIENT_INFO_H__
+#define __CLIENT_INFO_H__
+
+/* This is a class converted from previous ma_client_info struct,
+ * only to break circular dependency. Need further encapsulation */
+
+#include "service_common.h"
+
+#define MAX_MACLIENT_INFO_NUM 16
+
+typedef struct {
+       bool used{false};
+       char appid[MAX_APPID_LEN];
+       char wakeup_word[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN];
+       char wakeup_language[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN];
+       char wakeup_engine[MAX_APPID_LEN];
+       char supported_language[MAX_SUPPORTED_LANGUAGES_NUM][MAX_SUPPORTED_LANGUAGE_LEN];
+       bool custom_ui_option{false};
+       VOICE_KEY_SUPPORT_MODE voice_key_support_mode{VOICE_KEY_SUPPORT_MODE_PUSH_TO_TALK};
+       float voice_key_tap_duration{0.0f};
+
+       ma_preprocessing_allow_mode_e preprocessing_allow_mode{MA_PREPROCESSING_ALLOW_NONE};
+       char preprocessing_allow_appid[MAX_APPID_LEN];
+
+       boost::optional<std::string> audio_processing_appid;
+} ClientInfoItems;
+
+class CClientInfo {
+public:
+       CClientInfo() {}
+       virtual ~CClientInfo() {}
+
+       ClientInfoItems* getItems() { return mItems; }
+       void resetItems() { memset(&mItems, 0x00, sizeof(mItems)); }
+
+private:
+       ClientInfoItems mItems[MAX_MACLIENT_INFO_NUM];
+};
+
+#endif /* __CLIENT_INFO_H__ */
diff --git a/inc/package_update_monitor.h b/inc/package_update_monitor.h
new file mode 100644 (file)
index 0000000..34dc139
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 __PACKAGE_UPDATE_MONITOR_H__
+#define __PACKAGE_UPDATE_MONITOR_H__
+
+#include <boost/optional.hpp>
+#include <string>
+
+#include <package_manager.h>
+
+#include "client_info.h"
+
+class IPackageUpdateEventObserver {
+public:
+       virtual void OnUpdateStarted() = 0;
+       virtual void OnUpdateFinished() = 0;
+       virtual void OnRestartRequired() = 0;
+};
+
+class CPackageUpdateMonitor {
+public:
+       CPackageUpdateMonitor(IPackageUpdateEventObserver &observer, CClientInfo &clientInfo);
+       virtual ~CPackageUpdateMonitor();
+
+       void initialize();
+       void deinitialize();
+
+       CClientInfo& getClientInfo();
+       IPackageUpdateEventObserver& getObserver();
+
+protected:
+       /* The callback function invoked by Tizen's package manager library */
+       static void package_manager_event_cb(const char *type, const char *package,
+               package_manager_event_type_e event_type,
+               package_manager_event_state_e event_state,
+               int progress,
+               package_manager_error_e error, void *user_data);
+
+       IPackageUpdateEventObserver &mObserver;
+       CClientInfo &mClientInfo;
+       package_manager_h mHandle{NULL};
+};
+
+#endif /* __PACKAGE_UPDATE_MONITOR_H__ */
index 766e512..fd3c521 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "service_common.h"
 #include "service_config.h"
+#include "client_info.h"
 #include "client_manager.h"
 #include "service_plugin.h"
 #include "service_ipc_dbus.h"
@@ -62,8 +63,6 @@ typedef enum {
 #define WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID "db/multi-assistant/preprocessing_assistant_appid"
 #define WAKEUP_SETTINGS_KEY_PRELAUNCH_MODE "db/multi-assistant/prelaunch_mode"
 
-#define MAX_MACLIENT_INFO_NUM 16
-
 class CServiceMain : public IServiceIpcObserver {
 public:
        CServiceMain(IApplicationManager& applicationManager, IPreferenceManager& preferenceManager) :
@@ -162,24 +161,7 @@ private:
 private:
        std::string mCurrentLanguage{"en_US"};
 
-       typedef struct {
-               bool used{false};
-               char appid[MAX_APPID_LEN];
-               char wakeup_word[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN];
-               char wakeup_language[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN];
-               char wakeup_engine[MAX_APPID_LEN];
-               char supported_language[MAX_SUPPORTED_LANGUAGES_NUM][MAX_SUPPORTED_LANGUAGE_LEN];
-               bool custom_ui_option{false};
-               VOICE_KEY_SUPPORT_MODE voice_key_support_mode{VOICE_KEY_SUPPORT_MODE_PUSH_TO_TALK};
-               float voice_key_tap_duration{0.0f};
-
-               ma_preprocessing_allow_mode_e preprocessing_allow_mode{MA_PREPROCESSING_ALLOW_NONE};
-               char preprocessing_allow_appid[MAX_APPID_LEN];
-
-               boost::optional<std::string> audio_processing_appid;
-       } ma_client_info;
-
-       ma_client_info mClientInfo[MAX_MACLIENT_INFO_NUM];
+       CClientInfo mClientInfo;
 
        int mCurrentClientInfo{0};
        int mCurrentPreprocessingClientInfo{-1};
diff --git a/src/package_update_monitor.cpp b/src/package_update_monitor.cpp
new file mode 100644 (file)
index 0000000..b06f753
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2021 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 "package_update_monitor.h"
+
+#include <dlog.h>
+#include <pkgmgr-info.h>
+
+typedef struct {
+    int *return_value{nullptr};
+    CPackageUpdateMonitor *monitor{nullptr};
+} PackageListCallbackParams;
+
+CPackageUpdateMonitor::CPackageUpdateMonitor(
+       IPackageUpdateEventObserver &observer, CClientInfo &clientInfo) :
+       mObserver(observer),
+       mClientInfo(clientInfo)
+{
+}
+
+CPackageUpdateMonitor::~CPackageUpdateMonitor()
+{
+}
+
+void CPackageUpdateMonitor::initialize()
+{
+       int ret = package_manager_create(&mHandle);
+       if (ret == PACKAGE_MANAGER_ERROR_NONE) {
+               ret = package_manager_set_event_cb(mHandle,
+                       CPackageUpdateMonitor::package_manager_event_cb, this);
+               if (ret == PACKAGE_MANAGER_ERROR_NONE) {
+                       LOGD("package_manager_set_event_cb succeeded.");
+               } else {
+                       LOGE("package_manager_set_event_cb failed(%d)", ret);
+               }
+       } else {
+               LOGE("package_manager_create failed(%d)", ret);
+       }
+}
+
+void CPackageUpdateMonitor::deinitialize()
+{
+       if (mHandle) {
+               package_manager_unset_event_cb(mHandle);
+               package_manager_destroy(mHandle);
+               mHandle = NULL;
+       }
+}
+
+static bool is_wakeup_engine(const pkgmgrinfo_appinfo_h handle, CPackageUpdateMonitor *monitor)
+{
+       if (nullptr == monitor) return false;
+
+       CClientInfo &clientInfo = monitor->getClientInfo();
+       bool is_wakeup_engine = false;
+
+       char *appid = NULL;
+
+       int ret = pkgmgrinfo_appinfo_get_appid(handle, &appid);
+       if (PMINFO_R_OK == ret && NULL != appid) {
+               for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM;loop++) {
+                       if (clientInfo.getItems()[loop].used) {
+                               LOGD("comparing appid : %s %s", clientInfo.getItems()[loop].wakeup_engine, appid);
+                               if (0 == strncmp(clientInfo.getItems()[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
+                                       is_wakeup_engine = true;
+                               }
+                       }
+               }
+       } else {
+               LOGE("pkgmgrinfo_appinfo_get_appid failed! error code=%d", ret);
+       }
+
+       return is_wakeup_engine;
+}
+
+static bool is_voice_assistant(const pkgmgrinfo_appinfo_h handle, CPackageUpdateMonitor *monitor)
+{
+       bool is_voice_assistant = false;
+       char* metadata_value = NULL;
+       const char* voice_assistant_metadata = "http://tizen.org/metadata/multi-assistant/name";
+       int ret = pkgmgrinfo_appinfo_get_metadata_value(handle, voice_assistant_metadata, &metadata_value);
+       if (PMINFO_R_OK == ret && NULL != metadata_value) {
+               is_voice_assistant = true;
+       } else {
+               LOGE("pkgmgrinfo_appinfo_get_metadata_value failed! error code=%d", ret);
+       }
+       return is_voice_assistant;
+}
+
+static int pkg_app_list_cb(const pkgmgrinfo_appinfo_h handle, void *user_data)
+{
+       PackageListCallbackParams *params = (PackageListCallbackParams*)user_data;
+       if (!params) return 0;
+
+       int *result = params->return_value;
+       if (result) {
+               if (is_voice_assistant(handle, params->monitor)) {
+                       *result = 1;
+               } else if (is_wakeup_engine(handle, params->monitor)) {
+                       *result = 1;
+               }
+       }
+       return 0;
+}
+/*
+INFO: Package install/update/uninstall scenario
+Install and Uninstall are obviously simple.
+   Install: just INSTALL
+   Uninstall: just UNINSTALL
+Update package (change the source codes and Run As again), there are four scenarios:
+1. UPDATE
+   Source code change
+2. UNINSTALL -> INSTALL
+   This happens when Tizen IDE Property > Tizen SDK > Rapid Development Support > Check "Enable Project specific settings"
+   and change Application ID in tizen-manifest.xml file and Run As.
+3. UPDATE -> INSTALL
+   This happens when Tizen IDE Property > Tizen SDK > Rapid Development Support > Uncheck "Enable Project specific settings"
+   and change Application ID in tizen-manifest.xml file and Run As.
+   At UPDATE event, pkgid (package parameter) is invalid...
+4. UPDATE
+   Exceptionally, only UPDATE can be called when Application ID in tizen-manifest.xml file is changed.
+   At UPDATE event, pkgid (package parameter) is valid, and only appid is changed; the previous appid is invalid.
+*/
+void CPackageUpdateMonitor::package_manager_event_cb(const char *type, const char *package,
+               package_manager_event_type_e event_type,
+               package_manager_event_state_e event_state,
+               int progress,
+               package_manager_error_e error, void *user_data)
+{
+       CPackageUpdateMonitor* monitor =
+               static_cast<CPackageUpdateMonitor*>(user_data);
+
+       int ret = 0;
+       uid_t uid = getuid();
+       pkgmgrinfo_pkginfo_h handle = NULL;
+       static bool in_progress = false;
+       bool should_exit = false;
+       bool pkginfo_found = true;
+
+       if (!package || !type)
+               return;
+
+       if (PACKAGE_MANAGER_EVENT_TYPE_UPDATE != event_type &&
+               PACKAGE_MANAGER_EVENT_TYPE_INSTALL != event_type &&
+               PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL != event_type)
+               return;
+
+       if (PACKAGE_MANAGER_EVENT_STATE_STARTED != event_state &&
+               PACKAGE_MANAGER_EVENT_STATE_COMPLETED != event_state &&
+               PACKAGE_MANAGER_EVENT_STATE_FAILED != event_state)
+               return;
+
+       bool user = false;
+       LOGD("type=%s package=%s event_type=%d event_state=%d progress=%d error=%d",
+               type, package, event_type, event_state, progress, error);
+       ret = pkgmgrinfo_pkginfo_get_pkginfo(package, &handle);
+       if (ret != PMINFO_R_OK || NULL == handle) {
+               LOGW("Failed to call pkgmgrinfo_pkginfo_get_pkginfo(\"%s\",~) returned %d", package, ret);
+               /* Try to get in user packages */
+               user = true;
+               ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(package, uid, &handle);
+               if (ret != PMINFO_R_OK || NULL == handle) {
+                       LOGW("Failed to call pkgmgrinfo_pkginfo_get_pkginfo & get_usr_pkginfo(\"%s\",~) returned %d, uid : %d", package, ret, getuid());
+                       pkginfo_found = false;
+               }
+       }
+
+       if (pkginfo_found) {
+               PackageListCallbackParams params;
+               params.return_value = &ret;
+               params.monitor =  monitor;
+
+               if (user) {
+                       /* Try to get in user packages */
+                       ret = pkgmgrinfo_appinfo_get_usr_list(handle, PMINFO_ALL_APP, pkg_app_list_cb, (void *)&params, uid);
+                       if (ret != PMINFO_R_OK) {
+                               LOGW("Failed to get usr pkg list from pkginfo, returned %d", ret);
+                       }
+               }  else {
+                       /* Try to get in global packages */
+                       ret = pkgmgrinfo_appinfo_get_list(handle, PMINFO_ALL_APP, pkg_app_list_cb, (void *)&params);
+                       if (ret != PMINFO_R_OK) {
+                               LOGW("Failed to get pkg list from pkginfo, returned %d", ret);
+                       }
+               }
+       } else {
+               /* Even if we failed acquiring the pkginfo, proceed if we're uninstalling
+                       since at the time of uninstall completion, pkginfo would not exist */
+               if (in_progress) {
+                       if (PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL == event_type &&
+                               (PACKAGE_MANAGER_EVENT_STATE_COMPLETED == event_state ||
+                               PACKAGE_MANAGER_EVENT_STATE_FAILED == event_state)) {
+                               ret = 1;
+                       }
+               }
+       }
+       if (1 == ret) {
+               if (PACKAGE_MANAGER_EVENT_STATE_STARTED == event_state) {
+                       LOGI("processing PACKAGE_MANAGER_EVENT_STATE_STARTED event : %d", event_type);
+                       if (false == in_progress) {
+                               in_progress = true;
+                               if (monitor) {
+                                       monitor->getObserver().OnUpdateStarted();
+                               } else {
+                                       LOGE("observer is NULL");
+                               }
+                       }
+               } else if (PACKAGE_MANAGER_EVENT_STATE_COMPLETED == event_state ||
+                       PACKAGE_MANAGER_EVENT_STATE_FAILED == event_state) {
+                       LOGI("processing PACKAGE_MANAGER_EVENT_STATE_COMPLETED/FAILED event : %d %d",
+                               event_state, event_type);
+                       if (false == in_progress) {
+                               if (monitor) {
+                                       monitor->getObserver().OnUpdateStarted();
+                               } else {
+                                       LOGE("observer is NULL");
+                               }
+                       }
+                       /*
+                       if (service_main) {
+                               service_main->initialize_service_plugin();
+                               service_main->process_activated_setting();
+                       } else {
+                               LOGE("service_main is NULL");
+                       }
+                       */
+                       should_exit = true;
+                       in_progress = false;
+               }
+       }
+
+       if (handle) pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
+
+       if (should_exit) {
+               LOGI("Now restarting multi-assistant-service for reloading updated modules");
+               if (monitor) {
+                       monitor->getObserver().OnRestartRequired();
+               }
+       }
+
+       return;
+}
+
+CClientInfo& CPackageUpdateMonitor::getClientInfo()
+{
+       return mClientInfo;
+}
+
+IPackageUpdateEventObserver& CPackageUpdateMonitor::getObserver()
+{
+       return mObserver;
+}
index 4f1b7ec..8f84b20 100644 (file)
@@ -42,9 +42,10 @@ bool CServiceMain::check_preprocessing_assistant_exists()
        boost::optional<std::string> preprocessing_appid =
                mPreferenceManager.get_string(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
        if (preprocessing_appid) {
+               ClientInfoItems *items = mClientInfo.getItems();
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-                       if (mClientInfo[loop].used &&
-                               strncmp((*preprocessing_appid).c_str(), mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
+                       if (items[loop].used &&
+                               strncmp((*preprocessing_appid).c_str(), items[loop].appid, MAX_APPID_LEN) == 0) {
                                if (mClientManager.check_client_validity_by_appid(*preprocessing_appid)) {
                                        ret = true;
                                }
@@ -62,7 +63,8 @@ bool CServiceMain::is_current_assistant(pid_t pid)
        bool ret = false;
        const char *current_maclient_appid = NULL;
        if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
-               current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
+               ClientInfoItems *items = mClientInfo.getItems();
+               current_maclient_appid = items[mCurrentClientInfo].appid;
                pid_t pid_by_appid = mClientManager.find_client_pid_by_appid(
                        std::string{current_maclient_appid});
                if (pid == pid_by_appid) {
@@ -316,14 +318,15 @@ int CServiceMain::client_set_preprocessing_allow_mode(pid_t pid, ma_preprocessin
        }
 
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (mClientInfo[loop].used) {
-                       if (0 == pid_appid.compare(mClientInfo[loop].appid)) {
-                               mClientInfo[loop].preprocessing_allow_mode = mode;
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (items[loop].used) {
+                       if (0 == pid_appid.compare(items[loop].appid)) {
+                               items[loop].preprocessing_allow_mode = mode;
                                if (appid) {
-                                       strncpy(mClientInfo[loop].preprocessing_allow_appid, appid, MAX_APPID_LEN);
-                                       mClientInfo[loop].preprocessing_allow_appid[MAX_APPID_LEN - 1] = '\0';
+                                       strncpy(items[loop].preprocessing_allow_appid, appid, MAX_APPID_LEN);
+                                       items[loop].preprocessing_allow_appid[MAX_APPID_LEN - 1] = '\0';
                                } else {
-                                       mClientInfo[loop].preprocessing_allow_appid[0] = '\0';
+                                       items[loop].preprocessing_allow_appid[0] = '\0';
                                }
                        }
                }
@@ -345,7 +348,8 @@ int CServiceMain::client_send_preprocessing_result(pid_t pid, bool result)
 
        const char *current_maclient_appid = NULL;
        if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
-               current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
+               ClientInfoItems *items = mClientInfo.getItems();
+               current_maclient_appid = items[mCurrentClientInfo].appid;
        }
 
        if (result) {
@@ -398,15 +402,16 @@ int CServiceMain::client_add_wake_word(pid_t pid, const char* wake_word, const c
        }
 
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 == pid_appid.compare(mClientInfo[loop].appid)) {
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (items[loop].used &&
+                       0 == pid_appid.compare(items[loop].appid)) {
                        int ret = mServiceConfig.add_custom_wake_word(wake_word, language,
-                               mClientInfo[loop].wakeup_word,
-                               mClientInfo[loop].wakeup_language);
+                               items[loop].wakeup_word,
+                               items[loop].wakeup_language);
                        if (0 == ret) {
                                mServiceConfig.save_custom_wake_words(pid_appid.c_str(),
-                                       mClientInfo[loop].wakeup_word,
-                                       mClientInfo[loop].wakeup_language);
+                                       items[loop].wakeup_word,
+                                       items[loop].wakeup_language);
                        } else {
                                LOGE("add new wake word failed!");
                                return -1;
@@ -427,15 +432,16 @@ int CServiceMain::client_remove_wake_word(pid_t pid, const char* wake_word, cons
        }
 
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 == pid_appid.compare(mClientInfo[loop].appid)) {
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (items[loop].used &&
+                       0 == pid_appid.compare(items[loop].appid)) {
                        int ret = mServiceConfig.remove_custom_wake_word(wake_word, language,
-                               mClientInfo[loop].wakeup_word,
-                               mClientInfo[loop].wakeup_language);
+                               items[loop].wakeup_word,
+                               items[loop].wakeup_language);
                        if (0 == ret) {
                                mServiceConfig.save_custom_wake_words(pid_appid.c_str(),
-                                       mClientInfo[loop].wakeup_word,
-                                       mClientInfo[loop].wakeup_language);
+                                       items[loop].wakeup_word,
+                                       items[loop].wakeup_language);
                        }
                }
        }
@@ -496,11 +502,12 @@ int CServiceMain::ui_client_change_assistant(const char* appid)
 
                /* The appid parameter might not exist after this function call, so we use appid string in our mClientInfo */
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-                       if (mClientInfo[loop].used &&
-                               0 < strlen(mClientInfo[loop].appid) &&
-                               0 < strlen(mClientInfo[loop].wakeup_word[0])) {
-                               if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
-                                       launch_client_by_appid(mClientInfo[loop].appid, CLIENT_LAUNCH_MODE_ACTIVATION);
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       if (items[loop].used &&
+                               0 < strlen(items[loop].appid) &&
+                               0 < strlen(items[loop].wakeup_word[0])) {
+                               if (strncmp(appid, items[loop].appid, MAX_APPID_LEN) == 0) {
+                                       launch_client_by_appid(items[loop].appid, CLIENT_LAUNCH_MODE_ACTIVATION);
                                }
                        }
                }
@@ -532,67 +539,68 @@ int CServiceMain::add_assistant_info(ma_assistant_info_s* info) {
 
        int index = -1;
        int loop = 0;
+       ClientInfoItems *items = mClientInfo.getItems();
        while(-1 == index && loop < MAX_MACLIENT_INFO_NUM) {
-               if (false == mClientInfo[loop].used) {
+               if (false == items[loop].used) {
                        index = loop;
                }
                loop++;
        }
        if (-1 != index) {
-               mClientInfo[index].used = true;
-               mClientInfo[index].preprocessing_allow_mode = MA_PREPROCESSING_ALLOW_NONE;
-               mClientInfo[index].preprocessing_allow_appid[0] = '\0';
+               items[index].used = true;
+               items[index].preprocessing_allow_mode = MA_PREPROCESSING_ALLOW_NONE;
+               items[index].preprocessing_allow_appid[0] = '\0';
                MAS_LOGD("app_id(%s)", info->app_id);
-               strncpy(mClientInfo[index].appid, info->app_id, MAX_APPID_LEN);
-               mClientInfo[index].appid[MAX_APPID_LEN - 1] = '\0';
+               strncpy(items[index].appid, info->app_id, MAX_APPID_LEN);
+               items[index].appid[MAX_APPID_LEN - 1] = '\0';
 
-               if (is_current_preprocessing_assistant(mClientInfo[index].appid)) {
+               if (is_current_preprocessing_assistant(items[index].appid)) {
                        mCurrentPreprocessingClientInfo = index;
                }
 
                for (loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
                        if (loop < info->cnt_wakeup && info->wakeup_list[loop]) {
                                MAS_LOGD("wakeup_list(%d)(%s)(%s)", loop, info->wakeup_list[loop], info->wakeup_language[loop]);
-                               strncpy(mClientInfo[index].wakeup_word[loop], info->wakeup_list[loop], MAX_WAKEUP_WORD_LEN);
-                               mClientInfo[index].wakeup_word[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
+                               strncpy(items[index].wakeup_word[loop], info->wakeup_list[loop], MAX_WAKEUP_WORD_LEN);
+                               items[index].wakeup_word[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
                                if (info->wakeup_language[loop]) {
-                                       strncpy(mClientInfo[index].wakeup_language[loop], info->wakeup_language[loop], MAX_SUPPORTED_LANGUAGE_LEN);
-                                       mClientInfo[index].wakeup_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
+                                       strncpy(items[index].wakeup_language[loop], info->wakeup_language[loop], MAX_SUPPORTED_LANGUAGE_LEN);
+                                       items[index].wakeup_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
                                } else {
-                                       strncpy(mClientInfo[index].wakeup_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
+                                       strncpy(items[index].wakeup_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
                                }
                        } else {
-                               strncpy(mClientInfo[index].wakeup_word[loop], "", MAX_WAKEUP_WORD_LEN);
+                               strncpy(items[index].wakeup_word[loop], "", MAX_WAKEUP_WORD_LEN);
                        }
                }
 
                for (loop = 0;loop < MAX_SUPPORTED_LANGUAGES_NUM;loop++) {
                        if (loop < info->cnt_lang && info->supported_lang[loop]) {
                                MAS_LOGD("supported_lang(%d)(%s)", loop, info->supported_lang[loop]);
-                               strncpy(mClientInfo[index].supported_language[loop], info->supported_lang[loop], MAX_SUPPORTED_LANGUAGE_LEN);
-                               mClientInfo[index].supported_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
+                               strncpy(items[index].supported_language[loop], info->supported_lang[loop], MAX_SUPPORTED_LANGUAGE_LEN);
+                               items[index].supported_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
                        } else {
-                               strncpy(mClientInfo[index].supported_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
+                               strncpy(items[index].supported_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
                        }
                }
 
                MAS_LOGD("wakeup_engine(%s)", info->wakeup_engine);
                if (info->wakeup_engine) {
-                       strncpy(mClientInfo[index].wakeup_engine, info->wakeup_engine, MAX_APPID_LEN);
-                       mClientInfo[index].wakeup_engine[MAX_APPID_LEN - 1] = '\0';
+                       strncpy(items[index].wakeup_engine, info->wakeup_engine, MAX_APPID_LEN);
+                       items[index].wakeup_engine[MAX_APPID_LEN - 1] = '\0';
                } else {
-                       mClientInfo[index].wakeup_engine[0] = '\0';
+                       items[index].wakeup_engine[0] = '\0';
                        MAS_LOGW("Wakeup engine information not provided for : %s", info->app_id);
                }
-               mClientInfo[index].custom_ui_option = info->custom_ui_option;
+               items[index].custom_ui_option = info->custom_ui_option;
 
                MAS_LOGD("voice_key_support_mode(%d)", info->voice_key_support_mode);
-               mClientInfo[index].voice_key_support_mode = info->voice_key_support_mode;
+               items[index].voice_key_support_mode = info->voice_key_support_mode;
                MAS_LOGD("voice_key_tap_duration(%f)", info->voice_key_tap_duration);
-               mClientInfo[index].voice_key_tap_duration = info->voice_key_tap_duration;
+               items[index].voice_key_tap_duration = info->voice_key_tap_duration;
                MAS_LOGD("audio_processing_appid(%s)",
                        (info->audio_data_processing_appid ? (info->audio_data_processing_appid)->c_str() : "[NONE]"));
-               mClientInfo[index].audio_processing_appid = info->audio_data_processing_appid;
+               items[index].audio_processing_appid = info->audio_data_processing_appid;
        } else {
                MAS_LOGD("Couldn't find an empty slot for storing assistant info");
        }
@@ -643,7 +651,7 @@ int CServiceMain::initialize_service_plugin(void)
                return -1;
        }
 
-       memset(&mClientInfo, 0x00, sizeof(mClientInfo));
+       mClientInfo.resetItems();
        mCurrentClientInfo = -1;
        MAS_LOGI("mCurrentClientInfo : %d", mCurrentClientInfo);
        mCurrentPreprocessingClientInfo = -1;
@@ -652,33 +660,34 @@ int CServiceMain::initialize_service_plugin(void)
        if (0 == mServiceConfig.get_assistant_info(assistant_info_cb, this)) {
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
                        int inner_loop;
-                       if (0 < strlen(mClientInfo[loop].appid)) {
-                               mServiceConfig.load_custom_wake_words(mClientInfo[loop].appid,
-                                       mClientInfo[loop].wakeup_word, mClientInfo[loop].wakeup_language);
-                               if (0 < strlen(mClientInfo[loop].wakeup_engine)) {
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       if (0 < strlen(items[loop].appid)) {
+                               mServiceConfig.load_custom_wake_words(items[loop].appid,
+                                       items[loop].wakeup_word, items[loop].wakeup_language);
+                               if (0 < strlen(items[loop].wakeup_engine)) {
                                        mServicePlugin.set_assistant_wakeup_engine(
-                                               mClientInfo[loop].appid,
-                                               mClientInfo[loop].wakeup_engine);
+                                               items[loop].appid,
+                                               items[loop].wakeup_engine);
                                }
                                for (inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
-                                       if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
+                                       if (0 < strlen(items[loop].wakeup_word[inner_loop])) {
                                                MAS_LOGD("Registering wakeup word %s for app %s",
-                                                       mClientInfo[loop].wakeup_word[inner_loop], mClientInfo[loop].appid);
+                                                       items[loop].wakeup_word[inner_loop], items[loop].appid);
                                                if (0 != mServicePlugin.add_assistant_wakeup_word(
-                                                       mClientInfo[loop].appid,
-                                                       mClientInfo[loop].wakeup_word[inner_loop],
-                                                       mClientInfo[loop].wakeup_language[inner_loop])) {
+                                                       items[loop].appid,
+                                                       items[loop].wakeup_word[inner_loop],
+                                                       items[loop].wakeup_language[inner_loop])) {
                                                        MAS_LOGE("Fail to add assistant's wakeup word");
                                                }
                                        }
                                }
                                for (inner_loop = 0; inner_loop < MAX_SUPPORTED_LANGUAGES_NUM; inner_loop++) {
-                                       if (0 < strlen(mClientInfo[loop].supported_language[inner_loop])) {
+                                       if (0 < strlen(items[loop].supported_language[inner_loop])) {
                                                MAS_LOGD("Adding language %s for app %s",
-                                                       mClientInfo[loop].supported_language[inner_loop], mClientInfo[loop].appid);
+                                                       items[loop].supported_language[inner_loop], items[loop].appid);
                                                if (0 != mServicePlugin.add_assistant_language(
-                                                       mClientInfo[loop].appid,
-                                                       mClientInfo[loop].supported_language[inner_loop])) {
+                                                       items[loop].appid,
+                                                       items[loop].supported_language[inner_loop])) {
                                                        MAS_LOGE("Fail to add assistant's language");
                                                }
                                        }
@@ -723,8 +732,9 @@ int CServiceMain::process_activated_setting()
                const char *default_assistant = NULL;
                if (0 == mServicePlugin.get_default_assistant(&default_assistant)) {
                        if (NULL == default_assistant) {
-                               if (mClientInfo[0].used) {
-                                       default_assistant = mClientInfo[0].appid;
+                               ClientInfoItems *items = mClientInfo.getItems();
+                               if (items[0].used) {
+                                       default_assistant = items[0].appid;
                                        MAS_LOGW("No default assistant, setting %s as default", default_assistant);
                                        mServicePlugin.set_default_assistant(default_assistant);
                                } else {
@@ -741,7 +751,8 @@ int CServiceMain::get_current_client_pid()
 {
        int ret = -1;
        if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
-               const char *appid = mClientInfo[mCurrentClientInfo].appid;
+               ClientInfoItems *items = mClientInfo.getItems();
+               const char *appid = items[mCurrentClientInfo].appid;
                if (appid) {
                        ret = mClientManager.find_client_pid_by_appid(std::string{appid});
                }
@@ -753,7 +764,8 @@ pid_t CServiceMain::get_current_preprocessing_client_pid()
 {
        pid_t ret = -1;
        if (mCurrentPreprocessingClientInfo >= 0 && mCurrentPreprocessingClientInfo < MAX_MACLIENT_INFO_NUM) {
-               const char *appid = mClientInfo[mCurrentPreprocessingClientInfo].appid;
+               ClientInfoItems *items = mClientInfo.getItems();
+               const char *appid = items[mCurrentPreprocessingClientInfo].appid;
                if (appid) {
                        ret = mClientManager.find_client_pid_by_appid(std::string{appid});
                }
@@ -765,8 +777,9 @@ pid_t CServiceMain::get_current_audio_processing_pid()
 {
        pid_t ret = -1;
        if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
+               ClientInfoItems *items = mClientInfo.getItems();
                boost::optional<std::string> audio_processing_appid =
-                       mClientInfo[mCurrentClientInfo].audio_processing_appid;
+                       items[mCurrentClientInfo].audio_processing_appid;
                if (audio_processing_appid) {
                        boost::optional<pid_t> audio_processing_pid;
                        audio_processing_pid = mApplicationManager.get_pid_by_appid((*audio_processing_appid).c_str());
@@ -800,11 +813,12 @@ bool CServiceMain::get_client_custom_ui_option_by_appid(const char *appid)
 {
        bool ret = false;
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 < strlen(mClientInfo[loop].appid) &&
-                       0 < strlen(mClientInfo[loop].wakeup_word[0])) {
-                       if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
-                               ret = mClientInfo[loop].custom_ui_option;
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (items[loop].used &&
+                       0 < strlen(items[loop].appid) &&
+                       0 < strlen(items[loop].wakeup_word[0])) {
+                       if (strncmp(appid, items[loop].appid, MAX_APPID_LEN) == 0) {
+                               ret = items[loop].custom_ui_option;
                        }
                }
        }
@@ -824,13 +838,14 @@ const char* CServiceMain::get_client_appid_by_wakeup_word(const char *wakeup_wor
 
        if (NULL == wakeup_word) return NULL;
 
+       ClientInfoItems *items = mClientInfo.getItems();
        for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 < strlen(mClientInfo[loop].appid)) {
+               if (items[loop].used &&
+                       0 < strlen(items[loop].appid)) {
                        for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
-                               if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
-                                       if (0 == strncmp(wakeup_word, mClientInfo[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
-                                               appid = mClientInfo[loop].appid;
+                               if (0 < strlen(items[loop].wakeup_word[inner_loop])) {
+                                       if (0 == strncmp(wakeup_word, items[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
+                                               appid = items[loop].appid;
                                        }
                                }
                        }
@@ -840,19 +855,19 @@ const char* CServiceMain::get_client_appid_by_wakeup_word(const char *wakeup_wor
        /* Perform extended search, by eliminating blank characters */
        if (NULL == appid) {
                for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
-                       if (mClientInfo[loop].used &&
-                               0 < strlen(mClientInfo[loop].appid)) {
+                       if (items[loop].used &&
+                               0 < strlen(items[loop].appid)) {
                                for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
-                                       if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
+                                       if (0 < strlen(items[loop].wakeup_word[inner_loop])) {
                                                char comparand[MAX_WAKEUP_WORD_LEN];
                                                int comparand_index = 0;
                                                for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
-                                                       if (' ' != mClientInfo[loop].wakeup_word[inner_loop][index]) {
-                                                               comparand[comparand_index++] = mClientInfo[loop].wakeup_word[inner_loop][index];
+                                                       if (' ' != items[loop].wakeup_word[inner_loop][index]) {
+                                                               comparand[comparand_index++] = items[loop].wakeup_word[inner_loop][index];
                                                        }
                                                }
                                                if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
-                                                       appid = mClientInfo[loop].appid;
+                                                       appid = items[loop].appid;
                                                }
                                        }
                                }
@@ -869,12 +884,13 @@ int CServiceMain::set_current_client_by_wakeup_word(const char *wakeup_word)
        int ret = -1;
        int prev_selection = mCurrentClientInfo;
 
+       ClientInfoItems *items = mClientInfo.getItems();
        for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 < strlen(mClientInfo[loop].appid)) {
+               if (items[loop].used &&
+                       0 < strlen(items[loop].appid)) {
                        for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
-                               if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
-                                       if (0 == strncmp(wakeup_word, mClientInfo[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
+                               if (0 < strlen(items[loop].wakeup_word[inner_loop])) {
+                                       if (0 == strncmp(wakeup_word, items[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
                                                mCurrentClientInfo = loop;
                                                MAS_LOGI("mCurrentClientInfo : %d %s", mCurrentClientInfo, wakeup_word);
                                                ret = 0;
@@ -886,15 +902,15 @@ int CServiceMain::set_current_client_by_wakeup_word(const char *wakeup_word)
        /* Perform extended search, by eliminating blank characters */
        if (ret == -1) {
                for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
-                       if (mClientInfo[loop].used &&
-                               0 < strlen(mClientInfo[loop].appid)) {
+                       if (items[loop].used &&
+                               0 < strlen(items[loop].appid)) {
                                for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
-                                       if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
+                                       if (0 < strlen(items[loop].wakeup_word[inner_loop])) {
                                                char comparand[MAX_WAKEUP_WORD_LEN];
                                                int comparand_index = 0;
                                                for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
-                                                       if (' ' != mClientInfo[loop].wakeup_word[inner_loop][index]) {
-                                                               comparand[comparand_index++] = mClientInfo[loop].wakeup_word[inner_loop][index];
+                                                       if (' ' != items[loop].wakeup_word[inner_loop][index]) {
+                                                               comparand[comparand_index++] = items[loop].wakeup_word[inner_loop][index];
                                                        }
                                                }
                                                if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
@@ -910,7 +926,7 @@ int CServiceMain::set_current_client_by_wakeup_word(const char *wakeup_word)
 
        if (mCurrentClientInfo != prev_selection) {
                if (prev_selection >= 0 && prev_selection < MAX_MACLIENT_INFO_NUM) {
-                       pid_t pid = get_client_pid_by_appid(mClientInfo[prev_selection].appid);
+                       pid_t pid = get_client_pid_by_appid(items[prev_selection].appid);
                        mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_INACTIVE);
                }
        }
@@ -923,11 +939,12 @@ int CServiceMain::set_current_client_by_appid(const char *appid)
        int ret = -1;
        int prev_selection = mCurrentClientInfo;
 
+       ClientInfoItems *items = mClientInfo.getItems();
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (mClientInfo[loop].used &&
-                       0 < strlen(mClientInfo[loop].appid) &&
-                       0 < strlen(mClientInfo[loop].wakeup_word[0])) {
-                       if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
+               if (items[loop].used &&
+                       0 < strlen(items[loop].appid) &&
+                       0 < strlen(items[loop].wakeup_word[0])) {
+                       if (strncmp(appid, items[loop].appid, MAX_APPID_LEN) == 0) {
                                MAS_LOGI("mCurrentClientInfo : %d %s", mCurrentClientInfo, appid);
                                mCurrentClientInfo = loop;
                                ret = 0;
@@ -937,7 +954,7 @@ int CServiceMain::set_current_client_by_appid(const char *appid)
 
        if (mCurrentClientInfo != prev_selection) {
                if (prev_selection >= 0 && prev_selection < MAX_MACLIENT_INFO_NUM) {
-                       pid_t pid = get_client_pid_by_appid(mClientInfo[prev_selection].appid);
+                       pid_t pid = get_client_pid_by_appid(items[prev_selection].appid);
                        mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_INACTIVE);
                }
        }
@@ -968,10 +985,11 @@ int CServiceMain::launch_client_by_appid(const char *appid, CLIENT_LAUNCH_MODE l
        if (CLIENT_LAUNCH_MODE_ACTIVATION == launch_mode) {
                bool found = false;
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-                       if (mClientInfo[loop].used &&
-                               0 < strlen(mClientInfo[loop].appid)) {
-                               if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
-                                       mWakeupClientAppId = mClientInfo[loop].appid;
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       if (items[loop].used &&
+                               0 < strlen(items[loop].appid)) {
+                               if (strncmp(appid, items[loop].appid, MAX_APPID_LEN) == 0) {
+                                       mWakeupClientAppId = items[loop].appid;
                                        found = true;
                                }
                        }
@@ -1029,17 +1047,18 @@ int CServiceMain::update_voice_key_support_mode()
        const char *default_assistant = NULL;
        if (0 == mServicePlugin.get_default_assistant(&default_assistant)) {
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-                       if (mClientInfo[loop].used) {
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       if (items[loop].used) {
                                if (default_assistant &&
-                                       strncmp(default_assistant, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
-                                       float duration = mClientInfo[loop].voice_key_tap_duration;
+                                       strncmp(default_assistant, items[loop].appid, MAX_APPID_LEN) == 0) {
+                                       float duration = items[loop].voice_key_tap_duration;
                                        if (0.0f < duration) {
                                                mServicePlugin.set_voice_key_tap_duration(duration);
                                        } else {
                                                mServicePlugin.unset_voice_key_tap_duration();
                                        }
                                        mServicePlugin.set_voice_key_support_mode(
-                                               mClientInfo[loop].voice_key_support_mode);
+                                               items[loop].voice_key_support_mode);
                                        successful = true;
                                }
                        }
@@ -1056,9 +1075,10 @@ int CServiceMain::update_voice_key_support_mode()
 ma_preprocessing_allow_mode_e CServiceMain::get_preprocessing_allow_mode(const char* appid)
 {
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (appid && mClientInfo[loop].used) {
-                       if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
-                               return mClientInfo[loop].preprocessing_allow_mode;
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (appid && items[loop].used) {
+                       if (strncmp(appid, items[loop].appid, MAX_APPID_LEN) == 0) {
+                               return items[loop].preprocessing_allow_mode;
                        }
                }
        }
@@ -1073,8 +1093,9 @@ int CServiceMain::process_preprocessing_state_event(PREPROCESSING_STATE_EVENT ev
        const char* current_maclient_appid = NULL;
        const char* preprocessing_allow_appid = NULL;
        if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
-               current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
-               preprocessing_allow_appid = mClientInfo[mCurrentClientInfo].preprocessing_allow_appid;
+               ClientInfoItems *items = mClientInfo.getItems();
+               current_maclient_appid = items[mCurrentClientInfo].appid;
+               preprocessing_allow_appid = items[mCurrentClientInfo].preprocessing_allow_appid;
        }
        ma_preprocessing_allow_mode_e mode = get_preprocessing_allow_mode(current_maclient_appid);
 
@@ -1203,9 +1224,10 @@ ma_service_state_e CServiceMain::get_current_service_state()
 bool CServiceMain::is_valid_wakeup_engine(const char* appid)
 {
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM;loop++) {
-               if (mClientInfo[loop].used) {
-                       LOGD("comparing appid : %s %s", mClientInfo[loop].wakeup_engine, appid);
-                       if (0 == strncmp(mClientInfo[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
+               ClientInfoItems *items = mClientInfo.getItems();
+               if (items[loop].used) {
+                       LOGD("comparing appid : %s %s", items[loop].wakeup_engine, appid);
+                       if (0 == strncmp(items[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
                                return true;
                        }
                }
@@ -1223,9 +1245,10 @@ bool CServiceMain::is_wakeup_engine(const pkgmgrinfo_appinfo_h handle)
        int ret = pkgmgrinfo_appinfo_get_appid(handle, &appid);
        if (PMINFO_R_OK == ret && NULL != appid) {
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM;loop++) {
-                       if (mClientInfo[loop].used) {
-                               LOGD("comparing appid : %s %s", mClientInfo[loop].wakeup_engine, appid);
-                               if (0 == strncmp(mClientInfo[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       if (items[loop].used) {
+                               LOGD("comparing appid : %s %s", items[loop].wakeup_engine, appid);
+                               if (0 == strncmp(items[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
                                        is_wakeup_engine = true;
                                }
                        }
@@ -1492,7 +1515,8 @@ int CServiceMain::on_initialize(pid_t pid) {
 
                const char *current_maclient_appid = NULL;
                if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
-                       current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
+                       ClientInfoItems *items = mClientInfo.getItems();
+                       current_maclient_appid = items[mCurrentClientInfo].appid;
                }
 
                client_send_preprocessing_information(pid);
index 09b035f..e35099e 100644 (file)
@@ -5,3 +5,4 @@ ADD_SUBDIRECTORY(client-manager)
 ADD_SUBDIRECTORY(audio-manager)
 ADD_SUBDIRECTORY(service-main)
 ADD_SUBDIRECTORY(preference-manager-vconf)
+ADD_SUBDIRECTORY(package-update-monitor)
diff --git a/tests/utc/package-update-monitor/CMakeLists.txt b/tests/utc/package-update-monitor/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d0b7199
--- /dev/null
@@ -0,0 +1,50 @@
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR})
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror")
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wno-unused-function -Wno-sign-compare")
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wl,-zdefs" )
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIE")
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror")
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -std=c++11")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS}")
+
+ADD_DEFINITIONS("-DFULLVER=\"${FULLVER}\"")
+
+SET(TEST_SOURCES
+       test_package_update_monitor.cpp
+       ${CMAKE_SOURCE_DIR}/src/package_update_monitor.cpp
+)
+
+# Find Packages
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED
+       capi-appfw-application
+       capi-appfw-package-manager
+       multi-assistant
+       dlog
+       pkgmgr-info
+)
+
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+FIND_PACKAGE(GTest REQUIRED)
+INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS})
+LINK_DIRECTORIES(${GTEST_LIBRARY_DIRS})
+
+SET(TEST_PACKAGE_MANAGER test-package-update-monitor)
+ADD_EXECUTABLE(${TEST_PACKAGE_MANAGER}
+       ${TEST_SOURCES}
+       )
+
+TARGET_LINK_LIBRARIES(${TEST_PACKAGE_MANAGER} -ldl ${GTEST_LIBRARIES} pthread
+       ${EXTRA_LDFLAGS} ${pkgs_LDFLAGS})
+
+SET_TARGET_PROPERTIES(${TEST_PACKAGE_MANAGER} PROPERTIES
+       COMPILE_FLAGS "-fPIE")
+
+INSTALL(TARGETS ${TEST_PACKAGE_MANAGER} DESTINATION bin)
+
+ADD_TEST(NAME ${TEST_PACKAGE_MANAGER} COMMAND ${TEST_PACKAGE_MANAGER})
diff --git a/tests/utc/package-update-monitor/test_package_update_monitor.cpp b/tests/utc/package-update-monitor/test_package_update_monitor.cpp
new file mode 100644 (file)
index 0000000..a4fc299
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2020 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 <gtest/gtest.h>
+
+#include <string>
+
+#include "package_update_monitor.h"
+
+enum class ObserverEvent {
+       None,
+       UpdateStarted,
+       UpdateFinished,
+       RestartRequired
+};
+
+class CPackageUpdateMonitorFake : public CPackageUpdateMonitor
+{
+public:
+       CPackageUpdateMonitorFake(IPackageUpdateEventObserver &observer, CClientInfo &clientInfo) :
+               CPackageUpdateMonitor{observer, clientInfo} {}
+       void GenerateFakeInstallPackageEvent(std::string package) {
+               package_manager_event_cb("", package.c_str(),
+                       PACKAGE_MANAGER_EVENT_TYPE_INSTALL,
+                       PACKAGE_MANAGER_EVENT_STATE_STARTED,
+                       0, PACKAGE_MANAGER_ERROR_NONE, this);
+               package_manager_event_cb("", package.c_str(),
+                       PACKAGE_MANAGER_EVENT_TYPE_INSTALL,
+                       PACKAGE_MANAGER_EVENT_STATE_COMPLETED,
+                       100, PACKAGE_MANAGER_ERROR_NONE, this);
+       }
+};
+
+class CPackageUpdateEventObserver : public IPackageUpdateEventObserver {
+public:
+       void Reset() {
+               events.clear();
+       }
+       void OnUpdateStarted() override {
+               events.push_back(ObserverEvent::UpdateStarted);
+       }
+       void OnUpdateFinished() override {
+               events.push_back(ObserverEvent::UpdateFinished);
+       }
+       void OnRestartRequired() override {
+               events.push_back(ObserverEvent::RestartRequired);
+       }
+       std::vector<ObserverEvent> events;
+};
+
+class DefaultFixture : public testing::Test
+{
+public:
+       DefaultFixture() {
+       }
+       virtual ~DefaultFixture() {
+       }
+       void SetUp() override {
+               monitor.initialize();
+       }
+       void TearDown() override {
+               monitor.deinitialize();
+       }
+
+       CPackageUpdateEventObserver observer;
+       CClientInfo clientInfo;
+       CPackageUpdateMonitorFake monitor{observer, clientInfo};
+};
+
+int main(int argc, char** argv) {
+       testing::InitGoogleTest(&argc, argv);
+       int ret = RUN_ALL_TESTS();
+       return ret;
+}
+
+TEST_F(DefaultFixture, NoUpdateEventsForNonExistingApp) {
+       observer.Reset();
+
+       monitor.GenerateFakeInstallPackageEvent("org.tizen.non-existing-app");
+
+       ASSERT_TRUE(observer.events.empty());
+}
+TEST_F(DefaultFixture, UpdateStartsAndFinishesForVoiceAssistant) {
+       observer.Reset();
+
+       monitor.GenerateFakeInstallPackageEvent("org.tizen.sample-assistant");
+
+       std::vector<ObserverEvent> comparand{
+               ObserverEvent::UpdateStarted,
+               ObserverEvent::UpdateFinished
+       };
+       bool equality = (observer.events == comparand);
+
+       ASSERT_TRUE(equality);
+}