diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index fcca53abe99b300fe09d6642777f7b95117789de..2ef5ab00df4484362bb0341c7e810fcec48c7c12 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -1095,9 +1095,22 @@ add_library(FLEXRAN_AGENT
   ${OPENAIR2_DIR}/ENB_APP/flexran_agent_async.c
   ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c
   ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_slice_verification.c
+  ${OPENAIR2_DIR}/ENB_APP/flexran_agent_app.c
   )
 add_dependencies(FLEXRAN_AGENT rrc_flag)
 set(FLEXRAN_AGENT_LIB FLEXRAN_AGENT)
+add_library(flapp_sample SHARED
+  ${OPENAIR2_DIR}/ENB_APP/flexran_apps/sample.c
+)
+set_target_properties(flapp_sample PROPERTIES C_VISIBILITY_PRESET hidden)
+add_library(flapp_imsi SHARED
+  ${OPENAIR2_DIR}/ENB_APP/flexran_apps/imsi.c
+)
+set_target_properties(flapp_imsi PROPERTIES C_VISIBILITY_PRESET hidden)
+add_custom_target(flapp_all DEPENDS
+  flapp_sample
+  flapp_imsi
+)
 #include_directories(${OPENAIR2_DIR}/ENB_APP)
 
 set(PROTOBUF_LIB "protobuf-c")
diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c
index f1daacbb650225f681330339e8870603d8a73b2d..7ca466e8ffc654db733ee4e3b016d5b13b3df5c0 100644
--- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c
+++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c
@@ -40,6 +40,8 @@
 
 #include "common/utils/LOG/log.h"
 
+#include <dlfcn.h>
+
 /*Array containing the Agent-MAC interfaces*/
 AGENT_MAC_xface *agent_mac_xface[NUM_MAX_ENB];
 
@@ -53,10 +55,20 @@ struct lfds700_ringbuffer_state ringbuffer_state[NUM_MAX_ENB];
 struct lfds700_ringbuffer_element *slice_config_array[NUM_MAX_ENB];
 struct lfds700_ringbuffer_state slice_config_ringbuffer_state[NUM_MAX_ENB];
 
+/* Ringbuffer related to slice configuration if we need to wait for additional
+ * controller information */
+struct lfds700_ringbuffer_element *store_slice_config_array[NUM_MAX_ENB];
+struct lfds700_ringbuffer_state store_slice_config_ringbuffer_state[NUM_MAX_ENB];
+/* stores the (increasing) xid for messages to prevent double use */
+int xid = 50;
+
 /* Ringbuffer-related to slice configuration */
 struct lfds700_ringbuffer_element *ue_assoc_array[NUM_MAX_ENB];
 struct lfds700_ringbuffer_state ue_assoc_ringbuffer_state[NUM_MAX_ENB];
 
+/* a list of shared objects that are loaded into the user plane */
+SLIST_HEAD(flexran_so_handle, flexran_agent_so_handle_s) flexran_handles[NUM_MAX_ENB];
+
 int flexran_agent_mac_stats_reply_ue(mid_t mod_id,
                                     Protocol__FlexUeStatsReport **ue_report,
                                     int      n_ue,
@@ -1363,6 +1375,19 @@ void flexran_agent_init_mac_agent(mid_t mod_id) {
       &scrng,
       NULL);
 
+  struct lfds700_misc_prng_state sscrng;
+  i = posix_memalign((void **) &store_slice_config_array[mod_id],
+                     LFDS700_PAL_ATOMIC_ISOLATION_IN_BYTES,
+                     sizeof(struct lfds700_ringbuffer_element) * num_elements);
+  AssertFatal(i == 0,
+              "posix_memalign(): could not allocate aligned memory\n");
+  lfds700_ringbuffer_init_valid_on_current_logical_core(
+      &store_slice_config_ringbuffer_state[mod_id],
+      store_slice_config_array[mod_id],
+      num_elements,
+      &sscrng,
+      NULL);
+
   struct lfds700_misc_prng_state uarng;
   i = posix_memalign((void **) &ue_assoc_array[mod_id],
                      LFDS700_PAL_ATOMIC_ISOLATION_IN_BYTES,
@@ -1382,6 +1407,8 @@ void flexran_agent_init_mac_agent(mid_t mod_id) {
         RC.mac[mod_id]->UE_info.eNB_UE_stats[UE_PCCID(mod_id,i)][i].harq_pid = 0;
     }
   }
+
+  SLIST_INIT(&flexran_handles[mod_id]);
 }
 
 /***********************************************
@@ -1659,6 +1686,8 @@ int flexran_agent_unregister_mac_xface(mid_t mod_id)
   free(dl_mac_config_array[mod_id]);
   lfds700_ringbuffer_cleanup(&slice_config_ringbuffer_state[mod_id], NULL );
   free(slice_config_array[mod_id]);
+  lfds700_ringbuffer_cleanup(&store_slice_config_ringbuffer_state[mod_id], NULL );
+  free(store_slice_config_array[mod_id]);
   lfds700_ringbuffer_cleanup(&ue_assoc_ringbuffer_state[mod_id], NULL );
   free(ue_assoc_array[mod_id]);
   lfds700_misc_library_cleanup();
@@ -1698,7 +1727,81 @@ void helper_destroy_mac_slice_config(Protocol__FlexSliceConfig *slice_config) {
   flexran_agent_destroy_mac_slice_config(&helper);
 }
 
-void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice_config) {
+int check_scheduler(mid_t mod_id, char *s) {
+  if (!s)
+    return 1;
+  if (dlsym(NULL, s))
+    return 1;
+  flexran_agent_so_handle_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_handles[mod_id], entries) {
+    if (strcmp(so->name, s) == 0)
+      return 1;
+  }
+  return 0;
+}
+
+void request_scheduler(mid_t mod_id, char *s, int xid) {
+  LOG_W(FLEXRAN_AGENT,
+        "unknown scheduler %s, requesting controller to send it\n",
+        s);
+  Protocol__FlexControlDelegationRequest *req = malloc(sizeof(Protocol__FlexControlDelegationRequest));
+  DevAssert(req);
+  protocol__flex_control_delegation_request__init(req);
+  Protocol__FlexHeader *header = NULL;
+  int rc = flexran_create_header(
+      xid, PROTOCOL__FLEX_TYPE__FLPT_DELEGATE_REQUEST, &header);
+  AssertFatal(rc == 0, "%s(): cannot create header\n", __func__);
+  req->header = header;
+  req->delegation_type = PROTOCOL__FLEX_CONTROL_DELEGATION_TYPE__FLCDT_MAC_DL_UE_SCHEDULER;
+  req->name = strdup(s);
+
+  Protocol__FlexranMessage *msg = malloc(sizeof(Protocol__FlexranMessage));
+  DevAssert(msg);
+  protocol__flexran_message__init(msg);
+  msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_CONTROL_DEL_REQ_MSG;
+  msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
+  msg->control_del_req_msg = req;
+
+  int size = 0;
+  void *data = flexran_agent_pack_message(msg, &size);
+  if (flexran_agent_msg_send(mod_id, FLEXRAN_AGENT_DEFAULT, data, size, 0) < 0)
+    LOG_E(FLEXRAN_AGENT, "%s(): error while sending message\n", __func__);
+}
+
+Protocol__FlexranMessage *request_scheduler_timeout(
+    mid_t mod_id,
+    const Protocol__FlexranMessage *msg) {
+  const Protocol__FlexEnbConfigReply *ecr = msg->enb_config_reply_msg;
+
+  struct lfds700_misc_prng_state ls;
+  LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
+  lfds700_misc_prng_init(&ls);
+
+  Protocol__FlexranMessage *smsg = NULL;
+  struct lfds700_ringbuffer_state *state = &store_slice_config_ringbuffer_state[mod_id];
+  if (lfds700_ringbuffer_read(state, NULL, (void **) &smsg, &ls)) {
+    AssertFatal(msg == smsg,
+                "expected and returned enb_config_reply are not the same\n");
+    AssertFatal(ecr->header->xid == smsg->enb_config_reply_msg->header->xid,
+                "expected and returned enb_config_reply are not the same\n");
+  } else {
+    LOG_E(FLEXRAN_AGENT, "%s(): could not read config from ringbuffer\n", __func__);
+  }
+  /* call the helper so that scheduler names are correctly freed */
+  helper_destroy_mac_slice_config(ecr->cell_config[0]->slice_config);
+  ecr->cell_config[0]->slice_config = NULL;
+  /* we should not call flexran_agent_destroy_enb_config_reply(smsg); since
+   * upon timer removal, this is automatically done */
+  LOG_W(FLEXRAN_AGENT,
+        "remove stored slice config (xid %d) after timeout\n",
+        ecr->header->xid);
+  flexran_agent_destroy_timer(mod_id, ecr->header->xid);
+  return NULL;
+}
+
+void prepare_update_slice_config(mid_t mod_id,
+                                 Protocol__FlexSliceConfig **slice_config,
+                                 int request_objects) {
   if (!*slice_config) return;
   /* just use the memory and set to NULL in original */
   Protocol__FlexSliceConfig *sc = *slice_config;
@@ -1709,6 +1812,63 @@ void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice
   lfds700_misc_prng_init(&ls);
   enum lfds700_misc_flag overwrite_occurred_flag;
 
+  int put_on_hold = 0;
+  if (request_objects
+      && sc->dl->scheduler
+      && !check_scheduler(mod_id, sc->dl->scheduler)) {
+    request_scheduler(mod_id, sc->dl->scheduler, xid++);
+    put_on_hold = 1;
+  }
+  for (int i = 0; request_objects && i < sc->dl->n_slices; ++i) {
+    Protocol__FlexSlice *sl = sc->dl->slices[i];
+    if (sl->scheduler && !check_scheduler(mod_id, sl->scheduler)) {
+      request_scheduler(mod_id, sl->scheduler, xid++);
+      put_on_hold = 1;
+    }
+  }
+  if (put_on_hold) {
+    Protocol__FlexEnbConfigReply *reply = malloc(sizeof(Protocol__FlexEnbConfigReply));
+    DevAssert(reply);
+    protocol__flex_enb_config_reply__init(reply);
+    /* use the last used xid so we can correlate answer and waiting config */
+    int rc = flexran_create_header(
+        xid - 1, PROTOCOL__FLEX_TYPE__FLPT_GET_ENB_CONFIG_REPLY, &reply->header);
+    AssertFatal(rc == 0, "%s(): cannot create header\n", __func__);
+    reply->n_cell_config = 1;
+    reply->cell_config = malloc(sizeof(Protocol__FlexCellConfig *));
+    DevAssert(reply->cell_config);
+    reply->cell_config[0] = malloc(sizeof(Protocol__FlexCellConfig));
+    DevAssert(reply->cell_config[0]);
+    protocol__flex_cell_config__init(reply->cell_config[0]);
+    reply->cell_config[0]->slice_config = sc;
+
+    Protocol__FlexranMessage *msg = malloc(sizeof(Protocol__FlexranMessage));
+    DevAssert(msg);
+    protocol__flexran_message__init(msg);
+    msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_ENB_CONFIG_REPLY_MSG;
+    msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
+    msg->enb_config_reply_msg = reply;
+
+    Protocol__FlexEnbConfigReply *o;
+    lfds700_ringbuffer_write(&store_slice_config_ringbuffer_state[mod_id],
+                             NULL,
+                             (void *)msg,
+                             &overwrite_occurred_flag,
+                             NULL,
+                             (void **) &o,
+                             &ls);
+    AssertFatal(overwrite_occurred_flag == LFDS700_MISC_FLAG_LOWERED,
+                "unhandled: stored slice config for controller has been overwritten\n");
+
+    flexran_agent_create_timer(mod_id,
+                               3000,
+                               FLEXRAN_AGENT_TIMER_TYPE_PERIODIC,
+                               xid - 1,
+                               request_scheduler_timeout,
+                               msg); /* need reply for xid */
+    return;
+  }
+
   Protocol__FlexSliceConfig *overwritten_sc;
   lfds700_ringbuffer_write(&slice_config_ringbuffer_state[mod_id],
                            NULL,
@@ -1756,6 +1916,69 @@ void flexran_agent_slice_update(mid_t mod_id) {
     apply_update_dl_slice_config(mod_id, sc->dl);
     apply_update_ul_slice_config(mod_id, sc->ul);
     helper_destroy_mac_slice_config(sc);
+
+    flexran_agent_so_handle_t *so = NULL;
+    flexran_agent_so_handle_t *prev = NULL; // the previous to current element, if we delete in order to go back
+    //SLIST_FOREACH(so, &flexran_handles[mod_id], entries)
+    for(so = flexran_handles[mod_id].slh_first; so;) {
+      char *name = so->name;
+      int in_use = strcmp(flexran_get_dl_scheduler_name(mod_id), name) == 0
+                   || strcmp(flexran_get_ul_scheduler_name(mod_id), name) == 0;
+      Protocol__FlexSliceAlgorithm dl_algo = flexran_get_dl_slice_algo(mod_id);
+      if (!in_use && dl_algo != PROTOCOL__FLEX_SLICE_ALGORITHM__None) {
+        int n = flexran_get_num_dl_slices(mod_id);
+        for (int i = 0; i < n; ++i) {
+          Protocol__FlexSlice s;
+          /* NONE so it won't do any allocations */
+          flexran_get_dl_slice(mod_id, i, &s, PROTOCOL__FLEX_SLICE_ALGORITHM__None);
+          if (strcmp(s.scheduler, name) == 0) {
+            in_use = 1;
+            break;
+          }
+        }
+      }
+      Protocol__FlexSliceAlgorithm ul_algo = flexran_get_ul_slice_algo(mod_id);
+      if (!in_use && ul_algo != PROTOCOL__FLEX_SLICE_ALGORITHM__None) {
+        int n = flexran_get_num_ul_slices(mod_id);
+        for (int i = 0; i < n; ++i) {
+          Protocol__FlexSlice s;
+          /* NONE so it won't do any allocations */
+          flexran_get_ul_slice(mod_id, i, &s, PROTOCOL__FLEX_SLICE_ALGORITHM__None);
+          if (strcmp(s.scheduler, name) == 0) {
+            in_use = 1;
+            break;
+          }
+        }
+      }
+      if (!in_use) {
+        char s[512];
+        int rc = flexran_agent_map_name_to_delegated_object(mod_id, so->name, s, 512);
+        LOG_W(FLEXRAN_AGENT,
+              "removing %s (library handle %p) since it is not used in the user "
+              "plane anymore\n",
+              s,
+              so->dl_handle);
+        dlclose(so->dl_handle);
+        if (rc < 0) {
+          LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", so->name);
+        } else {
+          int rc = remove(s);
+          if (rc < 0)
+            LOG_E(FLEXRAN_AGENT, "cannot remove file %s: %s\n", s, strerror(errno));
+        }
+        free (so->name);
+        if (!prev) { //it's the head, start over
+          SLIST_REMOVE_HEAD(&flexran_handles[mod_id], entries);
+          so = flexran_handles[mod_id].slh_first;
+        } else {
+          SLIST_REMOVE(&flexran_handles[mod_id], so, flexran_agent_so_handle_s, entries);
+          so = prev->entries.sle_next;
+        }
+      } else {
+        prev = so;
+        so = so->entries.sle_next;
+      }
+    }
   }
 
   Protocol__FlexUeConfig *ue_config = NULL;
@@ -1765,3 +1988,92 @@ void flexran_agent_slice_update(mid_t mod_id) {
     free(ue_config);
   }
 }
+
+void flexran_agent_mac_inform_delegation(mid_t mod_id,
+                                         Protocol__FlexControlDelegation *cdm) {
+  LOG_W(FLEXRAN_AGENT,
+        "received FlexControlDelegation message for object '%s' xid %d\n",
+        cdm->name,
+        cdm->header->xid);
+  if (cdm->header->xid < xid - 1) {
+    LOG_I(FLEXRAN_AGENT,
+          "waiting for %d more messages (up to xid %d)\n",
+          xid - 1 - cdm->header->xid,
+          xid - 1);
+    return;
+  }
+  /* should receive up to xid - 1, otherwise it means there was no request */
+  AssertFatal(xid > cdm->header->xid,
+              "received control delegation with xid %d that we never requested "
+              "(last request %d)\n",
+              cdm->header->xid,
+              xid - 1);
+
+  /* Load the library so the user plane can search it */
+  char s[512];
+  int rc = flexran_agent_map_name_to_delegated_object(mod_id, cdm->name, s, 512);
+  if (rc < 0) {
+    LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", cdm->name);
+    return;
+  }
+  /* TODO where to unload/save handle?? */
+  void *h = dlopen(s, RTLD_NOW);
+  if (!h) {
+    LOG_E(FLEXRAN_AGENT, "dlopen(): %s\n", dlerror());
+    return;
+  }
+  LOG_I(FLEXRAN_AGENT, "library handle %p\n", h);
+
+  struct lfds700_misc_prng_state ls;
+  LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
+  lfds700_misc_prng_init(&ls);
+
+  Protocol__FlexranMessage *msg = NULL;
+  struct lfds700_ringbuffer_state *state = &store_slice_config_ringbuffer_state[mod_id];
+  if (lfds700_ringbuffer_read(state, NULL, (void **) &msg, &ls) == 0) {
+    LOG_E(FLEXRAN_AGENT, "%s(): could not read config from ringbuffer\n", __func__);
+    return;
+  }
+  AssertFatal(cdm->header->xid == msg->enb_config_reply_msg->header->xid,
+              "expected and retrieved xid of stored slice configuration does "
+              "not match (expected %d, retrieved %d)\n",
+              cdm->header->xid,
+              msg->enb_config_reply_msg->header->xid);
+
+  /* Since we recover, stop the timeout */
+  rc = flexran_agent_destroy_timer(mod_id, cdm->header->xid);
+
+  prepare_update_slice_config(
+      mod_id,
+      &msg->enb_config_reply_msg->cell_config[0]->slice_config,
+      0 /* don't do a request */);
+  /* we should not call flexran_agent_destroy_enb_config_reply(smsg); since
+   * upon timer removal, this is automatically done.
+   * prepare_update_slice_config() takes ownership of the slice config, it is
+   * freed inside */
+
+  flexran_agent_so_handle_t *so = malloc(sizeof(flexran_agent_so_handle_t));
+  DevAssert(so);
+  so->name = strdup(cdm->name);
+  DevAssert(so->name);
+  so->dl_handle = h;
+  SLIST_INSERT_HEAD(&flexran_handles[mod_id], so, entries);
+}
+
+void flexran_agent_mac_fill_loaded_mac_objects(
+    mid_t mod_id,
+    Protocol__FlexEnbConfigReply *reply) {
+  int n = 0;
+  flexran_agent_so_handle_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_handles[mod_id], entries)
+    ++n;
+  reply->n_loadedmacobjects = n;
+  reply->loadedmacobjects = calloc(n, sizeof(char *));
+  if (!reply->loadedmacobjects) {
+    reply->n_loadedmacobjects = 0;
+    return;
+  }
+  n = 0;
+  SLIST_FOREACH(so, &flexran_handles[mod_id], entries)
+    reply->loadedmacobjects[n++] = so->name;
+}
diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h
index 5a8d4f31aa70809c79c613b13ea91ad01ade528c..18e567c06198d0d6d50c3e4678e5234930673e70 100644
--- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h
+++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h
@@ -39,6 +39,12 @@
 // for flexran_agent_get_mac_xface()
 #include "flexran_agent_extern.h"
 
+/* Type for a list of shared objects used in the user plane (schedulers) */
+typedef struct flexran_agent_so_handle_s {
+  char *name; // name of the object
+  void *dl_handle; // handle of associated objects
+  SLIST_ENTRY (flexran_agent_so_handle_s) entries;
+} flexran_agent_so_handle_t;
 
 /* Initialization function for the agent structures etc */
 void flexran_agent_init_mac_agent(mid_t mod_id);
@@ -117,7 +123,9 @@ void flexran_agent_slice_update(mid_t mod_id);
 
 /* marks slice_config so that it can be applied later. Takes ownership of the
  * FlexSliceConfig message */
-void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice);
+void prepare_update_slice_config(mid_t mod_id,
+                                 Protocol__FlexSliceConfig **slice,
+                                 int request_objects);
 
 /* inserts a new ue_config into the structure keeping ue to slice association
  * updates and marks so it can be applied. Takes ownership of the FlexUeConfig message */
@@ -127,4 +135,13 @@ void prepare_ue_slice_assoc_update(mid_t mod_id, Protocol__FlexUeConfig **ue_con
  * flexran_agent_fill_mac_cell_config() */
 void flexran_agent_destroy_mac_slice_config(Protocol__FlexCellConfig *conf);
 
+/* information about a new (potentially relevant) control delegation message */
+void flexran_agent_mac_inform_delegation(mid_t mod_id,
+                                         Protocol__FlexControlDelegation *cdm);
+
+/* fill the enb_config_reply with the shared objects in use by the MAC sublayer */
+void flexran_agent_mac_fill_loaded_mac_objects(
+    mid_t mod_id,
+    Protocol__FlexEnbConfigReply *reply);
+
 #endif
diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c
index 93de0b11ec3d1c228b85bae254b24c559ecc7bab..42246c3ebf165606037ec207ed49303ef216d09f 100644
--- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c
+++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c
@@ -33,6 +33,9 @@
 #include "flexran_agent_mac_internal.h"
 #include "flexran_agent_mac_slice_verification.h"
 
+extern SLIST_HEAD(flexran_so_handle,
+                  flexran_agent_so_handle_s) flexran_handles[NUM_MAX_ENB];
+
 Protocol__FlexranMessage * flexran_agent_generate_diff_mac_stats_report(Protocol__FlexranMessage *new_message,
 									Protocol__FlexranMessage *old_message) {
 
@@ -1025,6 +1028,19 @@ int load_dl_scheduler_function(mid_t mod_id, const char *function_name) {
   
 }
 
+void *search_so(mid_t mod_id, char *name) {
+  if (!name) // no name -> nothing to return, so use NULL for executable
+    return NULL;
+  if (dlsym(NULL, name)) // found it in executable
+    return NULL;
+  flexran_agent_so_handle_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_handles[mod_id], entries) {
+    if (strcmp(so->name, name) == 0)
+      return so->dl_handle;
+  }
+  return NULL;
+}
+
 void update_or_remove_dl(mid_t mod_id, Protocol__FlexSlice *s) {
   if (s->params_case == PROTOCOL__FLEX_SLICE__PARAMS__NOT_SET
       && !s->label && !s->scheduler) {
@@ -1033,8 +1049,12 @@ void update_or_remove_dl(mid_t mod_id, Protocol__FlexSlice *s) {
     if (!rc)
       LOG_W(FLEXRAN_AGENT, "error while removing slice ID %d\n", s->id);
   } else {
-    LOG_I(FLEXRAN_AGENT, "updating DL slice ID %d\n", s->id);
-    const int rc = flexran_create_dl_slice(mod_id, s);
+    void *lib = search_so(mod_id, s->scheduler);
+    LOG_I(FLEXRAN_AGENT,
+          "updating DL slice ID %d (library handle %p)\n",
+          s->id,
+          lib);
+    const int rc = flexran_create_dl_slice(mod_id, s, lib);
     if (rc < 0)
       LOG_W(FLEXRAN_AGENT,
             "error while update slice ID %d: flexran_create_dl_slice() -> %d\n",
@@ -1050,8 +1070,12 @@ void update_or_remove_ul(mid_t mod_id, Protocol__FlexSlice *s) {
     if (!rc)
       LOG_W(FLEXRAN_AGENT, "error while removing slice ID %d\n", s->id);
   } else {
-    LOG_I(FLEXRAN_AGENT, "updating UL slice ID %d\n", s->id);
-    const int rc = flexran_create_ul_slice(mod_id, s);
+    void *lib = search_so(mod_id, s->scheduler);
+    LOG_I(FLEXRAN_AGENT,
+          "updating UL slice ID %d (library handle %p)\n",
+          s->id,
+          lib);
+    const int rc = flexran_create_ul_slice(mod_id, s, lib);
     if (rc < 0)
       LOG_W(FLEXRAN_AGENT,
             "error while updating slice ID %d: flexran_create_ul_slice() -> %d)\n",
@@ -1099,8 +1123,12 @@ void apply_update_dl_slice_config(mid_t mod_id, Protocol__FlexSliceDlUlConfig *d
       LOG_E(FLEXRAN_AGENT, "cannot update scheduling algorithm: slice algorithm loaded\n");
       return;
     }
-    LOG_I(FLEXRAN_AGENT, "loading DL new scheduling algorithm '%s'\n", dl->scheduler);
-    const int rc = flexran_set_dl_scheduler(mod_id, dl->scheduler);
+    void *lib = search_so(mod_id, dl->scheduler);
+    LOG_I(FLEXRAN_AGENT,
+          "loading DL new scheduling algorithm '%s' (library handle %p)\n",
+          dl->scheduler,
+          lib);
+    const int rc = flexran_set_dl_scheduler(mod_id, dl->scheduler, lib);
     if (rc < 0) {
       LOG_E(FLEXRAN_AGENT,
             "error while updating scheduling algorithm: "
@@ -1147,8 +1175,12 @@ void apply_update_ul_slice_config(mid_t mod_id, Protocol__FlexSliceDlUlConfig *u
       LOG_E(FLEXRAN_AGENT, "cannot update scheduling algorithm: slice algorithm loaded\n");
       return;
     }
-    LOG_I(FLEXRAN_AGENT, "loading new UL scheduling algorithm '%s'\n", ul->scheduler);
-    const int rc = flexran_set_ul_scheduler(mod_id, ul->scheduler);
+    void *lib = search_so(mod_id, ul->scheduler);
+    LOG_I(FLEXRAN_AGENT,
+          "loading new UL scheduling algorithm '%s' (library handle %p)\n",
+          ul->scheduler,
+          lib);
+    const int rc = flexran_set_ul_scheduler(mod_id, ul->scheduler, lib);
     if (rc < 0) {
       LOG_E(FLEXRAN_AGENT,
             "error while updating scheduling algorithm: "
diff --git a/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto b/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto
index ef0eea3bd2eb9ed7062ec11195e6856d246a806c..a646a978a9e63c05bd4cfff7468331a7a4d4fbe1 100644
--- a/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto
@@ -3,4 +3,25 @@ package protocol;
 
 enum flex_control_delegation_type {
      FLCDT_MAC_DL_UE_SCHEDULER = 1;		// DL UE scheduler delegation
+     FLCDT_AGENT_CONTROL_APP = 2;               // Generic control application
+}
+
+message flex_agent_reconfiguration_system {
+  optional string system = 1;
+  repeated flex_agent_reconfiguration_subsystem subsystems = 2;
+}
+
+message flex_agent_reconfiguration_subsystem {
+  optional string name = 1;   // Name of the object (app name or user plane object)
+  optional string behavior = 2;   // For an app: a callback in a shared library. For any other subsystem: an object name (shared library) with defined syntax.
+  map<string, flex_agent_reconfiguration_param> params = 3; // Param configurations depending on callback/shared library above.
+}
+
+message flex_agent_reconfiguration_param {
+  oneof param {
+    int32 integer = 1;
+    float floating = 2;
+    bool boolean = 3;
+    string str = 4;
+  }
 }
diff --git a/openair2/ENB_APP/MESSAGES/V2/flexran.proto b/openair2/ENB_APP/MESSAGES/V2/flexran.proto
index 3b47ff263be6533feac563e4608225b714a114a8..cec7f84b6e6aa8e556d2ffb16e4d344e4dadd719 100644
--- a/openair2/ENB_APP/MESSAGES/V2/flexran.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/flexran.proto
@@ -7,6 +7,7 @@ import "time_common.proto";
 import "config_messages.proto";
 import "controller_commands.proto";
 import "config_common.proto";
+import "control_delegation.proto";
 
 message flexran_message {
        optional flexran_direction msg_dir = 100;
@@ -32,6 +33,7 @@ message flexran_message {
 	      flex_ul_mac_config ul_mac_config_msg = 19;
               flex_disconnect disconnect_msg = 20;
               flex_ho_command ho_command_msg = 21;
+              flex_control_delegation_request control_del_req_msg = 22;
 	}
 }
 
@@ -157,6 +159,8 @@ message flex_enb_config_reply {
 	repeated flex_cell_config cell_config = 3;
         optional uint32 device_spec = 4;
         optional flex_s1ap_config s1ap = 5;
+        repeated string loadedApps = 6;
+        repeated string loadedMacObjects = 7;
 }
 
 message flex_ue_config_request {
@@ -225,11 +229,17 @@ message flex_ue_state_change {
 
 message flex_control_delegation {
 	optional flex_header header = 1;
-	optional uint32 delegation_type = 2;	// Bitmap of FLCDT_* flags
+	optional flex_control_delegation_type delegation_type = 2;	// The delegated object type, decides about further processing
 	optional bytes payload = 3;	  	// Byte array of shared lib containing the delegated functions
 	optional string name = 4;		// The delegated functions names ordered based on bitmap flags
 }
 
+message flex_control_delegation_request {
+  optional flex_header header = 1;
+  optional flex_control_delegation_type delegation_type = 2;
+  optional string name = 4;
+}
+
 //
 // Agent reconfiguration message
 //
@@ -237,6 +247,7 @@ message flex_control_delegation {
 message flex_agent_reconfiguration {
 	optional flex_header header = 1;
 	optional string policy = 2;		// The policy changes using YAML syntax in string format    
+  repeated flex_agent_reconfiguration_system systems = 3; // The policy changes.
 }
 
 // Extensions of the echo request and reply
diff --git a/openair2/ENB_APP/MESSAGES/V2/header.proto b/openair2/ENB_APP/MESSAGES/V2/header.proto
index 41b2b37f7a6e00ab8bfa24e2df36686b5d8cfa58..5e224f64f730eb6bd8e39ca4bd40b11ab53de706 100644
--- a/openair2/ENB_APP/MESSAGES/V2/header.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/header.proto
@@ -44,5 +44,6 @@ enum flex_type {
      FLPT_RECONFIGURE_AGENT = 16;
      FLPT_RRC_TRIGGERING = 17;
      FLPT_UL_MAC_CONFIG = 18;
+     FLPT_DELEGATE_REQUEST = 22;
 }
 
diff --git a/openair2/ENB_APP/flexran_agent.c b/openair2/ENB_APP/flexran_agent.c
index 519d2bef4bfa17e2ff9328addc0ce6089be3ec42..7d4546815fbc6e1d7ad290e2bf5657d6bd354a93 100644
--- a/openair2/ENB_APP/flexran_agent.c
+++ b/openair2/ENB_APP/flexran_agent.c
@@ -115,7 +115,6 @@ error:
   return NULL;
 }
 
-
 int channel_container_init = 0;
 int flexran_agent_start(mid_t mod_id)
 {
@@ -233,6 +232,9 @@ int flexran_agent_start(mid_t mod_id)
     agent_task_created = 1;
   }
 
+  /* Init the app sublayer */
+  flexran_agent_app_init(mod_id);
+
   pthread_mutex_init(&flexran->mutex_node_ctrl, NULL);
   pthread_cond_init(&flexran->cond_node_ctrl, NULL);
 
diff --git a/openair2/ENB_APP/flexran_agent.h b/openair2/ENB_APP/flexran_agent.h
index 42295fc111ca5efd72670cd7b9fd1b8a8ee1bb54..43e7d7210ab125dc6455e9e8cdbb0503efc7ad95 100644
--- a/openair2/ENB_APP/flexran_agent.h
+++ b/openair2/ENB_APP/flexran_agent.h
@@ -41,6 +41,7 @@
 #include "flexran_agent_rrc.h"
 #include "flexran_agent_pdcp.h"
 #include "flexran_agent_s1ap.h"
+#include "flexran_agent_app.h"
 #include "common/utils/LOG/log.h"
 #include "assertions.h"
 
diff --git a/openair2/ENB_APP/flexran_agent_app.c b/openair2/ENB_APP/flexran_agent_app.c
new file mode 100644
index 0000000000000000000000000000000000000000..5bf06ca63110a4bf49cca1d17477b751010311a1
--- /dev/null
+++ b/openair2/ENB_APP/flexran_agent_app.c
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */ 
+
+/*! \file flexran_agent_app.c
+ * \brief CM for handling of applications
+ * \author Robert Schmidt
+ * \date 2020
+ * \version 0.1
+ */
+
+#include "flexran_agent_app.h"
+#include "queue.h"
+#include <dlfcn.h>
+
+/* app definition */
+typedef struct flexran_agent_app_internal_s {
+  char *name;
+  void *dl_handle;
+  flexran_agent_app_t *app;
+
+  SLIST_ENTRY (flexran_agent_app_internal_s) entries;
+} flexran_agent_app_internal_t;
+
+/* a list of all apps */
+SLIST_HEAD(flexran_apps_s, flexran_agent_app_internal_s) flexran_apps[NUM_MAX_ENB];
+
+void flexran_agent_app_init(mid_t mod_id) {
+  //flexran_apps[mod_id] = SLIST_HEAD_INITIALIZER(flexran_apps[mod_id]);
+  SLIST_INIT(&flexran_apps[mod_id]);
+}
+
+int flexran_agent_start_app_direct(mid_t mod_id, flexran_agent_app_t *app, char *name) {
+  app->start(mod_id, NULL, 0);
+
+  flexran_agent_app_internal_t *so = malloc(sizeof(flexran_agent_app_internal_t));
+  DevAssert(so);
+  so->name = strdup(name);
+  DevAssert(so->name);
+  so->dl_handle = NULL;
+  so->app = app;
+  SLIST_INSERT_HEAD(&flexran_apps[mod_id], so, entries);
+  return 0;
+}
+
+int flexran_agent_start_app(mid_t mod_id, Protocol__FlexAgentReconfigurationSubsystem *sub) {
+  char s[512];
+  int rc = flexran_agent_map_name_to_delegated_object(mod_id, sub->name, s, 512);
+  if (rc < 0) {
+    LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", sub->name);
+    return -1;
+  }
+  void *h = dlopen(s, RTLD_NOW);
+  if (!h) {
+    LOG_E(FLEXRAN_AGENT, "cannot open shared object %s\n", s);
+    return -1;
+  }
+  flexran_agent_app_t *app = dlsym(h, "app");
+  if (!app) {
+    LOG_E(FLEXRAN_AGENT, "cannot locate app inside of shared object %s\n", s);
+    return -1;
+  }
+  app->start(mod_id, sub->params, sub->n_params);
+
+  flexran_agent_app_internal_t *so = malloc(sizeof(flexran_agent_app_internal_t));
+  DevAssert(so);
+  so->name = strdup(sub->name);
+  DevAssert(so->name);
+  so->dl_handle = h;
+  so->app = app;
+  SLIST_INSERT_HEAD(&flexran_apps[mod_id], so, entries);
+
+  return 0;
+}
+
+int flexran_agent_stop_app(mid_t mod_id, char *name) {
+  flexran_agent_app_internal_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_apps[mod_id], entries) {
+    if (strcmp(so->name, name) == 0) {
+      LOG_I(FLEXRAN_AGENT, "application %s seems to be running\n", name);
+      break;
+    }
+  }
+  if (!so) {
+    LOG_E(FLEXRAN_AGENT, "no such application %s found\n", name);
+    return -1;
+  }
+  flexran_agent_app_t *app = so->app;
+  app->stop(mod_id);
+
+  SLIST_REMOVE(&flexran_apps[mod_id], so, flexran_agent_app_internal_s, entries);
+  free(so->name);
+  if (so->dl_handle)
+    dlclose(so->dl_handle);
+  free(so);
+  so = NULL;
+  return 0;
+}
+
+int flexran_agent_reconfig_app(
+    mid_t mod_id,
+    Protocol__FlexAgentReconfigurationSubsystem *sub)
+{
+  flexran_agent_app_internal_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_apps[mod_id], entries) {
+    if (strcmp(so->name, sub->name) == 0) {
+      LOG_I(FLEXRAN_AGENT, "application %s is running\n", sub->name);
+      break;
+    }
+  }
+  if (!so) {
+    LOG_E(FLEXRAN_AGENT, "no such application %s found\n", sub->name);
+    return -1;
+  }
+  flexran_agent_app_t *app = so->app;
+  LOG_I(FLEXRAN_AGENT, "reconfiguring app %s\n", so->name);
+  app->reconfig(mod_id, sub->params, sub->n_params);
+  return 0;
+}
+
+int flexran_agent_custom_command_app(
+    mid_t mod_id,
+    Protocol__FlexAgentReconfigurationSubsystem *sub)
+{
+  flexran_agent_app_internal_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_apps[mod_id], entries) {
+    if (strcmp(so->name, sub->name) == 0) {
+      LOG_I(FLEXRAN_AGENT, "application %s is running\n", sub->name);
+      break;
+    }
+  }
+  if (!so) {
+    LOG_E(FLEXRAN_AGENT, "no such application %s found\n", sub->name);
+    return -1;
+  }
+  void (*cmd)(mid_t, Protocol__FlexAgentReconfigurationSubsystem *) = dlsym(so->dl_handle, sub->behavior);
+  if (!cmd) {
+    LOG_E(FLEXRAN_AGENT,
+          "cannot locate command %s inside of app %s\n",
+          sub->behavior,
+          so->name);
+    return -1;
+  }
+
+  LOG_I(FLEXRAN_AGENT,
+        "calling command %s inside of app %s\n",
+        sub->behavior,
+        so->name);
+  (*cmd)(mod_id, sub);
+  return 0;
+}
+
+void flexran_agent_handle_apps(
+    mid_t mod_id,
+    Protocol__FlexAgentReconfigurationSubsystem **subs,
+    int n_subs) {
+  for (int i = 0; i < n_subs; ++i) {
+    Protocol__FlexAgentReconfigurationSubsystem *s = subs[i];
+    if (strcmp(s->behavior, "start") == 0) {
+      int rc = flexran_agent_start_app(mod_id, s);
+      if (rc == 0)
+        LOG_I(FLEXRAN_AGENT, "application \"%s\" started\n", s->name);
+    } else if (strcmp(s->behavior, "stop") == 0) {
+      int rc = flexran_agent_stop_app(mod_id, s->name);
+      if (rc == 0)
+        LOG_I(FLEXRAN_AGENT, "application \"%s\" stopped\n", s->name);
+    } else if (strcmp(s->behavior, "reconfig") == 0) {
+      flexran_agent_reconfig_app(mod_id, s);
+    } else {
+      flexran_agent_custom_command_app(mod_id, s);
+    }
+  }
+}
+
+void flexran_agent_fill_loaded_apps(mid_t mod_id,
+                                    Protocol__FlexEnbConfigReply *reply) {
+  int n = 0;
+  flexran_agent_app_internal_t *so = NULL;
+  SLIST_FOREACH(so, &flexran_apps[mod_id], entries)
+    ++n;
+  reply->n_loadedapps = n;
+  reply->loadedapps = calloc(n, sizeof(char *));
+  if (!reply->loadedapps) {
+    reply->n_loadedapps = 0;
+    return;
+  }
+  n = 0;
+  SLIST_FOREACH(so, &flexran_apps[mod_id], entries)
+    reply->loadedapps[n++] = so->name;
+}
diff --git a/openair2/ENB_APP/flexran_agent_app.h b/openair2/ENB_APP/flexran_agent_app.h
new file mode 100644
index 0000000000000000000000000000000000000000..4723b344bf80f25dffb717ec486b0c44c45dd3e0
--- /dev/null
+++ b/openair2/ENB_APP/flexran_agent_app.h
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */ 
+
+/*! \file flexran_agent_app.h
+ * \brief Common app definitions
+ * \author Robert Schmidt
+ * \date 2020
+ * \version 0.1
+ */
+
+#ifndef BURST_ANALYSIS_LL_H_
+#define BURST_ANALYSIS_LL_H_
+
+#include "flexran_agent_common.h"
+#include "flexran_agent_async.h"
+#include "flexran_agent_extern.h"
+#include "flexran_agent_timer.h"
+#include "flexran_agent_defs.h"
+#include "flexran_agent_net_comm.h"
+#include "flexran_agent_ran_api.h"
+#include "flexran_agent_phy.h"
+#include "flexran_agent_mac.h"
+#include "flexran_agent_rrc.h"
+#include "flexran_agent_pdcp.h"
+#include "flexran_agent_s1ap.h"
+#include "common/utils/LOG/log.h"
+#include "assertions.h"
+
+/* App type: to be implemented by shared libraries */
+typedef struct {
+  int (*start)(mid_t mod_id,
+               Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+               int n_p);
+  int (*reconfig)(mid_t mod_id,
+                  Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+                  int n_p);
+  int (* stop)(mid_t mod_id);
+} flexran_agent_app_t;
+
+/* FlexRAN agent app handling init */
+void flexran_agent_app_init(mid_t mod_id);
+
+/* Start an app from within the agent */
+int flexran_agent_start_app_direct(mid_t mod_id, flexran_agent_app_t *app, char *name);
+
+/* FlexRAN agent handler to setup/teardown apps */
+void flexran_agent_handle_apps(
+    mid_t mod_id,
+    Protocol__FlexAgentReconfigurationSubsystem **subs,
+    int n_subs);
+
+/* Fills the enb_config_reply with the currently loaded (started) apps */
+void flexran_agent_fill_loaded_apps(mid_t mod_id,
+                                    Protocol__FlexEnbConfigReply *reply);
+
+#endif
diff --git a/openair2/ENB_APP/flexran_agent_common.c b/openair2/ENB_APP/flexran_agent_common.c
index 7569ca1ac155e45f9cb8dd831c7599c54372a53b..abb5b57e0cbb58626de2dec1686aa75da6e4e1e9 100644
--- a/openair2/ENB_APP/flexran_agent_common.c
+++ b/openair2/ENB_APP/flexran_agent_common.c
@@ -29,6 +29,9 @@
 #include <stdio.h>
 #include <time.h>
 #include <sys/stat.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "flexran_agent_common.h"
 #include "flexran_agent_common_internal.h"
@@ -39,6 +42,7 @@
 #include "flexran_agent_mac.h"
 #include "flexran_agent_rrc.h"
 #include "flexran_agent_s1ap.h"
+#include "flexran_agent_app.h"
 //#include "PHY/extern.h"
 #include "common/utils/LOG/log.h"
 #include "flexran_agent_mac_internal.h"
@@ -326,6 +330,12 @@ int flexran_agent_destroy_enb_config_reply(Protocol__FlexranMessage *msg) {
   if (reply->s1ap)
     flexran_agent_free_s1ap_cell_config(&reply->s1ap);
 
+  if (reply->loadedapps)
+    free(reply->loadedapps);
+
+  if (reply->loadedmacobjects)
+    free(reply->loadedmacobjects);
+
   free(reply->cell_config);
   free(reply);
   free(msg);
@@ -424,26 +434,65 @@ long timer_end(struct timespec start_time) {
 int flexran_agent_control_delegation(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {
   Protocol__FlexranMessage *input = (Protocol__FlexranMessage *)params;
   Protocol__FlexControlDelegation *control_delegation_msg = input->control_delegation_msg;
-  //  struct timespec vartime = timer_start();
-  //Write the payload lib into a file in the cache and load the lib
-  char lib_name[120];
+  *msg = NULL;
+
   char target[512];
-  snprintf(lib_name, sizeof(lib_name), "/%s.so", control_delegation_msg->name);
-  strcpy(target, RC.flexran[mod_id]->cache_name);
-  strcat(target, lib_name);
-  FILE *f;
-  f = fopen(target, "wb");
-
-  if (f) {
-    fwrite(control_delegation_msg->payload.data, control_delegation_msg->payload.len, 1, f);
-    fclose(f);
+  int len = snprintf(target, sizeof(target), "%s/libflex.%s.so",
+                     RC.flexran[mod_id]->cache_name,
+                     control_delegation_msg->name);
+  if (len >= sizeof(target)) {
+    LOG_E(FLEXRAN_AGENT, "target has been truncated, cannot write file name\n");
+    return 0;
+  }
+
+  if (control_delegation_msg->has_payload) {
+    /* use low-level API: check whether exists while creating so we can abort if
+     * it exists to not overwrite anything */
+    int fd = open(target, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
+    if (fd >= 0) {
+      ssize_t l = write(fd,
+                        control_delegation_msg->payload.data,
+                        control_delegation_msg->payload.len);
+      close(fd);
+      if (l < control_delegation_msg->payload.len) {
+        LOG_E(FLEXRAN_AGENT,
+              "could not write complete control delegation to %s: only %ld out of "
+              "%ld bytes\n",
+              target,
+              l,
+              control_delegation_msg->payload.len);
+        return 0;
+      } else if (l < 0) {
+        LOG_E(FLEXRAN_AGENT, "can not write control delegation data to %s: %s\n",
+              target, strerror(errno));
+        return 0;
+      }
+      LOG_I(FLEXRAN_AGENT, "wrote shared object %s\n", target);
+    } else {
+      if (errno == EEXIST) {
+        LOG_W(FLEXRAN_AGENT, "file %s already exists, remove it first\n", target);
+      } else {
+        LOG_E(FLEXRAN_AGENT, "can not write control delegation data to %s: %s\n",
+              target, strerror(errno));
+      }
+      return 0;
+    }
   } else {
-    LOG_W(FLEXRAN_AGENT, "[%d] can not write control delegation data to %s\n",
-          mod_id, target);
+    LOG_W(FLEXRAN_AGENT, "remove file %s\n", target);
+    int rc = remove(target);
+    if (rc < 0)
+      LOG_E(FLEXRAN_AGENT, "cannot remove file %s: %s\n", target, strerror(errno));
+  }
+
+  if (control_delegation_msg->has_delegation_type
+      && control_delegation_msg->delegation_type == PROTOCOL__FLEX_CONTROL_DELEGATION_TYPE__FLCDT_MAC_DL_UE_SCHEDULER
+      && control_delegation_msg->header
+      && control_delegation_msg->header->has_xid) {
+    /* Inform the MAC subsystem that a control delegation for it has arrived */
+    /* TODO this should be triggered by an agent reconfiguration? */
+    flexran_agent_mac_inform_delegation(mod_id, control_delegation_msg);
   }
 
-  //  long time_elapsed_nanos = timer_end(vartime);
-  *msg = NULL;
   return 0;
 }
 
@@ -452,10 +501,44 @@ int flexran_agent_destroy_control_delegation(Protocol__FlexranMessage *msg) {
   return 0;
 }
 
+int flexran_agent_map_name_to_delegated_object(mid_t mod_id, const char *name,
+    char *path, int maxlen) {
+  int len = snprintf(path, maxlen, "%s/libflex.%s.so",
+                     RC.flexran[mod_id]->cache_name, name);
+  if (len >= maxlen) {
+    LOG_E(FLEXRAN_AGENT, "path has been truncated, cannot read object\n");
+    return -1;
+  }
+
+  struct stat buf;
+  int status = stat(path, &buf);
+  if (status < 0) {
+    LOG_E(FLEXRAN_AGENT, "Could not stat object %s: %s\n", path, strerror(errno));
+    return -1;
+  }
+  return 0;
+}
+
 int flexran_agent_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {
   Protocol__FlexranMessage *input = (Protocol__FlexranMessage *)params;
   Protocol__FlexAgentReconfiguration *agent_reconfiguration_msg = input->agent_reconfiguration_msg;
-  apply_reconfiguration_policy(mod_id, agent_reconfiguration_msg->policy, strlen(agent_reconfiguration_msg->policy));
+  if (agent_reconfiguration_msg->policy) {
+    /* for compatibility: call old YAML configuration code, although we don't
+     * use it anymore */
+    apply_reconfiguration_policy(mod_id,
+                                 agent_reconfiguration_msg->policy,
+                                 strlen(agent_reconfiguration_msg->policy));
+  }
+  for (int i = 0; i < agent_reconfiguration_msg->n_systems; ++i) {
+    const Protocol__FlexAgentReconfigurationSystem *sys = agent_reconfiguration_msg->systems[i];
+    if (strcmp(sys->system, "app") == 0) {
+      flexran_agent_handle_apps(mod_id, sys->subsystems, sys->n_subsystems);
+    } else {
+      LOG_E(FLEXRAN_AGENT,
+            "unknown system name %s in flex_agent_reconfiguration message\n",
+            sys->system);
+    }
+  }
   *msg = NULL;
   return 0;
 }
@@ -465,6 +548,15 @@ int flexran_agent_destroy_agent_reconfiguration(Protocol__FlexranMessage *msg) {
   return 0;
 }
 
+int flexran_agent_destroy_control_delegation_request(Protocol__FlexranMessage *msg) {
+  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_CONTROL_DEL_REQ_MSG)
+    return -1;
+
+  free(msg->control_del_req_msg->header);
+  free(msg->control_del_req_msg->name);
+  free(msg);
+  return 0;
+}
 
 int flexran_agent_lc_config_reply(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {
   xid_t xid;
@@ -750,6 +842,10 @@ int flexran_agent_enb_config_reply(mid_t mod_id, const void *params, Protocol__F
   if (flexran_agent_get_s1ap_xface(mod_id))
     flexran_agent_fill_s1ap_cell_config(mod_id, &enb_config_reply_msg->s1ap);
 
+  flexran_agent_fill_loaded_apps(mod_id, enb_config_reply_msg);
+
+  flexran_agent_mac_fill_loaded_mac_objects(mod_id, enb_config_reply_msg);
+
   *msg = malloc(sizeof(Protocol__FlexranMessage));
 
   if(*msg == NULL) {
@@ -880,7 +976,9 @@ int flexran_agent_handle_enb_config_reply(mid_t mod_id, const void *params, Prot
 
   if (enb_config->n_cell_config > 0) {
     if (flexran_agent_get_mac_xface(mod_id) && enb_config->cell_config[0]->slice_config) {
-      prepare_update_slice_config(mod_id, &enb_config->cell_config[0]->slice_config);
+      prepare_update_slice_config(mod_id,
+                                  &enb_config->cell_config[0]->slice_config,
+                                  1 /* request objects if necessary */);
     }
     if (enb_config->cell_config[0]->has_eutra_band
         && enb_config->cell_config[0]->has_dl_freq
diff --git a/openair2/ENB_APP/flexran_agent_common.h b/openair2/ENB_APP/flexran_agent_common.h
index fe7b49107c299c05ab982a64f832ee57694e555b..ae690c712496621ef0e1cd1dc69958ec11474966 100644
--- a/openair2/ENB_APP/flexran_agent_common.h
+++ b/openair2/ENB_APP/flexran_agent_common.h
@@ -131,11 +131,20 @@ int flexran_agent_destroy_lc_config_request(Protocol__FlexranMessage *msg);
 /* Control delegation message constructor and destructor */
 int flexran_agent_control_delegation(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg);
 int flexran_agent_destroy_control_delegation(Protocol__FlexranMessage *msg);
+/* Map a name from a control_delegation message to a file name. The function
+ * stat()s the file to check existence and returns the complete path name in
+ * path. The length in path needs to be maxlen, the maximum length the path may
+ * have. */
+int flexran_agent_map_name_to_delegated_object(mid_t mod_id, const char *name,
+    char *path, int maxlen);
 
 /* Policy reconfiguration message constructor and destructor */
 int flexran_agent_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg);
 int flexran_agent_destroy_agent_reconfiguration(Protocol__FlexranMessage *msg);
 
+/* Policy reconfiguration request message destructor */
+int flexran_agent_destroy_control_delegation_request(Protocol__FlexranMessage *msg);
+
 /* rrc triggering measurement message constructor and destructor */
 int flexran_agent_rrc_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg);
 int flexran_agent_destroy_rrc_reconfiguration(Protocol__FlexranMessage *msg);
diff --git a/openair2/ENB_APP/flexran_agent_handler.c b/openair2/ENB_APP/flexran_agent_handler.c
index d991078cf74c19430703bb8f97a07f25586cb592..addcd6a1423a3d43ef4e08654c586f8d9c2ce619 100644
--- a/openair2/ENB_APP/flexran_agent_handler.c
+++ b/openair2/ENB_APP/flexran_agent_handler.c
@@ -32,6 +32,7 @@
 #include "flexran_agent_rrc.h"
 #include "flexran_agent_pdcp.h"
 #include "flexran_agent_s1ap.h"
+#include "flexran_agent_app.h"
 #include "flexran_agent_timer.h"
 #include "flexran_agent_ran_api.h"
 #include "common/utils/LOG/log.h"
@@ -80,6 +81,11 @@ flexran_agent_message_destruction_callback message_destruction_callback[] = {
   flexran_agent_destroy_ue_state_change,
   flexran_agent_destroy_control_delegation,
   flexran_agent_destroy_agent_reconfiguration,
+  NULL, /* flex_rrc_triggering */
+  NULL, /* flex_ul_mac_config */
+  NULL, /* flex_disconnect */
+  NULL, /* flex_ho_command */
+  flexran_agent_destroy_control_delegation_request,
 };
 
 /* static const char *flexran_agent_direction2String[] = { */
diff --git a/openair2/ENB_APP/flexran_agent_ran_api.c b/openair2/ENB_APP/flexran_agent_ran_api.c
index 79c992a4d7ce678609544616db7187e984f453c3..d126012737de6b0b28cb1731ff3cbfef03b46973 100644
--- a/openair2/ENB_APP/flexran_agent_ran_api.c
+++ b/openair2/ENB_APP/flexran_agent_ran_api.c
@@ -3092,12 +3092,13 @@ int flexran_get_ue_dl_slice_id(mid_t mod_id, mid_t ue_id) {
   return slices->s[idx]->id;
 }
 
-void flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) {
-  if (!mac_is_present(mod_id)) return;
+int flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) {
+  if (!mac_is_present(mod_id)) return 0;
   int idx = flexran_find_dl_slice(mod_id, slice_id);
-  if (idx < 0) return;
+  if (idx < 0) return 0;
   pp_impl_param_t *dl = &RC.mac[mod_id]->pre_processor_dl;
   dl->move_UE(dl->slices, ue_id, idx);
+  return 1;
 }
 
 int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id) {
@@ -3108,15 +3109,16 @@ int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id) {
   return slices->s[idx]->id;
 }
 
-void flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) {
-  if (!mac_is_present(mod_id)) return;
+int flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) {
+  if (!mac_is_present(mod_id)) return 0;
   int idx = flexran_find_ul_slice(mod_id, slice_id);
-  if (idx < 0) return;
+  if (idx < 0) return 0;
   pp_impl_param_t *ul = &RC.mac[mod_id]->pre_processor_ul;
   ul->move_UE(ul->slices, ue_id, idx);
+  return 1;
 }
 
-int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s) {
+int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object) {
   if (!mac_is_present(mod_id)) return 0;
   void *params = NULL;
   switch (s->params_case) {
@@ -3133,7 +3135,7 @@ int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s) {
   char *l = s->label ? strdup(s->label) : NULL;
   void *algo = &dl->dl_algo; // default scheduler
   if (s->scheduler) {
-    algo = dlsym(NULL, s->scheduler);
+    algo = dlsym(object, s->scheduler);
     if (!algo) {
       free(params);
       LOG_E(FLEXRAN_AGENT, "cannot locate scheduler '%s'\n", s->scheduler);
@@ -3193,7 +3195,7 @@ int flexran_get_num_dl_slices(mid_t mod_id) {
   return RC.mac[mod_id]->pre_processor_dl.slices->num;
 }
 
-int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s) {
+int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object) {
   if (!mac_is_present(mod_id)) return -1;
   void *params = NULL;
   switch (s->params_case) {
@@ -3210,7 +3212,7 @@ int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s) {
   char *l = s->label ? strdup(s->label) : NULL;
   void *algo = &ul->ul_algo; // default scheduler
   if (s->scheduler) {
-    algo = dlsym(NULL, s->scheduler);
+    algo = dlsym(object, s->scheduler);
     if (!algo) {
       free(params);
       LOG_E(FLEXRAN_AGENT, "cannot locate scheduler '%s'\n", s->scheduler);
@@ -3276,9 +3278,9 @@ char *flexran_get_dl_scheduler_name(mid_t mod_id) {
   return RC.mac[mod_id]->pre_processor_dl.dl_algo.name;
 }
 
-int flexran_set_dl_scheduler(mid_t mod_id, char *sched) {
+int flexran_set_dl_scheduler(mid_t mod_id, char *sched, void *object) {
   if (!mac_is_present(mod_id)) return -1;
-  void *d = dlsym(NULL, sched);
+  void *d = dlsym(object, sched);
   if (!d) return -2;
   pp_impl_param_t *dl_pp = &RC.mac[mod_id]->pre_processor_dl;
   dl_pp->dl_algo.unset(&dl_pp->dl_algo.data);
@@ -3292,9 +3294,9 @@ char *flexran_get_ul_scheduler_name(mid_t mod_id) {
   return RC.mac[mod_id]->pre_processor_ul.ul_algo.name;
 }
 
-int flexran_set_ul_scheduler(mid_t mod_id, char *sched) {
+int flexran_set_ul_scheduler(mid_t mod_id, char *sched, void *object) {
   if (!mac_is_present(mod_id)) return -1;
-  void *d = dlsym(NULL, sched);
+  void *d = dlsym(object, sched);
   if (!d) return -2;
   pp_impl_param_t *ul_pp = &RC.mac[mod_id]->pre_processor_ul;
   ul_pp->ul_algo.unset(&ul_pp->ul_algo.data);
diff --git a/openair2/ENB_APP/flexran_agent_ran_api.h b/openair2/ENB_APP/flexran_agent_ran_api.h
index 2d8a86f7d01de163f08c524ae80d2cbede17b81b..79115b0a168b0030af938b6291d34a0a2c8ac1bc 100644
--- a/openair2/ENB_APP/flexran_agent_ran_api.h
+++ b/openair2/ENB_APP/flexran_agent_ran_api.h
@@ -669,15 +669,16 @@ int flexran_set_ul_slice_algo(mid_t mod_id, Protocol__FlexSliceAlgorithm algo);
 /* Get the DL slice ID for a UE */
 int flexran_get_ue_dl_slice_id(mid_t mod_id, mid_t ue_id);
 /* Set the DL slice for a UE */
-void flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id);
+int flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id);
 
 /* Get the UL slice ID for a UE */
 int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id);
 /* Set the UL slice for a UE */
-void flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id);
+int flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id);
 
-/* Create slice in DL, returns the new slice index */
-int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s);
+/* Create slice in DL, returns the new slice index, object can be shared
+ * library for the scheduler or NULL */
+int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object);
 /* Remove slice in DL, returns new number of slices or -1 on error */
 int flexran_remove_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s);
 
@@ -691,8 +692,9 @@ void flexran_get_dl_slice(mid_t mod_id,
 /* Get the number of slices in DL */
 int flexran_get_num_dl_slices(mid_t mod_id);
 
-/* Create slice in UL, returns the new slice index */
-int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s);
+/* Create slice in UL, returns the new slice index, object can be shared library
+ * for the scheduler or NULL */
+int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object);
 /* Remove slice in UL */
 int flexran_remove_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s);
 
@@ -708,14 +710,14 @@ int flexran_get_num_ul_slices(mid_t mod_id);
 
 /* Get the name of/Set the DL scheduling algorithm. If slicing is active, this
  * corresponds to the default algorithm for slices, otherwise the currently
- * used one. */
+ * used one. object is a shared object in which to find the scheduler */
 char *flexran_get_dl_scheduler_name(mid_t mod_id);
-int flexran_set_dl_scheduler(mid_t mod_id, char *sched);
+int flexran_set_dl_scheduler(mid_t mod_id, char *sched, void *object);
 
 /* Get the name of/Set the UL scheduler algorithm. Same applies as for the DL
- * case */
+ * case. object is a shared object in which to find the scheduler */
 char *flexran_get_ul_scheduler_name(mid_t mod_id);
-int flexran_set_ul_scheduler(mid_t mod_id, char *sched);
+int flexran_set_ul_scheduler(mid_t mod_id, char *sched, void *object);
 
 /************************** S1AP **************************/
 /* Get the number of MMEs to be connected */
diff --git a/openair2/ENB_APP/flexran_agent_timer.c b/openair2/ENB_APP/flexran_agent_timer.c
index ffbac399834668a1e873731794effaa755e81bcb..3a613ca8e5cd4f14f5cf3149f144de0522b49cde 100644
--- a/openair2/ENB_APP/flexran_agent_timer.c
+++ b/openair2/ENB_APP/flexran_agent_timer.c
@@ -58,6 +58,11 @@ struct timesync {
 
   int                            timer_num;
   flexran_agent_timer_element_t *timer[MAX_NUM_TIMERS];
+
+  int                            add_num;
+  flexran_agent_timer_element_t *add_list[MAX_NUM_TIMERS];
+  int                            remove_num;
+  int                            remove_list[MAX_NUM_TIMERS];
   pthread_mutex_t                mutex_timer;
 
   int             exit;
@@ -76,8 +81,12 @@ err_code_t flexran_agent_timer_init(mid_t mod_id) {
   sync->current = 0;
   sync->next    = 0;
   sync->timer_num    = 0;
-  for (int i = 0; i < MAX_NUM_TIMERS; ++i)
+  sync->add_num = 0;
+  sync->remove_num = 0;
+  for (int i = 0; i < MAX_NUM_TIMERS; ++i) {
     sync->timer[i] = NULL;
+    sync->add_list[i] = NULL;
+  }
   pthread_mutex_init(&sync->mutex_timer, NULL);
   sync->exit   = 0;
 
@@ -170,12 +179,19 @@ void *flexran_agent_timer_thread(void *args) {
       break;
 
     pthread_mutex_lock(&sync->mutex_timer);
-    sync->current++;
+    for (int i = 0; i < sync->add_num; ++i) {
+      sync->timer[sync->timer_num] = sync->add_list[i];
+      sync->timer_num++;
+    }
+    sync->add_num = 0;
+    for (int i = 0; i < sync->remove_num; ++i)
+      flexran_agent_timer_remove_internal(sync, sync->remove_list[i]);
+    sync->remove_num = 0;
+    pthread_mutex_unlock(&sync->mutex_timer);
 
-    if (sync->current < sync->next) {
-      pthread_mutex_unlock(&sync->mutex_timer);
+    sync->current++;
+    if (sync->current < sync->next)
       continue;
-    }
 
     for (int i = 0; i < sync->timer_num; ++i) {
       flexran_agent_timer_element_t *t = sync->timer[i];
@@ -186,7 +202,6 @@ void *flexran_agent_timer_thread(void *args) {
       if (sync->next == sync->current || t->next < sync->next)
         sync->next = t->next;
     }
-    pthread_mutex_unlock(&sync->mutex_timer);
   }
   LOG_W(FLEXRAN_AGENT, "terminated timer thread\n");
   return NULL;
@@ -230,45 +245,35 @@ err_code_t flexran_agent_create_timer(mid_t    mod_id,
 
   struct timesync *sync = &timesync[mod_id];
   pthread_mutex_lock(&sync->mutex_timer);
-  if (sync->timer_num >= MAX_NUM_TIMERS) {
+  if (sync->timer_num + sync->add_num >= MAX_NUM_TIMERS) {
     pthread_mutex_unlock(&sync->mutex_timer);
     LOG_E(FLEXRAN_AGENT, "maximum number of timers (%d) reached while adding timer %d\n",
-          sync->timer_num, xid);
+          sync->timer_num + sync->add_num, xid);
     free(t);
     return TIMER_SETUP_FAILED;
   }
   /* TODO check that xid does not exist? */
-  t->next = sync->current + 1;
+  t->next = sync->current + sf;
   if (sync->next <= sync->current || t->next < sync->next)
     sync->next = t->next;
-  sync->timer[sync->timer_num] = t;
-  sync->timer_num++;
-  pthread_mutex_unlock(&sync->mutex_timer);
-  LOG_D(FLEXRAN_AGENT, "added new timer xid %d for agent %d\n", xid, mod_id);
-  return 0;
-}
-
-err_code_t flexran_agent_destroy_timers(mid_t mod_id) {
-  struct timesync *sync = &timesync[mod_id];
-  pthread_mutex_lock(&sync->mutex_timer);
-  for (int i = sync->timer_num - 1; i < 0; --i) {
-    flexran_agent_timer_remove_internal(sync, i);
-  }
+  sync->add_list[sync->add_num] = t;
+  sync->add_num++;
   pthread_mutex_unlock(&sync->mutex_timer);
+  LOG_I(FLEXRAN_AGENT, "added new timer xid %d for agent %d\n", xid, mod_id);
   return 0;
 }
 
 err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid) {
   struct timesync *sync = &timesync[mod_id];
-  pthread_mutex_lock(&sync->mutex_timer);
   for (int i = 0; i < sync->timer_num; ++i) {
     if (sync->timer[i]->xid == xid) {
-      flexran_agent_timer_remove_internal(sync, i);
+      pthread_mutex_lock(&sync->mutex_timer);
+      sync->remove_list[sync->remove_num] = i;
+      sync->remove_num++;
       pthread_mutex_unlock(&sync->mutex_timer);
       return 0;
     }
   }
-  pthread_mutex_unlock(&sync->mutex_timer);
   LOG_E(FLEXRAN_AGENT, "could not find timer %d\n", xid);
   return TIMER_ELEMENT_NOT_FOUND;
 }
@@ -277,7 +282,8 @@ err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid) {
 void flexran_agent_timer_remove_internal(struct timesync *sync, int index) {
   LOG_I(FLEXRAN_AGENT, "remove timer xid %d (index %d) for agent %d\n",
         sync->timer[index]->xid, index, sync->timer[index]->mod_id);
-  flexran_agent_destroy_flexran_message(sync->timer[index]->msg);
+  if (sync->timer[index]->msg)
+    flexran_agent_destroy_flexran_message(sync->timer[index]->msg);
   free(sync->timer[index]);
   for (int i = index + 1; i < sync->timer_num; ++i)
     sync->timer[i - 1] = sync->timer[i];
diff --git a/openair2/ENB_APP/flexran_agent_timer.h b/openair2/ENB_APP/flexran_agent_timer.h
index b1cf8df51f3d13f1e2452d24ad645a6dc230df49..c61f2543a3bf04b659deec8bfc4fa427d49ffcba 100644
--- a/openair2/ENB_APP/flexran_agent_timer.h
+++ b/openair2/ENB_APP/flexran_agent_timer.h
@@ -61,9 +61,6 @@ err_code_t flexran_agent_create_timer(mid_t    mod_id,
                                       flexran_agent_timer_callback_t cb,
                                       Protocol__FlexranMessage *msg);
 
-/* Destroy all existing timers */
-err_code_t flexran_agent_destroy_timers(mid_t mod_id);
-
 /* Destroy the timer for task with id xid */
 err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid);
 
diff --git a/openair2/ENB_APP/flexran_apps/imsi.c b/openair2/ENB_APP/flexran_apps/imsi.c
new file mode 100644
index 0000000000000000000000000000000000000000..6f75033f38757258e2dccfd56ced68b26f94bae6
--- /dev/null
+++ b/openair2/ENB_APP/flexran_apps/imsi.c
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file imsi.c
+ * \brief flexran IMSI UE-slice association app
+ * \author Robert Schmidt, Firas Abdeljelil
+ * \date 2020
+ * \version 0.1
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+#include "flexran_agent_common.h"
+#include "flexran_agent_async.h"
+#include "flexran_agent_extern.h"
+#include "flexran_agent_timer.h"
+#include "flexran_agent_defs.h"
+#include "flexran_agent_net_comm.h"
+#include "flexran_agent_ran_api.h"
+#include "flexran_agent_phy.h"
+#include "flexran_agent_mac.h"
+#include "flexran_agent_rrc.h"
+#include "flexran_agent_pdcp.h"
+#include "flexran_agent_s1ap.h"
+#include "flexran_agent_app.h"
+#include "common/utils/LOG/log.h"
+#include "assertions.h"
+#include "queue.h"
+typedef struct Map_regex_slice slist_data_t;
+struct Map_regex_slice {
+  char regex_[30] ;
+  int slice_dl;
+  int slice_ul;
+  regex_t regex;
+  SLIST_ENTRY (Map_regex_slice) entries;
+};
+bool reconfig = false;
+int ue[MAX_MOBILES_PER_ENB]={ [0 ... MAX_MOBILES_PER_ENB - 1] = -1 };
+
+SLIST_HEAD(slisthead, Map_regex_slice) head;
+
+int dump_list(void) {
+  int len = 1;
+  slist_data_t *datap = NULL;
+  SLIST_FOREACH(datap, &head, entries) {
+    LOG_W(FLEXRAN_AGENT, "%d: regex %s DL %d UL %d\n", len++, datap->regex_,
+          datap->slice_dl, datap->slice_ul);
+  }
+  return len;
+}
+
+void add(char *regex, int dl, int ul) {
+  slist_data_t *datap = malloc(sizeof(slist_data_t));
+  if (!datap) {
+    LOG_E(FLEXRAN_AGENT, "cannot allocate memory for slist_data_t\n");
+    return;
+  }
+  strcpy(datap->regex_, regex);
+  datap->slice_dl = dl;
+  datap->slice_ul = ul;
+  regcomp(&datap->regex, datap->regex_, 0);
+  SLIST_INSERT_HEAD(&head, datap, entries);
+    if (ul==-1)
+      LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice DL %d\n" ,regex, dl);
+    else if (dl==-1)
+      LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice UL %d\n" ,regex, ul);
+    else 
+      LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice DL %d UL %d\n",
+            regex, dl, ul);
+}
+void remove_(char *regex) {
+  slist_data_t *datap = NULL;
+  SLIST_FOREACH(datap, &head, entries) {
+    if (strcmp(datap->regex_, regex) == 0) {
+      SLIST_REMOVE(&head, datap, Map_regex_slice, entries);
+      if (datap->slice_dl==-1)
+        LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice UL %d\n",datap->regex_, datap->slice_ul);
+      else if (datap->slice_ul==-1)
+        LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice DL %d\n",datap->regex_, datap->slice_dl);
+      else
+      LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice DL %d UL %d\n",
+            datap->regex_, datap->slice_dl, datap->slice_ul);
+      regfree(&datap->regex);
+      free(datap);
+      return;
+    }
+  }
+  LOG_E(FLEXRAN_AGENT, "no regex %s found in list\n", regex);
+}
+
+Protocol__FlexranMessage *matching_tick(
+    mid_t mod_id,
+    const Protocol__FlexranMessage *msg) {
+  const int num = flexran_get_mac_num_ues(mod_id);
+  for (int i = 0; i < num; i++) {
+    int ue_id = flexran_get_mac_ue_id(mod_id, i);
+    if (ue[ue_id] >= 0 && !reconfig)
+      continue;
+    rnti_t rnti = flexran_get_mac_ue_crnti(mod_id, ue_id);
+    uint64_t imsi = flexran_get_ue_imsi(mod_id, rnti);
+    char snum[20];
+    sprintf(snum, "%" PRIu64, imsi);
+    slist_data_t *datap = NULL;
+    SLIST_FOREACH(datap, &head, entries) {
+      if (regexec(&datap->regex, snum, 0, NULL, 0) == 0) {
+
+        int dl = datap->slice_dl;
+        if (dl >= 0 && dl != flexran_get_ue_dl_slice_id(mod_id, ue_id)) {
+          int ra = flexran_set_ue_dl_slice_id(mod_id, ue_id, dl);
+          if (ra)
+            LOG_I(FLEXRAN_AGENT,
+                  "RNTI %04x/IMSI %s in the slice_dl %d\n",
+                  rnti,
+                  snum,
+                  dl);
+          else
+            LOG_W(FLEXRAN_AGENT, "No such DL slice %d\n", dl);
+        }
+
+        int ul = datap->slice_ul;
+        if (ul >= 0 && ul != flexran_get_ue_ul_slice_id(mod_id, ue_id)) {
+          int rb = flexran_set_ue_ul_slice_id(mod_id, ue_id, ul);
+          if (rb)
+            LOG_I(FLEXRAN_AGENT,
+                  "RNTI %04x/IMSI %s in the slice_ul %d\n",
+                  rnti,
+                  snum,
+                  ul);
+          else
+            LOG_W(FLEXRAN_AGENT, "No such DL slice %d\n", dl);
+        }
+      }
+    }
+    ue[ue_id] = ue_id;
+  }
+  reconfig = false;
+  return NULL;
+}
+void add_param(Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, int n_p) {
+  char s[30];
+  reconfig = true;
+  bool supp=false;
+  bool sl_E=false; 
+  int dl=-1;
+  int ul=-1;
+  slist_data_t *datap = NULL;
+  for (int i = 0; i < n_p; ++i) {
+    switch (p[i]->value->param_case) {
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM__NOT_SET:
+        LOG_I(FLEXRAN_AGENT, "    param %s is not set\n", p[i]->key);
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_INTEGER: 
+        if( strcmp(p[i]->key, "slice_dl")==0)
+           dl= p[i]->value->integer;    
+        else
+          ul= p[i]->value->integer;    
+        sl_E=true; 
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_STR:
+        strcpy(s,p[i]->value->str);  
+        break;
+      default:
+        LOG_W(FLEXRAN_AGENT, "    type of param %s is unknown\n", p[i]->key);
+        break;
+    };
+  }
+  if (!SLIST_EMPTY(&head)){
+    SLIST_FOREACH(datap, &head, entries) {
+      if (strcmp(datap->regex_, s) == 0) {
+        if (datap->slice_dl!=dl || datap->slice_ul!=ul)
+          supp=true;
+      }
+    }
+  }
+  if (sl_E==false)
+    remove_(s);
+  else {
+    if (supp==true)
+      remove_(s);
+    add(s,dl,ul);
+  }
+  /* re-verify all UEs again */
+  for (int i = 0; i < MAX_MOBILES_PER_ENB; ++i)
+    ue[i] = -1;
+}
+/* imsi_start: entry point for imsi app. Installs a callback using the timer
+ * API to have function imsi_tick() called every 5s */
+#define TIMER 32
+int imsi_start(mid_t mod_id,
+               Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+               int n_p)
+{
+  LOG_W(FLEXRAN_AGENT, "%s(): enable IMSI app timer\n", __func__);
+  flexran_agent_create_timer(mod_id,
+                             500,
+                             FLEXRAN_AGENT_TIMER_TYPE_PERIODIC,
+                             TIMER,
+                             matching_tick,
+                             NULL);
+  SLIST_INIT(&head);
+  return 0;
+}
+
+int imsi_reconfig(mid_t mod_id,
+                  Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+                  int n_p)
+{ 
+  add_param(p, n_p);
+  return 0;
+}
+
+/* stop function: cleans up before app can be unloaded. Here: stop the timer */
+int imsi_stop(mid_t mod_id) {
+  LOG_W(FLEXRAN_AGENT, "%s(): disable IMSI app timer\n", __func__);
+  flexran_agent_destroy_timer(mod_id, TIMER);
+  
+  return 0;
+}
+
+/* app definition. visibility should be set to default so this symbol is
+ * reachable from outside */
+flexran_agent_app_t __attribute__ ((visibility ("default"))) app = {
+  .start = imsi_start,
+  .reconfig = imsi_reconfig,
+  .stop =  imsi_stop
+};
diff --git a/openair2/ENB_APP/flexran_apps/sample.c b/openair2/ENB_APP/flexran_apps/sample.c
new file mode 100644
index 0000000000000000000000000000000000000000..65be2a43df388e1cc8654f2e52efcfa6e69f0600
--- /dev/null
+++ b/openair2/ENB_APP/flexran_apps/sample.c
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file sample.c
+ * \brief flexran sample app
+ * \author Robert Schmidt
+ * \date 2020
+ * \version 0.1
+ */
+
+#include "flexran_agent_common.h"
+#include "flexran_agent_async.h"
+#include "flexran_agent_extern.h"
+#include "flexran_agent_timer.h"
+#include "flexran_agent_defs.h"
+#include "flexran_agent_net_comm.h"
+#include "flexran_agent_ran_api.h"
+#include "flexran_agent_phy.h"
+#include "flexran_agent_mac.h"
+#include "flexran_agent_rrc.h"
+#include "flexran_agent_pdcp.h"
+#include "flexran_agent_s1ap.h"
+#include "flexran_agent_app.h"
+#include "common/utils/LOG/log.h"
+#include "assertions.h"
+
+/* callback to be called for event: shows exemplary variable use and use of RAN
+ * API */
+int tick;
+Protocol__FlexranMessage *sample_tick(
+    mid_t mod_id,
+    const Protocol__FlexranMessage *msg) {
+  tick++;
+  const int num = flexran_get_mac_num_ues(mod_id);
+  LOG_I(FLEXRAN_AGENT, "%s(): tick %d number UEs %d\n", __func__, tick, num);
+  return NULL;
+}
+
+void print_param(Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, int n_p) {
+  LOG_I(FLEXRAN_AGENT, "%s(): received %d parameters\n", __func__, n_p);
+  for (int i = 0; i < n_p; ++i) {
+    switch (p[i]->value->param_case) {
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM__NOT_SET:
+        LOG_I(FLEXRAN_AGENT, "    param %s is not set\n", p[i]->key);
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_INTEGER:
+        LOG_I(FLEXRAN_AGENT, "    param %s is %d\n", p[i]->key, p[i]->value->integer);
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_FLOATING:
+        LOG_I(FLEXRAN_AGENT, "    param %s is %f\n", p[i]->key, p[i]->value->floating);
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_BOOLEAN:
+        LOG_I(FLEXRAN_AGENT, "    param %s is %s\n", p[i]->key, p[i]->value->boolean ? "true" : "false");
+        break;
+      case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_STR:
+        LOG_I(FLEXRAN_AGENT, "    param %s is %s\n", p[i]->key, p[i]->value->str);
+        break;
+      default:
+        LOG_W(FLEXRAN_AGENT, "    type of param %s is unknown\n", p[i]->key);
+        break;
+    };
+  }
+}
+
+/* sa_start: entry point for sample app. Installs a callback using the timer
+ * API to have function sample_tick() called every 5s */
+#define TIMER 22
+int sa_start(mid_t mod_id,
+             Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+             int n_p) {
+  LOG_W(FLEXRAN_AGENT, "%s(): enable sample app timer\n", __func__);
+  flexran_agent_create_timer(mod_id,
+                             5000,
+                             FLEXRAN_AGENT_TIMER_TYPE_PERIODIC,
+                             TIMER,
+                             sample_tick,
+                             NULL);
+  print_param(p, n_p);
+  return 0;
+}
+
+int sa_reconfig(mid_t mod_id,
+                Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p,
+                int n_p)
+{
+  print_param(p, n_p);
+  return 0;
+}
+
+/* stop function: cleans up before app can be unloaded. Here: stop the timer */
+int sa_stop(mid_t mod_id) {
+  LOG_W(FLEXRAN_AGENT, "%s(): disable sample app timer\n", __func__);
+  flexran_agent_destroy_timer(mod_id, TIMER);
+  return 0;
+}
+
+/* app definition. visibility should be set to default so this symbol is
+ * reachable from outside */
+flexran_agent_app_t __attribute__ ((visibility ("default"))) app = {
+  .start = sa_start,
+  .reconfig = sa_reconfig,
+  .stop =  sa_stop
+};