From 49b63b604528ab119c132e91511e8db3d818bd97 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Thu, 16 Apr 2020 17:38:31 +0300
Subject: [PATCH] session-item: refactor export to use a process similar to
 activate

+ expose the export transition in the session item class
+ make the export-related flags immutable
+ add an export error flag
+ update and improve documentation
---
 lib/wp/endpoint-link.c            |   3 +-
 lib/wp/session-item.c             | 244 ++++++++++++++++--------------
 lib/wp/session-item.h             |  71 +++++----
 modules/module-si-adapter.c       |  30 ++--
 modules/module-si-standard-link.c |  14 +-
 tests/wp/session-item.c           |  30 ++--
 6 files changed, 213 insertions(+), 179 deletions(-)

diff --git a/lib/wp/endpoint-link.c b/lib/wp/endpoint-link.c
index 96d093b2..24046eca 100644
--- a/lib/wp/endpoint-link.c
+++ b/lib/wp/endpoint-link.c
@@ -383,7 +383,6 @@ impl_request_state (void *object, enum pw_endpoint_link_state state)
 
   switch (state) {
   case PW_ENDPOINT_LINK_STATE_ACTIVE:
-    wp_session_item_deactivate (WP_SESSION_ITEM (self->item));
     wp_session_item_activate (WP_SESSION_ITEM (self->item),
         (GAsyncReadyCallback) on_item_activated, self);
     break;
@@ -440,7 +439,7 @@ on_si_link_flags_changed (WpSiLink * item, WpSiFlags flags,
 {
   enum pw_endpoint_link_state old_state = self->info.state;
 
-  if (flags & WP_SI_FLAG_IN_ERROR)
+  if (flags & WP_SI_FLAG_EXPORT_ERROR)
     self->info.state = PW_ENDPOINT_LINK_STATE_ERROR;
   else if (flags & WP_SI_FLAG_ACTIVE)
     self->info.state = PW_ENDPOINT_LINK_STATE_ACTIVE;
diff --git a/lib/wp/session-item.c b/lib/wp/session-item.c
index 22a92649..ea8e6990 100644
--- a/lib/wp/session-item.c
+++ b/lib/wp/session-item.c
@@ -14,6 +14,7 @@
 #define G_LOG_DOMAIN "wp-si"
 
 #include "session-item.h"
+#include "debug.h"
 #include "private.h"
 #include "error.h"
 #include "wpenums.h"
@@ -169,7 +170,7 @@ wp_session_item_default_get_associated_proxy (WpSessionItem * self,
 }
 
 static guint
-wp_session_item_default_get_next_step (WpSessionItem * self,
+wp_session_item_default_activate_get_next_step (WpSessionItem * self,
     WpTransition * transition, guint step)
 {
   /* the default implementation just activates instantly,
@@ -181,32 +182,25 @@ enum {
   EXPORT_STEP_ENDPOINT = WP_TRANSITION_STEP_CUSTOM_START,
   EXPORT_STEP_STREAMS,
   EXPORT_STEP_LINK,
-  EXPORT_STEP_FINISH,
 };
 
 static guint
-default_export_get_next_step (WpSessionItem * self, WpTransition * transition,
-    guint step)
+wp_session_item_default_export_get_next_step (WpSessionItem * self,
+    WpTransition * transition, guint step)
 {
   WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
 
   switch (step) {
   case WP_TRANSITION_STEP_NONE:
-    if (WP_IS_SI_ENDPOINT (self)) {
-      priv->flags |= WP_SI_FLAG_EXPORTING;
-      g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
+    if (WP_IS_SI_ENDPOINT (self))
       return EXPORT_STEP_ENDPOINT;
-    }
-    else if (WP_IS_SI_LINK (self)) {
-      priv->flags |= WP_SI_FLAG_EXPORTING;
-      g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
+    else if (WP_IS_SI_LINK (self))
       return EXPORT_STEP_LINK;
-    }
     else {
       wp_transition_return_error (transition, g_error_new (
               WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
-              "Cannot export WpSessionItem of unknown type (%s:%p)",
-              G_OBJECT_TYPE_NAME (self), self));
+              "Cannot export WpSessionItem of unknown type " WP_OBJECT_FORMAT,
+              WP_OBJECT_ARGS (self)));
       return WP_TRANSITION_STEP_ERROR;
     }
 
@@ -221,15 +215,12 @@ default_export_get_next_step (WpSessionItem * self, WpTransition * transition,
     /* go to next step only when all impl proxies are augmented */
     if (g_hash_table_size (priv->impl_streams) ==
         wp_si_endpoint_get_n_streams (WP_SI_ENDPOINT (self)))
-      return EXPORT_STEP_FINISH;
+      return WP_TRANSITION_STEP_NONE;
     else
       return step;
 
   case EXPORT_STEP_LINK:
     g_return_val_if_fail (WP_IS_SI_LINK (self), WP_TRANSITION_STEP_ERROR);
-    return EXPORT_STEP_FINISH;
-
-  case EXPORT_STEP_FINISH:
     return WP_TRANSITION_STEP_NONE;
 
   default:
@@ -263,8 +254,8 @@ on_export_proxy_augmented (WpProxy * proxy, GAsyncResult * res, gpointer data)
 }
 
 static void
-default_export_execute_step (WpSessionItem * self, WpTransition * transition,
-    guint step)
+wp_session_item_default_export_execute_step (WpSessionItem * self,
+    WpTransition * transition, guint step)
 {
   WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
   g_autoptr (WpSession) session = g_weak_ref_get (&priv->session);
@@ -312,67 +303,18 @@ default_export_execute_step (WpSessionItem * self, WpTransition * transition,
         transition);
     break;
 
-  case EXPORT_STEP_FINISH:
-    priv->flags &= ~WP_SI_FLAG_EXPORTING;
-    priv->flags |= WP_SI_FLAG_EXPORTED;
-    g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
-    wp_transition_advance (transition);
-    break;
-
   default:
     g_return_if_reached ();
   }
 }
+
 static void
-default_export_rollback (WpSessionItem * self)
+wp_session_item_default_export_rollback (WpSessionItem * self)
 {
   WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
-  static const guint flags = (WP_SI_FLAG_EXPORTING | WP_SI_FLAG_EXPORTED);
-
   g_clear_pointer (&priv->impl_streams, g_hash_table_unref);
   g_clear_object (&priv->impl_proxy);
   g_weak_ref_set (&priv->session, NULL);
-
-  if (priv->flags & flags) {
-    priv->flags &= ~flags;
-    g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
-  }
-}
-
-static void
-wp_session_item_default_export (WpSessionItem * self,
-      WpSession * session, GCancellable * cancellable,
-      GAsyncReadyCallback callback, gpointer callback_data)
-{
-  WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
-  WpTransition *transition;
-
-  g_weak_ref_set (&priv->session, session);
-
-  transition = wp_transition_new (wp_si_transition_get_type (),
-      self, cancellable, callback, callback_data);
-  wp_transition_set_source_tag (transition, wp_session_item_default_export);
-
-  WP_SI_TRANSITION (transition)->get_next_step = default_export_get_next_step;
-  WP_SI_TRANSITION (transition)->execute_step = default_export_execute_step;
-  WP_SI_TRANSITION (transition)->rollback = default_export_rollback;
-  wp_transition_advance (transition);
-}
-
-static gboolean
-wp_session_item_default_export_finish (WpSessionItem * self,
-    GAsyncResult * res, GError ** error)
-{
-  g_return_val_if_fail (
-      g_async_result_is_tagged (res, wp_session_item_default_export), FALSE);
-
-  return wp_transition_finish (res, error);
-}
-
-static void
-wp_session_item_default_unexport (WpSessionItem * self)
-{
-  default_export_rollback (self);
 }
 
 static void
@@ -385,10 +327,10 @@ wp_session_item_class_init (WpSessionItemClass * klass)
 
   klass->reset = wp_session_item_default_reset;
   klass->get_associated_proxy = wp_session_item_default_get_associated_proxy;
-  klass->get_next_step = wp_session_item_default_get_next_step;
-  klass->export = wp_session_item_default_export;
-  klass->export_finish = wp_session_item_default_export_finish;
-  klass->unexport = wp_session_item_default_unexport;
+  klass->activate_get_next_step = wp_session_item_default_activate_get_next_step;
+  klass->export_get_next_step = wp_session_item_default_export_get_next_step;
+  klass->export_execute_step = wp_session_item_default_export_execute_step;
+  klass->export_rollback = wp_session_item_default_export_rollback;
 
   /**
    * WpSessionItem::flags-changed:
@@ -574,17 +516,14 @@ wp_session_item_get_configuration (WpSessionItem * self)
 }
 
 static void
-on_transition_completed (WpTransition * transition, GParamSpec * pspec,
+on_activate_transition_completed (WpTransition * transition, GParamSpec * pspec,
     WpSessionItem * self)
 {
   WpSessionItemPrivate *priv =
       wp_session_item_get_instance_private (self);
 
-  if (wp_transition_had_error (transition))
-    priv->flags |= WP_SI_FLAG_IN_ERROR;
-  else
-    priv->flags |= WP_SI_FLAG_ACTIVE;
-
+  priv->flags |= wp_transition_had_error (transition) ?
+      WP_SI_FLAG_ACTIVATE_ERROR : WP_SI_FLAG_ACTIVE;
   priv->flags &= ~WP_SI_FLAG_ACTIVATING;
   g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
 }
@@ -595,15 +534,30 @@ on_transition_completed (WpTransition * transition, GParamSpec * pspec,
  * @callback: (scope async): a callback to call when activation is finished
  * @callback_data: (closure): data passed to @callback
  *
- * Activates the item asynchronously. This internally starts a #WpTransition
- * that calls into #WpSessionItemClass.get_next_step() and
- * #WpSessionItemClass.execute_step() to advance.
- *
- * You can use wp_session_item_activate_finish() in the @callback to figure out
+ * Activates the item asynchronously.
+ * You can use wp_session_item_activate_finish() in the @callback to get
  * the result of this operation.
  *
- * Normally this function is called internally by the session; there is no need
- * to activate an item externally, except for unit testing purposes.
+ * This internally starts a #WpTransition that calls into
+ * #WpSessionItemClass.activate_get_next_step() and
+ * #WpSessionItemClass.activate_execute_step() to advance.
+ * If the transition fails, #WpSessionItemClass.activate_rollback() is called
+ * to reverse previous actions.
+ *
+ * The default implementation of the above virtual functions activates the
+ * item successfully without doing anything. In order to implement a meaningful
+ * session item, you should override all 3 of them.
+ *
+ * When this method is called, the %WP_SI_FLAG_ACTIVATING flag is set. When
+ * the operation finishes successfully, that flag is cleared and replaced with
+ * either %WP_SI_FLAG_ACTIVE or %WP_SI_FLAG_ACTIVATE_ERROR, depending on the
+ * success outcome of the operation. In order to clear
+ * %WP_SI_FLAG_ACTIVATE_ERROR, you can either call wp_session_item_deactivate()
+ * or wp_session_item_activate() to try activating again.
+ *
+ * This method cannot be called if another operation (activation or export) is
+ * in progress (%WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS) or if the item is
+ * already activated.
  */
 void
 wp_session_item_activate (WpSessionItem * self,
@@ -615,24 +569,26 @@ wp_session_item_activate (WpSessionItem * self,
   WpSessionItemPrivate *priv =
       wp_session_item_get_instance_private (self);
 
-  g_return_if_fail (!(priv->flags & (WP_SI_FLAG_ACTIVATING | WP_SI_FLAG_ACTIVE)));
+  g_return_if_fail (!(priv->flags &
+      (WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS | WP_SI_FLAG_ACTIVE)));
 
-  /* TODO: add a way to cancel the transition if reset() is called in the meantime */
+  /* TODO: add a way to cancel the transition if deactivate() is called in the meantime */
   WpTransition *transition = wp_transition_new (wp_si_transition_get_type (),
       self, NULL, callback, callback_data);
   wp_transition_set_source_tag (transition, wp_session_item_activate);
   g_signal_connect (transition, "notify::completed",
-      (GCallback) on_transition_completed, self);
+      (GCallback) on_activate_transition_completed, self);
 
+  priv->flags &= ~WP_SI_FLAG_ACTIVATE_ERROR;
   priv->flags |= WP_SI_FLAG_ACTIVATING;
   g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
 
   WP_SI_TRANSITION (transition)->get_next_step =
-      WP_SESSION_ITEM_GET_CLASS (self)->get_next_step;
+      WP_SESSION_ITEM_GET_CLASS (self)->activate_get_next_step;
   WP_SI_TRANSITION (transition)->execute_step =
-      WP_SESSION_ITEM_GET_CLASS (self)->execute_step;
+      WP_SESSION_ITEM_GET_CLASS (self)->activate_execute_step;
   WP_SI_TRANSITION (transition)->rollback =
-      WP_SESSION_ITEM_GET_CLASS (self)->rollback;
+      WP_SESSION_ITEM_GET_CLASS (self)->activate_rollback;
   wp_transition_advance (transition);
 }
 
@@ -648,6 +604,7 @@ gboolean
 wp_session_item_activate_finish (WpSessionItem * self, GAsyncResult * res,
     GError ** error)
 {
+  g_return_val_if_fail (WP_IS_SESSION_ITEM (self), FALSE);
   g_return_val_if_fail (
       g_async_result_is_tagged (res, wp_session_item_activate), FALSE);
   return wp_transition_finish (res, error);
@@ -667,14 +624,13 @@ wp_session_item_deactivate (WpSessionItem * self)
   g_return_if_fail (WP_IS_SESSION_ITEM (self));
 
   WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
-  static const guint flags =
-      (WP_SI_FLAG_ACTIVATING | WP_SI_FLAG_ACTIVE | WP_SI_FLAG_IN_ERROR);
+  static const guint flags = 0xf; /* all activation flags */
 
   //TODO cancel job if ACTIVATING
 
   if (priv->flags & WP_SI_FLAG_ACTIVE &&
-      WP_SESSION_ITEM_GET_CLASS (self)->rollback)
-    WP_SESSION_ITEM_GET_CLASS (self)->rollback (self);
+      WP_SESSION_ITEM_GET_CLASS (self)->activate_rollback)
+    WP_SESSION_ITEM_GET_CLASS (self)->activate_rollback (self);
 
   if (priv->flags & flags) {
     priv->flags &= ~flags;
@@ -682,19 +638,52 @@ wp_session_item_deactivate (WpSessionItem * self)
   }
 }
 
+static void
+on_export_transition_completed (WpTransition * transition, GParamSpec * pspec,
+    WpSessionItem * self)
+{
+  WpSessionItemPrivate *priv =
+      wp_session_item_get_instance_private (self);
+
+  priv->flags |= wp_transition_had_error (transition) ?
+      WP_SI_FLAG_EXPORT_ERROR : WP_SI_FLAG_EXPORTED;
+  priv->flags &= ~WP_SI_FLAG_EXPORTING;
+  g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
+}
+
 /**
- * wp_session_item_export: (virtual export)
+ * wp_session_item_export:
  * @self: the session item
  * @session: the session on which to export this item
  * @callback: (scope async): a callback to call when exporting is finished
  * @callback_data: (closure): data passed to @callback
  *
  * Exports this item asynchronously on PipeWire, making it part of the
- * specified @session.
+ * specified @session. You can use wp_session_item_export_finish() in the
+ * @callback to get the result of this operation.
+ *
+ * This internally starts a #WpTransition that calls into
+ * #WpSessionItemClass.export_get_next_step() and
+ * #WpSessionItemClass.export_execute_step() to advance.
+ * If the transition fails, #WpSessionItemClass.export_rollback() is called
+ * to reverse previous actions.
+ *
+ * Exporting is internally implemented for endpoints (items that implement
+ * #WpSiEndpoint) and endpoint links (items that implement #WpSiLink). On other
+ * items the default implementation will immediately call the @callback,
+ * reporting error. You can extend this to export custom interfaces by
+ * overriding the virtual functions mentioned above.
+ *
+ * When this method is called, the %WP_SI_FLAG_EXPORTING flag is set. When
+ * the operation finishes successfully, that flag is cleared and replaced with
+ * either %WP_SI_FLAG_EXPORTED or %WP_SI_FLAG_EXPORT_ERROR, depending on the
+ * success outcome of the operation. In order to clear
+ * %WP_SI_FLAG_EXPORT_ERROR, you can either call wp_session_item_unexport()
+ * or wp_session_item_export() to try exporting again.
  *
- * Exporting only makes sense for endpoints (items that implement #WpSiEndpoint)
- * and endpoint links (items that implement #WpSiLink). On other items the
- * default implementation will immediately call the @callback, reporting error.
+ * This method cannot be called if another operation (activation or export) is
+ * in progress (%WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS) or if the item is
+ * already exported.
  */
 void
 wp_session_item_export (WpSessionItem * self, WpSession * session,
@@ -702,19 +691,37 @@ wp_session_item_export (WpSessionItem * self, WpSession * session,
 {
   g_return_if_fail (WP_IS_SESSION_ITEM (self));
   g_return_if_fail (WP_IS_SESSION (session));
-  g_return_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->export);
 
   WpSessionItemPrivate *priv =
       wp_session_item_get_instance_private (self);
 
-  g_return_if_fail (!(priv->flags & (WP_SI_FLAG_EXPORTING | WP_SI_FLAG_EXPORTED)));
+  g_return_if_fail (!(priv->flags &
+      (WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS | WP_SI_FLAG_EXPORTED)));
 
-  WP_SESSION_ITEM_GET_CLASS (self)->export (self, session, NULL,
-      callback, callback_data);
+  g_weak_ref_set (&priv->session, session);
+
+  /* TODO: add a way to cancel the transition if unexport() is called in the meantime */
+  WpTransition *transition = wp_transition_new (wp_si_transition_get_type (),
+      self, NULL, callback, callback_data);
+  wp_transition_set_source_tag (transition, wp_session_item_export);
+  g_signal_connect (transition, "notify::completed",
+      (GCallback) on_export_transition_completed, self);
+
+  priv->flags &= ~WP_SI_FLAG_EXPORT_ERROR;
+  priv->flags |= WP_SI_FLAG_EXPORTING;
+  g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
+
+  WP_SI_TRANSITION (transition)->get_next_step =
+      WP_SESSION_ITEM_GET_CLASS (self)->export_get_next_step;
+  WP_SI_TRANSITION (transition)->execute_step =
+      WP_SESSION_ITEM_GET_CLASS (self)->export_execute_step;
+  WP_SI_TRANSITION (transition)->rollback =
+      WP_SESSION_ITEM_GET_CLASS (self)->export_rollback;
+  wp_transition_advance (transition);
 }
 
 /**
- * wp_session_item_export_finish: (virtual export_finish)
+ * wp_session_item_export_finish:
  * @self: the session item
  * @res: the async operation result
  * @error: (out) (optional): the error of the operation, if any
@@ -726,13 +733,13 @@ wp_session_item_export_finish (WpSessionItem * self, GAsyncResult * res,
     GError ** error)
 {
   g_return_val_if_fail (WP_IS_SESSION_ITEM (self), FALSE);
-  g_return_val_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->export_finish, FALSE);
-
-  return WP_SESSION_ITEM_GET_CLASS (self)->export_finish (self, res, error);
+  g_return_val_if_fail (
+      g_async_result_is_tagged (res, wp_session_item_export), FALSE);
+  return wp_transition_finish (res, error);
 }
 
 /**
- * wp_session_item_unexport: (virtual unexport)
+ * wp_session_item_unexport:
  * @self: the session item
  *
  * Reverses the effects of a previous call to wp_session_item_export().
@@ -747,9 +754,18 @@ void
 wp_session_item_unexport (WpSessionItem * self)
 {
   g_return_if_fail (WP_IS_SESSION_ITEM (self));
-  g_return_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->unexport);
+
+  WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
+  static const guint flags = 0xf0; /* all export flags */
 
   //TODO cancel job if EXPORTING
 
-  WP_SESSION_ITEM_GET_CLASS (self)->unexport (self);
+  if (priv->flags & WP_SI_FLAG_EXPORTED &&
+      WP_SESSION_ITEM_GET_CLASS (self)->export_rollback)
+    WP_SESSION_ITEM_GET_CLASS (self)->export_rollback (self);
+
+  if (priv->flags & flags) {
+    priv->flags &= ~flags;
+    g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
+  }
 }
diff --git a/lib/wp/session-item.h b/lib/wp/session-item.h
index e2617cf1..0f8f45dd 100644
--- a/lib/wp/session-item.h
+++ b/lib/wp/session-item.h
@@ -28,30 +28,42 @@ G_DECLARE_DERIVABLE_TYPE (WpSessionItem, wp_session_item,
  * WpSiFlags:
  * @WP_SI_FLAG_ACTIVATING: set when an activation transition is in progress
  * @WP_SI_FLAG_ACTIVE: set when an activation transition completes successfully
- * @WP_SI_FLAG_IN_ERROR: set when there was an error in the activation process;
- *   to recover, the handler must call wp_session_item_reset() before anything
- *   else
- * @WP_SI_FLAG_CONFIGURED: must be set by subclasses when all the required
- *   (%WP_SI_CONFIG_OPTION_REQUIRED) configuration options have been set
+ * @WP_SI_FLAG_ACTIVATE_ERROR: set when there was an error in the activation
+ *   process; to clear, call wp_session_item_deactivate()
  * @WP_SI_FLAG_EXPORTING: set when an export operation is in progress
  * @WP_SI_FLAG_EXPORTED: set when the item has exported all necessary objects
  *   to PipeWire
+ * @WP_SI_FLAG_EXPORT_ERROR: set when there was an error in the export
+ *   process; to clear, call wp_session_item_unexport()
+ * @WP_SI_FLAG_CONFIGURED: must be set by subclasses when all the required
+ *   (%WP_SI_CONFIG_OPTION_REQUIRED) configuration options have been set
  */
 typedef enum {
   /* immutable flags, set internally */
   WP_SI_FLAG_ACTIVATING = (1<<0),
   WP_SI_FLAG_ACTIVE = (1<<1),
-  WP_SI_FLAG_IN_ERROR = (1<<4),
+  WP_SI_FLAG_ACTIVATE_ERROR = (1<<2),
+
+  WP_SI_FLAG_EXPORTING = (1<<4),
+  WP_SI_FLAG_EXPORTED = (1<<5),
+  WP_SI_FLAG_EXPORT_ERROR = (1<<6),
 
   /* flags that can be changed by subclasses */
   WP_SI_FLAG_CONFIGURED = (1<<8),
-  WP_SI_FLAG_EXPORTING = (1<<9),
-  WP_SI_FLAG_EXPORTED = (1<<10),
 
   /* implementation-specific flags */
   WP_SI_FLAG_CUSTOM_START = (1<<16),
 } WpSiFlags;
 
+/**
+ * WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS:
+ *
+ * A #WpSiFlags mask that can be used to test if an async operation
+ * (activate or export) is currently in progress.
+ */
+#define WP_SI_FLAGS_MASK_OPERATION_IN_PROGRESS \
+    (WP_SI_FLAG_ACTIVATING | WP_SI_FLAG_EXPORTING)
+
 /**
  * WpSiConfigOptionFlags:
  * @WP_SI_CONFIG_OPTION_WRITEABLE: the option can be set externally
@@ -68,14 +80,18 @@ typedef enum {
  * @get_associated_proxy: See wp_session_item_get_associated_proxy()
  * @configure: See wp_session_item_configure()
  * @get_configuration: See wp_session_item_get_configuration()
- * @get_next_step: Implements #WpTransitionClass.get_next_step() for the
- *   transition of wp_session_item_activate()
- * @execute_step: Implements #WpTransitionClass.execute_step() for the
- *   transition of wp_session_item_activate()
- * @rollback:
- * @export: See wp_session_item_export()
- * @export_finish: See wp_session_item_export_finish()
- * @unexport: See wp_session_item_unexport()
+ * @activate_get_next_step: Implements #WpTransitionClass.get_next_step()
+ *   for the transition of wp_session_item_activate()
+ * @activate_execute_step: Implements #WpTransitionClass.execute_step()
+ *   for the transition of wp_session_item_activate()
+ * @activate_rollback: Reverses any effects of the activation process;
+ *   see wp_session_item_activate()
+ * @export_get_next_step: Implements #WpTransitionClass.get_next_step()
+ *   for the transition of wp_session_item_export()
+ * @export_execute_step: Implements #WpTransitionClass.execute_step()
+ *   for the transition of wp_session_item_export()
+ * @export_rollback: Reverses any effects of the export process;
+ *   see wp_session_item_export()
  */
 struct _WpSessionItemClass
 {
@@ -88,18 +104,17 @@ struct _WpSessionItemClass
   gboolean (*configure) (WpSessionItem * self, GVariant * args);
   GVariant * (*get_configuration) (WpSessionItem * self);
 
-  guint (*get_next_step) (WpSessionItem * self, WpTransition * transition,
-      guint step);
-  void (*execute_step) (WpSessionItem * self, WpTransition * transition,
-      guint step);
-  void (*rollback) (WpSessionItem * self);
-
-  void (*export) (WpSessionItem * self,
-      WpSession * session, GCancellable * cancellable,
-      GAsyncReadyCallback callback, gpointer callback_data);
-  gboolean (*export_finish) (WpSessionItem * self, GAsyncResult * res,
-      GError ** error);
-  void (*unexport) (WpSessionItem * self);
+  guint (*activate_get_next_step) (WpSessionItem * self,
+      WpTransition * transition, guint step);
+  void (*activate_execute_step) (WpSessionItem * self,
+      WpTransition * transition, guint step);
+  void (*activate_rollback) (WpSessionItem * self);
+
+  guint (*export_get_next_step) (WpSessionItem * self,
+      WpTransition * transition, guint step);
+  void (*export_execute_step) (WpSessionItem * self,
+      WpTransition * transition, guint step);
+  void (*export_rollback) (WpSessionItem * self);
 };
 
 WP_API
diff --git a/modules/module-si-adapter.c b/modules/module-si-adapter.c
index cd65e1da..0cd8d5b0 100644
--- a/modules/module-si-adapter.c
+++ b/modules/module-si-adapter.c
@@ -69,15 +69,6 @@ si_adapter_reset (WpSessionItem * item)
   g_clear_object (&self->node);
 }
 
-static void
-si_adapter_rollback (WpSessionItem * item)
-{
-  WpSiAdapter *self = WP_SI_ADAPTER (item);
-
-  g_clear_object (&self->ports_om);
-  wp_session_item_clear_flag (item, WP_SI_FLAG_CONFIGURED);
-}
-
 static gpointer
 si_adapter_get_associated_proxy (WpSessionItem * item, GType proxy_type)
 {
@@ -186,7 +177,7 @@ si_adapter_configure (WpSessionItem * item, GVariant * args)
 }
 
 static guint
-si_adapter_get_next_step (WpSessionItem * item,
+si_adapter_activate_get_next_step (WpSessionItem * item,
      WpTransition * transition, guint step)
 {
   switch (step) {
@@ -270,8 +261,8 @@ on_ports_changed (WpObjectManager *om, WpTransition * transition)
 }
 
 static void
-si_adapter_execute_step (WpSessionItem * item, WpTransition * transition,
-    guint step)
+si_adapter_activate_execute_step (WpSessionItem * item,
+    WpTransition * transition, guint step)
 {
   WpSiAdapter *self = WP_SI_ADAPTER (item);
 
@@ -356,6 +347,15 @@ si_adapter_execute_step (WpSessionItem * item, WpTransition * transition,
   }
 }
 
+static void
+si_adapter_activate_rollback (WpSessionItem * item)
+{
+  WpSiAdapter *self = WP_SI_ADAPTER (item);
+
+  g_clear_object (&self->ports_om);
+  wp_session_item_clear_flag (item, WP_SI_FLAG_CONFIGURED);
+}
+
 static void
 si_adapter_class_init (WpSiAdapterClass * klass)
 {
@@ -365,9 +365,9 @@ si_adapter_class_init (WpSiAdapterClass * klass)
   si_class->get_associated_proxy = si_adapter_get_associated_proxy;
   si_class->configure = si_adapter_configure;
   si_class->get_configuration = si_adapter_get_configuration;
-  si_class->get_next_step = si_adapter_get_next_step;
-  si_class->execute_step = si_adapter_execute_step;
-  si_class->rollback = si_adapter_rollback;
+  si_class->activate_get_next_step = si_adapter_activate_get_next_step;
+  si_class->activate_execute_step = si_adapter_activate_execute_step;
+  si_class->activate_rollback = si_adapter_activate_rollback;
 }
 
 static guint
diff --git a/modules/module-si-standard-link.c b/modules/module-si-standard-link.c
index 0e5249bb..a1531ce2 100644
--- a/modules/module-si-standard-link.c
+++ b/modules/module-si-standard-link.c
@@ -143,7 +143,7 @@ si_standard_link_configure (WpSessionItem * item, GVariant * args)
 }
 
 static guint
-si_standard_link_get_next_step (WpSessionItem * item,
+si_standard_link_activate_get_next_step (WpSessionItem * item,
      WpTransition * transition, guint step)
 {
   WpSiStandardLink *self = wp_transition_get_source_object (transition);
@@ -320,8 +320,8 @@ create_links (WpSiStandardLink * self, GVariant * out_ports, GVariant * in_ports
 }
 
 static void
-si_standard_link_execute_step (WpSessionItem * item, WpTransition * transition,
-    guint step)
+si_standard_link_activate_execute_step (WpSessionItem * item,
+    WpTransition * transition, guint step)
 {
   WpSiStandardLink *self = WP_SI_STANDARD_LINK (item);
 
@@ -379,7 +379,7 @@ si_standard_link_execute_step (WpSessionItem * item, WpTransition * transition,
 }
 
 static void
-si_standard_link_rollback (WpSessionItem * item)
+si_standard_link_activate_rollback (WpSessionItem * item)
 {
   WpSiStandardLink *self = WP_SI_STANDARD_LINK (item);
   WpSiEndpoint *out_endpoint, *in_endpoint;
@@ -410,9 +410,9 @@ si_standard_link_class_init (WpSiStandardLinkClass * klass)
   si_class->reset = si_standard_link_reset;
   si_class->configure = si_standard_link_configure;
   si_class->get_configuration = si_standard_link_get_configuration;
-  si_class->get_next_step = si_standard_link_get_next_step;
-  si_class->execute_step = si_standard_link_execute_step;
-  si_class->rollback = si_standard_link_rollback;
+  si_class->activate_get_next_step = si_standard_link_activate_get_next_step;
+  si_class->activate_execute_step = si_standard_link_activate_execute_step;
+  si_class->activate_rollback = si_standard_link_activate_rollback;
 }
 
 static GVariant *
diff --git a/tests/wp/session-item.c b/tests/wp/session-item.c
index 7c2208d2..bcc638c1 100644
--- a/tests/wp/session-item.c
+++ b/tests/wp/session-item.c
@@ -58,7 +58,7 @@ si_dummy_configure (WpSessionItem * item, GVariant * args)
 }
 
 static guint
-si_dummy_get_next_step (WpSessionItem * item,
+si_dummy_activate_get_next_step (WpSessionItem * item,
      WpTransition * transition, guint step)
 {
   switch (step) {
@@ -94,7 +94,7 @@ si_dummy_step_1 (gpointer data)
 }
 
 static void
-si_dummy_execute_step (WpSessionItem * item, WpTransition * transition,
+si_dummy_activate_execute_step (WpSessionItem * item, WpTransition * transition,
     guint step)
 {
   TestSiDummy *self = TEST_SI_DUMMY (item);
@@ -117,7 +117,7 @@ si_dummy_execute_step (WpSessionItem * item, WpTransition * transition,
 }
 
 static void
-si_dummy_rollback (WpSessionItem * item)
+si_dummy_activate_rollback (WpSessionItem * item)
 {
   TestSiDummy *self = TEST_SI_DUMMY (item);
 
@@ -133,9 +133,9 @@ si_dummy_class_init (TestSiDummyClass * klass)
 
   si_class->configure = si_dummy_configure;
   si_class->get_configuration = si_dummy_get_configuration;
-  si_class->get_next_step = si_dummy_get_next_step;
-  si_class->execute_step = si_dummy_execute_step;
-  si_class->rollback = si_dummy_rollback;
+  si_class->activate_get_next_step = si_dummy_activate_get_next_step;
+  si_class->activate_execute_step = si_dummy_activate_execute_step;
+  si_class->activate_rollback = si_dummy_activate_rollback;
 }
 
 static void
@@ -159,11 +159,13 @@ test_flags (void)
   g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CUSTOM_START);
   g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_CUSTOM_START);
 
-  /* internal flag, cannot be set */
-  signalled_flags = 0;
-  wp_session_item_set_flag (item, WP_SI_FLAG_ACTIVATING);
-  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CUSTOM_START);
-  g_assert_cmpint (signalled_flags, ==, 0);
+  /* internal flags cannot be set */
+  for (gint i = 0; i < 8; i++) {
+    signalled_flags = 0;
+    wp_session_item_set_flag (item, 1 << i);
+    g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CUSTOM_START);
+    g_assert_cmpint (signalled_flags, ==, 0);
+  }
 
   signalled_flags = WP_SI_FLAG_CUSTOM_START;
   wp_session_item_clear_flag (item, WP_SI_FLAG_CUSTOM_START);
@@ -297,13 +299,15 @@ test_activation_error (void)
   g_main_loop_run (loop);
 
   g_assert_cmpint (wp_session_item_get_flags (item), ==,
-      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_IN_ERROR);
+      WP_SI_FLAG_ACTIVATE_ERROR | WP_SI_FLAG_CONFIGURED);
   g_assert_cmpint (signalled_flags, ==,
-      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_IN_ERROR);
+      WP_SI_FLAG_ACTIVATE_ERROR | WP_SI_FLAG_CONFIGURED);
   g_assert_false (dummy->step_1_done);
   g_assert_false (dummy->step_2_done);
   g_assert_true (dummy->cleaned_up);
 
+  /* deactivate should not call activate_rollback,
+     it should only clear the error flag */
   dummy->cleaned_up = FALSE;
   wp_session_item_deactivate (item);
 
-- 
GitLab