From 536143c1d708dcf23d9978bd7d861125d11273a7 Mon Sep 17 00:00:00 2001
From: Slava Monich <slava.monich@jolla.com>
Date: Wed, 20 Jan 2021 18:56:57 +0200
Subject: [PATCH] [gbinder] Fixed a threading issue. JB#42956

Make sure that transaction handlers don't get invoked on a worker thread.
It was possible in cases if an incoming transaction arrives while we are
waiting for response from an asynchronous call to servicemanager (which
is actually a synchronous call made on a worker thread).

Invoking callbacks on a worker thread would completely break libgbinder's
synchronization model. It was a pretty unlikely scenario, though.
---
 src/gbinder_client.c                          | 107 +++++----
 src/gbinder_client_p.h                        |  22 +-
 src/gbinder_ipc.c                             | 212 ++++++++++++------
 src/gbinder_ipc.h                             |  46 ++--
 src/gbinder_servicemanager.c                  |  27 ++-
 src/gbinder_servicemanager_aidl.c             |  27 ++-
 src/gbinder_servicemanager_hidl.c             |  27 ++-
 src/gbinder_servicemanager_p.h                |  15 +-
 src/gbinder_types_p.h                         |   5 +-
 unit/unit_ipc/unit_ipc.c                      |  25 ++-
 .../unit_servicemanager/unit_servicemanager.c |  13 +-
 unit/unit_servicename/unit_servicename.c      |  13 +-
 unit/unit_servicepoll/unit_servicepoll.c      |  13 +-
 13 files changed, 353 insertions(+), 199 deletions(-)

diff --git a/src/gbinder_client.c b/src/gbinder_client.c
index d54bbe6..4d9c0e3 100644
--- a/src/gbinder_client.c
+++ b/src/gbinder_client.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -178,6 +178,66 @@ gbinder_client_transact_destroy(
     g_slice_free(GBinderClientTx, tx);
 }
 
+/*==========================================================================*
+ * Internal interface
+ *==========================================================================*/
+
+GBinderRemoteReply*
+gbinder_client_transact_sync_reply2(
+    GBinderClient* self,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status,
+    const GBinderIpcSyncApi* api)
+{
+    if (G_LIKELY(self)) {
+        GBinderRemoteObject* obj = self->remote;
+
+        if (G_LIKELY(!obj->dead)) {
+            if (!req) {
+                const GBinderClientIfaceRange* r = gbinder_client_find_range
+                    (gbinder_client_cast(self), code);
+
+                /* Default empty request (just the header, no parameters) */
+                if (r) {
+                    req = r->basic_req;
+                }
+            }
+            return api->sync_reply(obj->ipc, obj->handle, code, req, status);
+        }
+        GDEBUG("Refusing to perform transaction with a dead object");
+    }
+    return NULL;
+}
+
+int
+gbinder_client_transact_sync_oneway2(
+    GBinderClient* self,
+    guint32 code,
+    GBinderLocalRequest* req,
+    const GBinderIpcSyncApi* api)
+{
+    if (G_LIKELY(self)) {
+        GBinderRemoteObject* obj = self->remote;
+
+        if (G_LIKELY(!obj->dead)) {
+            if (!req) {
+                const GBinderClientIfaceRange* r = gbinder_client_find_range
+                    (gbinder_client_cast(self), code);
+
+                /* Default empty request (just the header, no parameters) */
+                if (r) {
+                    req = r->basic_req;
+                }
+            }
+            return api->sync_oneway(obj->ipc, obj->handle, code, req);
+        }
+        GDEBUG("Refusing to perform transaction with a dead object");
+        return (-ESTALE);
+    }
+    return (-EINVAL);
+}
+
 /*==========================================================================*
  * Interface
  *==========================================================================*/
@@ -319,25 +379,8 @@ gbinder_client_transact_sync_reply(
     GBinderLocalRequest* req,
     int* status)
 {
-    if (G_LIKELY(self)) {
-        GBinderRemoteObject* obj = self->remote;
-
-        if (G_LIKELY(!obj->dead)) {
-            if (!req) {
-                const GBinderClientIfaceRange* r = gbinder_client_find_range
-                    (gbinder_client_cast(self), code);
-
-                /* Default empty request (just the header, no parameters) */
-                if (r) {
-                    req = r->basic_req;
-                }
-            }
-            return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
-                code, req, status);
-        }
-        GDEBUG("Refusing to perform transaction with a dead object");
-    }
-    return NULL;
+    return gbinder_client_transact_sync_reply2(self, code, req, status,
+        &gbinder_ipc_sync_main);
 }
 
 int
@@ -346,26 +389,8 @@ gbinder_client_transact_sync_oneway(
     guint32 code,
     GBinderLocalRequest* req)
 {
-    if (G_LIKELY(self)) {
-        GBinderRemoteObject* obj = self->remote;
-
-        if (G_LIKELY(!obj->dead)) {
-            if (!req) {
-                const GBinderClientIfaceRange* r = gbinder_client_find_range
-                    (gbinder_client_cast(self), code);
-
-                /* Default empty request (just the header, no parameters) */
-                if (r) {
-                    req = r->basic_req;
-                }
-            }
-            return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
-                code, req);
-        }
-        GDEBUG("Refusing to perform transaction with a dead object");
-        return (-ESTALE);
-    }
-    return (-EINVAL);
+    return gbinder_client_transact_sync_oneway2(self, code, req,
+        &gbinder_ipc_sync_main);
 }
 
 gulong
diff --git a/src/gbinder_client_p.h b/src/gbinder_client_p.h
index 55bae03..3b3f5c3 100644
--- a/src/gbinder_client_p.h
+++ b/src/gbinder_client_p.h
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -41,6 +41,24 @@ struct gbinder_client {
     GBinderRemoteObject* remote;
 };
 
+GBinderRemoteReply*
+gbinder_client_transact_sync_reply2(
+    GBinderClient* self,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status,
+    const GBinderIpcSyncApi* api)
+    G_GNUC_WARN_UNUSED_RESULT
+    GBINDER_INTERNAL;
+
+int
+gbinder_client_transact_sync_oneway2(
+    GBinderClient* self,
+    guint32 code,
+    GBinderLocalRequest* req,
+    const GBinderIpcSyncApi* api)
+    GBINDER_INTERNAL;
+
 #define gbinder_client_ipc(client) ((client)->remote->ipc)
 
 #endif /* GBINDER_CLIENT_PRIVATE_H */
diff --git a/src/gbinder_ipc.c b/src/gbinder_ipc.c
index a7c6f2e..5831474 100644
--- a/src/gbinder_ipc.c
+++ b/src/gbinder_ipc.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -210,6 +210,23 @@ GBinderIpcLooper*
 gbinder_ipc_looper_new(
     GBinderIpc* ipc);
 
+static
+GBinderRemoteReply*
+gbinder_ipc_transact_sync_reply_worker(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status);
+
+static
+int
+gbinder_ipc_transact_sync_oneway_worker(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req);
+
 /*==========================================================================*
  * Utilities
  *==========================================================================*/
@@ -1433,30 +1450,15 @@ void
 gbinder_ipc_tx_internal_exec(
     GBinderIpcTxPriv* priv)
 {
-    static const GBinderHandlerFunctions handler_fn = {
-        .can_loop = NULL,
-        .transact = gbinder_ipc_tx_handler_transact
-    };
     GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv);
-    GBinderIpcTx* pub = &priv->pub;
-    GBinderIpc* self = pub->ipc;
-    GBinderObjectRegistry* reg = &self->priv->object_registry;
-    GBinderHandler handler = { &handler_fn };
+    GBinderIpc* ipc = priv->pub.ipc;
 
-    /* Perform synchronous transaction */
     if (tx->flags & GBINDER_TX_FLAG_ONEWAY) {
-        tx->status = gbinder_driver_transact(self->driver, reg, &handler,
-            tx->handle, tx->code, tx->req, NULL);
+        tx->status = gbinder_ipc_transact_sync_oneway_worker(ipc, tx->handle,
+            tx->code, tx->req);
     } else {
-        tx->reply = gbinder_remote_reply_new(&self->priv->object_registry);
-        tx->status = gbinder_driver_transact(self->driver, reg, &handler,
-            tx->handle, tx->code, tx->req, tx->reply);
-        if (tx->status != GBINDER_STATUS_OK &&
-            gbinder_remote_reply_is_empty(tx->reply)) {
-            /* Drop useless reply */
-            gbinder_remote_reply_unref(tx->reply);
-            tx->reply = NULL;
-        }
+        tx->reply = gbinder_ipc_transact_sync_reply_worker(ipc, tx->handle,
+            tx->code, tx->req, &tx->status);
     }
 }
 
@@ -1579,6 +1581,128 @@ gbinder_ipc_tx_proc(
     gbinder_idle_callback_schedule(tx->completion);
 }
 
+/*==========================================================================*
+ * GBinderIpcSyncApi for worker threads
+ *==========================================================================*/
+
+static
+GBinderRemoteReply*
+gbinder_ipc_transact_sync_reply_worker(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status)
+{
+    /* Must be invoked on worker thread */
+    if (G_LIKELY(self)) {
+        static const GBinderHandlerFunctions handler_fn = {
+            .can_loop = NULL,
+            .transact = gbinder_ipc_tx_handler_transact
+        };
+        GBinderHandler handler = { &handler_fn };
+        GBinderIpcPriv* priv = self->priv;
+        GBinderObjectRegistry* reg = &priv->object_registry;
+        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
+        int ret = gbinder_driver_transact(self->driver, reg, &handler,
+            handle, code, req, reply);
+
+        if (status) *status = ret;
+        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
+            return reply;
+        } else {
+            gbinder_remote_reply_unref(reply);
+        }
+    } else {
+        if (status) *status = (-EINVAL);
+    }
+    return NULL;
+}
+
+static
+int
+gbinder_ipc_transact_sync_oneway_worker(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req)
+{
+    /* Must be invoked on worker thread */
+    if (G_LIKELY(self)) {
+        static const GBinderHandlerFunctions handler_fn = {
+            .can_loop = NULL,
+            .transact = gbinder_ipc_tx_handler_transact
+        };
+        GBinderHandler handler = { &handler_fn };
+        GBinderIpcPriv* priv = self->priv;
+
+        return gbinder_driver_transact(self->driver, &priv->object_registry,
+            &handler, handle, code, req, NULL);
+    } else {
+        return (-EINVAL);
+    }
+}
+
+const GBinderIpcSyncApi gbinder_ipc_sync_worker = {
+    .sync_reply = gbinder_ipc_transact_sync_reply_worker,
+    .sync_oneway = gbinder_ipc_transact_sync_oneway_worker
+};
+
+/*==========================================================================*
+ * GBinderIpcSyncApi for the main thread
+ *==========================================================================*/
+
+static
+GBinderRemoteReply*
+gbinder_ipc_transact_sync_reply(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status)
+{
+    if (G_LIKELY(self)) {
+        GBinderIpcPriv* priv = self->priv;
+        GBinderObjectRegistry* reg = &priv->object_registry;
+        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
+        int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
+            code, req, reply);
+
+        if (status) *status = ret;
+        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
+            return reply;
+        } else {
+            gbinder_remote_reply_unref(reply);
+        }
+    } else {
+        if (status) *status = (-EINVAL);
+    }
+    return NULL;
+}
+
+static
+int
+gbinder_ipc_transact_sync_oneway(
+    GBinderIpc* self,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req)
+{
+    if (G_LIKELY(self)) {
+        GBinderIpcPriv* priv = self->priv;
+
+        return gbinder_driver_transact(self->driver, &priv->object_registry,
+            NULL, handle, code, req, NULL);
+    } else {
+        return (-EINVAL);
+    }
+}
+
+const GBinderIpcSyncApi gbinder_ipc_sync_main = {
+    .sync_reply = gbinder_ipc_transact_sync_reply,
+    .sync_oneway = gbinder_ipc_transact_sync_oneway
+};
+
 /*==========================================================================*
  * Interface
  *==========================================================================*/
@@ -1652,50 +1776,6 @@ gbinder_ipc_object_registry(
     return G_LIKELY(self) ? &self->priv->object_registry : NULL;
 }
 
-GBinderRemoteReply*
-gbinder_ipc_transact_sync_reply(
-    GBinderIpc* self,
-    guint32 handle,
-    guint32 code,
-    GBinderLocalRequest* req,
-    int* status)
-{
-    if (G_LIKELY(self)) {
-        GBinderIpcPriv* priv = self->priv;
-        GBinderObjectRegistry* reg = &priv->object_registry;
-        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
-        int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
-            code, req, reply);
-
-        if (status) *status = ret;
-        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
-            return reply;
-        } else {
-            gbinder_remote_reply_unref(reply);
-        }
-    } else {
-        if (status) *status = (-EINVAL);
-    }
-    return NULL;
-}
-
-int
-gbinder_ipc_transact_sync_oneway(
-    GBinderIpc* self,
-    guint32 handle,
-    guint32 code,
-    GBinderLocalRequest* req)
-{
-    if (G_LIKELY(self)) {
-        GBinderIpcPriv* priv = self->priv;
-
-        return gbinder_driver_transact(self->driver, &priv->object_registry,
-            NULL, handle, code, req, NULL);
-    } else {
-        return (-EINVAL);
-    }
-}
-
 gulong
 gbinder_ipc_transact(
     GBinderIpc* self,
diff --git a/src/gbinder_ipc.h b/src/gbinder_ipc.h
index 87fb4a8..09e7697 100644
--- a/src/gbinder_ipc.h
+++ b/src/gbinder_ipc.h
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -67,6 +67,31 @@ void
     int status,
     void* user_data);
 
+typedef
+GBinderRemoteReply*
+(*GBinderIpcSyncReplyFunc)(
+    GBinderIpc* ipc,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req,
+    int* status);
+
+typedef
+int
+(*GBinderIpcSyncOnewayFunc)(
+    GBinderIpc* ipc,
+    guint32 handle,
+    guint32 code,
+    GBinderLocalRequest* req);
+
+struct gbinder_ipc_sync_api {
+    GBinderIpcSyncReplyFunc sync_reply;
+    GBinderIpcSyncOnewayFunc sync_oneway;
+};
+
+extern const GBinderIpcSyncApi gbinder_ipc_sync_main GBINDER_INTERNAL;
+extern const GBinderIpcSyncApi gbinder_ipc_sync_worker GBINDER_INTERNAL;
+
 GBinderIpc*
 gbinder_ipc_new(
     const char* dev)
@@ -111,23 +136,6 @@ gbinder_ipc_invalidate_remote_handle(
     guint32 handle)
     GBINDER_INTERNAL;
 
-GBinderRemoteReply*
-gbinder_ipc_transact_sync_reply(
-    GBinderIpc* ipc,
-    guint32 handle,
-    guint32 code,
-    GBinderLocalRequest* req,
-    int* status)
-    GBINDER_INTERNAL;
-
-int
-gbinder_ipc_transact_sync_oneway(
-    GBinderIpc* ipc,
-    guint32 handle,
-    guint32 code,
-    GBinderLocalRequest* req)
-    GBINDER_INTERNAL;
-
 gulong
 gbinder_ipc_transact(
     GBinderIpc* ipc,
diff --git a/src/gbinder_servicemanager.c b/src/gbinder_servicemanager.c
index 45dbb27..71b1aba 100644
--- a/src/gbinder_servicemanager.c
+++ b/src/gbinder_servicemanager.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -199,7 +199,8 @@ gbinder_servicemanager_list_tx_exec(
 {
     GBinderServiceManagerListTxData* data = tx->user_data;
 
-    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->list(data->sm);
+    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
+        list(data->sm, &gbinder_ipc_sync_worker);
 }
 
 static
@@ -243,8 +244,9 @@ gbinder_servicemanager_get_service_tx_exec(
 {
     GBinderServiceManagerGetServiceTxData* data = tx->user_data;
 
-    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->get_service
-            (data->sm, data->name, &data->status);
+    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
+        get_service(data->sm, data->name, &data->status,
+            &gbinder_ipc_sync_worker);
 }
 
 static
@@ -286,8 +288,8 @@ gbinder_servicemanager_add_service_tx_exec(
 {
     GBinderServiceManagerAddServiceTxData* data = tx->user_data;
 
-    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->add_service
-            (data->sm, data->name, data->obj);
+    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
+        add_service(data->sm, data->name, data->obj, &gbinder_ipc_sync_worker);
 }
 
 static
@@ -776,7 +778,8 @@ gbinder_servicemanager_list_sync(
     GBinderServiceManager* self)
 {
     if (G_LIKELY(self)) {
-        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->list(self);
+        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
+            list(self, &gbinder_ipc_sync_main);
     }
     return NULL;
 }
@@ -815,8 +818,8 @@ gbinder_servicemanager_get_service_sync(
     GBinderRemoteObject* obj = NULL;
 
     if (G_LIKELY(self) && name) {
-        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->get_service
-            (self, name, status);
+        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->
+            get_service(self, name, status, &gbinder_ipc_sync_main);
         if (obj) {
             GBinderServiceManagerPriv* priv = self->priv;
 
@@ -866,8 +869,8 @@ gbinder_servicemanager_add_service_sync(
     GBinderLocalObject* obj)
 {
     if (G_LIKELY(self) && name && obj) {
-        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->add_service
-            (self, name, obj);
+        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
+            add_service(self, name, obj, &gbinder_ipc_sync_main);
     } else {
         return (-EINVAL);
     }
diff --git a/src/gbinder_servicemanager_aidl.c b/src/gbinder_servicemanager_aidl.c
index 08322ee..3efb39d 100644
--- a/src/gbinder_servicemanager_aidl.c
+++ b/src/gbinder_servicemanager_aidl.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -35,9 +35,9 @@
 #include "gbinder_servicemanager_aidl.h"
 #include "gbinder_servicepoll.h"
 #include "gbinder_eventloop_p.h"
+#include "gbinder_client_p.h"
 #include "gbinder_log.h"
 
-#include <gbinder_client.h>
 #include <gbinder_local_request.h>
 #include <gbinder_remote_reply.h>
 
@@ -172,7 +172,8 @@ gbinder_servicemanager_aidl_add_service_req(
 static
 char**
 gbinder_servicemanager_aidl_list(
-    GBinderServiceManager* manager)
+    GBinderServiceManager* manager,
+    const GBinderIpcSyncApi* api)
 {
     GPtrArray* list = g_ptr_array_new();
     GBinderClient* client = manager->client;
@@ -181,8 +182,8 @@ gbinder_servicemanager_aidl_list(
     GBinderLocalRequest* req = klass->list_services_req(client, 0);
     GBinderRemoteReply* reply;
 
-    while ((reply = gbinder_client_transact_sync_reply(client,
-        LIST_SERVICES_TRANSACTION, req, NULL)) != NULL) {
+    while ((reply = gbinder_client_transact_sync_reply2(client,
+        LIST_SERVICES_TRANSACTION, req, NULL, api)) != NULL) {
         char* service = gbinder_remote_reply_read_string16(reply);
 
         gbinder_remote_reply_unref(reply);
@@ -205,15 +206,16 @@ GBinderRemoteObject*
 gbinder_servicemanager_aidl_get_service(
     GBinderServiceManager* self,
     const char* name,
-    int* status)
+    int* status,
+    const GBinderIpcSyncApi* api)
 {
     GBinderRemoteObject* obj;
     GBinderRemoteReply* reply;
     GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 
     gbinder_local_request_append_string16(req, name);
-    reply = gbinder_client_transact_sync_reply(self->client,
-        CHECK_SERVICE_TRANSACTION, req, status);
+    reply = gbinder_client_transact_sync_reply2(self->client,
+        CHECK_SERVICE_TRANSACTION, req, status, api);
 
     obj = gbinder_remote_reply_read_object(reply);
     gbinder_remote_reply_unref(reply);
@@ -226,14 +228,15 @@ int
 gbinder_servicemanager_aidl_add_service(
     GBinderServiceManager* manager,
     const char* name,
-    GBinderLocalObject* obj)
+    GBinderLocalObject* obj,
+    const GBinderIpcSyncApi* api)
 {
     int status;
     GBinderClient* client = manager->client;
     GBinderLocalRequest* req = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS
         (manager)->add_service_req(client, name, obj);
-    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply(client,
-        ADD_SERVICE_TRANSACTION, req, &status);
+    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2(client,
+        ADD_SERVICE_TRANSACTION, req, &status, api);
 
     gbinder_remote_reply_unref(reply);
     gbinder_local_request_unref(req);
diff --git a/src/gbinder_servicemanager_hidl.c b/src/gbinder_servicemanager_hidl.c
index 242fd85..69c60f5 100644
--- a/src/gbinder_servicemanager_hidl.c
+++ b/src/gbinder_servicemanager_hidl.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -31,9 +31,9 @@
  */
 
 #include "gbinder_servicemanager_p.h"
+#include "gbinder_client_p.h"
 #include "gbinder_log.h"
 
-#include <gbinder_client.h>
 #include <gbinder_local_object.h>
 #include <gbinder_local_request.h>
 #include <gbinder_remote_reply.h>
@@ -147,11 +147,12 @@ gbinder_servicemanager_hidl_notification(
 static
 char**
 gbinder_servicemanager_hidl_list(
-    GBinderServiceManager* self)
+    GBinderServiceManager* self,
+    const GBinderIpcSyncApi* api)
 {
     GBinderLocalRequest* req = gbinder_client_new_request(self->client);
-    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
-        (self->client, LIST_TRANSACTION, req, NULL);
+    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2
+        (self->client, LIST_TRANSACTION, req, NULL, api);
 
     gbinder_local_request_unref(req);
     if (reply) {
@@ -178,7 +179,8 @@ GBinderRemoteObject*
 gbinder_servicemanager_hidl_get_service(
     GBinderServiceManager* self,
     const char* fqinstance,
-    int* status)
+    int* status,
+    const GBinderIpcSyncApi* api)
 {
     /* e.g. "android.hardware.radio@1.1::IRadio/slot1" */
     const char* sep = strchr(fqinstance, '/');
@@ -193,8 +195,8 @@ gbinder_servicemanager_hidl_get_service(
         gbinder_local_request_append_hidl_string(req, fqname);
         gbinder_local_request_append_hidl_string(req, name);
 
-        reply = gbinder_client_transact_sync_reply(self->client,
-            GET_TRANSACTION, req, status);
+        reply = gbinder_client_transact_sync_reply2(self->client,
+            GET_TRANSACTION, req, status, api);
 
         if (reply) {
             GBinderReader reader;
@@ -225,7 +227,8 @@ int
 gbinder_servicemanager_hidl_add_service(
     GBinderServiceManager* self,
     const char* name,
-    GBinderLocalObject* obj)
+    GBinderLocalObject* obj,
+    const GBinderIpcSyncApi* api)
 {
     int status;
     GBinderRemoteReply* reply;
@@ -235,8 +238,8 @@ gbinder_servicemanager_hidl_add_service(
     gbinder_local_request_append_hidl_string(req, name);
     gbinder_local_request_append_local_object(req, obj);
 
-    reply = gbinder_client_transact_sync_reply(self->client,
-        ADD_TRANSACTION, req, &status);
+    reply = gbinder_client_transact_sync_reply2(self->client,
+        ADD_TRANSACTION, req, &status, api);
 
     gbinder_remote_reply_unref(reply);
     gbinder_local_request_unref(req);
diff --git a/src/gbinder_servicemanager_p.h b/src/gbinder_servicemanager_p.h
index 5939026..ac472c3 100644
--- a/src/gbinder_servicemanager_p.h
+++ b/src/gbinder_servicemanager_p.h
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -66,12 +66,11 @@ typedef struct gbinder_servicemanager_class {
     const char* default_device;
 
     /* Methods (synchronous) */
-    char** (*list)(GBinderServiceManager* self);
-    GBinderRemoteObject* (*get_service)
-        (GBinderServiceManager* self, const char* name, int* status);
-    int (*add_service)
-        (GBinderServiceManager* self, const char* name,
-            GBinderLocalObject* obj);
+    char** (*list)(GBinderServiceManager* self, const GBinderIpcSyncApi* api);
+    GBinderRemoteObject* (*get_service)(GBinderServiceManager* self,
+        const char* name, int* status, const GBinderIpcSyncApi* api);
+    int (*add_service)(GBinderServiceManager* self, const char* name,
+        GBinderLocalObject* obj, const GBinderIpcSyncApi* api);
 
     /* Checking/normalizing watch names */
     GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name)
diff --git a/src/gbinder_types_p.h b/src/gbinder_types_p.h
index 580a915..aaf2284 100644
--- a/src/gbinder_types_p.h
+++ b/src/gbinder_types_p.h
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -45,6 +45,7 @@ typedef struct gbinder_output_data GBinderOutputData;
 typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
 typedef struct gbinder_servicepoll GBinderServicePoll;
 typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
+typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi;
 
 #define GBINDER_INLINE_FUNC static inline
 #define GBINDER_INTERNAL G_GNUC_INTERNAL
diff --git a/unit/unit_ipc/unit_ipc.c b/unit/unit_ipc/unit_ipc.c
index b21b8a6..282aba4 100644
--- a/unit/unit_ipc/unit_ipc.c
+++ b/unit/unit_ipc/unit_ipc.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -84,10 +84,16 @@ test_null(
 
     g_assert(!gbinder_ipc_ref(null));
     gbinder_ipc_unref(null);
-    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, NULL));
-    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, &status));
-    g_assert(status == (-EINVAL));
-    g_assert(gbinder_ipc_transact_sync_oneway(null, 0, 0, NULL) == (-EINVAL));
+    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, NULL));
+    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, &status));
+    g_assert_cmpint(status, == ,-EINVAL);
+    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, NULL));
+    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, &status));
+    g_assert_cmpint(status, == ,-EINVAL);
+    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(null, 0, 0, NULL), == ,
+        -EINVAL);
+    g_assert_cmpint(gbinder_ipc_sync_worker.sync_oneway(null, 0, 0, NULL), == ,
+        -EINVAL);
     g_assert(!gbinder_ipc_transact(null, 0, 0, 0, NULL, NULL, NULL, NULL));
     g_assert(!gbinder_ipc_transact_custom(null, NULL, NULL, NULL, NULL));
     g_assert(!gbinder_ipc_object_registry(null));
@@ -187,8 +193,7 @@ test_sync_oneway(
     GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 
     test_binder_br_transaction_complete(fd);
-    g_assert(gbinder_ipc_transact_sync_oneway(ipc, 0, 1, req) ==
-        GBINDER_STATUS_OK);
+    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0);
     gbinder_local_request_unref(req);
     gbinder_ipc_unref(ipc);
     gbinder_ipc_exit();
@@ -225,7 +230,7 @@ test_sync_reply_ok_status(
     test_binder_br_noop(fd);
     test_binder_br_reply(fd, handle, code, data->bytes);
 
-    tx_reply = gbinder_ipc_transact_sync_reply(ipc, handle, code, req, status);
+    tx_reply = gbinder_ipc_sync_main.sync_reply(ipc, handle, code, req, status);
     g_assert(tx_reply);
 
     result_out = gbinder_remote_reply_read_string16(tx_reply);
@@ -275,7 +280,7 @@ test_sync_reply_error(
     test_binder_br_noop(fd);
     test_binder_br_reply_status(fd, expected_status);
 
-    g_assert(!gbinder_ipc_transact_sync_reply(ipc, handle, code, req, &status));
+    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
     g_assert(status == expected_status);
 
     gbinder_local_request_unref(req);
diff --git a/unit/unit_servicemanager/unit_servicemanager.c b/unit/unit_servicemanager/unit_servicemanager.c
index 7b947cf..48ada68 100644
--- a/unit/unit_servicemanager/unit_servicemanager.c
+++ b/unit/unit_servicemanager/unit_servicemanager.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -157,7 +157,8 @@ typedef struct test_servicemanager {
 static
 char**
 test_servicemanager_list(
-    GBinderServiceManager* sm)
+    GBinderServiceManager* sm,
+    const GBinderIpcSyncApi* api)
 {
     TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 
@@ -169,7 +170,8 @@ GBinderRemoteObject*
 test_servicemanager_get_service(
     GBinderServiceManager* sm,
     const char* name,
-    int* status)
+    int* status,
+    const GBinderIpcSyncApi* api)
 {
     TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 
@@ -191,7 +193,8 @@ int
 test_servicemanager_add_service(
     GBinderServiceManager* sm,
     const char* name,
-    GBinderLocalObject* obj)
+    GBinderLocalObject* obj,
+    const GBinderIpcSyncApi* api)
 {
     TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 
diff --git a/unit/unit_servicename/unit_servicename.c b/unit/unit_servicename/unit_servicename.c
index 4f4a035..b18c245 100644
--- a/unit/unit_servicename/unit_servicename.c
+++ b/unit/unit_servicename/unit_servicename.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2019-2020 Jolla Ltd.
- * Copyright (C) 2019-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2019-2021 Jolla Ltd.
+ * Copyright (C) 2019-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -101,7 +101,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 static
 char**
 test_servicemanager_list(
-    GBinderServiceManager* manager)
+    GBinderServiceManager* manager,
+    const GBinderIpcSyncApi* api)
 {
     char** ret;
     TestServiceManager* self = TEST_SERVICEMANAGER(manager);
@@ -118,7 +119,8 @@ GBinderRemoteObject*
 test_servicemanager_get_service(
     GBinderServiceManager* manager,
     const char* name,
-    int* status)
+    int* status,
+    const GBinderIpcSyncApi* api)
 {
     *status = (-ENOENT);
     return NULL;
@@ -129,7 +131,8 @@ int
 test_servicemanager_add_service(
     GBinderServiceManager* manager,
     const char* name,
-    GBinderLocalObject* obj)
+    GBinderLocalObject* obj,
+    const GBinderIpcSyncApi* api)
 {
     TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 
diff --git a/unit/unit_servicepoll/unit_servicepoll.c b/unit/unit_servicepoll/unit_servicepoll.c
index cc2b79c..1c1af4b 100644
--- a/unit/unit_servicepoll/unit_servicepoll.c
+++ b/unit/unit_servicepoll/unit_servicepoll.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018-2020 Jolla Ltd.
- * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
+ * Copyright (C) 2018-2021 Jolla Ltd.
+ * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
  *
  * You may use this file under the terms of BSD license as follows:
  *
@@ -79,7 +79,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 static
 char**
 test_servicemanager_list(
-    GBinderServiceManager* manager)
+    GBinderServiceManager* manager,
+    const GBinderIpcSyncApi* api)
 {
     char** ret;
     TestServiceManager* self = TEST_SERVICEMANAGER(manager);
@@ -96,7 +97,8 @@ GBinderRemoteObject*
 test_servicemanager_get_service(
     GBinderServiceManager* manager,
     const char* name,
-    int* status)
+    int* status,
+    const GBinderIpcSyncApi* api)
 {
     *status = (-ENOENT);
     return NULL;
@@ -107,7 +109,8 @@ int
 test_servicemanager_add_service(
     GBinderServiceManager* manager,
     const char* name,
-    GBinderLocalObject* obj)
+    GBinderLocalObject* obj,
+    const GBinderIpcSyncApi* api)
 {
     TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 
-- 
GitLab