api: add new api to register insert/remove callback 88/74188/6 accepted/tizen/common/20160615.193630 accepted/tizen/ivi/20160616.002455 accepted/tizen/mobile/20160616.002410 accepted/tizen/tv/20160616.002402 accepted/tizen/wearable/20160616.002351 submit/tizen/20160615.062014
authorTaeyoung Kim <ty317.kim@samsung.com>
Mon, 13 Jun 2016 05:58:08 +0000 (14:58 +0900)
committertaeyoung <ty317.kim@samsung.com>
Wed, 15 Jun 2016 06:03:29 +0000 (15:03 +0900)
- Previously, storage event register apis get the storage ID
  by the input parameter. Storage IDs are created when
  storages are connected, and storage IDs are changed
  even if same storage is re-connected. Thus Previous apis
  cannot support "insert" event.

- Now, new apis are added. They get storage type to register
  events. Thus they can support "insert" event too.

   = int storage_set_changed_cb(storage_type_e type, storage_changed_cb callback, void *user_data);
   = int storage_unset_changed_cb(storage_type_e type, storage_changed_cb callback);

Change-Id: Icbc6513f2777f8df760365c5c65512714b295590
Signed-off-by: taeyoung <ty317.kim@samsung.com>
include/common.h
include/storage-expand.h
src/storage-external-dbus.c
src/storage-external.c
src/storage.c

index ec9ca67..a819ecc 100644 (file)
@@ -43,13 +43,16 @@ extern "C" {
 #endif
 
 enum storage_cb_type {
-       STORAGE_CALLBACK_STATE,
+       STORAGE_CALLBACK_ID,
+       STORAGE_CALLBACK_TYPE,
        STORAGE_CALLBACK_MAX,
 };
 
 struct storage_cb_info {
        int id;
+       storage_type_e type;
        storage_state_changed_cb state_cb;
+       storage_changed_cb type_cb;
        void *user_data;
 };
 
index c7b67a1..f0e5382 100644 (file)
@@ -108,12 +108,11 @@ int storage_foreach_device_supported(storage_device_supported_cb callback, void
 
 /**
  * @brief Gets the absolute path to the root directory of the given storage.
- * @details Files saved on the internal/external storage are readable or writeable by all applications.
- *          When an application is uninstalled, the files written by that application are not removed from the internal/external storage.
- *
  * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
  *
- * @remarks If you want to access files or directories in internal storage, you must declare http://tizen.org/privilege/mediastorage.\n
+ * @remarks Files saved on the internal/external storage are readable or writable by all applications.\n
+ * When an application is uninstalled, the files written by that application are not removed from the internal/external storage.\n
+ * If you want to access files or directories in internal storage, you must declare http://tizen.org/privilege/mediastorage.\n
  * If you want to access files or directories in external storage, you must declare http://tizen.org/privilege/externalstorage.\n
  * You must release @a path using free().
  *
@@ -151,12 +150,11 @@ typedef enum {
 
 /**
  * @brief Gets the absolute path to the each directory of the given storage.
- * @details Files saved on the internal/external storage are readable or writeable by all applications.
- *          When an application is uninstalled, the files written by that application are not removed from the internal/external storage.
- *
  * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
  *
- * @remarks The directory path may not exist, so you must make sure that it exists before using it.\n
+ * @remarks Files saved on the internal/external storage are readable or writable by all applications.\n
+ * When an application is uninstalled, the files written by that application are not removed from the internal/external storage.\n
+ * The directory path may not exist, so you must make sure that it exists before using it.\n
  * If you want to access files or directories in internal storage except #STORAGE_DIRECTORY_SYSTEM_RINGTONES, you must declare http://tizen.org/privilege/mediastorage.\n
  * If you want to access files or directories in #STORAGE_DIRECTORY_SYSTEM_RINGTONES, you must declare %http://tizen.org/privilege/systemsettings.\n
  * If you want to access files or directories in external storage, you must declare http://tizen.org/privilege/externalstorage.\n
@@ -276,6 +274,83 @@ int storage_set_state_changed_cb(int storage_id, storage_state_changed_cb callba
 int storage_unset_state_changed_cb(int storage_id, storage_state_changed_cb callback);
 
 /**
+ * @brief Enumeration of storage device types
+ * @since_tizen 3.0
+ */
+typedef enum {
+       STORAGE_DEV_EXT_SDCARD = 1001,     /**< sdcard device (external storage) */
+       STORAGE_DEV_EXT_USB_MASS_STORAGE,  /**< USB storage device (external storage) */
+} storage_dev_e;
+
+/**
+ * @brief Called when the state of a storage type changes.
+ *
+ * @since_tizen 3.0
+ *
+ * @param[in] storage_id The unique storage ID
+ * @param[in] type The type of the storage device
+ * @param[in] state The state of the storage
+ * @param[in] fstype The type of the file system
+ * @param[in] fsuuid The uuid of the file system
+ * @param[in] mountpath The mount path of the file system
+ * @param[in] primary The primary partition
+ * @param[in] flags The flags for the storage status
+ * @param[in] user_data The user data
+ *
+ * @pre        storage_set_changed_cb() will invoke this callback function.
+ * @see        storage_set_changed_cb()
+ * @see        storage_unset_changed_cb()
+ */
+typedef void (*storage_changed_cb)(int storage_id,
+               storage_dev_e dev, storage_state_e state,
+               const char *fstype, const char *fsuuid, const char *mountpath,
+               bool primary, int flags, void *user_data);
+
+/**
+ * @brief Registers a callback function to be invoked when the state of the specified storage device type changes.
+ *
+ * @since_tizen 3.0
+ *
+ * @param[in] type The type of the storage device
+ * @param[in] callback The callback function to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ *
+ * @retval #STORAGE_ERROR_NONE               Successful
+ * @retval #STORAGE_ERROR_INVALID_PARAMETER  Invalid parameter
+ * @retval #STORAGE_ERROR_NOT_SUPPORTED      Storage not supported
+ * @retval #STORAGE_ERROR_OPERATION_FAILED   Operation failed
+ *
+ * @post storage_changed_cb() will be invoked if the state of the registered storage type changes.
+ * @see storage_changed_cb()
+ * @see storage_unset_changed_cb()
+ */
+int storage_set_changed_cb(storage_type_e type, storage_changed_cb callback, void *user_data);
+
+/**
+ * @brief Unregisters the callback function for storage type state changes.
+ *
+ * @since_tizen 3.0
+ *
+ * @param[in] type The type of the the storage device
+ * @param[in] callback The callback function to unregister
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ *
+ * @retval #STORAGE_ERROR_NONE               Successful
+ * @retval #STORAGE_ERROR_INVALID_PARAMETER  Invalid parameter
+ * @retval #STORAGE_ERROR_NOT_SUPPORTED      Storage not supported
+ * @retval #STORAGE_ERROR_OPERATION_FAILED   Operation failed
+ *
+ * @see storage_changed_cb()
+ * @see storage_set_changed_cb()
+ */
+int storage_unset_changed_cb(storage_type_e type, storage_changed_cb callback);
+
+/**
  * @brief Gets the total space of the given storage in bytes.
  *
  * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
index 60a439e..2793e40 100755 (executable)
 
 #define STORAGE_EXT_GET_LIST       "GetDeviceList"
 
-#define STORAGE_EXT_OBJECT_ADDED   "ObjectAdded"
-#define STORAGE_EXT_OBJECT_REMOVED "ObjectRemoved"
 #define STORAGE_EXT_DEVICE_CHANGED "DeviceChanged"
+#define STORAGE_EXT_DEVICE_ADDED   "DeviceAdded"
+#define STORAGE_EXT_DEVICE_REMOVED "DeviceRemoved"
 
 #define DBUS_REPLY_TIMEOUT (-1)
 
-#define DEV_PREFIX           "/dev/"
-
 struct storage_ext_callback {
        storage_ext_changed_cb func;
        void *data;
        guint block_id;
-       guint blockmanager_id;
 };
 
 static dd_list *changed_list;
@@ -214,78 +211,7 @@ out:
 }
 
 //LCOV_EXCL_START Not called Callback
-static char *get_devnode_from_path(char *path)
-{
-       if (!path)
-               return NULL;
-       /* 1 means '/' */
-       return path + strlen(STORAGE_EXT_PATH_DEVICES) + 1;
-}
-//LCOV_EXCL_STOP
-
-//LCOV_EXCL_START Not called Callback
-static void storage_ext_object_path_changed(enum storage_ext_state state,
-               GVariant *params, gpointer user_data)
-{
-       storage_ext_device *dev = NULL;
-       dd_list *elem;
-       struct storage_ext_callback *callback;
-       char *path = NULL;
-       char *devnode;
-       int ret;
-
-       if (!params)
-               return;
-
-       g_variant_get(params, "(s)", &path);
-
-       devnode = get_devnode_from_path(path);
-       if (!devnode)
-               goto out;
-
-       dev = calloc(1, sizeof(storage_ext_device));
-       if (!dev)
-               goto out;
-
-       dev->devnode = strdup(devnode);
-       if (dev->devnode == NULL) {
-               _E("strdup() failed");
-               goto out;
-       }
-
-       DD_LIST_FOREACH(changed_list, elem, callback) {
-               if (!callback->func)
-                       continue;
-               ret = callback->func(dev, state, callback->data);
-               if (ret < 0)
-                       _E("Failed to call callback for devnode(%s, %d)", devnode, ret);
-       }
-
-out:
-       if (dev) {
-               free(dev->devnode);
-               free(dev);
-       }
-       free(path);
-}
-//LCOV_EXCL_STOP
-
-//LCOV_EXCL_START Not called Callback
-static void storage_ext_device_added(GVariant *params, gpointer user_data)
-{
-       storage_ext_object_path_changed(STORAGE_EXT_ADDED, params, user_data);
-}
-//LCOV_EXCL_STOP
-
-//LCOV_EXCL_START Not called Callback
-static void storage_ext_device_removed(GVariant *params, gpointer user_data)
-{
-       storage_ext_object_path_changed(STORAGE_EXT_REMOVED, params, user_data);
-}
-//LCOV_EXCL_STOP
-
-//LCOV_EXCL_START Not called Callback
-static void storage_ext_device_changed(GVariant *params, gpointer user_data)
+static void storage_ext_device_changed(GVariant *params, enum storage_ext_state state, gpointer user_data)
 {
        storage_ext_device *dev;
        dd_list *elem;
@@ -317,7 +243,7 @@ static void storage_ext_device_changed(GVariant *params, gpointer user_data)
        DD_LIST_FOREACH(changed_list, elem, callback) {
                if (!callback->func)
                        continue;
-               ret = callback->func(dev, STORAGE_EXT_CHANGED, callback->data);
+               ret = callback->func(dev, state, callback->data);
                if (ret < 0)
                        _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret);
        }
@@ -336,6 +262,7 @@ static void storage_ext_changed(GDBusConnection *conn,
                gpointer user_data)
 {
        size_t iface_len, signal_len;
+       enum storage_ext_state state;
 
        if (!params || !sender || !path || !iface || !signal)
                return;
@@ -343,26 +270,29 @@ static void storage_ext_changed(GDBusConnection *conn,
        iface_len = strlen(iface) + 1;
        signal_len = strlen(signal) + 1;
 
-       if (!strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len)) {
-               if (!strncmp(signal, STORAGE_EXT_OBJECT_ADDED, signal_len))
-                       storage_ext_device_added(params, user_data);
-               else if (!strncmp(signal, STORAGE_EXT_OBJECT_REMOVED, signal_len))
-                       storage_ext_device_removed(params, user_data);
+       if (strncmp(iface, STORAGE_EXT_IFACE, iface_len))
                return;
-       }
 
-       if (!strncmp(iface, STORAGE_EXT_IFACE, iface_len) &&
-               !strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len)) {
-               storage_ext_device_changed(params, user_data);
+       if (!strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len))
+               state = STORAGE_EXT_CHANGED;
+
+       else if (!strncmp(signal, STORAGE_EXT_DEVICE_ADDED, signal_len))
+               state = STORAGE_EXT_ADDED;
+
+       else if (!strncmp(signal, STORAGE_EXT_DEVICE_REMOVED, signal_len))
+               state = STORAGE_EXT_REMOVED;
+
+       else
                return;
-       }
+
+       storage_ext_device_changed(params, state, user_data);
 }
 //LCOV_EXCL_STOP
 
 int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
 {
        GDBusConnection *conn;
-       guint block_id = NULL, blockmanager_id = NULL;
+       guint block_id = 0;
        struct storage_ext_callback *callback;
        dd_list *elem;
 
@@ -372,7 +302,7 @@ int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
        DD_LIST_FOREACH(changed_list, elem, callback) {
                if (callback->func != func)
                        continue;
-               if (callback->block_id == 0 || callback->blockmanager_id == 0)
+               if (callback->block_id == 0)
                        continue;
 
                return -EEXIST;
@@ -398,41 +328,22 @@ int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
        block_id = g_dbus_connection_signal_subscribe(conn,
                        STORAGE_EXT_BUS_NAME,
                        STORAGE_EXT_IFACE,
-                       STORAGE_EXT_DEVICE_CHANGED,
                        NULL,
                        NULL,
-                       G_DBUS_SIGNAL_FLAGS_NONE,
-                       storage_ext_changed,
-                       NULL,
-                       NULL);
-       if (block_id == 0) {
-               free(callback);
-               _E("Failed to subscrive bus signal");
-               return -EPERM;
-       }
-
-       blockmanager_id = g_dbus_connection_signal_subscribe(conn,
-                       STORAGE_EXT_BUS_NAME,
-                       STORAGE_EXT_IFACE_MANAGER,
-                       NULL,
-                       STORAGE_EXT_PATH_MANAGER,
                        NULL,
                        G_DBUS_SIGNAL_FLAGS_NONE,
                        storage_ext_changed,
                        NULL,
                        NULL);
-       if (blockmanager_id == 0) {
-//LCOV_EXCL_START System Error
+       if (block_id == 0) {
                free(callback);
                _E("Failed to subscrive bus signal");
                return -EPERM;
-//LCOV_EXCL_STOP
        }
 
        callback->func = func;
        callback->data = data;
        callback->block_id = block_id;
-       callback->blockmanager_id = blockmanager_id;
 
        DD_LIST_APPEND(changed_list, callback);
 
@@ -461,8 +372,6 @@ void storage_ext_unregister_device_change(storage_ext_changed_cb func)
                        continue;
                if (callback->block_id > 0)
                        g_dbus_connection_signal_unsubscribe(conn, callback->block_id);
-               if (callback->blockmanager_id > 0)
-                       g_dbus_connection_signal_unsubscribe(conn, callback->blockmanager_id);
 
                DD_LIST_REMOVE(changed_list, callback);
                free(callback);
index c23153d..695c211 100755 (executable)
@@ -38,6 +38,9 @@ static int storage_ext_get_dev_state(storage_ext_device *dev,
                return -EINVAL;
 
        switch (blk_state) {
+       case STORAGE_EXT_ADDED:
+               *state = STORAGE_STATE_UNMOUNTABLE;
+               return 0;
        case STORAGE_EXT_REMOVED:
                *state = STORAGE_STATE_REMOVED;
                return 0;
@@ -154,7 +157,7 @@ int storage_ext_foreach_device_list(storage_device_supported_cb callback, void *
 }
 
 //LCOV_EXCL_START Not called Callback
-static int storage_ext_state_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
+static int storage_ext_id_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
 {
        enum storage_cb_type type = (enum storage_cb_type)data;
        struct storage_cb_info *cb_info;
@@ -165,7 +168,7 @@ static int storage_ext_state_changed(storage_ext_device *dev, enum storage_ext_s
        if (!dev)
                return -EINVAL;
 
-       if (type != STORAGE_CALLBACK_STATE)
+       if (type != STORAGE_CALLBACK_ID)
                return 0;
 
        ret = storage_ext_get_dev_state(dev, blk_state, &state);
@@ -174,38 +177,124 @@ static int storage_ext_state_changed(storage_ext_device *dev, enum storage_ext_s
                return ret;
        }
 
-       DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_STATE], elem, cb_info)
+       DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_ID], elem, cb_info)
                cb_info->state_cb(cb_info->id, state, cb_info->user_data);
 
        return 0;
 }
-//LCOV_EXCL_STOP
 
-int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info)
+static int storage_ext_type_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
 {
+       enum storage_cb_type type = (enum storage_cb_type)data;
        struct storage_cb_info *cb_info;
        dd_list *elem;
-       int ret, n;
+       storage_state_e state;
+       int ret;
+       storage_dev_e strdev;
+       const char *fstype, *fsuuid, *mountpath;
+
+       if (!dev)
+               return -EINVAL;
 
-       if (type < 0 || type >= STORAGE_CALLBACK_MAX)
+       if (type != STORAGE_CALLBACK_TYPE)
                return -EINVAL;
 
+       ret = storage_ext_get_dev_state(dev, blk_state, &state);
+       if (ret < 0) {
+               _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
+               return ret;
+       }
+
+       if (dev->type == STORAGE_EXT_SCSI)
+               strdev = STORAGE_DEV_EXT_USB_MASS_STORAGE;
+       else if (dev->type == STORAGE_EXT_MMC)
+               strdev = STORAGE_DEV_EXT_SDCARD;
+       else {
+               _E("Invalid dev type (%d)", dev->type);
+               return -EINVAL;
+       }
+
+       fstype = (dev->fs_type ? (const char *)dev->fs_type : "");
+       fsuuid = (dev->fs_uuid ? (const char *)dev->fs_uuid : "");
+       mountpath = (dev->mount_point ? (const char *)dev->mount_point : "");
+
+       DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_TYPE], elem, cb_info)
+               if (cb_info->type_cb)
+                       cb_info->type_cb(dev->storage_id, strdev, state,
+                                       fstype, fsuuid, mountpath, dev->primary,
+                                       dev->flags, cb_info->user_data);
+
+       return 0;
+}
+
+//LCOV_EXCL_STOP
+
+static bool check_if_callback_exist(enum storage_cb_type type,
+               struct storage_cb_info *info, struct storage_cb_info **cb_data)
+{
+       struct storage_cb_info *cb_info;
+       dd_list *elem;
+
        if (!info)
+               return false;
+
+       if (type == STORAGE_CALLBACK_ID) {
+               DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
+                       if (cb_info->id == info->id &&
+                           cb_info->state_cb == info->state_cb) {
+                               goto out;
+                       }
+               }
+       }
+
+       if (type == STORAGE_CALLBACK_TYPE) {
+               DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
+                       if (cb_info->type == info->type &&
+                           cb_info->type_cb == info->type_cb)
+                               goto out;
+               }
+       }
+
+       return false;
+
+out:
+       if (cb_data)
+               *cb_data = cb_info;
+
+       return true;
+}
+
+int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info)
+{
+       struct storage_cb_info *cb_info;
+       int n, ret;
+       storage_ext_changed_cb callback;
+
+       if (!info)
+               return -EINVAL;
+
+       switch (type) {
+       case STORAGE_CALLBACK_ID:
+               callback = storage_ext_id_changed;
+               break;
+       case STORAGE_CALLBACK_TYPE:
+               callback = storage_ext_type_changed;
+               break;
+       default:
+               _E("Invalid callback type (%d)", type);
                return -EINVAL;
+       }
 
-       /* check if it is the first request */
        n = DD_LIST_LENGTH(cb_list[type]);
        if (n == 0) {
-               ret = storage_ext_register_device_change(storage_ext_state_changed, (void *)type);
+               ret = storage_ext_register_device_change(callback, (void *)type);
                if (ret < 0)
                        return -EPERM;
        }
 
-       /* check for the same request */
-       DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
-               if (cb_info->id == info->id &&
-                   cb_info->state_cb == info->state_cb)
-                       return -EEXIST;
+       if (check_if_callback_exist(type, info, NULL)) {
+               _E("The callback is already registered");
+               return 0;
        }
 
        /* add device changed callback to list (local) */
@@ -222,33 +311,39 @@ int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *i
 int storage_ext_unregister_cb(enum storage_cb_type type, struct storage_cb_info *info)
 {
        struct storage_cb_info *cb_info;
-       dd_list *elem;
        int n;
-
-       if (type < 0 || type >= STORAGE_CALLBACK_MAX)
-               return -EINVAL;
+       storage_ext_changed_cb callback;
 
        if (!info)
                return -EINVAL;
 
-       /* search for the same element with callback */
-       DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
-               if (cb_info->id == info->id &&
-                   cb_info->state_cb == info->state_cb)
-                       break;
+       switch (type) {
+       case STORAGE_CALLBACK_ID:
+               callback = storage_ext_id_changed;
+               break;
+       case STORAGE_CALLBACK_TYPE:
+               callback = storage_ext_type_changed;
+               break;
+       default:
+               _E("Invalid callback type (%d)", type);
+               return -EINVAL;
        }
 
-       if (!cb_info)
-               return -EINVAL;
+       if (!check_if_callback_exist(type, info, &cb_info)) {
+               _E("The callback is not registered");
+               return 0;
+       }
 
        /* remove device callback from list (local) */
-       DD_LIST_REMOVE(cb_list[type], cb_info);
-       free(cb_info);
+       if (cb_info) {
+               DD_LIST_REMOVE(cb_list[type], cb_info);
+               free(cb_info);
+       }
 
        /* check if this callback is last element */
        n = DD_LIST_LENGTH(cb_list[type]);
        if (n == 0)
-               storage_ext_unregister_device_change(storage_ext_state_changed);
+               storage_ext_unregister_device_change(callback);
 
        return 0;
 }
index be647ca..7a35693 100644 (file)
@@ -284,7 +284,7 @@ API int storage_set_state_changed_cb(int storage_id, storage_state_changed_cb ca
        info.state_cb = callback;
        info.user_data = user_data;
 
-       ret = storage_ext_register_cb(STORAGE_CALLBACK_STATE, &info);
+       ret = storage_ext_register_cb(STORAGE_CALLBACK_ID, &info);
        if (ret < 0) {
                _E("Failed to register callback : id(%d)", storage_id); //LCOV_EXCL_LINE
                return STORAGE_ERROR_OPERATION_FAILED;
@@ -317,7 +317,7 @@ API int storage_unset_state_changed_cb(int storage_id, storage_state_changed_cb
        info.id = storage_id;
        info.state_cb = callback;
 
-       ret = storage_ext_unregister_cb(STORAGE_CALLBACK_STATE, &info);
+       ret = storage_ext_unregister_cb(STORAGE_CALLBACK_ID, &info);
        if (ret < 0) {
                _E("Failed to unregister callback : id(%d)", storage_id); //LCOV_EXCL_LINE
                return STORAGE_ERROR_OPERATION_FAILED;
@@ -401,3 +401,70 @@ out:
        *bytes = avail;
        return STORAGE_ERROR_NONE;
 }
+
+API int storage_set_changed_cb(storage_type_e type, storage_changed_cb callback, void *user_data)
+{
+       int ret;
+       struct storage_cb_info info;
+
+       if (type == STORAGE_TYPE_INTERNAL) {
+               _E("Internal storage is not supported");
+               return STORAGE_ERROR_NOT_SUPPORTED;
+       }
+
+       if (type != STORAGE_TYPE_EXTERNAL) {
+               _E("Invalid type (%d)", type);
+               return STORAGE_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!callback) {
+               _E("Callback is NULL");
+               return STORAGE_ERROR_INVALID_PARAMETER;
+       }
+
+       /* external storage */
+       info.type = type;
+       info.type_cb = callback;
+       info.user_data = user_data;
+
+       ret = storage_ext_register_cb(STORAGE_CALLBACK_TYPE, &info);
+       if (ret < 0) {
+               _E("Failed to register storage callback(ret:%d)", ret); //LCOV_EXCL_LINE
+               return STORAGE_ERROR_OPERATION_FAILED;
+       }
+
+       return STORAGE_ERROR_NONE;
+}
+
+API int storage_unset_changed_cb(storage_type_e type, storage_changed_cb callback)
+{
+       struct storage_cb_info info;
+       int ret;
+
+       if (type == STORAGE_TYPE_INTERNAL) {
+               _E("Internal storage is not supported");
+               return STORAGE_ERROR_NOT_SUPPORTED;
+       }
+
+       if (type != STORAGE_TYPE_EXTERNAL) {
+               _E("Invalid type (%d)", type);
+               return STORAGE_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!callback) {
+               _E("Callback is NULL");
+               return STORAGE_ERROR_INVALID_PARAMETER;
+       }
+
+       /* external storage */
+       info.type = type;
+       info.type_cb = callback;
+
+       ret = storage_ext_unregister_cb(STORAGE_CALLBACK_TYPE, &info);
+       if (ret < 0) {
+               _E("Failed to unregister storage callback(ret:%d)", ret); //LCOV_EXCL_LINE
+               return STORAGE_ERROR_OPERATION_FAILED;
+       }
+
+       return STORAGE_ERROR_NONE;
+}