Add API functions to update-control to trigger specific update stages 63/310263/8
authorAntoni <a.adaszkiewi@samsung.com>
Fri, 12 Apr 2024 10:08:10 +0000 (12:08 +0200)
committerAntoni <a.adaszkiewi@samsung.com>
Fri, 17 May 2024 15:38:07 +0000 (17:38 +0200)
Following functions are asynchronous and have associated functions to
set/unset their callbacks.

 * update_control_do_ro_update_async - triggers the RO update part of
upgrade process (does not reboot)

* update_control_do_finish_update_async - finishes update (reboots and
does a few additional things)

The update_control_do_update function behavior is unchanged.
To use these new functions (to recieve the callbacks)
the cilent needs to have a main loop.

Change-Id: Iee128224f7185e8e274a999549002edfd4bf4dd1

include/update_control.h
packaging/update-manager.conf
packaging/update-manager.xml
src/update_control.c
update-manager/common/common-dbus-manager.c
update-manager/fota/fota-installer.c
update-manager/fota/fota-manager.h

index fee2cf23cb073a8b35dd16fa913e671947ab9d83..16b17bc235befdbfabe2c8e3d6502288723320a3 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 
 /**
  * @brief Enumeration for the update control error.
- * @since_tizen 5.0
+ * @since_tizen 9.0
  */
 typedef enum {
        UPDATE_CONTROL_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
@@ -48,11 +48,14 @@ typedef enum {
        UPDATE_CONTROL_ERROR_CONNECTION_REFUSED = TIZEN_ERROR_CONNECTION_REFUSED, /**< Connection refused */
        UPDATE_CONTROL_ERROR_PROTOCOL_NOT_SUPPORTED = TIZEN_ERROR_PROTOCOL_NOT_SUPPORTED, /**< Protocol not supported */
        UPDATE_CONTROL_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< Time out */
+       UPDATE_CONTROL_ERROR_RESOURCE_BUSY = TIZEN_ERROR_RESOURCE_BUSY, /**<Device or resource busy */
        UPDATE_CONTROL_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Function not implemented */
        UPDATE_CONTROL_ERROR_INVALID_PACKAGE = TIZEN_ERROR_UPDATE_CONTROL | 0x01, /**< Invalid package */
        UPDATE_CONTROL_ERROR_INVALID_URI = TIZEN_ERROR_UPDATE_CONTROL | 0x02, /**< Invalid URI */
        UPDATE_CONTROL_ERROR_PACKAGE_NOT_SUPPORTED = TIZEN_ERROR_UPDATE_CONTROL | 0x03, /**< Package type not supported */
        UPDATE_CONTROL_ERROR_SYSTEM_ERROR = TIZEN_ERROR_UPDATE_CONTROL | 0x04, /**< System error */
+       UPDATE_CONTROL_ERROR_RO_UPDATE_IN_PROGRESS = TIZEN_ERROR_UPDATE_CONTROL | 0x05, /**< RO update is in progress */
+       UPDATE_CONTROL_ERROR_RO_UPDATE_NOT_COMPLETED = TIZEN_ERROR_UPDATE_CONTROL | 0x06, /**< RO update has not completed */
 } update_control_error_e;
 
 
@@ -70,6 +73,37 @@ typedef enum {
 } update_control_property_e;
 
 
+
+/**
+ * @brief Callback type to be invoked after the RO update or the finish update process finishes.
+ * @details The callback will be triggered when the operation triggered by update_control_do_ro_update() \n
+ * or update_control_do_finish_update() finishes (does not have to be successful). Examine the \p result parameter \n
+ * to get the result of the operation. Both RO update and finish update operations can have following results:
+ * - #UPDATE_CONTROL_ERROR_NONE Successful
+ * - #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * - #UPDATE_CONTROL_ERROR_INVALID_OPERATION Upgrade script returned an error
+ * - #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * Finish update operation can additionally result in following:
+ * - #UPDATE_CONTROL_RO_UPDATE_IN_PROGRESS - RO update is still in progress
+ * - #UPDATE_CONTROL_ERROR_RO_UPDATE_NOT_COMPLETED - RO update has not finished (and is not currently in progress), \n
+ * most likely meaning the RO update has not been triggered yet
+ *
+ * @since_tizen 9.0
+ * @remarks This callback will be invoked in the main event loop of the client.
+ * @param[in] result Result of the operation that triggered the callback
+ * @param[in] user_data User data given when setting the callback
+ * @pre Register the callback using update_control_set_ro_update_cb() or update_control_set_finish_update_cb(). \n
+ * The callback will be invoked only when the operation associated with the registering function finishes. \n
+ * @see update_control_set_ro_update_cb()
+ * @see update_control_set_finish_update_cb()
+ * @see update_control_unset_ro_update_cb()
+ * @see update_control_unset_finish_update_cb()
+ * @see update_control_do_ro_update()
+ * @see update_control_do_finish_update()
+ */
+typedef void (*update_control_cb) (const update_control_error_e result, const void *user_data);
+
+
 /**
  * @brief Initializes the update controller.
  *
@@ -139,12 +173,12 @@ int update_control_download_package(void);
 
 /**
  * @platform
- * @brief Requests triggering update to new firmware.
- *
+ * @brief Triggers upgrade script and tells it to perform RO update and finish update operations (full upgrade process).
+ * @details This starts the script in the background and quickly returns. It doesn't check the return value of the script.
  * @since_tizen 5.0
  * @privlevel platform
  * @privilege %http://tizen.org/privilege/updatecontrol.admin
- * @return @c 0 on success,
+ * @return @c 0 if RO update and finish were triggered successfully (doesn't guarantee upgrade script COMPLETED successfully),
  *         otherwise a negative error value
  * @retval #UPDATE_CONTROL_ERROR_NONE Successful
  * @retval #UPDATE_CONTROL_ERROR_PERMISSION_DENIED Permission denied
@@ -158,6 +192,146 @@ int update_control_download_package(void);
 int update_control_do_update(void);
 
 
+/**
+ * @brief Sets a callback to trigger once RO update has completed.
+ *
+ * @since_tizen 9.0
+ * @remarks If there already is a callback set using this function, calling it again will overwrite it.
+ * @param[in] user_cb The callback to be triggered once the operation has completed
+ * @param[in] user_data Data to be passed to the callback
+ * @return @c 0 if callback was set, otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_PARAMETER @user_cb is null
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @post @user_cb will be called after an operation launched by update_control_do_ro_update_async() finishes.
+ * @see update_control_unset_ro_update_cb()
+ * @see update_control_do_ro_update_async()
+ */
+int update_control_set_ro_update_cb(update_control_cb user_cb, const void* user_data);
+
+
+/**
+ * @brief Unsets the callback set by update_control_set_ro_update_cb().
+ *
+ * @since_tizen 9.0
+ * @remarks Using this function after launching associated operation but before \n
+ * receiving the callback will not cause any errors, but will make tracking the result of \n
+ * the operation impossible.
+ * @return @c 0 if callback was unset, otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_OPERATION No callback to unset
+ * @pre A callback has to be set using update_control_set_ro_update_cb() before calling \n
+ * this function.
+ * @see update_control_set_ro_update_cb()
+ */
+int update_control_unset_ro_update_cb(void);
+
+
+/**
+ * @platform
+ * @brief Triggers upgrade script and tells it to perform @b ONLY RO update asynchronously.
+ * @details This function does not perform the finishing of update process (among others rebooting). \n
+ * To finish the update use update_control_do_finish_update_async(). If you wish to trigger both operations synchronously, \n
+ * use update_control_do_update().
+ *
+ * @since_tizen 9.0
+ * @privlevel platform
+ * @privilege %http://tizen.org/privilege/updatecontrol.admin
+ * @return @c 0 if basic checks succeeded (does not guarantee the operation triggered completed successfully), \n
+ * otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_OPERATION Callback was not set
+ * @pre update_control_initialize() has to be called before calling this function.
+ * @pre A callback has to be set using update_control_set_ro_update_cb() before calling \n
+ * this function.
+ * @post A callback set by update_control_set_ro_update_cb() will be invoked \n
+ * after RO update finishes (does not guarantee the process was successful). \n
+ * To check the result of the operation, examine the result code passed to the callback.
+ * @see update_control_initialize()
+ * @see update_control_set_ro_update_cb()
+ */
+int update_control_do_ro_update_async(void);
+
+
+/**
+ * @brief Sets a callback to trigger once finish update operation has completed.
+ *
+ * @since_tizen 9.0
+ * @remarks If there already is a callback set using this function, calling it again will overwrite it.
+ * @param[in] user_cb The callback to be triggered once the operation has completed
+ * @param[in] user_data Data to be passed to the callback
+ * @return @c 0 if callback was set, otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_PARAMETER @user_cb is null
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @post @user_cb will be called after an operation launched by update_control_do_finish_update_async() finishes.
+ * @see update_control_unset_finish_update_cb()
+ * @see update_control_do_finish_update_async()
+ */
+int update_control_set_finish_update_cb(update_control_cb user_cb, const void* user_data);
+
+
+/**
+ * @brief Unsets the callback set by update_control_set_finish_update_cb().
+ *
+ * @since_tizen 9.0
+ * @remarks Using this function after launching associated operation but before \n
+ * receiving the callback will not cause any errors, but will make tracking the result of \n
+ * the operation impossible.
+ * @return @c 0 if callback was unset, otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_OPERATION No callback to unset
+ * @pre A callback has to be set using update_control_set_finish_update_cb() before calling \n
+ * this function.
+ * @see update_control_set_finish_update_cb()
+ */
+int update_control_unset_finish_update_cb(void);
+
+
+/**
+ * @platform
+ * @brief Triggers upgrade script and tells it to perform @b ONLY the finish update process asynchronously.
+ * @details This function does not perform the RO update process. \n
+ * To trigger RO use update_control_do_ro_update_async(). If you wish to trigger both operations synchronously, \n
+ * use update_control_do_update().
+ *
+ * @since_tizen 9.0
+ * @privlevel platform
+ * @privilege %http://tizen.org/privilege/updatecontrol.admin
+ * @remarks This function can be used to check whether the RO update process is still in progress.
+ * @return @c 0 if basic checks succeeded (does not guarantee the operation triggered finished successfully), \n
+ * otherwise a negative error value
+ * @retval #UPDATE_CONTROL_ERROR_NONE Successful
+ * @retval #UPDATE_CONTROL_ERROR_NOT_SUPPORTED Not supported
+ * @retval #UPDATE_CONTROL_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #UPDATE_CONTROL_ERROR_SYSTEM_ERROR System error
+ * @retval #UPDATE_CONTROL_ERROR_INVALID_OPERATION Callback was not set
+ * @pre update_control_initialize() has to be called before calling this function.
+ * @pre A callback has to be set using update_control_set_finish_update_cb() before calling \n
+ * this function.
+ * @pre In order for the finish update process to complete successfully, this function has \n
+ * to be called after the RO update process launched by update_control_do_ro_update_async() \n
+ * completes @b SUCCESSFULLY. Calling this function before that will not return an error.
+ * @post A callback set by update_control_set_finish_update_cb() will be invoked \n
+ * after the finish update process finishes. To check the result of the operation, examine \n
+ * the result code passed to the callback. If the process was successful, the callback \n
+ * will not be invoked, because the system will be rebooting.
+ * @see update_control_initialize()
+ * @see update_control_set_finish_update_cb()
+ * @see update_control_do_ro_update_async()
+ */
+int update_control_do_finish_update_async(void);
+
+
 /**
  * @platform
  * @brief Makes reservation for update.
index e0370014046f6c48ee94efc3ef18be6d869b681a..34be1b6dd2886005c297da4d03b541e667634b2e 100644 (file)
@@ -4,12 +4,16 @@
 <busconfig>
   <policy user="root">
     <allow own="org.tizen.update.manager"/>
-    <allow send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="install" />
+    <allow send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="RoUpdate" />
+    <allow send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="FinishUpdate" />
+    <allow send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="RoUpdateAndFinishUpdate" />
   </policy>
   <policy context="default">
     <deny own="org.tizen.update.manager"/>
     <deny send_destination="org.tizen.update.manager" send_type="method_call"/>
-    <check send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="install" privilege="http://tizen.org/privilege/updatecontrol.admin" />
+    <check send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="RoUpdate" privilege="http://tizen.org/privilege/updatecontrol.admin" />
+    <check send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="FinishUpdate" privilege="http://tizen.org/privilege/updatecontrol.admin" />
+    <check send_destination="org.tizen.update.manager" send_interface="org.tizen.update.manager" send_member="RoUpdateAndFinishUpdate" privilege="http://tizen.org/privilege/updatecontrol.admin" />
     <allow send_destination="org.tizen.update.manager" send_interface="org.freedesktop.DBus.Properties" send_member="GetAll" />
   </policy>
 </busconfig>
index 6191c5ed9587c6122233a43a1011ab8678836da0..417085ad4eb98441a963d9b0d50654f39a24c4e5 100644 (file)
@@ -1,7 +1,13 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <node name="/org/tizen/update/manager">
        <interface name="org.tizen.update.manager">
-               <method name="install">
+               <method name="RoUpdate">
+                       <arg name="status" direction="out" type="i" />
+               </method>
+               <method name="FinishUpdate">
+                       <arg name="status" direction="out" type="i" />
+               </method>
+               <method name="RoUpdateAndFinishUpdate">
                        <arg name="status" direction="out" type="i" />
                </method>
        </interface>
index efa87906ae0a9b14b00d31647639d2e727468b5a..01299d615dbde658e5ea750b73a85073bad4c6ad 100644 (file)
  * limitations under the License.
  */
 
+#include "update_control_internal.h"
+
 #include <dlfcn.h>
 #include <unistd.h>
-#include "update_control_internal.h"
+
+#include <glib.h>
+#include <gio/gio.h>
 
 #include <system/syscommon-plugin-update-control.h>
 #include <system/syscommon-plugin-update-control-interface.h>
@@ -30,6 +34,113 @@ static bool initialized = false;
 static bool plugin_found = false;
 static OrgTizenUpdateManager *proxy = NULL;
 
+typedef struct {
+       update_control_cb cb;
+       const void *data;
+} _update_control_user_cb_info;
+
+static _update_control_user_cb_info *ro_update_user_cb_info = NULL;
+static _update_control_user_cb_info *finish_update_user_cb_info = NULL;
+
+static void update_control_ro_update_cb_internal(GObject *source_object, GAsyncResult *res, gpointer data)
+{
+       _I("update_control_ro_update_cb_internal triggered");
+
+       if (ro_update_user_cb_info == NULL) {
+               _E("ro_update callback was unexpectedly unset");
+               return;
+       }
+
+       update_control_error_e result = UPDATE_CONTROL_ERROR_NONE;
+
+       if (proxy == NULL) {
+               _E("Not initialized");
+               result = UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+               goto trigger_user_cb;
+       }
+
+       gint status = 0;
+       GError *error = NULL;
+
+       org_tizen_update_manager_call_ro_update_finish(proxy, &status, res, &error);
+
+       if (error != NULL) {
+               _E("DBus <RoUpdate> method call failed: %s", error->message);
+               g_error_free(error);
+               result = UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+               goto trigger_user_cb;
+       }
+
+       _I("Successfully called DBus <RoUpdate> method");
+       if (status < 0) {
+               _E("DBus method <RoUpdate> returned error code: %d", status);
+               result = UPDATE_CONTROL_ERROR_INVALID_OPERATION;
+               goto trigger_user_cb;
+       }
+
+       _I("RO update completed: %d", status);
+
+trigger_user_cb:
+       ro_update_user_cb_info->cb(result, ro_update_user_cb_info->data);
+
+       return;
+}
+
+static void update_control_finish_update_cb_internal(GObject *source_object, GAsyncResult *res, gpointer data)
+{
+       _I("update_control_finish_update_cb_internal triggered");
+
+       if (finish_update_user_cb_info == NULL) {
+               _E("finish_update callback was unexpectedly unset");
+               return;
+       }
+
+       update_control_error_e result = UPDATE_CONTROL_ERROR_NONE;
+
+       if (proxy == NULL) {
+               _E("Not initialized");
+               result = UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+               goto trigger_user_cb;
+       }
+
+       gint status = 0;
+       GError *error = NULL;
+
+       org_tizen_update_manager_call_finish_update_finish(proxy, &status, res, &error);
+
+       if (error != NULL) {
+               _E("DBus <FinishUpdate> method call failed: %s", error->message);
+               g_error_free(error);
+               result =  UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+               goto trigger_user_cb;
+       }
+
+       _I("Successfully called DBus <FinishUpdate> method");
+
+       if (status == -2) {
+               _I("Cannot finish update because RO update is still in progress");
+               result = UPDATE_CONTROL_ERROR_RO_UPDATE_IN_PROGRESS;
+               goto trigger_user_cb;
+       }
+
+       if (status == - 3) {
+               _I("Cannot finish udpate because RO update has not completed");
+               result = UPDATE_CONTROL_ERROR_RO_UPDATE_NOT_COMPLETED;
+               goto trigger_user_cb;
+       }
+
+       if (status < 0) {
+               _E("DBus method <FinishUpdate> returned error code: %d", status);
+               result = UPDATE_CONTROL_ERROR_INVALID_OPERATION;
+               goto trigger_user_cb;
+       }
+
+trigger_user_cb:
+       finish_update_user_cb_info->cb(result, finish_update_user_cb_info->data);
+
+       return;
+}
+
 API int update_control_initialize(void)
 {
        CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
@@ -107,6 +218,12 @@ API int update_control_deinitialize(void)
                _I("Failed to find initialized plugin, passed");
        }
 
+       if (ro_update_user_cb_info != NULL)
+               free(ro_update_user_cb_info);
+
+       if (finish_update_user_cb_info != NULL)
+               free(finish_update_user_cb_info);
+
        initialized = false;
        plugin_found = false;
        return UPDATE_CONTROL_ERROR_NONE;
@@ -149,16 +266,16 @@ API int update_control_do_update(void)
                return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
        }
 
-       org_tizen_update_manager_call_install_sync(proxy, &status, NULL, &error);
+       org_tizen_update_manager_call_ro_update_and_finish_update_sync(proxy, &status, NULL, &error);
        if (error != NULL) {
-               _E("Failed to call DBus <install> method: %s", error->message);
+               _E("Failed to call DBus <RoUpdateAndFinishUpdate> method: %s", error->message);
                g_error_free(error);
                return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
        }
 
-       _I("Successfully called DBus <install> method call");
+       _I("Successfully called DBus <RoUpdateAndFinishUpdate> method call");
        if (status < 0) {
-               _E("DBus method <install> returned error code: %d", status);
+               _E("DBus method <RoUpdateAndFinishUpdate> returned error code: %d", status);
                return UPDATE_CONTROL_ERROR_INVALID_OPERATION;
        }
 
@@ -166,6 +283,134 @@ API int update_control_do_update(void)
        return UPDATE_CONTROL_ERROR_NONE;
 }
 
+API int update_control_set_ro_update_cb(update_control_cb user_cb, const void* user_data)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("udpate_control_set_ro_update_cb called");
+
+       if (user_cb == NULL) {
+               _E("user callback is null - cannot set");
+               return UPDATE_CONTROL_ERROR_INVALID_PARAMETER;
+       }
+
+       if (ro_update_user_cb_info != NULL) {
+               _I("Overwriting previously set callback");
+               free(ro_update_user_cb_info);
+       }
+
+       ro_update_user_cb_info = (_update_control_user_cb_info *)malloc(sizeof(*ro_update_user_cb_info));
+       if (ro_update_user_cb_info == NULL) {
+               _E("Failed to malloc");
+               return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+       }
+
+       ro_update_user_cb_info->cb = user_cb;
+       ro_update_user_cb_info->data = user_data;
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
+API int update_control_unset_ro_update_cb(void)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("update_control_unset_ro_update_cb called");
+
+       if (ro_update_user_cb_info == NULL) {
+               _E("Cannot unset ro_update callback - it was not set");
+               return UPDATE_CONTROL_ERROR_INVALID_OPERATION;
+       }
+
+       free(ro_update_user_cb_info);
+       ro_update_user_cb_info = NULL;
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
+API int update_control_do_ro_update_async(void)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("update_control_do_ro_update_async called");
+
+       if (proxy == NULL) {
+               _E("Not initialized");
+               return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+       }
+
+       if (ro_update_user_cb_info == NULL) {
+               _E("Cannot launch async operation - callback was not set");
+               return UPDATE_CONTROL_ERROR_INVALID_OPERATION;
+       }
+
+       org_tizen_update_manager_call_ro_update(proxy, NULL, update_control_ro_update_cb_internal, NULL);
+       _I("Called DBus <RoUpdate> method asynchronously");
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
+API int update_control_set_finish_update_cb(update_control_cb user_cb, const void* user_data)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("udpate_control_set_finish_update_cb called");
+
+       if (user_cb == NULL) {
+               _E("user callback is null - cannot set");
+               return UPDATE_CONTROL_ERROR_INVALID_PARAMETER;
+       }
+
+       if (finish_update_user_cb_info != NULL) {
+               _I("Overwriting previously set callback");
+               free(finish_update_user_cb_info);
+       }
+
+       finish_update_user_cb_info = (_update_control_user_cb_info *)malloc(sizeof(*finish_update_user_cb_info));
+       if (finish_update_user_cb_info == NULL) {
+               _E("Failed to malloc");
+               return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+       }
+
+       finish_update_user_cb_info->cb = user_cb;
+       finish_update_user_cb_info->data = user_data;
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
+API int update_control_unset_finish_update_cb(void)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("update_control_unset_finish_update_cb called");
+
+       if (finish_update_user_cb_info == NULL) {
+               _E("Cannot unset finish_update callback - it was not set");
+               return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+       }
+
+       free(finish_update_user_cb_info);
+       finish_update_user_cb_info = NULL;
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
+API int update_control_do_finish_update_async(void)
+{
+       CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+       _I("update_control_do_finish_async called");
+
+       if (proxy == NULL) {
+               _E("Not initialized");
+               return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+       }
+
+       if (finish_update_user_cb_info == NULL) {
+               _E("Cannot launch async operation - callback was not set");
+               return UPDATE_CONTROL_ERROR_INVALID_OPERATION;
+       }
+
+       org_tizen_update_manager_call_finish_update(proxy, NULL, update_control_finish_update_cb_internal, NULL);
+       _I("Called DBus <FinishUpdate> method asynchronously");
+
+       return UPDATE_CONTROL_ERROR_NONE;
+}
+
 API int update_control_make_reservation(struct tm *reservation_time)
 {
        CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
index ea1cd8ba14c2e64ff4a21a3a10358ed2f44cec85..a422c05bdade8aa36345a952e1ec8b886d4d59b8 100644 (file)
@@ -5,6 +5,18 @@
 
 static guint owner_id;
 
+typedef enum {
+       RO_UPDATE,
+       FINISH,
+       RO_UPDATE_AND_FINISH,
+} dbus_handler_action;
+
+typedef struct _dbus_handler_arg {
+       dbus_handler_action action;
+       OrgTizenUpdateManager *skeleton;
+       GDBusMethodInvocation *invocation;
+} dbus_handler_arg;
+
 static pid_t dbus_get_sender_pid(GDBusMethodInvocation *invocation)
 {
        const gchar *sender = g_dbus_method_invocation_get_sender(invocation);
@@ -37,37 +49,155 @@ static pid_t dbus_get_sender_pid(GDBusMethodInvocation *invocation)
        return pid;
 }
 
-gboolean dbus_manager_install(OrgTizenUpdateManager *skeleton, GDBusMethodInvocation *invocation, gpointer user_data)
+void *dbus_call_handler(void *arg)
 {
        int ret = 0;
+       pid_t pid = 0;
+       dbus_handler_arg *handler_arg = (dbus_handler_arg *) arg;
+
+       if (handler_arg->action != FINISH)
+               pid = dbus_get_sender_pid(handler_arg->invocation);
+
+       switch (handler_arg->action) {
+               case RO_UPDATE:
+                       ret = fota_installer_ro_update(pid);
+                       if (ret < 0)
+                               _FLOGW("Failed to perform <RoUpdate>: %d", ret);
+                       org_tizen_update_manager_complete_ro_update(handler_arg->skeleton, handler_arg->invocation, ret);
+                       break;
+               case FINISH:
+                       ret = fota_installer_finish_update();
+                       if (ret < 0)
+                               _FLOGW("Failed to perform <FinishUpdate>: %d", ret);
+                       org_tizen_update_manager_complete_finish_update(handler_arg->skeleton, handler_arg->invocation, ret);
+                       break;
+               case RO_UPDATE_AND_FINISH:
+                       ret = fota_installer_ro_update_and_finish_update(pid);
+                       if (ret < 0)
+                               _FLOGW("Failed to perform <RoUpdateAndFinishUpdate>: %d", ret);
+                       org_tizen_update_manager_complete_ro_update_and_finish_update(handler_arg->skeleton, handler_arg->invocation, ret);
+                       break;
+       }
+
+       free(arg);
+       g_thread_exit(0);
+}
+
+gboolean dbus_manager_ro_update(OrgTizenUpdateManager *skeleton, GDBusMethodInvocation *invocation)
+{
+       _FLOGD("Dbus status: <RoUpdate> called");
+       dbus_handler_arg *arg = (dbus_handler_arg *)malloc(sizeof(dbus_handler_arg));
+       if (!arg) {
+               _FLOGE("Failed to malloc");
+               goto fail;
+       }
+
+       arg->action = RO_UPDATE;
+       arg->skeleton = skeleton;
+       arg->invocation = invocation;
+
+       GError *error = NULL;
+       g_thread_try_new(NULL, (GThreadFunc)dbus_call_handler, (void *)arg, &error);
+       if (error != NULL) {
+               _FLOGE("Failed to create thread: %s", error->message);
+               goto fail;
+       }
+
+       return TRUE;
+
+fail:
+       if (arg)
+               free(arg);
+
+       org_tizen_update_manager_complete_ro_update(skeleton, invocation, -1);
+       return TRUE;
+}
+
+gboolean dbus_manager_finish_update(OrgTizenUpdateManager *skeleton, GDBusMethodInvocation *invocation)
+{
+       _FLOGD("Dbus status: <FinishUpdate> called");
+       dbus_handler_arg *arg = (dbus_handler_arg *)malloc(sizeof(dbus_handler_arg));
+       if (!arg) {
+               _FLOGE("Failed to malloc");
+               goto fail;
+       }
+
+       arg->action = FINISH;
+       arg->skeleton = skeleton;
+       arg->invocation = invocation;
+
+       GError *error = NULL;
+       g_thread_try_new(NULL, (GThreadFunc)dbus_call_handler, (void *)arg, &error);
+       if (error != NULL) {
+               _FLOGE("Failed to create thread: %s", error->message);
+               goto fail;
+       }
+
+       return TRUE;
+
+fail:
+       if (arg)
+               free(arg);
+
+       org_tizen_update_manager_complete_finish_update(skeleton, invocation, -1);
+       return TRUE;
+}
 
-       _FLOGD("Dbus status : install called");
-       pid_t pid = dbus_get_sender_pid(invocation);
-       ret = fota_installer_execute(pid);
-       if (ret < 0)
-               _FLOGW("Failed to install delta with fota : %d", ret);
-       org_tizen_update_manager_complete_install(skeleton, invocation, ret);
+/*
+ * This is not supposed to be called asynchronously, so this doesn't need a separate thread.
+ * This new thread is only for consistency with functions above.
+ */
+gboolean dbus_manager_ro_update_and_finish_update(OrgTizenUpdateManager *skeleton, GDBusMethodInvocation *invocation)
+{
+       _FLOGD("Dbus status: <RoUpdateAndFinishUpdate> called");
+       dbus_handler_arg *arg = (dbus_handler_arg *)malloc(sizeof(dbus_handler_arg));
+       if (!arg) {
+               _FLOGE("Failed to malloc");
+               goto fail;
+       }
+
+       arg->action = RO_UPDATE_AND_FINISH;
+       arg->skeleton = skeleton;
+       arg->invocation = invocation;
+
+       GError *error = NULL;
+       g_thread_try_new(NULL, (GThreadFunc)dbus_call_handler, (void *)arg, &error);
+       if (error != NULL) {
+               _FLOGE("Failed to create thread: %s", error->message);
+               goto fail;
+       }
 
        return TRUE;
+
+fail:
+       if (arg)
+               free(arg);
+
+       org_tizen_update_manager_complete_ro_update_and_finish_update(skeleton, invocation, -1);
+       return TRUE;
 }
 
 void dbus_manager_on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data)
 {
-       _CLOGD("Dbus status : name lost");
+       _CLOGD("Dbus status: name lost");
 }
 
 void dbus_manager_on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
 {
-       _CLOGD("Dbus status : name acquired");
+       _CLOGD("Dbus status: name acquired");
 
        OrgTizenUpdateManager *skeleton = org_tizen_update_manager_skeleton_new();
-       g_signal_connect(skeleton, "handle-install", G_CALLBACK(dbus_manager_install), NULL);
+
+       g_signal_connect(skeleton, "handle-ro-update", G_CALLBACK(dbus_manager_ro_update), NULL);
+       g_signal_connect(skeleton, "handle-finish-update", G_CALLBACK(dbus_manager_finish_update), NULL);
+       g_signal_connect(skeleton, "handle-ro-update-and-finish-update", G_CALLBACK(dbus_manager_ro_update_and_finish_update), NULL);
+
        g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(skeleton), connection, DBUS_NODE_NAME, NULL);
 }
 
 void dbus_manager_on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
 {
-       _CLOGD("Dbus status : bus acquired");
+       _CLOGD("Dbus status: bus acquired");
 }
 
 int dbus_manager_init()
index b0d7953d000cb55301a0683280ad54fc15715920..82d0e61f9f3007efb23fd3fa0caaeb27150ffd54 100644 (file)
@@ -9,6 +9,11 @@
 #define FOTA_TRIGGER_FILE "upgrade-trigger.sh"
 #define FOTA_TRIGGER_PATH FOTA_DIR "/" FOTA_TRIGGER_FILE
 
+/* Empty string here is intentional */
+#define FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE ""
+#define FOTA_TRIGGER_MODE_RO_UPDATE "--ro-update"
+#define FOTA_TRIGGER_MODE_FINISH_UPDATE "--finish"
+
 #define FOTA_INSTALL_REBOOT_REASON "fota"
 
 static char *get_sender_appid(pid_t pid)
@@ -71,62 +76,54 @@ static char *find_delta_dir(const char *appid)
        return client_delta_path;
 }
 
-int fota_installer_execute(pid_t sender_pid)
+int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
 {
-       int ret = 0, status = 0, exec_status = 0;
+       int ret = 0;
        char *appid = NULL;
-       gchar *client_delta_path = NULL;
-       pid_t pid;
        bool check_sha = false;
 
        /* Check client have delta.tar */
        appid = get_sender_appid(sender_pid);
 
        /* Find the delta file path */
-       client_delta_path = find_delta_dir(appid);
+       *client_delta_path = find_delta_dir(appid);
        if (appid != NULL) {
                free(appid);
                appid = NULL;
        }
 
-       if (client_delta_path == NULL) {
+       if (*client_delta_path == NULL) {
                _FLOGE("Delta file not found");
-               status = -2;
-               goto execute_destroy;
+               return -1;
        }
-       _FLOGI("Delta found: %s", client_delta_path);
+       _FLOGI("Delta found: %s", *client_delta_path);
 
        /* Prepare fota dir */
        ret = util_file_mkdir(FOTA_DIR);
        if (ret < 0) {
-               status = -1;
-               goto execute_destroy;
+               return -1;
        }
 
        /* Check client have appropriate delta  */
-       if (check_is_delta_appropriate(client_delta_path, &check_sha) != 0) {
-               status = -1;
-               goto execute_destroy;
+       if (check_is_delta_appropriate(*client_delta_path, &check_sha) != 0) {
+               return -1;
        }
 
        /* Setup local update flag */
        ret = util_file_mkdir(FOTA_STATUS_DIR);
        if (ret < 0) {
-               status = -1;
-               goto execute_destroy;
+               return -1;
        }
 
        ret = util_file_write_line(FOTA_STATUS_FLAG_PATH, NULL);
        if (ret < 0) {
-               status = -1;
-               goto execute_destroy;
+               return -1;
        }
 
        /* Trigger update */
-       ret = util_file_untar(client_delta_path, FOTA_DIR, FOTA_TRIGGER_FILE);
+       ret = util_file_untar(*client_delta_path, FOTA_DIR, FOTA_TRIGGER_FILE);
        if (ret < 0) {
-               status = -1;
-               goto execute_destroy;
+               return -1;
        }
 
        if (check_sha) {
@@ -134,12 +131,29 @@ int fota_installer_execute(pid_t sender_pid)
                if (verify_checksum(FOTA_CHECKSUM_PATH, FOTA_TRIGGER_PATH) == 0) {
                        _FLOGI("Checksum of %s is correct", FOTA_TRIGGER_PATH);
                } else {
-                       status = -1;
                        _FLOGE("Failed checksum verification of %s", FOTA_TRIGGER_PATH);
-                       goto execute_destroy;
+                       return -1;
                }
        }
 
+       return 0;
+}
+
+int fota_installer_ro_update_and_finish_update(pid_t sender_pid)
+{
+       int ret = 0, status = 0, exec_status = 0;
+       gchar *client_delta_path = NULL;
+       pid_t pid;
+
+       ret = find_delta_and_prepare_script(sender_pid, &client_delta_path);
+       if (ret < 0) {
+               status = ret;
+               goto execute_destroy;
+       }
+
+       /* This shouldn't fail. Added to make compiler happy */
+       assert(client_delta_path);
+
        /* All basic checks succeeded, following is what happens next: We
         * double-fork() the process and run actual upgrade script in the child
         * process. We do this so that we can at least inform API client that
@@ -162,9 +176,12 @@ int fota_installer_execute(pid_t sender_pid)
                                exit(EXIT_SUCCESS);
                }
 
-               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path, NULL);
+               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path,
+                       FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE, NULL);
+
                if (ret == -1)
-                       _FLOGE("Failed to execl(%s %s)", FOTA_TRIGGER_PATH, client_delta_path);
+                       _FLOGE("Failed to execl(%s %s %s)", FOTA_TRIGGER_PATH, client_delta_path,
+                               FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE);
 
                exit(EXIT_FAILURE);
        }
@@ -184,3 +201,93 @@ execute_destroy:
 
        return status;
 }
+
+int fota_installer_ro_update(pid_t sender_pid)
+{
+       int ret = 0, status = 0, exec_status = 0;
+       gchar *client_delta_path = NULL;
+       pid_t pid;
+
+       ret = find_delta_and_prepare_script(sender_pid, &client_delta_path);
+       if (ret < 0) {
+               status = ret;
+               goto execute_destroy;
+       }
+
+       /* This shouldn't fail. Added to make compiler happy */
+       assert(client_delta_path);
+
+       /*
+       * This is supposed to bo called asynchronously, so we do
+       * care if and when the script finishes.
+       */
+       pid = fork();
+       if (pid < 0) {
+               _FLOGE("Failed to fork");
+               status = -1;
+               goto execute_destroy;
+       } else if (pid == 0) {
+               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path,
+                       FOTA_TRIGGER_MODE_RO_UPDATE, NULL);
+
+               if (ret == -1)
+                       _FLOGE("Failed to execl(%s %s %s)", FOTA_TRIGGER_PATH, client_delta_path,
+                               FOTA_TRIGGER_MODE_RO_UPDATE);
+
+               exit(EXIT_FAILURE);
+       }
+
+       ret = waitpid(pid, &exec_status, 0);
+       if (ret == -1) {
+               _FLOGE("Failed to wait child");
+               status = -1;
+               goto execute_destroy;
+       }
+       exec_status = WEXITSTATUS(exec_status);
+
+       status = exec_status > 0 ? -exec_status : exec_status;
+
+execute_destroy:
+       if (client_delta_path)
+               free(client_delta_path);
+
+       return status;
+}
+
+int fota_installer_finish_update(void)
+{
+       int ret = 0, exec_status = 0;
+       pid_t pid;
+
+       if (verify_checksum(FOTA_CHECKSUM_PATH, FOTA_TRIGGER_PATH) == 0) {
+               _FLOGI("Checksum of %s is correct", FOTA_TRIGGER_PATH);
+       } else {
+               _FLOGE("Failed checksum verification of %s", FOTA_TRIGGER_PATH);
+               return -1;
+       }
+       /*
+       * This is supposed to bo called asynchronously, so we do
+       * care if and when the script finishes.
+       */
+       pid = fork();
+       if (pid < 0) {
+               _FLOGE("Failed to fork");
+               return -1;
+       } else if (pid == 0) {
+               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, FOTA_TRIGGER_MODE_FINISH_UPDATE, NULL);
+
+               if (ret == -1)
+                       _FLOGE("Failed to execl(%s %s)", FOTA_TRIGGER_PATH, FOTA_TRIGGER_MODE_FINISH_UPDATE);
+
+               exit(EXIT_FAILURE);
+       }
+
+       ret = waitpid(pid, &exec_status, 0);
+       if (ret == -1) {
+               _FLOGE("Failed to wait child");
+               return -1;
+       }
+       exec_status = WEXITSTATUS(exec_status);
+
+       return exec_status > 0 ? -exec_status : exec_status;
+}
\ No newline at end of file
index 9f271e15ae9acaf74b7a36ae51723ab386190ba8..af36888a11ba43812d96aa1f911e5e057e67e6b8 100644 (file)
@@ -48,7 +48,9 @@ int fota_client_info_checker_init(void);
 int fota_client_info_checker_fini(void);
 char *fota_client_info_get_appid(void);
 
-int fota_installer_execute(pid_t pid);
+int fota_installer_ro_update_and_finish_update(pid_t sender_pid);
+int fota_installer_ro_update(pid_t sender_pid);
+int fota_installer_finish_update(void);
 
 void fota_storage_checker_plug(int, const char *);
 void fota_storage_checker_unplug(int, const char *);