Compare commits

...

5 Commits

Author SHA1 Message Date
Slava Monich
e196c66264 Version 1.1.18 2022-01-08 15:39:43 +02:00
Slava Monich
8f62f4d65c Merge pull request #89 from monich/nodrop
Disassociate auto-created proxies
2022-01-08 15:28:20 +02:00
Slava Monich
8583a72d11 [gbinder] Disassociate auto-created proxies. JB#56889
They are independent on each other and can be destroyed in any order.
2022-01-07 21:22:32 +02:00
Slava Monich
381446eb4f [gbinder] Expose gbinder_ipc_name as an internal function
To be used by other objects e.g. GBinderProxyObject
2022-01-07 18:26:17 +02:00
Slava Monich
eabf5683f4 [gbinder] Logging improvements
Logging device name is useful for understanding what's going on when
multiple binder devices are involved.
2022-01-07 18:25:25 +02:00
9 changed files with 112 additions and 149 deletions

View File

@@ -16,7 +16,7 @@
VERSION_MAJOR = 1
VERSION_MINOR = 1
VERSION_RELEASE = 17
VERSION_RELEASE = 18
# Version for pkg-config
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)

6
debian/changelog vendored
View File

@@ -1,3 +1,9 @@
libgbinder (1.1.18) unstable; urgency=low
* Disassociate auto-created proxies to stop them from piling up
-- Slava Monich <slava.monich@jolla.com> Sat, 08 Jan 2022 15:35:56 +0200
libgbinder (1.1.17) unstable; urgency=low
* Don't release remote proxy handle too early (sometimes hever)

View File

@@ -1,6 +1,6 @@
Name: libgbinder
Version: 1.1.17
Version: 1.1.18
Release: 0
Summary: Binder client library
License: BSD

View File

@@ -82,6 +82,7 @@ struct gbinder_driver {
void* vm;
gsize vmsize;
char* dev;
const char* name;
const GBinderIo* io;
const GBinderRpcProtocol* protocol;
};
@@ -565,7 +566,8 @@ gbinder_driver_handle_transaction(
tx.flags, &txstatus);
break;
default:
GWARN("Unhandled transaction %s 0x%08x", iface, tx.code);
GWARN("Unhandled transaction %s 0x%08x from %s", iface, tx.code,
self->name);
break;
}
@@ -895,6 +897,9 @@ gbinder_driver_new(
self->vm = vm;
self->vmsize = vmsize;
self->dev = g_strdup(dev);
self->name = self->dev + /* Shorter version for logging */
(g_str_has_prefix(self->dev, "/dev/") ? 5 : 0);
if (gbinder_system_ioctl(fd, BINDER_SET_MAX_THREADS,
&max_threads) < 0) {
GERR("%s failed to set max threads (%u): %s", dev,

View File

@@ -207,9 +207,6 @@ typedef struct gbinder_ipc_tx_custom {
GDestroyNotify fn_custom_destroy;
} GBinderIpcTxCustom;
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
{ return self->priv->name; }
static
GBinderIpcLooper*
gbinder_ipc_looper_new(
@@ -1298,10 +1295,10 @@ gbinder_ipc_priv_get_local_object(
if (obj) {
gbinder_local_object_ref(obj);
} else {
GWARN("Unknown local object %p", pointer);
GWARN("Unknown local object %p %s", pointer, priv->name);
}
} else {
GWARN("Unknown local object %p", pointer);
GWARN("Unknown local object %p %s", pointer, priv->name);
}
g_mutex_unlock(&priv->local_objects_mutex);
/* Unlock */
@@ -1345,6 +1342,8 @@ gbinder_ipc_priv_get_remote_object(
}
GVERBOSE_("%p handle %u %s", obj, handle, gbinder_ipc_name(self));
g_hash_table_replace(priv->remote_objects, key, obj);
} else {
GWARN("Unknown handle %u %s", handle, priv->name);
}
g_mutex_unlock(&priv->remote_objects_mutex);
/* Unlock */
@@ -1904,6 +1903,13 @@ gbinder_ipc_unref(
}
}
const char*
gbinder_ipc_name(
GBinderIpc* self)
{
return G_LIKELY(self) ? self->priv->name : NULL;
}
GBinderObjectRegistry*
gbinder_ipc_object_registry(
GBinderIpc* self)

View File

@@ -113,6 +113,11 @@ gbinder_ipc_unref(
GBinderIpc* ipc)
GBINDER_INTERNAL;
const char*
gbinder_ipc_name(
GBinderIpc* ipc)
GBINDER_INTERNAL;
void
gbinder_ipc_looper_check(
GBinderIpc* ipc)

View File

@@ -60,12 +60,9 @@ struct gbinder_proxy_tx {
};
struct gbinder_proxy_object_priv {
gulong remote_death_id;
gboolean acquired;
gboolean dropped;
GBinderProxyTx* tx;
GMutex mutex; /* Protects the hashtable below */
GHashTable* subproxies;
};
G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
@@ -77,79 +74,12 @@ G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
#define THIS_TYPE GBINDER_TYPE_PROXY_OBJECT
#define PARENT_CLASS gbinder_proxy_object_parent_class
static
void
gbinder_proxy_object_subproxy_gone(
gpointer proxy,
GObject* subproxy)
{
GBinderProxyObject* self = THIS(proxy);
GBinderProxyObjectPriv* priv = self->priv;
/* Lock */
g_mutex_lock(&priv->mutex);
g_hash_table_remove(priv->subproxies, subproxy);
if (g_hash_table_size(priv->subproxies) == 0) {
g_hash_table_unref(priv->subproxies);
priv->subproxies = NULL;
}
g_mutex_unlock(&priv->mutex);
/* Unlock */
}
static
void
gbinder_proxy_object_drop_subproxies(
GBinderProxyObject* self)
{
GBinderProxyObjectPriv* priv = self->priv;
GSList* list = NULL;
/* Lock */
g_mutex_lock(&priv->mutex);
if (priv->subproxies) {
GHashTableIter it;
gpointer value;
g_hash_table_iter_init(&it, priv->subproxies);
while (g_hash_table_iter_next(&it, NULL, &value)) {
list = g_slist_append(list, gbinder_local_object_ref(value));
g_object_weak_unref(G_OBJECT(value),
gbinder_proxy_object_subproxy_gone, self);
}
g_hash_table_destroy(priv->subproxies);
priv->subproxies = NULL;
}
g_mutex_unlock(&priv->mutex);
/* Unlock */
/* Drop (and possibly destroy) the objects outside of the lock */
g_slist_free_full(list, (GDestroyNotify) gbinder_local_object_drop);
}
static
void
gbinder_proxy_remote_death_proc(
GBinderRemoteObject* obj,
void* proxy)
{
GBinderProxyObject* self = THIS(proxy);
GBinderProxyObjectPriv* priv = self->priv;
GDEBUG("Remote object %u died on %s", obj->handle, obj->ipc->dev);
gbinder_remote_object_remove_handler(obj, priv->remote_death_id);
priv->remote_death_id = 0;
/* Drop the implicit reference */
gbinder_local_object_unref(&self->parent);
}
/*==========================================================================*
* Converter
*==========================================================================*/
typedef struct gbinder_proxy_object_converter {
GBinderObjectConverter pub;
GBinderProxyObject* proxy;
GBinderIpc* remote;
GBinderIpc* local;
} GBinderProxyObjectConverter;
@@ -183,8 +113,6 @@ gbinder_proxy_object_converter_handle_to_local(
guint32 handle)
{
GBinderProxyObjectConverter* c = gbinder_proxy_object_converter_cast(pub);
GBinderProxyObject* proxy = c->proxy;
GBinderProxyObjectPriv* priv = proxy->priv;
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(c->remote);
GBinderRemoteObject* remote = gbinder_object_registry_get_remote(reg,
handle, REMOTE_REGISTRY_CAN_CREATE /* but don't acquire */);
@@ -193,32 +121,7 @@ gbinder_proxy_object_converter_handle_to_local(
if (!local && !remote->dead) {
/* GBinderProxyObject will reference GBinderRemoteObject */
GBinderProxyObject* subp = gbinder_proxy_object_new(c->local, remote);
/*
* Auto-created proxies may get spontaneously destroyed and
* not necessarily on the UI thread.
*/
subp->priv->remote_death_id = gbinder_remote_object_add_death_handler
(remote, gbinder_proxy_remote_death_proc, subp);
/*
* Remote keeps an implicit reference to this auto-created
* proxy. The reference gets released when the remote object
* dies, i.e. by gbinder_proxy_remote_death_proc().
*/
gbinder_local_object_ref(local = GBINDER_LOCAL_OBJECT(subp));
/* Lock */
g_mutex_lock(&priv->mutex);
if (!priv->subproxies) {
priv->subproxies = g_hash_table_new(g_direct_hash, g_direct_equal);
}
g_hash_table_insert(priv->subproxies, subp, subp);
g_object_weak_ref(G_OBJECT(subp),
gbinder_proxy_object_subproxy_gone, proxy);
g_mutex_unlock(&priv->mutex);
/* Unlock */
local = &gbinder_proxy_object_new(c->local, remote)->parent;
}
/* Release the reference returned by gbinder_object_registry_get_remote */
@@ -242,7 +145,6 @@ gbinder_proxy_object_converter_init(
GBinderIpc* dest = proxy->parent.ipc;
memset(convert, 0, sizeof(*convert));
convert->proxy = proxy;
convert->remote = remote;
convert->local = local;
pub->f = &gbinder_converter_fn;
@@ -347,7 +249,7 @@ gbinder_proxy_object_handle_transaction(
GBinderProxyObjectPriv* priv = self->priv;
GBinderRemoteObject* remote = self->remote;
if (!priv->dropped && !gbinder_remote_object_is_dead(remote)) {
if (!priv->dropped && !remote->dead) {
GBinderLocalRequest* fwd;
GBinderProxyTx* tx = g_slice_new0(GBinderProxyTx);
GBinderProxyObjectConverter convert;
@@ -375,6 +277,7 @@ gbinder_proxy_object_handle_transaction(
gbinder_local_request_unref(fwd);
*status = GBINDER_STATUS_OK;
} else {
GVERBOSE_("dropped: %d dead:%d", priv->dropped, remote->dead);
*status = (-EBADMSG);
}
return NULL;
@@ -398,10 +301,9 @@ gbinder_proxy_object_acquire(
{
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
GBinderRemoteObject* remote = self->remote;
if (priv->remote_death_id && !priv->dropped && !priv->acquired) {
GBinderRemoteObject* remote = self->remote;
if (!remote->dead && !priv->dropped && !priv->acquired) {
/* Not acquired yet */
priv->acquired = TRUE;
gbinder_driver_acquire(remote->ipc->driver, remote->handle);
@@ -418,7 +320,6 @@ gbinder_proxy_object_drop(
GBinderProxyObjectPriv* priv = self->priv;
priv->dropped = TRUE;
gbinder_proxy_object_drop_subproxies(self);
GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->drop(object);
}
@@ -444,6 +345,8 @@ gbinder_proxy_object_new(
if (object) {
GBinderProxyObject* self = THIS(object);
GDEBUG("Proxy %p %s => %u %s created", self, gbinder_ipc_name(src),
remote->handle, gbinder_ipc_name(remote->ipc));
self->remote = gbinder_remote_object_ref(remote);
return self;
}
@@ -462,15 +365,25 @@ gbinder_proxy_object_finalize(
{
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
GBinderLocalObject* local = &self->parent;
GBinderRemoteObject* remote = self->remote;
/*
* gbinder_local_object_finalize() will also try to do the same thing
* i.e. invalidate self but proxy objects need to do it before releasing
* the handle, to leave no room for race conditions. That's not very good
* because it grabs ipc-wide mutex but shouldn'd have much of an impact
* on the performance because finalizing a proxy is not supposed to be a
* frequent operation.
*/
gbinder_ipc_invalidate_local_object(local->ipc, local);
if (priv->acquired) {
gbinder_driver_release(remote->ipc->driver, remote->handle);
}
gbinder_proxy_object_drop_subproxies(self);
gbinder_remote_object_remove_handler(remote, priv->remote_death_id);
GDEBUG("Proxy %p %s => %u %s gone", self,
gbinder_ipc_name(self->parent.ipc), remote->handle,
gbinder_ipc_name(remote->ipc));
gbinder_remote_object_unref(remote);
g_mutex_clear(&priv->mutex);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
@@ -483,7 +396,6 @@ gbinder_proxy_object_init(
THIS_TYPE, GBinderProxyObjectPriv);
self->priv = priv;
g_mutex_init(&priv->mutex);
}
static

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2022 Jolla Ltd.
* Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -55,6 +55,7 @@ static GHashTable* test_fd_map = NULL;
static GHashTable* test_node_map = NULL;
static GPrivate test_looper = G_PRIVATE_INIT(NULL);
static GPrivate test_tx_state = G_PRIVATE_INIT(NULL);
static guint32 last_auto_handle = 0;
G_LOCK_DEFINE_STATIC(test_binder);
static GMainLoop* test_binder_exit_loop = NULL;
@@ -125,6 +126,7 @@ typedef struct test_binder_node TestBinderNode;
struct test_binder_node {
int fd;
char* path;
const char* name;
TestBinder* binder;
TestBinderNode* other;
TEST_LOOPER looper_enabled;
@@ -146,7 +148,6 @@ typedef struct test_binder {
GHashTable* object_map; /* GBinderLocalObject* => handle */
GHashTable* handle_map; /* handle => GBinderLocalObject* */
TestBinderSubmitThread* submit_thread;
guint32 last_auto_handle;
GMutex mutex;
gboolean passthrough;
TestBinderNode node[2];
@@ -437,13 +438,16 @@ test_binder_register_object_locked(
g_assert(!g_hash_table_contains(binder->object_map, obj));
g_assert(!g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)));
if (h == AUTO_HANDLE) {
h = ++(binder->last_auto_handle);
h = ++last_auto_handle;
while (g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)) ||
g_hash_table_contains(binder->object_map, GINT_TO_POINTER(h))) {
h = ++(binder->last_auto_handle);
h = ++last_auto_handle;
}
} else if (last_auto_handle < h) {
/* Avoid re-using handles, to make debugging easier */
last_auto_handle = h;
}
GDEBUG("Object %p <=> handle %u", obj, h);
GDEBUG("Object %p <=> handle %u %s", obj, h, binder->node[0].name);
g_hash_table_insert(binder->handle_map, GINT_TO_POINTER(h), obj);
g_hash_table_insert(binder->object_map, obj, GINT_TO_POINTER(h));
g_object_weak_ref(G_OBJECT(obj), test_binder_local_object_gone, binder);
@@ -463,10 +467,10 @@ test_io_passthough_handle_to_object(
gpointer obj = g_hash_table_lookup(binder->handle_map, key);
GDEBUG("Handle %u => object %p %s", (guint) handle, obj,
binder->node[0].path);
binder->node[0].name);
return GPOINTER_TO_SIZE(obj);
}
GDEBUG("Unexpected handle %u %s", (guint) handle, binder->node[0].path);
GDEBUG("Unexpected handle %u %s", (guint) handle, binder->node[0].name);
return 0;
}
@@ -484,13 +488,13 @@ test_io_passthough_object_to_handle(
guint64 handle = GPOINTER_TO_SIZE(value);
GDEBUG("Object %p => handle %u %s", key, (guint) handle,
binder->node[0].path);
binder->node[0].name);
return handle;
} else if (key) {
GDEBUG("Auto-registering object %p %s", key, binder->node[0].path);
GDEBUG("Auto-registering object %p %s", key, binder->node[0].name);
return test_binder_register_object_locked(binder, key, AUTO_HANDLE);
} else {
GDEBUG("Unexpected object %p %s", key, binder->node[0].path);
GDEBUG("Unexpected object %p %s", key, binder->node[0].name);
return 0;
}
}
@@ -565,7 +569,7 @@ test_binder_node_read(
const guint32 cmd = node->next_cmd[0];
const guint total = 4 + _IOC_SIZE(cmd);
/* Alread have one ready */
/* Already have one ready */
if (!(test_binder_cmd_read_flags(cmd) & flags)) {
GDEBUG("Cmd 0x%08x not for %d", cmd, gettid());
*bytes_read = 0;
@@ -667,7 +671,7 @@ test_tx_state_acquire(
while (g_atomic_pointer_get(&node->tx_state) != my_tx_state &&
!g_atomic_pointer_compare_and_exchange(&node->tx_state, NULL,
my_tx_state)) {
GDEBUG("Thread %d is waiting to become a transacton thread",
GDEBUG("Thread %d is waiting to become a transaction thread",
my_tx_state->tid);
test_io_short_wait();
}
@@ -696,7 +700,7 @@ test_tx_state_release(
my_tx_state->depth--;
my_tx_state->stack = g_renew(TEST_TX_STATE, my_tx_state->stack,
my_tx_state->depth);
GDEBUG("Thread %d is still a transacton thread", my_tx_state->tid);
GDEBUG("Thread %d is still a transaction thread", my_tx_state->tid);
}
}
@@ -843,6 +847,8 @@ test_io_passthough_write_64(
buffer->buffer = GPOINTER_TO_SIZE(copy);
g_hash_table_replace(fd->destroy_map, copy, NULL);
}
} else {
GDEBUG("Unexpected object type 0x%08x", *obj_ptr);
}
}
g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
@@ -1594,6 +1600,7 @@ test_binder_fd_new(
binder_fd->node = node;
binder_fd->fd = dup(node->fd);
binder_fd->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
GDEBUG("fd %d => %d", binder_fd->fd, node->fd);
return binder_fd;
}
@@ -1602,11 +1609,12 @@ gbinder_system_open(
const char* path,
int flags)
{
static const char path_prefix[] = "/dev/";
static const char binder_suffix[] = "binder";
static const char binder_private_suffix[] = "binder-private";
static const char private_suffix[] = "-private";
if (path && g_str_has_prefix(path, "/dev/") &&
if (path && g_str_has_prefix(path, path_prefix) &&
(g_str_has_suffix(path, binder_suffix) ||
g_str_has_suffix(path, binder_private_suffix))) {
TestBinderFd* fd;
@@ -1631,7 +1639,7 @@ gbinder_system_open(
node = binder->node + PRIVATE;
node->path = g_strdup(path);
binder->node[PUBLIC].path = g_strndup(path,
strlen(path) - strlen(private_suffix) - 1);
strlen(path) - strlen(private_suffix));
}
if (!test_node_map) {
@@ -1644,6 +1652,7 @@ gbinder_system_open(
this_node->binder = binder;
this_node->fd = fds[i];
this_node->other = binder->node + ((i + 1) % 2);
this_node->name = this_node->path + strlen(path_prefix);
g_hash_table_replace(test_node_map, this_node->path, this_node);
}
binder->object_map = g_hash_table_new
@@ -1651,8 +1660,8 @@ gbinder_system_open(
binder->handle_map = g_hash_table_new
(g_direct_hash, g_direct_equal);
GDEBUG("Created %s (%d) <=> %s (%d) binder",
binder->node[0].path, binder->node[0].fd,
binder->node[1].path, binder->node[1].fd);
binder->node[0].name, binder->node[0].fd,
binder->node[1].name, binder->node[1].fd);
}
fd = test_binder_fd_new(node);
if (!test_fd_map) {

View File

@@ -438,7 +438,7 @@ test_param(
typedef struct test_obj_data {
GMainLoop* loop;
GBinderLocalObject* tmp_proxy;
GBinderLocalObject* obj2;
gboolean obj_call_handled;
gboolean obj_call_finished;
gboolean obj2_call_handled;
@@ -539,9 +539,9 @@ test_obj_cb(
g_assert(gbinder_reader_at_end(&reader));
/* Make sure temporary proxy won't get destroyed too early */
test->tmp_proxy = test_binder_object(gbinder_driver_fd(obj->ipc->driver),
test->obj2 = test_binder_object(gbinder_driver_fd(obj->ipc->driver),
obj2->handle);
g_assert(test->tmp_proxy);
g_assert(test->obj2);
/* Call remote object */
client2 = gbinder_client_new(obj2, TEST_IFACE2);
@@ -599,39 +599,54 @@ test_obj_run(
GBinderRemoteObject* remote_obj;
GBinderRemoteObject* remote_proxy;
GBinderClient* proxy_client;
GBinderIpc* ipc_remote_obj;
GBinderIpc* ipc_obj;
GBinderIpc* ipc_proxy;
GBinderIpc* ipc_remote_proxy;
GBinderLocalRequest* req;
int fd_obj, fd_proxy;
int fd_remote_obj, fd_obj, fd_proxy, fd_remote_proxy;
test_config_init(&config, NULL);
memset(&test, 0, sizeof(test));
test.loop = g_main_loop_new(NULL, FALSE);
ipc_proxy = gbinder_ipc_new(DEV);
ipc_obj = gbinder_ipc_new(DEV_PRIV);
fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
ipc_remote_obj = gbinder_ipc_new(DEV_PRIV);
ipc_obj = gbinder_ipc_new(DEV);
ipc_proxy = gbinder_ipc_new(DEV2);
ipc_remote_proxy = gbinder_ipc_new(DEV2_PRIV);
fd_remote_obj = gbinder_driver_fd(ipc_remote_obj->driver);
fd_obj = gbinder_driver_fd(ipc_obj->driver);
obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_obj_cb, &test);
obj2 = gbinder_local_object_new(ipc_obj, TEST_IFACES2, test_obj2_cb, &test);
remote_obj = gbinder_remote_object_new(ipc_proxy,
fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
fd_remote_proxy = gbinder_driver_fd(ipc_remote_proxy->driver);
obj = gbinder_local_object_new(ipc_remote_obj, TEST_IFACES, test_obj_cb, &test);
GDEBUG("obj %p", obj);
remote_obj = gbinder_remote_object_new(ipc_obj,
test_binder_register_object(fd_obj, obj, AUTO_HANDLE),
REMOTE_OBJECT_CREATE_ALIVE);
/* remote_proxy(DEV_PRIV) => proxy (DEV) => obj (DEV) => DEV_PRIV */
g_assert(!gbinder_proxy_object_new(NULL, remote_obj));
/* remote_proxy(DEV2_PRIV) => proxy (DEV) => obj(DEV2_PRIV) */
g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
remote_proxy = gbinder_remote_object_new(ipc_obj,
GDEBUG("proxy %p", proxy);
remote_proxy = gbinder_remote_object_new(ipc_remote_proxy,
test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE),
REMOTE_OBJECT_CREATE_ALIVE);
proxy_client = gbinder_client_new(remote_proxy, TEST_IFACE);
test_binder_set_passthrough(fd_remote_obj, TRUE);
test_binder_set_passthrough(fd_obj, TRUE);
test_binder_set_passthrough(fd_proxy, TRUE);
test_binder_set_passthrough(fd_remote_proxy, TRUE);
test_binder_set_looper_enabled(fd_remote_obj, TEST_LOOPER_ENABLE);
test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
test_binder_set_looper_enabled(fd_remote_proxy, TEST_LOOPER_ENABLE);
/* Pass object reference via proxy */
obj2 = gbinder_local_object_new(ipc_remote_proxy, TEST_IFACES2, test_obj2_cb, &test);
GDEBUG("obj2 %p", obj2);
req = gbinder_client_new_request(proxy_client);
gbinder_local_request_append_int32(req, TX_PARAM1);
gbinder_local_request_append_local_object(req, obj2);
@@ -646,19 +661,24 @@ test_obj_run(
g_assert(test.obj_call_finished);
g_assert(test.obj2_call_handled);
g_assert(test.obj2_call_finished);
g_assert(test.tmp_proxy);
gbinder_local_object_unref(test.tmp_proxy);
g_assert(test.obj2);
gbinder_local_object_unref(test.obj2);
test_binder_unregister_objects(fd_remote_obj);
test_binder_unregister_objects(fd_obj);
test_binder_unregister_objects(fd_proxy);
test_binder_unregister_objects(fd_remote_proxy);
gbinder_local_object_drop(obj);
gbinder_local_object_drop(obj2);
gbinder_local_object_drop(&proxy->parent);
gbinder_remote_object_unref(remote_obj);
gbinder_remote_object_unref(remote_proxy);
gbinder_client_unref(proxy_client);
gbinder_ipc_unref(ipc_remote_obj);
gbinder_ipc_unref(ipc_obj);
gbinder_ipc_unref(ipc_proxy);
gbinder_ipc_unref(ipc_remote_proxy);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, test.loop);
test_config_deinit(&config);