[gbinder] Support for passing object references via proxy. JB#52557

GBinderProxyObject automatically creates proxies for the remote objects
found in the transaction payload.
This commit is contained in:
Slava Monich 2021-02-17 13:35:55 +02:00
parent 0c85299efc
commit 82f71a6c91
50 changed files with 2146 additions and 519 deletions

View File

@ -30,9 +30,9 @@ This package contains the development library for %{name}.
%build
make LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
make -C test/binder-bridge release
make -C test/binder-list release
make -C test/binder-ping release
make -C test/binder-bridge KEEP_SYMBOLS=1 release
make -C test/binder-list KEEP_SYMBOLS=1 release
make -C test/binder-ping KEEP_SYMBOLS=1 release
%install
rm -rf %{buildroot}

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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:
*
@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@ -112,6 +112,47 @@ gbinder_buffer_contents_unref(
}
}
/*==========================================================================*
* GBinderBufferContentsList
* It's actually a GSList containing GBinderBufferContents refs.
*==========================================================================*/
GBinderBufferContentsList*
gbinder_buffer_contents_list_add(
GBinderBufferContentsList* list,
GBinderBufferContents* contents)
{
/* Prepend rather than append for better efficiency */
return contents ? (GBinderBufferContentsList*) g_slist_prepend((GSList*)
list, gbinder_buffer_contents_ref(contents)) : list;
}
GBinderBufferContentsList*
gbinder_buffer_contents_list_dup(
GBinderBufferContentsList* list)
{
GSList* out = NULL;
if (list) {
GSList* l = (GSList*) list;
/* The order gets reversed but it doesn't matter */
while (l) {
out = g_slist_prepend(out, gbinder_buffer_contents_ref(l->data));
l = l->next;
}
}
return (GBinderBufferContentsList*) out;
}
void
gbinder_buffer_contents_list_free(
GBinderBufferContentsList* list)
{
g_slist_free_full((GSList*) list, (GDestroyNotify)
gbinder_buffer_contents_unref);
}
/*==========================================================================*
* GBinderBuffer
*==========================================================================*/

View File

@ -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:
*
@ -88,6 +88,22 @@ gbinder_buffer_contents_unref(
GBinderBufferContents* contents)
GBINDER_INTERNAL;
GBinderBufferContentsList*
gbinder_buffer_contents_list_add(
GBinderBufferContentsList* list,
GBinderBufferContents* contents)
GBINDER_INTERNAL;
GBinderBufferContentsList*
gbinder_buffer_contents_list_dup(
GBinderBufferContentsList* list)
GBINDER_INTERNAL;
void
gbinder_buffer_contents_list_free(
GBinderBufferContentsList* list)
GBINDER_INTERNAL;
#endif /* GBINDER_BUFFER_PRIVATE_H */
/*

View File

@ -100,6 +100,7 @@ typedef struct gbinder_driver_context {
GBinderObjectRegistry* reg;
GBinderHandler* handler;
GBinderCleanup* unrefs;
GBinderBufferContentsList* bufs;
} GBinderDriverContext;
static
@ -341,19 +342,19 @@ gbinder_driver_cmd_data(
static
gboolean
gbinder_driver_death_notification(
gbinder_driver_handle_cookie(
GBinderDriver* self,
guint32 cmd,
GBinderRemoteObject* obj)
{
GBinderIoBuf write;
guint8 buf[4 + GBINDER_MAX_DEATH_NOTIFICATION_SIZE];
guint8 buf[4 + GBINDER_MAX_HANDLE_COOKIE_SIZE];
guint32* data = (guint32*)buf;
data[0] = cmd;
memset(&write, 0, sizeof(write));
write.ptr = (uintptr_t)buf;
write.size = 4 + self->io->encode_death_notification(data + 1, obj);
write.size = 4 + self->io->encode_handle_cookie(data + 1, obj);
return gbinder_driver_write(self, &write) >= 0;
}
@ -384,6 +385,7 @@ gbinder_driver_context_init(
context->reg = reg;
context->handler = handler;
context->unrefs = NULL;
context->bufs = NULL;
}
static
@ -392,6 +394,7 @@ gbinder_driver_context_cleanup(
GBinderDriverContext* context)
{
gbinder_cleanup_free(context->unrefs);
gbinder_buffer_contents_list_free(context->bufs);
}
static
@ -513,9 +516,13 @@ gbinder_driver_handle_transaction(
/* Transfer data ownership to the request */
if (tx.data && tx.size) {
GBinderBuffer* buf = gbinder_buffer_new(self,
tx.data, tx.size, tx.objects);
gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size);
gbinder_remote_request_set_data(req, tx.code,
gbinder_buffer_new(self, tx.data, tx.size, tx.objects));
gbinder_remote_request_set_data(req, tx.code, buf);
context->bufs = gbinder_buffer_contents_list_add(context->bufs,
gbinder_buffer_contents(buf));
} else {
GASSERT(!tx.objects);
gbinder_driver_free_buffer(self, tx.data);
@ -548,6 +555,8 @@ gbinder_driver_handle_transaction(
/* No reply for one-way transactions */
if (!(tx.flags & GBINDER_TX_FLAG_ONEWAY)) {
if (reply) {
context->bufs = gbinder_buffer_contents_list_add(context->bufs,
gbinder_local_reply_contents(reply));
gbinder_driver_reply_data(self, gbinder_local_reply_data(reply));
} else {
gbinder_driver_reply_status(self, txstatus);
@ -614,7 +623,7 @@ gbinder_driver_handle_command(
} else if (cmd == io->br.increfs) {
guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
GBinderLocalObject* obj = gbinder_object_registry_get_local
(reg, io->decode_binder_ptr_cookie(data));
(reg, io->decode_ptr_cookie(data));
GVERBOSE("> BR_INCREFS %p", obj);
gbinder_local_object_handle_increfs(obj);
@ -623,7 +632,7 @@ gbinder_driver_handle_command(
gbinder_driver_cmd_data(self, io->bc.increfs_done, data, buf);
} else if (cmd == io->br.decrefs) {
GBinderLocalObject* obj = gbinder_object_registry_get_local
(reg, io->decode_binder_ptr_cookie(data));
(reg, io->decode_ptr_cookie(data));
GVERBOSE("> BR_DECREFS %p", obj);
if (obj) {
@ -637,16 +646,21 @@ gbinder_driver_handle_command(
} else if (cmd == io->br.acquire) {
guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
GBinderLocalObject* obj = gbinder_object_registry_get_local
(reg, io->decode_binder_ptr_cookie(data));
(reg, io->decode_ptr_cookie(data));
GVERBOSE("> BR_ACQUIRE %p", obj);
gbinder_local_object_handle_acquire(obj);
if (obj) {
/* BC_ACQUIRE_DONE will be sent after the request is handled */
gbinder_local_object_handle_acquire(obj, context->bufs);
gbinder_local_object_unref(obj);
GVERBOSE("< BC_ACQUIRE_DONE %p", obj);
} else {
/* This shouldn't normally happen. Just send the same data back. */
GVERBOSE("< BC_ACQUIRE_DONE");
gbinder_driver_cmd_data(self, io->bc.acquire_done, data, buf);
}
} else if (cmd == io->br.release) {
GBinderLocalObject* obj = gbinder_object_registry_get_local
(reg, io->decode_binder_ptr_cookie(data));
(reg, io->decode_ptr_cookie(data));
GVERBOSE("> BR_RELEASE %p", obj);
if (obj) {
@ -660,19 +674,24 @@ gbinder_driver_handle_command(
} else if (cmd == io->br.transaction) {
gbinder_driver_handle_transaction(self, context, data);
} else if (cmd == io->br.dead_binder) {
guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE];
guint64 handle = 0;
GBinderRemoteObject* obj;
io->decode_cookie(data, &handle);
GVERBOSE("> BR_DEAD_BINDER %llu", (long long unsigned int)handle);
obj = gbinder_object_registry_get_remote(reg, (guint32)handle);
obj = gbinder_object_registry_get_remote(reg, (guint32)handle,
REMOTE_REGISTRY_DONT_CREATE);
if (obj) {
/* BC_DEAD_BINDER_DONE will be sent after the request is handled */
gbinder_remote_object_handle_death_notification(obj);
gbinder_remote_object_unref(obj);
}
GVERBOSE("< BC_DEAD_BINDER_DONE %llu", (long long unsigned int)handle);
} else {
/* This shouldn't normally happen. Just send the same data back. */
GVERBOSE("< BC_DEAD_BINDER_DONE %llu", (long long unsigned int)
handle);
gbinder_driver_cmd_data(self, io->bc.dead_binder_done, data, buf);
}
} else if (cmd == io->br.clear_death_notification_done) {
GVERBOSE("> BR_CLEAR_DEATH_NOTIFICATION_DONE");
} else {
@ -765,19 +784,38 @@ gbinder_driver_txstatus(
io->decode_transaction_data(data, &tx);
gbinder_driver_verbose_transaction_data("BR_REPLY", &tx);
/* Transfer data ownership to the request */
/* Transfer data ownership to the reply */
if (tx.data && tx.size) {
GBinderBuffer* buf = gbinder_buffer_new(self,
tx.data, tx.size, tx.objects);
gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size);
gbinder_remote_reply_set_data(reply,
gbinder_buffer_new(self, tx.data, tx.size, tx.objects));
gbinder_remote_reply_set_data(reply, buf);
context->bufs = gbinder_buffer_contents_list_add(context->bufs,
gbinder_buffer_contents(buf));
} else {
GASSERT(!tx.objects);
gbinder_driver_free_buffer(self, tx.data);
}
/*
* Filter out special cases. It's a bit unfortunate that
* libgbinder API historically mixed TF_STATUS_CODE payload
* with special delivery errors. It's not a bit deal though,
* because in real life TF_STATUS_CODE transactions are not
* being used that often, if at all.
*/
switch (tx.status) {
case (-EAGAIN):
case GBINDER_STATUS_FAILED:
case GBINDER_STATUS_DEAD_OBJECT:
txstatus = (-EFAULT);
GWARN("Replacing tx status %d with %d", tx.status, txstatus);
break;
default:
txstatus = tx.status;
GASSERT(txstatus != (-EAGAIN));
if (txstatus == (-EAGAIN)) txstatus = (-EFAULT);
break;
}
} else {
gbinder_driver_handle_command(self, context, cmd, data);
}
@ -952,6 +990,48 @@ gbinder_driver_protocol(
return self->protocol;
}
gboolean
gbinder_driver_acquire_done(
GBinderDriver* self,
GBinderLocalObject* obj)
{
GBinderIoBuf write;
guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
guint32* data = (guint32*)buf;
const GBinderIo* io = self->io;
data[0] = io->bc.acquire_done;
memset(&write, 0, sizeof(write));
write.ptr = (uintptr_t)buf;
write.size = 4 + io->encode_ptr_cookie(data + 1, obj);
GVERBOSE("< BC_ACQUIRE_DONE %p", obj);
return gbinder_driver_write(self, &write) >= 0;
}
gboolean
gbinder_driver_dead_binder_done(
GBinderDriver* self,
GBinderRemoteObject* obj)
{
if (G_LIKELY(obj)) {
GBinderIoBuf write;
guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE];
guint32* data = (guint32*)buf;
const GBinderIo* io = self->io;
data[0] = io->bc.dead_binder_done;
memset(&write, 0, sizeof(write));
write.ptr = (uintptr_t)buf;
write.size = 4 + io->encode_cookie(data + 1, obj->handle);
GVERBOSE("< BC_DEAD_BINDER_DONE %u", obj->handle);
return gbinder_driver_write(self, &write) >= 0;
} else {
return FALSE;
}
}
gboolean
gbinder_driver_request_death_notification(
GBinderDriver* self,
@ -959,7 +1039,7 @@ gbinder_driver_request_death_notification(
{
if (G_LIKELY(obj)) {
GVERBOSE("< BC_REQUEST_DEATH_NOTIFICATION 0x%08x", obj->handle);
return gbinder_driver_death_notification(self,
return gbinder_driver_handle_cookie(self,
self->io->bc.request_death_notification, obj);
} else {
return FALSE;
@ -973,7 +1053,7 @@ gbinder_driver_clear_death_notification(
{
if (G_LIKELY(obj)) {
GVERBOSE("< BC_CLEAR_DEATH_NOTIFICATION 0x%08x", obj->handle);
return gbinder_driver_death_notification(self,
return gbinder_driver_handle_cookie(self,
self->io->bc.clear_death_notification, obj);
} else {
return FALSE;
@ -1201,38 +1281,23 @@ gbinder_driver_transact(
return txstatus;
}
int
gbinder_driver_ping(
GBinderDriver* self,
GBinderObjectRegistry* reg,
guint32 handle)
{
const GBinderRpcProtocol* protocol = self->protocol;
GBinderLocalRequest* req = gbinder_local_request_new(self->io, NULL);
GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
GBinderWriter writer;
int ret;
gbinder_local_request_init_writer(req, &writer);
protocol->write_ping(&writer);
ret = gbinder_driver_transact(self, reg, NULL, handle, protocol->ping_tx,
req, reply);
gbinder_local_request_unref(req);
gbinder_remote_reply_unref(reply);
return ret;
}
GBinderLocalRequest*
gbinder_driver_local_request_new(
GBinderDriver* self,
const char* iface)
{
return gbinder_local_request_new_iface(self->io, self->protocol, iface);
}
GBinderLocalRequest*
gbinder_driver_local_request_new_ping(
GBinderDriver* self)
{
GBinderLocalRequest* req = gbinder_local_request_new(self->io, NULL);
GBinderWriter writer;
gbinder_local_request_init_writer(req, &writer);
self->protocol->write_rpc_header(&writer, iface);
self->protocol->write_ping(&writer);
return req;
}

View File

@ -84,6 +84,18 @@ gbinder_driver_protocol(
GBinderDriver* driver)
GBINDER_INTERNAL;
gboolean
gbinder_driver_acquire_done(
GBinderDriver* driver,
GBinderLocalObject* obj)
GBINDER_INTERNAL;
gboolean
gbinder_driver_dead_binder_done(
GBinderDriver* driver,
GBinderRemoteObject* obj)
GBINDER_INTERNAL;
gboolean
gbinder_driver_request_death_notification(
GBinderDriver* driver,
@ -161,19 +173,17 @@ gbinder_driver_transact(
GBinderRemoteReply* reply)
GBINDER_INTERNAL;
int
gbinder_driver_ping(
GBinderDriver* driver,
GBinderObjectRegistry* reg,
guint32 handle)
GBINDER_INTERNAL;
GBinderLocalRequest*
gbinder_driver_local_request_new(
GBinderDriver* driver,
const char* iface)
GBINDER_INTERNAL;
GBinderLocalRequest*
gbinder_driver_local_request_new_ping(
GBinderDriver* self)
GBINDER_INTERNAL;
#endif /* GBINDER_DRIVER_H */
/*

View File

@ -90,20 +90,50 @@ GBINDER_IO_FN(write_read)(
return ret;
}
/* Returns size of the object */
static
gsize
GBINDER_IO_FN(object_size)(
const void* obj)
{
if (obj) {
const struct binder_object_header* hdr = obj;
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
return sizeof(struct flat_binder_object);
case BINDER_TYPE_FD:
return sizeof(struct binder_fd_object);
case BINDER_TYPE_FDA:
return sizeof(struct binder_fd_array_object);
case BINDER_TYPE_PTR:
return sizeof(struct binder_buffer_object);
}
}
return 0;
}
/* Returns size of the object's extra data */
static
gsize
GBINDER_IO_FN(object_data_size)(
const void* obj)
{
const struct binder_buffer_object* buf = obj;
if (obj) {
const struct binder_object_header* hdr = obj;
if (buf && buf->hdr.type == BINDER_TYPE_PTR) {
return buf->length;
} else {
return 0;
switch (hdr->type) {
case BINDER_TYPE_PTR:
return ((struct binder_buffer_object*)obj)->length;
case BINDER_TYPE_FDA:
return ((struct binder_fd_array_object*)obj)->num_fds * 4;
}
}
return 0;
}
/* Writes pointer to the buffer */
static
@ -118,6 +148,19 @@ GBINDER_IO_FN(encode_pointer)(
return sizeof(*dest);
}
/* Writes cookie to the buffer */
static
guint
GBINDER_IO_FN(encode_cookie)(
void* out,
guint64 cookie)
{
binder_uintptr_t* dest = out;
*dest = (uintptr_t)cookie;
return sizeof(*dest);
}
/* Encodes flat_binder_object */
static
guint
@ -197,7 +240,7 @@ GBINDER_IO_FN(encode_buffer_object)(
static
guint
GBINDER_IO_FN(encode_death_notification)(
GBINDER_IO_FN(encode_handle_cookie)(
void* out,
GBinderRemoteObject* obj)
{
@ -209,6 +252,20 @@ GBINDER_IO_FN(encode_death_notification)(
return sizeof(*dest);
}
static
guint
GBINDER_IO_FN(encode_ptr_cookie)(
void* out,
GBinderLocalObject* obj)
{
struct binder_ptr_cookie* dest = out;
/* We never send these cookies and don't expect them back */
dest->ptr = (uintptr_t)obj;
dest->cookie = 0;
return sizeof(dest);
}
/* Fills binder_transaction_data for BC_TRANSACTION/REPLY */
static
void
@ -412,7 +469,7 @@ GBINDER_IO_FN(decode_cookie)(
/* Decode struct binder_ptr_cookie */
static
void*
GBINDER_IO_FN(decode_binder_ptr_cookie)(
GBINDER_IO_FN(decode_ptr_cookie)(
const void* data)
{
const struct binder_ptr_cookie* ptr = data;
@ -422,6 +479,24 @@ GBINDER_IO_FN(decode_binder_ptr_cookie)(
return (void*)(uintptr_t)ptr->ptr;
}
static
guint
GBINDER_IO_FN(decode_binder_handle)(
const void* data,
guint32* handle)
{
const struct flat_binder_object* obj = data;
/* Caller guarantees that data points to an object */
if (obj->hdr.type == BINDER_TYPE_HANDLE) {
if (handle) {
*handle = obj->handle;
}
return sizeof(*obj);
}
return 0;
}
static
guint
GBINDER_IO_FN(decode_binder_object)(
@ -436,7 +511,8 @@ GBINDER_IO_FN(decode_binder_object)(
switch (obj->hdr.type) {
case BINDER_TYPE_HANDLE:
if (out) {
*out = gbinder_object_registry_get_remote(reg, obj->handle);
*out = gbinder_object_registry_get_remote(reg, obj->handle,
REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE);
}
return sizeof(*obj);
case BINDER_TYPE_BINDER:
@ -552,15 +628,18 @@ const GBinderIo GBINDER_IO_PREFIX = {
.failed_reply = BR_FAILED_REPLY
},
.object_size = GBINDER_IO_FN(object_size),
.object_data_size = GBINDER_IO_FN(object_data_size),
/* Encoders */
.encode_pointer = GBINDER_IO_FN(encode_pointer),
.encode_cookie = GBINDER_IO_FN(encode_cookie),
.encode_local_object = GBINDER_IO_FN(encode_local_object),
.encode_remote_object = GBINDER_IO_FN(encode_remote_object),
.encode_fd_object = GBINDER_IO_FN(encode_fd_object),
.encode_buffer_object = GBINDER_IO_FN(encode_buffer_object),
.encode_death_notification = GBINDER_IO_FN(encode_death_notification),
.encode_handle_cookie = GBINDER_IO_FN(encode_handle_cookie),
.encode_ptr_cookie = GBINDER_IO_FN(encode_ptr_cookie),
.encode_transaction = GBINDER_IO_FN(encode_transaction),
.encode_transaction_sg = GBINDER_IO_FN(encode_transaction_sg),
.encode_reply = GBINDER_IO_FN(encode_reply),
@ -570,7 +649,8 @@ const GBinderIo GBINDER_IO_PREFIX = {
/* Decoders */
.decode_transaction_data = GBINDER_IO_FN(decode_transaction_data),
.decode_cookie = GBINDER_IO_FN(decode_cookie),
.decode_binder_ptr_cookie = GBINDER_IO_FN(decode_binder_ptr_cookie),
.decode_ptr_cookie = GBINDER_IO_FN(decode_ptr_cookie),
.decode_binder_handle = GBINDER_IO_FN(decode_binder_handle),
.decode_binder_object = GBINDER_IO_FN(decode_binder_object),
.decode_buffer_object = GBINDER_IO_FN(decode_buffer_object),
.decode_fd_object = GBINDER_IO_FN(decode_fd_object),
@ -586,7 +666,7 @@ G_STATIC_ASSERT(sizeof(struct flat_binder_object) <=
G_STATIC_ASSERT(sizeof(struct binder_buffer_object) <=
GBINDER_MAX_BUFFER_OBJECT_SIZE);
G_STATIC_ASSERT(sizeof(struct binder_handle_cookie) <=
GBINDER_MAX_DEATH_NOTIFICATION_SIZE);
GBINDER_MAX_HANDLE_COOKIE_SIZE);
G_STATIC_ASSERT(sizeof(struct binder_transaction_data) <=
GBINDER_MAX_BC_TRANSACTION_SIZE);
G_STATIC_ASSERT(sizeof(struct binder_transaction_data_sg) <=

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 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:
*
@ -125,7 +125,8 @@ struct gbinder_io {
guint failed_reply;
} br;
/* Size of the object's extra data */
/* Size of the object and its extra data */
gsize (*object_size)(const void* obj);
gsize (*object_data_size)(const void* obj);
/* Writes pointer to the buffer. The destination buffer must have
@ -134,6 +135,12 @@ struct gbinder_io {
#define GBINDER_MAX_POINTER_SIZE (8)
guint (*encode_pointer)(void* out, const void* pointer);
/* Writes cookie to the buffer. The destination buffer must have
* at least GBINDER_IO_MAX_COOKIE_SIZE bytes available. The
* actual size is returned. */
#define GBINDER_MAX_COOKIE_SIZE GBINDER_MAX_POINTER_SIZE
guint (*encode_cookie)(void* out, guint64 cookie);
/* Encode flat_buffer_object */
#define GBINDER_MAX_BINDER_OBJECT_SIZE (24)
guint (*encode_local_object)(void* out, GBinderLocalObject* obj);
@ -145,9 +152,13 @@ struct gbinder_io {
guint (*encode_buffer_object)(void* out, const void* data, gsize size,
const GBinderParent* parent);
/* Encode death notification */
#define GBINDER_MAX_DEATH_NOTIFICATION_SIZE (12)
guint (*encode_death_notification)(void* out, GBinderRemoteObject* obj);
/* Encode binder_handle_cookie */
#define GBINDER_MAX_HANDLE_COOKIE_SIZE (12)
guint (*encode_handle_cookie)(void* out, GBinderRemoteObject* obj);
/* Encode binder_ptr_cookie */
#define GBINDER_MAX_PTR_COOKIE_SIZE (16)
guint (*encode_ptr_cookie)(void* out, GBinderLocalObject* obj);
/* Encode BC_TRANSACTION/BC_TRANSACTION_SG data */
#define GBINDER_MAX_BC_TRANSACTION_SIZE (64)
@ -174,10 +185,9 @@ struct gbinder_io {
/* Decoders */
void (*decode_transaction_data)(const void* data, GBinderIoTxData* tx);
#define GBINDER_MAX_PTR_COOKIE_SIZE (16)
void* (*decode_binder_ptr_cookie)(const void* data);
void* (*decode_ptr_cookie)(const void* data);
guint (*decode_cookie)(const void* data, guint64* cookie);
guint (*decode_binder_handle)(const void* obj, guint32* handle);
guint (*decode_binder_object)(const void* data, gsize size,
GBinderObjectRegistry* reg, GBinderRemoteObject** obj);
guint (*decode_buffer_object)(GBinderBuffer* buf, gsize offset,

View File

@ -64,6 +64,7 @@ struct gbinder_ipc_priv {
GThreadPool* tx_pool;
GHashTable* tx_table;
char* key;
const char* name;
GBinderObjectRegistry object_registry;
GMutex remote_objects_mutex;
@ -207,7 +208,7 @@ typedef struct gbinder_ipc_tx_custom {
} GBinderIpcTxCustom;
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
{ return gbinder_driver_dev(self->driver); }
{ return self->priv->name; }
static
GBinderIpcLooper*
@ -685,7 +686,7 @@ gbinder_ipc_looper_transact(
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (gbinder_ipc_looper_remove_primary(looper)) {
GDEBUG("Primary looper %s is blocked", looper->name);
GVERBOSE("Primary looper %s is blocked", looper->name);
looper->next = priv->blocked_loopers;
priv->blocked_loopers = looper;
was_blocked = TRUE;
@ -712,7 +713,7 @@ gbinder_ipc_looper_transact(
/* Block until asynchronous transaction gets completed. */
done = 0;
if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done)) {
GDEBUG("Looper %s is released", looper->name);
GVERBOSE("Looper %s is released", looper->name);
GASSERT(done == TX_DONE);
}
}
@ -1115,19 +1116,27 @@ gbinder_ipc_tx_handler_transact(
static
void
gbinder_ipc_invalidate_remote_handle_locked(
GBinderIpcPriv* priv,
GBinderIpc* self,
guint32 handle)
{
GBinderIpcPriv* priv = self->priv;
/* Caller holds priv->remote_objects_mutex */
if (priv->remote_objects) {
GVERBOSE_("handle %u", handle);
g_hash_table_remove(priv->remote_objects, GINT_TO_POINTER(handle));
const gpointer key = GINT_TO_POINTER(handle);
#if GUTIL_LOG_VERBOSE
const gpointer obj = g_hash_table_lookup(priv->remote_objects, key);
#endif
if (g_hash_table_remove(priv->remote_objects, key)) {
GVERBOSE_("handle %u %p %s", handle, obj, gbinder_ipc_name(self));
if (g_hash_table_size(priv->remote_objects) == 0) {
g_hash_table_unref(priv->remote_objects);
priv->remote_objects = NULL;
}
}
}
}
void
gbinder_ipc_invalidate_remote_handle(
@ -1138,7 +1147,7 @@ gbinder_ipc_invalidate_remote_handle(
/* Lock */
g_mutex_lock(&priv->remote_objects_mutex);
gbinder_ipc_invalidate_remote_handle_locked(priv, handle);
gbinder_ipc_invalidate_remote_handle_locked(self, handle);
g_mutex_unlock(&priv->remote_objects_mutex);
/* Unlock */
}
@ -1173,12 +1182,14 @@ gbinder_ipc_local_object_disposed(
/* Lock */
g_mutex_lock(&priv->local_objects_mutex);
if (obj->object.ref_count == 1 && priv->local_objects) {
g_hash_table_remove(priv->local_objects, obj);
if (g_hash_table_remove(priv->local_objects, obj)) {
GVERBOSE_("%p %s", obj, gbinder_ipc_name(self));
if (g_hash_table_size(priv->local_objects) == 0) {
g_hash_table_unref(priv->local_objects);
priv->local_objects = NULL;
}
}
}
g_mutex_unlock(&priv->local_objects_mutex);
/* Unlock */
}
@ -1193,7 +1204,7 @@ gbinder_ipc_remote_object_disposed(
/* Lock */
g_mutex_lock(&priv->remote_objects_mutex);
if (obj->object.ref_count == 1) {
gbinder_ipc_invalidate_remote_handle_locked(priv, obj->handle);
gbinder_ipc_invalidate_remote_handle_locked(self, obj->handle);
}
g_mutex_unlock(&priv->remote_objects_mutex);
/* Unlock */
@ -1211,11 +1222,13 @@ gbinder_ipc_register_local_object(
if (!priv->local_objects) {
priv->local_objects = g_hash_table_new(g_direct_hash, g_direct_equal);
}
if (!g_hash_table_contains(priv->local_objects, obj)) {
g_hash_table_insert(priv->local_objects, obj, obj);
GVERBOSE_("%p %s", obj, gbinder_ipc_name(self));
}
g_mutex_unlock(&priv->local_objects_mutex);
/* Unlock */
GVERBOSE_("%p", obj);
gbinder_ipc_looper_check(self);
}
@ -1252,6 +1265,7 @@ GBinderRemoteObject*
gbinder_ipc_priv_get_remote_object(
GBinderIpcPriv* priv,
guint32 handle,
REMOTE_REGISTRY_CREATE create,
gboolean maybe_dead)
{
GBinderRemoteObject* obj = NULL;
@ -1264,16 +1278,22 @@ gbinder_ipc_priv_get_remote_object(
}
if (obj) {
gbinder_remote_object_ref(obj);
} else {
} else if (create != REMOTE_REGISTRY_DONT_CREATE) {
GBinderIpc* self = priv->self;
/*
* If maybe_dead is TRUE, the caller is supposed to try reanimating
* the object on the main thread not holding any global locks.
*/
obj = gbinder_remote_object_new(priv->self, handle, maybe_dead);
obj = gbinder_remote_object_new(self, handle, maybe_dead ?
REMOTE_OBJECT_CREATE_DEAD : (create == REMOTE_REGISTRY_CAN_CREATE) ?
REMOTE_OBJECT_CREATE_ALIVE :
REMOTE_OBJECT_CREATE_ACQUIRED);
if (!priv->remote_objects) {
priv->remote_objects = g_hash_table_new
(g_direct_hash, g_direct_equal);
}
GVERBOSE_("%p handle %u %s", obj, handle, gbinder_ipc_name(self));
g_hash_table_replace(priv->remote_objects, key, obj);
}
g_mutex_unlock(&priv->remote_objects_mutex);
@ -1282,14 +1302,47 @@ gbinder_ipc_priv_get_remote_object(
return obj;
}
GBinderRemoteObject*
gbinder_ipc_get_remote_object(
GBinderLocalObject*
gbinder_ipc_find_local_object(
GBinderIpc* self,
guint32 handle,
gboolean maybe_dead)
GBinderIpcLocalObjectCheckFunc func,
void* user_data)
{
GBinderLocalObject* found = NULL;
if (self) {
GBinderIpcPriv* priv = self->priv;
/* Lock */
g_mutex_lock(&priv->local_objects_mutex);
if (priv->local_objects) {
GHashTableIter it;
gpointer value;
g_hash_table_iter_init(&it, priv->local_objects);
while (g_hash_table_iter_next(&it, NULL, &value)) {
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(value);
if (func(obj, user_data)) {
found = gbinder_local_object_ref(obj);
break;
}
}
}
g_mutex_unlock(&priv->local_objects_mutex);
/* Unlock */
}
return found;
}
GBinderRemoteObject*
gbinder_ipc_get_service_manager(
GBinderIpc* self)
{
/* GBinderServiceManager makes sure that GBinderIpc pointer is not NULL */
return gbinder_ipc_priv_get_remote_object(self->priv, handle, maybe_dead);
return gbinder_ipc_priv_get_remote_object(self->priv,
GBINDER_SERVICEMANAGER_HANDLE, REMOTE_REGISTRY_CAN_CREATE, TRUE);
}
GBINDER_INLINE_FUNC
@ -1338,10 +1391,11 @@ static
GBinderRemoteObject*
gbinder_ipc_object_registry_get_remote(
GBinderObjectRegistry* reg,
guint32 handle)
guint32 handle,
REMOTE_REGISTRY_CREATE create)
{
return gbinder_ipc_priv_get_remote_object
(gbinder_ipc_priv_from_object_registry(reg), handle, FALSE);
(gbinder_ipc_priv_from_object_registry(reg), handle, create, FALSE);
}
/*==========================================================================*
@ -1769,6 +1823,9 @@ gbinder_ipc_new(
gbinder_ipc_table = g_hash_table_new(g_str_hash, g_str_equal);
}
g_hash_table_replace(gbinder_ipc_table, priv->key, self);
/* With "/dev/" prefix, it may be too long to be a thread name */
priv->name = priv->key +
(g_str_has_prefix(priv->key, "/dev/") ? 5 : 0);
}
}
pthread_mutex_unlock(&gbinder_ipc_mutex);
@ -1801,10 +1858,39 @@ GBinderObjectRegistry*
gbinder_ipc_object_registry(
GBinderIpc* self)
{
/* Only used by unit tests */
return G_LIKELY(self) ? &self->priv->object_registry : NULL;
}
const GBinderIo*
gbinder_ipc_io(
GBinderIpc* self)
{
return G_LIKELY(self) ? gbinder_driver_io(self->driver) : NULL;
}
const GBinderRpcProtocol*
gbinder_ipc_protocol(
GBinderIpc* self)
{
return G_LIKELY(self) ? gbinder_driver_protocol(self->driver) : NULL;
}
int
gbinder_ipc_ping_sync(
GBinderIpc* ipc,
guint32 handle,
const GBinderIpcSyncApi* api)
{
GBinderDriver* driver = ipc->driver;
GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver);
guint32 code = gbinder_driver_protocol(driver)->ping_tx;
int ret;
gbinder_remote_reply_unref(api->sync_reply(ipc, handle, code, req, &ret));
gbinder_local_request_unref(req);
return ret;
}
gulong
gbinder_ipc_transact(
GBinderIpc* self,
@ -1951,7 +2037,11 @@ gbinder_ipc_dispose(
GVERBOSE_("%s", self->dev);
/* Lock */
pthread_mutex_lock(&gbinder_ipc_mutex);
GASSERT(gbinder_ipc_table);
/*
* gbinder_ipc_dispose() can be invoked more than once (typically
* at shutdown) and gbinder_ipc_table here may actually happen to
* be NULL, hence the check.
*/
if (gbinder_ipc_table) {
GBinderIpcPriv* priv = self->priv;

View File

@ -59,6 +59,12 @@ struct gbinder_ipc_tx {
void* user_data;
};
typedef
gboolean
(*GBinderIpcLocalObjectCheckFunc)(
GBinderLocalObject* obj,
void* user_data);
typedef
void
(*GBinderIpcReplyFunc)(
@ -117,6 +123,24 @@ gbinder_ipc_object_registry(
GBinderIpc* ipc)
GBINDER_INTERNAL;
const GBinderIo*
gbinder_ipc_io(
GBinderIpc* ipc)
GBINDER_INTERNAL;
const GBinderRpcProtocol*
gbinder_ipc_protocol(
GBinderIpc* ipc)
GBINDER_INTERNAL;
GBinderLocalObject*
gbinder_ipc_find_local_object(
GBinderIpc* ipc,
GBinderIpcLocalObjectCheckFunc func,
void* user_data)
GBINDER_INTERNAL
G_GNUC_WARN_UNUSED_RESULT;
void
gbinder_ipc_register_local_object(
GBinderIpc* ipc,
@ -124,11 +148,10 @@ gbinder_ipc_register_local_object(
GBINDER_INTERNAL;
GBinderRemoteObject*
gbinder_ipc_get_remote_object(
GBinderIpc* ipc,
guint32 handle,
gboolean maybe_dead)
GBINDER_INTERNAL;
gbinder_ipc_get_service_manager(
GBinderIpc* self)
GBINDER_INTERNAL
G_GNUC_WARN_UNUSED_RESULT;
void
gbinder_ipc_invalidate_remote_handle(
@ -136,6 +159,13 @@ gbinder_ipc_invalidate_remote_handle(
guint32 handle)
GBINDER_INTERNAL;
int
gbinder_ipc_ping_sync(
GBinderIpc* ipc,
guint32 handle,
const GBinderIpcSyncApi* api)
GBINDER_INTERNAL;
gulong
gbinder_ipc_transact(
GBinderIpc* ipc,

View File

@ -34,6 +34,7 @@
#include "gbinder_driver.h"
#include "gbinder_ipc.h"
#include "gbinder_buffer_p.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply_p.h"
#include "gbinder_remote_request.h"
@ -41,6 +42,7 @@
#include "gbinder_log.h"
#include <gutil_strv.h>
#include <gutil_macros.h>
#include <errno.h>
@ -51,6 +53,11 @@ struct gbinder_local_object_priv {
void* user_data;
};
typedef struct gbinder_local_object_acquire_data {
GBinderLocalObject* object;
GBinderBufferContentsList* bufs;
} GBinderLocalObjectAcquireData;
G_DEFINE_TYPE(GBinderLocalObject, gbinder_local_object, G_TYPE_OBJECT)
#define PARENT_CLASS gbinder_local_object_parent_class
@ -285,10 +292,10 @@ gbinder_local_object_handle_later(
static
gboolean
gbinder_local_object_handle_increfs_proc(
gpointer local)
gbinder_local_object_increfs_proc(
gpointer user_data)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data);
self->weak_refs++;
g_signal_emit(self, gbinder_local_object_signals
@ -298,10 +305,10 @@ gbinder_local_object_handle_increfs_proc(
static
gboolean
gbinder_local_object_handle_decrefs_proc(
gpointer local)
gbinder_local_object_decrefs_proc(
gpointer user_data)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data);
GASSERT(self->weak_refs > 0);
self->weak_refs--;
@ -311,30 +318,66 @@ gbinder_local_object_handle_decrefs_proc(
}
static
gboolean
gbinder_local_object_handle_acquire_proc(
gpointer local)
void
gbinder_local_object_default_acquire(
GBinderLocalObject* self)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
self->strong_refs++;
gbinder_local_object_ref(self);
GVERBOSE_("%p => %d", self, self->strong_refs);
g_signal_emit(self, gbinder_local_object_signals
[SIGNAL_STRONG_REFS_CHANGED], 0);
return G_SOURCE_REMOVE;
}
static
gboolean
gbinder_local_object_handle_release_proc(
gpointer local)
gbinder_local_object_acquire_proc(
gpointer user_data)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
GBinderLocalObjectAcquireData* data = user_data;
GBinderLocalObject* self = data->object;
GBINDER_LOCAL_OBJECT_GET_CLASS(self)->acquire(self);
return G_SOURCE_REMOVE;
}
static
void
gbinder_local_object_acquire_done(
gpointer user_data)
{
GBinderLocalObjectAcquireData* data = user_data;
GBinderLocalObject* self = data->object;
gbinder_driver_acquire_done(self->ipc->driver, self);
gbinder_local_object_unref(self);
gbinder_buffer_contents_list_free(data->bufs);
return gutil_slice_free(data);
}
static
void
gbinder_local_object_default_release(
GBinderLocalObject* self)
{
GASSERT(self->strong_refs > 0);
if (self->strong_refs > 0) {
self->strong_refs--;
GVERBOSE_("%p => %d", self, self->strong_refs);
g_signal_emit(self, gbinder_local_object_signals
[SIGNAL_STRONG_REFS_CHANGED], 0);
gbinder_local_object_unref(self);
}
}
static
gboolean
gbinder_local_object_release_proc(
gpointer obj)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(obj);
GBINDER_LOCAL_OBJECT_GET_CLASS(self)->release(self);
return G_SOURCE_REMOVE;
}
@ -527,33 +570,51 @@ void
gbinder_local_object_handle_increfs(
GBinderLocalObject* self)
{
gbinder_local_object_handle_later(self,
gbinder_local_object_handle_increfs_proc);
gbinder_local_object_handle_later(self, gbinder_local_object_increfs_proc);
}
void
gbinder_local_object_handle_decrefs(
GBinderLocalObject* self)
{
gbinder_local_object_handle_later(self,
gbinder_local_object_handle_decrefs_proc);
gbinder_local_object_handle_later(self, gbinder_local_object_decrefs_proc);
}
void
gbinder_local_object_handle_acquire(
GBinderLocalObject* self)
GBinderLocalObject* self,
GBinderBufferContentsList* bufs)
{
gbinder_local_object_ref(self);
gbinder_local_object_handle_later(self,
gbinder_local_object_handle_acquire_proc);
if (G_LIKELY(self)) {
GBinderLocalObjectPriv* priv = self->priv;
GBinderLocalObjectAcquireData* data =
g_slice_new(GBinderLocalObjectAcquireData);
/*
* This is a bit complicated :)
* GBinderProxyObject derived from GBinderLocalObject acquires a
* reference to the remote object in addition to performing the
* default GBinderLocalObject action later on the main thread.
* We must ensure that remote object doesn't go away before we
* acquire our reference to it. One of the references to that
* remote object (possibly the last one) may be associated with
* the transaction buffer contained in GBinderBufferContentsList.
* We don't know exactly which one we need, so we keep all those
* buffers alive until we are done with BR_ACQUIRE.
*/
data->object = gbinder_local_object_ref(self);
data->bufs = gbinder_buffer_contents_list_dup(bufs);
g_main_context_invoke_full(priv->context, G_PRIORITY_DEFAULT,
gbinder_local_object_acquire_proc, data,
gbinder_local_object_acquire_done);
}
}
void
gbinder_local_object_handle_release(
GBinderLocalObject* self)
{
gbinder_local_object_handle_later(self,
gbinder_local_object_handle_release_proc);
gbinder_local_object_handle_later(self, gbinder_local_object_release_proc);
}
/*==========================================================================*
@ -614,6 +675,8 @@ gbinder_local_object_class_init(
gbinder_local_object_default_handle_looper_transaction;
klass->can_handle_transaction =
gbinder_local_object_default_can_handle_transaction;
klass->acquire = gbinder_local_object_default_acquire;
klass->release = gbinder_local_object_default_release;
klass->drop = gbinder_local_object_default_drop;
gbinder_local_object_signals[SIGNAL_WEAK_REFS_CHANGED] =

View File

@ -77,6 +77,8 @@ typedef struct gbinder_local_object_class {
GBinderLocalReply* (*handle_looper_transaction)
(GBinderLocalObject* self, GBinderRemoteRequest* req, guint code,
guint flags, int* status);
void (*acquire)(GBinderLocalObject* self);
void (*release)(GBinderLocalObject* self);
void (*drop)(GBinderLocalObject* self);
/* Need to add some placeholders if this class becomes public */
} GBinderLocalObjectClass;
@ -166,7 +168,8 @@ gbinder_local_object_handle_decrefs(
void
gbinder_local_object_handle_acquire(
GBinderLocalObject* obj)
GBinderLocalObject* obj,
GBinderBufferContentsList* bufs)
GBINDER_INTERNAL;
void

View File

@ -43,6 +43,7 @@ struct gbinder_local_reply {
gint refcount;
GBinderWriterData data;
GBinderOutputData out;
GBinderBufferContents* contents;
};
GBINDER_INLINE_FUNC
@ -96,10 +97,14 @@ gbinder_local_reply_new(
GBinderLocalReply*
gbinder_local_reply_set_contents(
GBinderLocalReply* self,
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
{
if (self) {
gbinder_writer_data_set_contents(&self->data, buffer);
gbinder_writer_data_set_contents(&self->data, buffer, convert);
gbinder_buffer_contents_unref(self->contents);
self->contents = gbinder_buffer_contents_ref
(gbinder_buffer_contents(buffer));
}
return self;
}
@ -114,7 +119,8 @@ gbinder_local_reply_free(
gutil_int_array_free(data->offsets, TRUE);
g_byte_array_free(data->bytes, TRUE);
gbinder_cleanup_free(data->cleanup);
g_slice_free(GBinderLocalReply, self);
gbinder_buffer_contents_unref(self->contents);
gutil_slice_free(self);
}
GBinderLocalReply*
@ -147,6 +153,13 @@ gbinder_local_reply_data(
return G_LIKELY(self) ? &self->out : NULL;
}
GBinderBufferContents*
gbinder_local_reply_contents(
GBinderLocalReply* self)
{
return G_LIKELY(self) ? self->contents : NULL;
}
void
gbinder_local_reply_cleanup(
GBinderLocalReply* self,

View File

@ -47,10 +47,16 @@ gbinder_local_reply_data(
GBinderLocalReply* reply)
GBINDER_INTERNAL;
GBinderBufferContents*
gbinder_local_reply_contents(
GBinderLocalReply* reply)
GBINDER_INTERNAL;
GBinderLocalReply*
gbinder_local_reply_set_contents(
GBinderLocalReply* reply,
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
#endif /* GBINDER_LOCAL_REPLY_PRIVATE_H */

View File

@ -31,6 +31,7 @@
*/
#include "gbinder_local_request_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_output_data.h"
#include "gbinder_writer_p.h"
#include "gbinder_buffer_p.h"
@ -91,6 +92,7 @@ gbinder_local_request_new(
if (init) {
gsize size;
gconstpointer data = g_bytes_get_data(init, &size);
writer->bytes = g_byte_array_sized_new(size);
g_byte_array_append(writer->bytes, data, size);
} else {
@ -103,15 +105,33 @@ gbinder_local_request_new(
return NULL;
}
GBinderLocalRequest*
gbinder_local_request_new_iface(
const GBinderIo* io,
const GBinderRpcProtocol* protocol,
const char* iface)
{
GBinderLocalRequest* self = gbinder_local_request_new(io, NULL);
if (self && G_LIKELY(protocol) && G_LIKELY(iface)) {
GBinderWriter writer;
gbinder_local_request_init_writer(self, &writer);
protocol->write_rpc_header(&writer, iface);
}
return self;
}
GBinderLocalRequest*
gbinder_local_request_new_from_data(
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
{
GBinderLocalRequest* self = gbinder_local_request_new
(gbinder_buffer_io(buffer), NULL);
if (self) {
gbinder_writer_data_append_contents(&self->data, buffer, 0);
gbinder_writer_data_append_contents(&self->data, buffer, 0, convert);
}
return self;
}
@ -120,10 +140,11 @@ void
gbinder_local_request_append_contents(
GBinderLocalRequest* self,
GBinderBuffer* buffer,
gsize offset)
gsize off,
GBinderObjectConverter* convert)
{
if (self) {
gbinder_writer_data_append_contents(&self->data, buffer, offset);
gbinder_writer_data_append_contents(&self->data, buffer, off, convert);
}
}

View File

@ -43,21 +43,30 @@ gbinder_local_request_new(
GBytes* init)
GBINDER_INTERNAL;
GBinderOutputData*
gbinder_local_request_data(
GBinderLocalRequest* req)
GBinderLocalRequest*
gbinder_local_request_new_iface(
const GBinderIo* io,
const GBinderRpcProtocol* protocol,
const char* iface)
GBINDER_INTERNAL;
GBinderLocalRequest*
gbinder_local_request_new_from_data(
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
GBinderOutputData*
gbinder_local_request_data(
GBinderLocalRequest* req)
GBINDER_INTERNAL;
void
gbinder_local_request_append_contents(
GBinderLocalRequest* req,
GBinderBuffer* buffer,
gsize offset)
gsize offset,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
#endif /* GBINDER_LOCAL_REQUEST_PRIVATE_H */

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GBINDER_OBJECT_CONVERTER_H
#define GBINDER_OBJECT_CONVERTER_H
#include "gbinder_types_p.h"
typedef struct gbinder_object_converter_functions {
GBinderLocalObject* (*handle_to_local)(GBinderObjectConverter*, guint32);
} GBinderObjectConverterFunctions;
struct gbinder_object_converter {
const GBinderObjectConverterFunctions* f;
const GBinderIo* io;
const GBinderRpcProtocol* protocol;
};
/* Inline wrappers */
GBINDER_INLINE_FUNC
GBinderLocalObject*
gbinder_object_converter_handle_to_local(
GBinderObjectConverter* convert,
guint32 handle)
{
return convert ? convert->f->handle_to_local(convert, handle) : NULL;
}
#endif /* GBINDER_OBJECT_CONVERTER_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@ -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,13 +35,19 @@
#include "gbinder_types_p.h"
typedef enum gbinder_remote_registry_create {
REMOTE_REGISTRY_DONT_CREATE,
REMOTE_REGISTRY_CAN_CREATE,
REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE
} REMOTE_REGISTRY_CREATE;
typedef struct gbinder_object_registry_functions {
void (*ref)(GBinderObjectRegistry* reg);
void (*unref)(GBinderObjectRegistry* reg);
GBinderLocalObject* (*get_local)(GBinderObjectRegistry* reg,
void* pointer);
GBinderRemoteObject* (*get_remote)(GBinderObjectRegistry* reg,
guint32 handle);
guint32 handle, REMOTE_REGISTRY_CREATE create);
} GBinderObjectRegistryFunctions;
struct gbinder_object_registry {
@ -81,9 +87,10 @@ GBINDER_INLINE_FUNC
GBinderRemoteObject*
gbinder_object_registry_get_remote(
GBinderObjectRegistry* reg,
guint32 handle)
guint32 handle,
REMOTE_REGISTRY_CREATE create)
{
return reg ? reg->f->get_remote(reg, handle) : NULL;
return reg ? reg->f->get_remote(reg, handle, create) : NULL;
}
#endif /* GBINDER_OBJECT_REGISTRY_H */

View File

@ -38,8 +38,12 @@
#include "gbinder_local_reply.h"
#include "gbinder_remote_object_p.h"
#include "gbinder_remote_request_p.h"
#include "gbinder_remote_reply.h"
#include "gbinder_remote_reply_p.h"
#include "gbinder_object_converter.h"
#include "gbinder_object_registry.h"
#include "gbinder_driver.h"
#include "gbinder_ipc.h"
#include "gbinder_log.h"
#include <gutil_macros.h>
@ -56,16 +60,195 @@ struct gbinder_proxy_tx {
};
struct gbinder_proxy_object_priv {
gulong remote_death_id;
gboolean dropped;
GBinderProxyTx* tx;
GMutex mutex; /* Protects the hashtable below */
GHashTable* subproxies;
};
GType gbinder_proxy_object_get_type(void) GBINDER_INTERNAL;
G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
GBINDER_TYPE_LOCAL_OBJECT)
#define GBINDER_IS_PROXY_OBJECT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \
GBINDER_TYPE_PROXY_OBJECT)
#define THIS(obj) GBINDER_PROXY_OBJECT(obj)
#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;
GBINDER_INLINE_FUNC
GBinderProxyObjectConverter*
gbinder_proxy_object_converter_cast(
GBinderObjectConverter* pub)
{
return G_CAST(pub, GBinderProxyObjectConverter, pub);
}
static
gboolean
gbinder_proxy_object_converter_check(
GBinderLocalObject* obj,
void* remote)
{
if (GBINDER_IS_PROXY_OBJECT(obj) && THIS(obj)->remote == remote) {
/* Found matching proxy object */
return TRUE;
}
/* Keep looking */
return FALSE;
}
static
GBinderLocalObject*
gbinder_proxy_object_converter_handle_to_local(
GBinderObjectConverter* pub,
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 */);
GBinderLocalObject* local = gbinder_ipc_find_local_object(c->local,
gbinder_proxy_object_converter_check, remote);
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 */
}
/* Release the reference returned by gbinder_object_registry_get_remote */
gbinder_remote_object_unref(remote);
return local;
}
static
void
gbinder_proxy_object_converter_init(
GBinderProxyObjectConverter* convert,
GBinderProxyObject* proxy,
GBinderIpc* remote,
GBinderIpc* local)
{
static const GBinderObjectConverterFunctions gbinder_converter_fn = {
.handle_to_local = gbinder_proxy_object_converter_handle_to_local
};
GBinderObjectConverter* pub = &convert->pub;
GBinderIpc* dest = proxy->parent.ipc;
memset(convert, 0, sizeof(*convert));
convert->proxy = proxy;
convert->remote = remote;
convert->local = local;
pub->f = &gbinder_converter_fn;
pub->io = gbinder_ipc_io(dest);
pub->protocol = gbinder_ipc_protocol(dest);
}
/*==========================================================================*
* Implementation
*==========================================================================*/
@ -110,11 +293,31 @@ gbinder_proxy_tx_reply(
void* user_data)
{
GBinderProxyTx* tx = user_data;
GBinderLocalReply* fwd = gbinder_remote_reply_copy_to_local(reply);
GBinderProxyObject* self = tx->proxy;
GBinderProxyObjectConverter convert;
GBinderLocalReply* fwd;
/*
* For proxy objects auto-created by the reply, the remote side (the
* one sent the reply) will be the remote GBinderIpc and this object's
* GBinderIpc will be the local, i.e. those proxies will work in the
* same direction as the top level object. The direction gets inverted
* twice.
*/
gbinder_proxy_object_converter_init(&convert, self, ipc, self->parent.ipc);
fwd = gbinder_remote_reply_convert_to_local(reply, &convert.pub);
tx->id = 0;
gbinder_proxy_tx_dequeue(tx);
gbinder_remote_request_complete(tx->req, fwd, status);
gbinder_remote_request_complete(tx->req, fwd,
(status > 0) ? (-EFAULT) : status);
if (status == GBINDER_STATUS_DEAD_OBJECT) {
/*
* Some kernels sometimes don't bother sending us death notifications.
* Let's also interpret BR_DEAD_REPLY as an obituary to make sure that
* we don't keep dead remote objects around.
*/
gbinder_remote_object_commit_suicide(self->remote);
}
gbinder_local_reply_unref(fwd);
}
@ -139,16 +342,14 @@ gbinder_proxy_object_handle_transaction(
guint flags,
int* status)
{
GBinderProxyObject* self = GBINDER_PROXY_OBJECT(object);
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
GBinderRemoteObject* remote = self->remote;
if (!priv->dropped && !gbinder_remote_object_is_dead(remote)) {
GBinderIpc* remote_ipc = remote->ipc;
GBinderDriver* remote_driver = remote_ipc->driver;
GBinderLocalRequest* fwd =
gbinder_remote_request_translate_to_local(req, remote_driver);
GBinderLocalRequest* fwd;
GBinderProxyTx* tx = g_slice_new0(GBinderProxyTx);
GBinderProxyObjectConverter convert;
g_object_ref(tx->proxy = self);
tx->req = gbinder_remote_request_ref(req);
@ -158,8 +359,17 @@ gbinder_proxy_object_handle_transaction(
/* Mark the incoming request as pending */
gbinder_remote_request_block(req);
/*
* For auto-created proxy objects, this object's GBinderIpc will
* become a remote, and the remote's GBinderIpc will become local
* because they work in the opposite direction.
*/
gbinder_proxy_object_converter_init(&convert, self, object->ipc,
remote->ipc);
/* Forward the transaction */
tx->id = gbinder_ipc_transact(remote_ipc, remote->handle, code, flags,
fwd = gbinder_remote_request_convert_to_local(req, &convert.pub);
tx->id = gbinder_ipc_transact(remote->ipc, remote->handle, code, flags,
fwd, gbinder_proxy_tx_reply, gbinder_proxy_tx_destroy, tx);
gbinder_local_request_unref(fwd);
*status = GBINDER_STATUS_OK;
@ -180,15 +390,50 @@ gbinder_proxy_object_can_handle_transaction(
return GBINDER_LOCAL_TRANSACTION_SUPPORTED;
}
static
void
gbinder_proxy_object_acquire(
GBinderLocalObject* object)
{
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
if (priv->remote_death_id && !object->strong_refs) {
GBinderRemoteObject* remote = self->remote;
/* First strong ref, acquire the attached remote object */
gbinder_driver_acquire(remote->ipc->driver, remote->handle);
}
GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->acquire(object);
}
static
void
gbinder_proxy_object_release(
GBinderLocalObject* object)
{
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
if (priv->remote_death_id && object->strong_refs == 1) {
GBinderRemoteObject* remote = self->remote;
/* Last strong ref, release the attached remote object */
gbinder_driver_release(remote->ipc->driver, remote->handle);
}
GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->release(object);
}
static
void
gbinder_proxy_object_drop(
GBinderLocalObject* object)
{
GBinderProxyObject* self = GBINDER_PROXY_OBJECT(object);
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
priv->dropped = TRUE;
gbinder_proxy_object_drop_subproxies(self);
GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->drop(object);
}
@ -209,10 +454,10 @@ gbinder_proxy_object_new(
* to the remote object.
*/
GBinderLocalObject* object = gbinder_local_object_new_with_type
(GBINDER_TYPE_PROXY_OBJECT, src, NULL, NULL, NULL);
(THIS_TYPE, src, NULL, NULL, NULL);
if (object) {
GBinderProxyObject* self = GBINDER_PROXY_OBJECT(object);
GBinderProxyObject* self = THIS(object);
self->remote = gbinder_remote_object_ref(remote);
return self;
@ -230,9 +475,13 @@ void
gbinder_proxy_object_finalize(
GObject* object)
{
GBinderProxyObject* self = GBINDER_PROXY_OBJECT(object);
GBinderProxyObject* self = THIS(object);
GBinderProxyObjectPriv* priv = self->priv;
gbinder_proxy_object_drop_subproxies(self);
gbinder_remote_object_remove_handler(self->remote, priv->remote_death_id);
gbinder_remote_object_unref(self->remote);
g_mutex_clear(&priv->mutex);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
@ -241,8 +490,11 @@ void
gbinder_proxy_object_init(
GBinderProxyObject* self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GBINDER_TYPE_PROXY_OBJECT,
GBinderProxyObjectPriv);
GBinderProxyObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
THIS_TYPE, GBinderProxyObjectPriv);
self->priv = priv;
g_mutex_init(&priv->mutex);
}
static
@ -256,6 +508,8 @@ gbinder_proxy_object_class_init(
object_class->finalize = gbinder_proxy_object_finalize;
klass->can_handle_transaction = gbinder_proxy_object_can_handle_transaction;
klass->handle_transaction = gbinder_proxy_object_handle_transaction;
klass->acquire = gbinder_proxy_object_acquire;
klass->release = gbinder_proxy_object_release;
klass->drop = gbinder_proxy_object_drop;
}

View File

@ -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:
*
@ -40,15 +40,16 @@
struct gbinder_remote_object_priv {
GMainContext* context;
gboolean acquired;
};
typedef GObjectClass GBinderRemoteObjectClass;
GType gbinder_remote_object_get_type(void) GBINDER_INTERNAL;
G_DEFINE_TYPE(GBinderRemoteObject, gbinder_remote_object, G_TYPE_OBJECT)
GType gbinder_remote_object_get_type(void);
#define GBINDER_TYPE_REMOTE_OBJECT (gbinder_remote_object_get_type())
#define GBINDER_REMOTE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GBINDER_TYPE_REMOTE_OBJECT, GBinderRemoteObject))
#define PARENT_CLASS gbinder_remote_object_parent_class
#define THIS_TYPE (gbinder_remote_object_get_type())
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,THIS_TYPE,GBinderRemoteObject)
enum gbinder_remote_object_signal {
SIGNAL_DEATH,
@ -65,31 +66,31 @@ static guint gbinder_remote_object_signals[SIGNAL_COUNT] = { 0 };
static
void
gbinder_remote_object_died_on_main_thread(
gbinder_remote_object_handle_death_on_main_thread(
GBinderRemoteObject* self)
{
if (!self->dead) {
GBinderIpc* ipc = self->ipc;
GBinderDriver* driver = ipc->driver;
GBinderRemoteObjectPriv* priv = self->priv;
GASSERT(!self->dead);
if (!self->dead) {
self->dead = TRUE;
priv->acquired = FALSE;
/* ServiceManager always has the same handle, and can be reanimated. */
if (self->handle != GBINDER_SERVICEMANAGER_HANDLE) {
gbinder_ipc_invalidate_remote_handle(self->ipc, self->handle);
gbinder_ipc_invalidate_remote_handle(ipc, self->handle);
}
gbinder_driver_clear_death_notification(driver, self);
gbinder_driver_release(driver, self->handle);
gbinder_driver_dead_binder_done(driver, self);
g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
}
}
static
gboolean
gbinder_remote_object_died_handle(
gbinder_remote_object_death_notification_proc(
gpointer self)
{
gbinder_remote_object_died_on_main_thread(GBINDER_REMOTE_OBJECT(self));
gbinder_remote_object_handle_death_on_main_thread(THIS(self));
return G_SOURCE_REMOVE;
}
@ -108,16 +109,20 @@ gbinder_remote_object_reanimate(
*/
if (self->dead) {
GBinderIpc* ipc = self->ipc;
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
guint32 handle = self->handle;
/* Kick the horse */
GASSERT(self->handle == GBINDER_SERVICEMANAGER_HANDLE);
if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
if (gbinder_ipc_ping_sync(ipc, handle, &gbinder_ipc_sync_main) == 0) {
GBinderRemoteObjectPriv* priv = self->priv;
GBinderDriver* driver = ipc->driver;
/* Wow, it's alive! */
self->dead = FALSE;
gbinder_ipc_looper_check(self->ipc); /* For death notifications */
gbinder_driver_acquire(ipc->driver, self->handle);
gbinder_driver_request_death_notification(ipc->driver, self);
priv->acquired = TRUE;
gbinder_ipc_looper_check(ipc); /* For death notifications */
gbinder_driver_acquire(driver, handle);
gbinder_driver_request_death_notification(driver, self);
}
}
return !self->dead;
@ -131,8 +136,26 @@ gbinder_remote_object_handle_death_notification(
* checked the object pointer */
GVERBOSE_("%p %u", self, self->handle);
g_main_context_invoke_full(self->priv->context, G_PRIORITY_DEFAULT,
gbinder_remote_object_died_handle, gbinder_remote_object_ref(self),
g_object_unref);
gbinder_remote_object_death_notification_proc,
gbinder_remote_object_ref(self), g_object_unref);
}
void
gbinder_remote_object_commit_suicide(
GBinderRemoteObject* self)
{
/* This function is only invoked by GBinderProxyObject in context of
* the main thread, the object pointer is checked by the caller */
if (!self->dead) {
GBinderRemoteObjectPriv* priv = self->priv;
self->dead = TRUE;
priv->acquired = FALSE;
GVERBOSE_("%p %u", self, self->handle);
gbinder_ipc_invalidate_remote_handle(self->ipc, self->handle);
/* Don't submit BC_DEAD_BINDER_DONE because this is a suicide */
g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
}
}
/*==========================================================================*
@ -143,17 +166,29 @@ GBinderRemoteObject*
gbinder_remote_object_new(
GBinderIpc* ipc,
guint32 handle,
gboolean dead)
REMOTE_OBJECT_CREATE create)
{
if (G_LIKELY(ipc)) {
GBinderRemoteObject* self = g_object_new
(GBINDER_TYPE_REMOTE_OBJECT, NULL);
GBinderRemoteObject* self = g_object_new(THIS_TYPE, NULL);
GBinderRemoteObjectPriv* priv = self->priv;
self->ipc = gbinder_ipc_ref(ipc);
self->handle = handle;
if (!(self->dead = dead)) {
switch (create) {
case REMOTE_OBJECT_CREATE_DEAD:
self->dead = TRUE;
break;
case REMOTE_OBJECT_CREATE_ACQUIRED:
priv->acquired = TRUE;
/* fallthrough */
case REMOTE_OBJECT_CREATE_ALIVE:
break;
}
if (!self->dead) {
gbinder_ipc_looper_check(self->ipc); /* For death notifications */
if (priv->acquired) {
gbinder_driver_acquire(ipc->driver, handle);
}
gbinder_driver_request_death_notification(ipc->driver, self);
}
return self;
@ -166,7 +201,7 @@ gbinder_remote_object_ref(
GBinderRemoteObject* self)
{
if (G_LIKELY(self)) {
g_object_ref(GBINDER_REMOTE_OBJECT(self));
g_object_ref(THIS(self));
return self;
} else {
return NULL;
@ -178,7 +213,7 @@ gbinder_remote_object_unref(
GBinderRemoteObject* self)
{
if (G_LIKELY(self)) {
g_object_unref(GBINDER_REMOTE_OBJECT(self));
g_object_unref(THIS(self));
}
}
@ -230,7 +265,7 @@ gbinder_remote_object_init(
GBinderRemoteObject* self)
{
GBinderRemoteObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
GBINDER_TYPE_REMOTE_OBJECT, GBinderRemoteObjectPriv);
THIS_TYPE, GBinderRemoteObjectPriv);
priv->context = g_main_context_default();
self->priv = priv;
@ -239,29 +274,32 @@ gbinder_remote_object_init(
static
void
gbinder_remote_object_dispose(
GObject* remote)
GObject* object)
{
GBinderRemoteObject* self = GBINDER_REMOTE_OBJECT(remote);
GBinderRemoteObject* self = THIS(object);
gbinder_ipc_remote_object_disposed(self->ipc, self);
G_OBJECT_CLASS(gbinder_remote_object_parent_class)->dispose(remote);
G_OBJECT_CLASS(PARENT_CLASS)->dispose(object);
}
static
void
gbinder_remote_object_finalize(
GObject* remote)
GObject* object)
{
GBinderRemoteObject* self = GBINDER_REMOTE_OBJECT(remote);
GBinderRemoteObject* self = THIS(object);
GBinderRemoteObjectPriv* priv = self->priv;
GBinderIpc* ipc = self->ipc;
GBinderDriver* driver = ipc->driver;
if (!self->dead) {
gbinder_driver_clear_death_notification(driver, self);
if (priv->acquired) {
gbinder_driver_release(driver, self->handle);
}
}
gbinder_ipc_unref(ipc);
G_OBJECT_CLASS(gbinder_remote_object_parent_class)->finalize(remote);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
static

View File

@ -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:
*
@ -51,11 +51,17 @@ struct gbinder_remote_object {
#define gbinder_remote_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
#define gbinder_remote_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
typedef enum gbinder_remote_object_create {
REMOTE_OBJECT_CREATE_DEAD,
REMOTE_OBJECT_CREATE_ALIVE,
REMOTE_OBJECT_CREATE_ACQUIRED
} REMOTE_OBJECT_CREATE;
GBinderRemoteObject*
gbinder_remote_object_new(
GBinderIpc* ipc,
guint32 handle,
gboolean maybe_dead)
REMOTE_OBJECT_CREATE create)
GBINDER_INTERNAL;
gboolean
@ -68,6 +74,11 @@ gbinder_remote_object_handle_death_notification(
GBinderRemoteObject* obj)
GBINDER_INTERNAL;
void
gbinder_remote_object_commit_suicide(
GBinderRemoteObject* self)
GBINDER_INTERNAL;
#endif /* GBINDER_REMOTE_OBJECT_PRIVATE_H */
/*

View File

@ -117,6 +117,14 @@ gbinder_remote_reply_is_empty(
GBinderLocalReply*
gbinder_remote_reply_copy_to_local(
GBinderRemoteReply* self)
{
return gbinder_remote_reply_convert_to_local(self, NULL);
}
GBinderLocalReply*
gbinder_remote_reply_convert_to_local(
GBinderRemoteReply* self,
GBinderObjectConverter* convert)
{
if (G_LIKELY(self)) {
GBinderReaderData* d = &self->data;
@ -124,7 +132,7 @@ gbinder_remote_reply_copy_to_local(
if (reg) {
return gbinder_local_reply_set_contents
(gbinder_local_reply_new(reg->io), d->buffer);
(gbinder_local_reply_new(reg->io), d->buffer, convert);
}
}
return NULL;

View File

@ -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:
*
@ -42,6 +42,12 @@ gbinder_remote_reply_new(
GBinderObjectRegistry* reg)
GBINDER_INTERNAL;
GBinderLocalReply*
gbinder_remote_reply_convert_to_local(
GBinderRemoteReply* reply,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
void
gbinder_remote_reply_set_data(
GBinderRemoteReply* reply,

View File

@ -34,6 +34,7 @@
#include "gbinder_reader_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_request_p.h"
#include "gbinder_object_converter.h"
#include "gbinder_object_registry.h"
#include "gbinder_buffer_p.h"
#include "gbinder_driver.h"
@ -86,31 +87,31 @@ gbinder_remote_request_copy_to_local(
if (G_LIKELY(self)) {
GBinderReaderData* d = &self->data;
return gbinder_local_request_new_from_data(d->buffer);
return gbinder_local_request_new_from_data(d->buffer, NULL);
}
return NULL;
}
GBinderLocalRequest*
gbinder_remote_request_translate_to_local(
gbinder_remote_request_convert_to_local(
GBinderRemoteRequest* req,
GBinderDriver* driver)
GBinderObjectConverter* convert)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReaderData* data = &self->data;
if (!driver || (gbinder_driver_protocol(driver) == self->protocol)) {
if (!convert || convert->protocol == self->protocol) {
/* The same protocol, the same format of RPC header */
return gbinder_local_request_new_from_data(data->buffer);
return gbinder_local_request_new_from_data(data->buffer, convert);
} else {
/* Need to translate to another format */
GBinderLocalRequest* local = gbinder_driver_local_request_new
(driver, self->iface);
GBinderLocalRequest* local = gbinder_local_request_new_iface
(convert->io, convert->protocol, self->iface);
gbinder_local_request_append_contents(local, data->buffer,
self->header_size);
self->header_size, convert);
return local;
}
}

View File

@ -57,9 +57,9 @@ gbinder_remote_request_set_data(
GBINDER_INTERNAL;
GBinderLocalRequest*
gbinder_remote_request_translate_to_local(
gbinder_remote_request_convert_to_local(
GBinderRemoteRequest* req,
GBinderDriver* driver)
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
#endif /* GBINDER_REMOTE_REQUEST_PRIVATE_H */

View File

@ -529,9 +529,8 @@ gbinder_servicemanager_new_with_type(
if (!dev) dev = klass->default_device;
ipc = gbinder_ipc_new(dev);
if (ipc) {
/* Create a possibly dead remote object */
GBinderRemoteObject* object = gbinder_ipc_get_remote_object
(ipc, GBINDER_SERVICEMANAGER_HANDLE, TRUE);
/* Create a (possibly) dead service manager object */
GBinderRemoteObject* object = gbinder_ipc_get_service_manager(ipc);
if (object) {
gboolean first_ref;

View File

@ -39,9 +39,6 @@
#include <glib-object.h>
/* As a special case, ServiceManager's handle is zero */
#define GBINDER_SERVICEMANAGER_HANDLE (0)
typedef struct gbinder_servicemanager_priv GBinderServiceManagerPriv;
typedef struct gbinder_servicemanager {

View File

@ -36,10 +36,12 @@
#include <gbinder_types.h>
typedef struct gbinder_buffer_contents GBinderBufferContents;
typedef struct gbinder_buffer_contents_list GBinderBufferContentsList;
typedef struct gbinder_cleanup GBinderCleanup;
typedef struct gbinder_driver GBinderDriver;
typedef struct gbinder_handler GBinderHandler;
typedef struct gbinder_io GBinderIo;
typedef struct gbinder_object_converter GBinderObjectConverter;
typedef struct gbinder_object_registry GBinderObjectRegistry;
typedef struct gbinder_output_data GBinderOutputData;
typedef struct gbinder_proxy_object GBinderProxyObject;
@ -72,6 +74,9 @@ typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi;
#define HIDL_DEBUG_TRANSACTION HIDL_FOURCC('D','B','G')
#define HIDL_HASH_CHAIN_TRANSACTION HIDL_FOURCC('H','S','H')
/* As a special case, ServiceManager's handle is zero */
#define GBINDER_SERVICEMANAGER_HANDLE (0)
#endif /* GBINDER_TYPES_PRIVATE_H */
/*

View File

@ -32,6 +32,8 @@
#include "gbinder_writer_p.h"
#include "gbinder_buffer_p.h"
#include "gbinder_local_object.h"
#include "gbinder_object_converter.h"
#include "gbinder_io.h"
#include "gbinder_log.h"
@ -55,61 +57,87 @@ GBINDER_INLINE_FUNC GBinderWriterPriv* gbinder_writer_cast(GBinderWriter* pub)
GBINDER_INLINE_FUNC GBinderWriterData* gbinder_writer_data(GBinderWriter* pub)
{ return G_LIKELY(pub) ? gbinder_writer_cast(pub)->data : NULL; }
static
void
gbinder_writer_data_buffer_cleanup(
gpointer data)
{
gbinder_buffer_contents_unref((GBinderBufferContents*)data);
}
void
gbinder_writer_data_set_contents(
GBinderWriterData* data,
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
{
g_byte_array_set_size(data->bytes, 0);
gutil_int_array_set_count(data->offsets, 0);
data->buffers_size = 0;
gbinder_cleanup_reset(data->cleanup);
gbinder_writer_data_append_contents(data, buffer, 0);
gbinder_writer_data_append_contents(data, buffer, 0, convert);
}
void
gbinder_writer_data_append_contents(
GBinderWriterData* data,
GBinderBuffer* buffer,
gsize off)
gsize off,
GBinderObjectConverter* convert)
{
GBinderBufferContents* contents = gbinder_buffer_contents(buffer);
if (contents) {
gsize bufsize;
GByteArray* dest = data->bytes;
const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize);
void** objects = gbinder_buffer_objects(buffer);
if (off < bufsize) {
g_byte_array_append(data->bytes, bufdata + off, bufsize - off);
}
data->cleanup = gbinder_cleanup_add(data->cleanup,
gbinder_writer_data_buffer_cleanup,
data->cleanup = gbinder_cleanup_add(data->cleanup, (GDestroyNotify)
gbinder_buffer_contents_unref,
gbinder_buffer_contents_ref(contents));
if (objects && *objects) {
const GBinderIo* io = gbinder_buffer_io(buffer);
/* GBinderIo must be the same because it's defined by the kernel */
GASSERT(io == data->io);
if (!data->offsets) {
data->offsets = gutil_int_array_new();
}
while (*objects) {
const guint8* obj = *objects++;
gsize offset = obj - bufdata;
gsize objsize = io->object_data_size(obj);
gsize objsize, offset = obj - bufdata;
GBinderLocalObject* local;
guint32 handle;
GASSERT(offset > 0 && offset < bufsize);
gutil_int_array_append(data->offsets, (int)offset);
/* Size of each buffer has to be 8-byte aligned */
data->buffers_size += G_ALIGN8(objsize);
GASSERT(offset >= off && offset < bufsize);
if (offset > off) {
/* Copy serialized data preceeding this object */
g_byte_array_append(dest, bufdata + off, offset - off);
off = offset;
}
/* Offset in the destination buffer */
gutil_int_array_append(data->offsets, dest->len);
/* Convert remote object into local if necessary */
if (convert && io->decode_binder_handle(obj, &handle) &&
(local = gbinder_object_converter_handle_to_local
(convert, handle))) {
const guint pos = dest->len;
g_byte_array_set_size(dest, pos +
GBINDER_MAX_BINDER_OBJECT_SIZE);
objsize = io->encode_local_object(dest->data + pos, local);
g_byte_array_set_size(dest, pos + objsize);
/* Keep the reference */
data->cleanup = gbinder_cleanup_add(data->cleanup,
(GDestroyNotify) gbinder_local_object_unref, local);
} else {
objsize = io->object_size(obj);
g_byte_array_append(dest, obj, objsize);
}
/* Size of each buffer has to be 8-byte aligned */
data->buffers_size += G_ALIGN8(io->object_data_size(obj));
off += objsize;
}
}
if (off < bufsize) {
/* Copy remaining data */
g_byte_array_append(dest, bufdata + off, bufsize - off);
}
}
}

View File

@ -54,14 +54,16 @@ gbinder_writer_init(
void
gbinder_writer_data_set_contents(
GBinderWriterData* data,
GBinderBuffer* buffer)
GBinderBuffer* buffer,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
void
gbinder_writer_data_append_contents(
GBinderWriterData* data,
GBinderBuffer* buffer,
gsize data_offset)
gsize data_offset,
GBinderObjectConverter* convert)
GBINDER_INTERNAL;
void

View File

@ -136,7 +136,6 @@ app_init(
GError* error = NULL;
GOptionContext* options = g_option_context_new("SRC DST NAME IFACES...");
gutil_log_timestamp = FALSE;
gutil_log_default.level = GLOG_LEVEL_DEFAULT;
g_option_context_add_main_entries(options, entries, NULL);

View File

@ -53,7 +53,7 @@ GLOG_MODULE_DEFINE2("test_binder", gutil_log_default);
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(g_free);
static GPrivate test_tx_state = G_PRIVATE_INIT(NULL);
G_LOCK_DEFINE_STATIC(test_binder);
static GMainLoop* test_binder_exit_loop = NULL;
@ -78,8 +78,16 @@ static GMainLoop* test_binder_exit_loop = NULL;
#define TF_ACCEPT_FDS 0x10
#define READ_FLAG_TX_COMPLETION (0x01)
#define READ_FLAG_TX_OTHER (0x02)
#define READ_FLAG_ALL (READ_FLAG_TX_COMPLETION|READ_FLAG_TX_OTHER)
#define READ_FLAG_TX_INCOMING (0x02)
#define READ_FLAG_TX_REPLY (0x04)
#define READ_FLAG_TX_ERROR (0x08)
#define READ_FLAG_TX_OTHER (0x10)
#define READ_FLAGS_ALL (\
READ_FLAG_TX_COMPLETION | \
READ_FLAG_TX_INCOMING | \
READ_FLAG_TX_REPLY | \
READ_FLAG_TX_ERROR | \
READ_FLAG_TX_OTHER)
typedef struct test_binder_io TestBinderIo;
@ -98,9 +106,18 @@ typedef struct test_binder_submit_thread {
TestBinder* binder;
} TestBinderSubmitThread;
typedef enum test_tx_state {
TEST_TX_STATE_NONE,
TEST_TX_STATE_ONEWAY,
TEST_TX_STATE_ACTIVE,
TEST_TX_STATE_REPLY
} TEST_TX_STATE;
/* TestBinderTxState has to be stacked to support nested transactions */
typedef struct test_binder_tx_state {
int tid;
gboolean one_way;
int depth;
TEST_TX_STATE* stack;
} TestBinderTxState;
typedef struct test_binder_node TestBinderNode;
@ -218,6 +235,7 @@ typedef struct binder_buffer_64 {
#define BR_DECREFS_64 _IOR('r', 10, BinderPtrCookie64)
#define BR_NOOP _IO('r', 12)
#define BR_DEAD_BINDER_64 _IOR('r', 15, guint64)
#define BR_CLEAR_DEATH_NOTIFICATION_DONE_64 _IOR('r', 16, guint64)
#define BR_FAILED_REPLY _IO('r', 17)
static
@ -371,6 +389,22 @@ test_binder_exit_wait(
G_UNLOCK(test_binder);
}
static
void
test_binder_object_dead_locked(
TestBinder* binder,
guint64 handle)
{
/* Caller has to remove the object from handle_map and object_map */
guint32 cmd[3];
/* Send DEAD_BINDER to both ends of the socket */
cmd[0] = BR_DEAD_BINDER_64;
*(guint64*)(cmd + 1) = GPOINTER_TO_SIZE(handle);
write(binder->node[0].fd, cmd, sizeof(cmd));
write(binder->node[1].fd, cmd, sizeof(cmd));
}
static
void
test_binder_local_object_gone(
@ -383,16 +417,10 @@ test_binder_local_object_gone(
GDEBUG("Object %p is gone", obj);
if (g_hash_table_contains(binder->object_map, obj)) {
gpointer handle = g_hash_table_lookup(binder->object_map, obj);
guint32 cmd[3];
test_binder_object_dead_locked(binder, GPOINTER_TO_SIZE(handle));
g_hash_table_remove(binder->handle_map, handle);
g_hash_table_remove(binder->object_map, obj);
/* Send DEAD_BINDER to both ends of the socket */
cmd[0] = BR_DEAD_BINDER_64;
*(guint64*)(cmd + 1) = GPOINTER_TO_SIZE(handle);
write(binder->node[0].fd, cmd, sizeof(cmd));
write(binder->node[1].fd, cmd, sizeof(cmd));
}
G_UNLOCK(test_binder);
}
@ -507,10 +535,14 @@ test_binder_cmd_read_flags(
{
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
return READ_FLAG_TX_COMPLETION;
case BR_TRANSACTION_64:
return READ_FLAG_TX_INCOMING;
case BR_REPLY_64:
return READ_FLAG_TX_REPLY;
case BR_FAILED_REPLY:
case BR_DEAD_REPLY:
case BR_REPLY_64:
return READ_FLAG_TX_COMPLETION;
return READ_FLAG_TX_ERROR;
default:
return READ_FLAG_TX_OTHER;
}
@ -613,6 +645,84 @@ test_io_short_wait()
usleep(100000); /* 100 ms */
}
static
TestBinderTxState*
test_tx_state_acquire(
TestBinderNode* node,
TEST_TX_STATE state)
{
TestBinderTxState* my_tx_state = g_private_get(&test_tx_state);
if (my_tx_state) {
g_assert_cmpint(my_tx_state->tid, == ,gettid());
} else {
my_tx_state = g_new0(TestBinderTxState, 1);
my_tx_state->tid = gettid(); /* For debugging */
g_private_set(&test_tx_state, my_tx_state);
}
my_tx_state->stack = g_renew(TEST_TX_STATE, my_tx_state->stack,
my_tx_state->depth + 1);
my_tx_state->stack[my_tx_state->depth++] = state;
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",
my_tx_state->tid);
test_io_short_wait();
}
return my_tx_state;
}
static
void
test_tx_state_release(
TestBinderNode* node)
{
TestBinderTxState* my_tx_state = g_private_get(&test_tx_state);
g_assert(my_tx_state);
g_assert(my_tx_state->depth > 0);
g_assert_cmpint(my_tx_state->tid, == ,gettid());
if (my_tx_state->depth == 1) {
GDEBUG("Thread %d done with the transaction", my_tx_state->tid);
g_assert(g_atomic_pointer_compare_and_exchange(&node->tx_state,
my_tx_state, NULL));
g_private_set(&test_tx_state, NULL);
g_free(my_tx_state->stack);
g_free(my_tx_state);
} else {
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);
}
}
static
TEST_TX_STATE
test_tx_state_get(
TestBinderTxState* tx_state)
{
if (tx_state) {
g_assert_cmpint(tx_state->depth, > ,0);
return tx_state->stack[tx_state->depth - 1];
} else {
return TEST_TX_STATE_NONE;
}
}
static
void
test_tx_state_set(
TestBinderTxState* tx_state,
TEST_TX_STATE state)
{
g_assert(tx_state);
g_assert_cmpint(tx_state->depth, > ,0);
tx_state->stack[tx_state->depth - 1] = state;
}
static
gssize
test_io_passthough_write_64(
@ -620,14 +730,17 @@ test_io_passthough_write_64(
const void* bytes,
gsize bytes_to_write)
{
const guint code = *(guint32*)bytes;
const guint32 code = *(guint32*)bytes;
TestBinderNode* node = fd->node;
TestBinderNode* other = node->other;
TestBinder* binder = node->binder;
BinderTransactionData64* tx = NULL;
TestBinderTxState* my_tx_state = g_private_get(&test_tx_state);
const BinderHandleCookie64* hc;
gssize bytes_written;
guint32* buf;
guint32* cmd;
guint64* cookie;
guint32 buflen;
void* data;
@ -636,9 +749,18 @@ test_io_passthough_write_64(
case BC_ACQUIRE:
case BC_RELEASE:
case BC_REQUEST_DEATH_NOTIFICATION_64:
case BC_CLEAR_DEATH_NOTIFICATION_64:
case BC_DEAD_BINDER_DONE:
return bytes_to_write;
case BC_CLEAR_DEATH_NOTIFICATION_64:
hc = (const BinderHandleCookie64*)((guint32*)bytes + 1);
g_assert(bytes_to_write == (sizeof(guint32) + sizeof(*hc)));
buflen = sizeof(guint32) + sizeof(*cookie);
cmd = g_memdup(bytes, buflen);
*cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE_64;
*(guint64*)(cmd + 1) = hc->cookie;
g_assert(write(other->fd, cmd, buflen) == buflen);
g_free(cmd);
return bytes_to_write;
default:
break;
}
@ -646,42 +768,21 @@ test_io_passthough_write_64(
buf = cmd = g_memdup(bytes, bytes_to_write);
data = cmd + 1;
switch (*cmd) {
case BR_TRANSACTION_64:
*cmd = BC_TRANSACTION_64;
tx = data;
break;
case BC_TRANSACTION_64:
case BC_TRANSACTION_SG_64:
*cmd = BR_TRANSACTION_64;
tx = data;
tx->sender_pid = getpid();
tx->sender_euid = geteuid();
if (my_tx_state) {
g_assert_cmpint(my_tx_state->tid, == ,gettid());
} else {
my_tx_state = g_new0(TestBinderTxState, 1);
my_tx_state->tid = gettid(); /* For debugging */
g_private_set(&test_tx_state, my_tx_state);
}
my_tx_state->one_way = (tx->flags & TF_ONE_WAY) ? TRUE : FALSE;
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",
my_tx_state->tid);
test_io_short_wait();
}
my_tx_state = test_tx_state_acquire(node, (tx->flags & TF_ONE_WAY) ?
TEST_TX_STATE_ONEWAY : TEST_TX_STATE_ACTIVE);
GDEBUG("Transaction thread %d", my_tx_state->tid);
break;
case BR_REPLY_64:
*cmd = BC_REPLY_64;
tx = data;
break;
case BC_REPLY_64:
case BC_REPLY_SG_64:
/* Prepend BR_TRANSACTION_COMPLETE */
GDEBUG("Thread %d inserting BR_TRANSACTION_COMPLETE => fd %d",
gettid(), fd->fd);
GDEBUG("Thread %d inserting BR_TRANSACTION_COMPLETE before reply => "
"fd %d", gettid(), fd->fd);
buf = g_realloc(buf, bytes_to_write + 4);
cmd = buf + 1;
data = cmd + 1;
@ -689,6 +790,8 @@ test_io_passthough_write_64(
*buf = BR_TRANSACTION_COMPLETE;
*cmd = BR_REPLY_64;
tx = data;
my_tx_state = test_tx_state_acquire(node, TEST_TX_STATE_ONEWAY);
GDEBUG("Reply thread %d", my_tx_state->tid);
break;
}
@ -744,21 +847,21 @@ test_io_passthough_write_64(
g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
tx->data_buffer = GPOINTER_TO_SIZE(data_buffer);
tx->data_offsets = GPOINTER_TO_SIZE(data_offsets);
if (tx->flags & TF_ONE_WAY) {
if ((tx->flags & TF_ONE_WAY) || is_reply) {
const guint32 c = BR_TRANSACTION_COMPLETE;
GDEBUG("Thread %d inserting BR_TRANSACTION_COMPLETE for "
"one-way transaction => fd %d", gettid(), node->other->fd);
g_assert(!is_reply);
g_assert(write(node->other->fd, &c, sizeof(c)) == sizeof(c));
GDEBUG("Thread %d inserting BR_TRANSACTION_COMPLETE for %s "
"=> fd %d", gettid(), is_reply ? "reply" :
"one-way transaction", other->fd);
g_assert(write(other->fd, &c, sizeof(c)) == sizeof(c));
}
} else {
const guint32 c = BR_DEAD_REPLY;
/* Fail the transaction */
GDEBUG("Thread %d inserting BR_DEAD_REPLY => fd %d", gettid(),
node->other->fd);
g_assert(write(node->other->fd, &c, sizeof(c)) == sizeof(c));
other->fd);
g_assert(write(other->fd, &c, sizeof(c)) == sizeof(c));
data = buf;
*cmd = 0;
}
@ -844,11 +947,11 @@ test_io_handle_write_read_64(
/*
* If this thread is not performing a transaction (and passthrough
* mode is enabled), don't steal completion commands from other
* threads.
* threads (but allow incoming transactions).
*/
const gint max_read_flags = (!binder->passthrough ||
(node_tx_state && node_tx_state == my_tx_state)) ?
READ_FLAG_ALL : READ_FLAG_TX_OTHER;
READ_FLAGS_ALL : (READ_FLAG_TX_OTHER | READ_FLAG_TX_INCOMING);
const gint looper = GPOINTER_TO_INT(g_private_get(&test_looper));
if (looper <= 0) {
@ -876,7 +979,6 @@ test_io_handle_write_read_64(
int nbytes = 0;
int avail = wr->read_size - wr->read_consumed;
int total = 0;
gboolean transaction_complete = FALSE;
guint8* buf = GSIZE_TO_POINTER(wr->read_buffer + wr->read_consumed);
guint32* cmd;
@ -884,17 +986,56 @@ test_io_handle_write_read_64(
g_assert_cmpint(nbytes, <= ,avail);
switch (cmd[0]) {
case BR_TRANSACTION_COMPLETE:
if (!node_tx_state || node_tx_state != my_tx_state ||
!my_tx_state->one_way) {
/* Still expecting BR_REPLY */
if (node_tx_state) {
g_assert(node_tx_state == my_tx_state);
switch (test_tx_state_get(my_tx_state)) {
case TEST_TX_STATE_REPLY:
case TEST_TX_STATE_ONEWAY:
/* Done with the transaction */
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY |
READ_FLAG_TX_ERROR);
test_tx_state_set(my_tx_state, TEST_TX_STATE_NONE);
break;
case TEST_TX_STATE_ACTIVE:
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING);
test_tx_state_set(my_tx_state, TEST_TX_STATE_REPLY);
break;
case TEST_TX_STATE_NONE:
g_assert_not_reached();
break;
}
/* fallthrough */
} else {
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY |
READ_FLAG_TX_ERROR);
}
break;
case BR_REPLY_64:
if (node_tx_state) {
g_assert(node_tx_state == my_tx_state);
test_tx_state_set(my_tx_state, TEST_TX_STATE_NONE);
}
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY |
READ_FLAG_TX_ERROR);
break;
case BR_FAILED_REPLY:
case BR_DEAD_REPLY:
case BR_REPLY_64:
can_read &= ~READ_FLAG_TX_COMPLETION;
transaction_complete = TRUE;
if (node_tx_state) {
g_assert(node_tx_state == my_tx_state);
test_tx_state_set(my_tx_state, TEST_TX_STATE_NONE);
}
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY |
READ_FLAG_TX_ERROR);
break;
case BR_TRANSACTION_64:
/* Don't swallow transaction related commands too early */
can_read &= ~(READ_FLAG_TX_COMPLETION |
READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY |
READ_FLAG_TX_ERROR);
break;
}
memcpy(buf, cmd, nbytes);
@ -905,10 +1046,9 @@ test_io_handle_write_read_64(
g_free(cmd);
}
if (transaction_complete && my_tx_state) {
g_assert(g_atomic_pointer_compare_and_exchange(&node->tx_state,
my_tx_state, NULL));
GDEBUG("Thread %d done with the transaction", gettid());
if (my_tx_state && my_tx_state == node_tx_state &&
test_tx_state_get(my_tx_state) == TEST_TX_STATE_NONE) {
test_tx_state_release(node);
}
if (nbytes < 0) {
@ -1292,7 +1432,8 @@ test_binder_node_clear(
g_hash_table_unref(test_node_map);
test_node_map = NULL;
if (test_binder_exit_loop) {
g_main_loop_quit(test_binder_exit_loop);
GVERBOSE_("All nodes are gone");
test_quit_later(test_binder_exit_loop);
}
}
close(node->fd);
@ -1343,21 +1484,41 @@ test_binder_handle(
GBinderLocalObject* obj)
{
TestBinder* binder = test_binder_from_fd(fd);
gpointer key = GSIZE_TO_POINTER(obj);
int h = -1;
g_assert(binder);
g_assert(obj);
G_LOCK(test_binder);
if (g_hash_table_contains(binder->object_map, key)) {
h = GPOINTER_TO_INT(g_hash_table_lookup(binder->object_map, key));
if (g_hash_table_contains(binder->object_map, obj)) {
h = GPOINTER_TO_INT(g_hash_table_lookup(binder->object_map, obj));
}
G_UNLOCK(test_binder);
return h;
}
GBinderLocalObject*
test_binder_object(
int fd,
guint handle)
{
TestBinder* binder = test_binder_from_fd(fd);
gpointer key = GSIZE_TO_POINTER(handle);
GBinderLocalObject* obj = NULL;
g_assert(binder);
G_LOCK(test_binder);
if (g_hash_table_contains(binder->handle_map, key)) {
obj = gbinder_local_object_ref
(g_hash_table_lookup(binder->handle_map, key));
}
G_UNLOCK(test_binder);
return obj;
}
guint
test_binder_register_object(
int fd,
@ -1381,11 +1542,17 @@ test_binder_unregister_objects(
int fd)
{
TestBinder* binder;
GHashTableIter it;
gpointer handle, obj;
G_LOCK(test_binder);
binder = test_binder_from_fd_locked(fd);
g_hash_table_remove_all(binder->object_map);
g_hash_table_remove_all(binder->handle_map);
g_hash_table_iter_init(&it, binder->handle_map);
while (g_hash_table_iter_next(&it, &handle, &obj)) {
test_binder_object_dead_locked(binder, GPOINTER_TO_SIZE(handle));
g_hash_table_remove(binder->object_map, obj);
g_hash_table_iter_remove(&it);
}
G_UNLOCK(test_binder);
}

View File

@ -134,6 +134,12 @@ test_binder_handle(
int fd,
GBinderLocalObject* obj);
GBinderLocalObject*
test_binder_object(
int fd,
guint handle)
G_GNUC_WARN_UNUSED_RESULT; /* Need to unref */
guint
test_binder_register_object(
int fd,

View File

@ -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:
*
@ -64,6 +64,17 @@ test_quit_later_n(
GMainLoop* loop,
guint n);
/*
* Makes sure that we own the context for the entire duration of the test.
* That prevents many race conditions - all callbacks that are supposed to
* be invoked on the main thread, are actually invoked on the main thread
* (rather than a random worker thread which happens to acquire the context).
*/
void
test_run_in_context(
const TestOpt* opt,
GTestFunc func);
#define TEST_TIMEOUT_SEC (20)
/* Helper macros */

View File

@ -39,6 +39,11 @@ typedef struct test_quit_later_data{
guint n;
} TestQuitLaterData;
typedef struct test_context_data{
GMainLoop* loop;
GTestFunc func;
} TestContextData;
static
gboolean
test_timeout_expired(
@ -104,6 +109,40 @@ test_quit_later(
g_idle_add(test_quit_later_cb, loop);
}
static
gboolean
test_run_in_context_cb(
gpointer data)
{
TestContextData* test = data;
test->func();
g_main_loop_quit(test->loop);
return G_SOURCE_REMOVE;
}
void
test_run_in_context(
const TestOpt* opt,
GTestFunc func)
{
TestContextData test;
test.loop = g_main_loop_new(NULL, FALSE);
test.func = func;
/*
* This makes sure that we own the context for the entire duration
* of the test. That prevents many race conditions - all callbacks
* that are supposed to be invoked on the main thread, are actually
* invoked on the main thread (rather than a random worker thread
* which happens to acquire the context).
*/
g_idle_add(test_run_in_context_cb, &test);
test_run(opt, test.loop);
g_main_loop_unref(test.loop);
}
void
test_run(
const TestOpt* opt,

View File

@ -315,9 +315,21 @@ test_basic_reply(
}
static
gboolean
void
test_basic_death(
GBinderRemoteObject* obj,
void* loop)
{
GDEBUG("Source object died");
/* Exit the loop */
g_main_loop_quit((GMainLoop*)loop);
}
static
void
test_basic_run(
gpointer main_loop)
void)
{
TestBasic test;
TestConfig config;
@ -330,12 +342,13 @@ test_basic_run(
GBinderIpc* dest_priv_ipc;
GBinderBridge* bridge;
TestLocalObject* obj;
GBinderRemoteObject* br_src_obj;
GBinderRemoteObject* src_obj;
GBinderLocalRequest* req;
GBinderClient* src_client;
const char* name = "test";
const char* fqname = TEST_IFACE "/test";
int src_fd, dest_fd, n = 0;
int src_fd, dest_fd, h, n = 0;
gulong id;
test_config_init(&config, NULL);
@ -384,9 +397,9 @@ test_basic_run(
gbinder_servicemanager_remove_handler(src, id);
/* Get a remote reference to the object created by the bridge */
src_obj = gbinder_servicemanager_get_service_sync(src, fqname, NULL);
g_assert(src_obj); /* autoreleased */
g_assert(!src_obj->dead);
br_src_obj = gbinder_servicemanager_get_service_sync(src, fqname, NULL);
g_assert(gbinder_remote_object_ref(br_src_obj)); /* autoreleased */
g_assert(!br_src_obj->dead);
/*
* This is a trick specific to test_binder simulation. We need to
@ -398,7 +411,8 @@ test_basic_run(
* Note that the original src_obj gets autoreleased and doesn't need
* to be explicitly unreferenced.
*/
src_obj = gbinder_remote_object_new(src_priv_ipc, src_obj->handle, FALSE);
src_obj = gbinder_remote_object_new(src_priv_ipc,
br_src_obj->handle, REMOTE_OBJECT_CREATE_ALIVE);
/* Make a call */
GDEBUG("Submitting a call");
@ -412,11 +426,27 @@ test_basic_run(
/* Wait for completion */
test_run(&test_opt, test.loop);
/* Kill the destination object and wait for auto-created object to die */
g_assert(!br_src_obj->dead);
id = gbinder_remote_object_add_death_handler(br_src_obj, test_basic_death,
test.loop);
h = test_binder_handle(dest_fd, &obj->parent);
g_assert_cmpint(h, > ,0); /* Zero is servicemanager */
GDEBUG("Killing destination object, handle %d", h);
gbinder_local_object_drop(&obj->parent);
test_binder_br_dead_binder(dest_fd, h);
/* Wait for the auto-created object to die */
test_run(&test_opt, test.loop);
g_assert(br_src_obj->dead);
gbinder_remote_object_remove_handler(br_src_obj, id);
GDEBUG("Done");
gbinder_bridge_free(bridge);
gbinder_local_object_unref(&obj->parent);
gbinder_remote_object_unref(src_obj);
gbinder_remote_object_unref(br_src_obj);
test_servicemanager_hidl_free(test.src_impl);
test_servicemanager_hidl_free(dest_impl);
gbinder_servicemanager_unref(src);
@ -433,10 +463,6 @@ test_basic_run(
test_binder_exit_wait(&test_opt, test.loop);
test_config_deinit(&config);
g_main_loop_unref(test.loop);
/* And exit the test loop */
g_main_loop_quit((GMainLoop*)main_loop);
return G_SOURCE_REMOVE;
}
static
@ -444,18 +470,7 @@ void
test_basic(
void)
{
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
/*
* This makes sure that we own the context for the entire duration
* of the test. That prevents many race conditions - all callbacks
* that are supposed to be invoked on the main thread, are actually
* invoked on the main thread (rather than a worker thread which
* happens to acquire the context).
*/
g_idle_add(test_basic_run, loop);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
test_run_in_context(&test_opt, test_basic_run);
}
/*==========================================================================*

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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:
*
@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@ -68,15 +68,47 @@ test_null(
gbinder_buffer_free(buf2);
gbinder_buffer_free(NULL);
gbinder_buffer_contents_list_free(NULL);
g_assert(!gbinder_buffer_driver(NULL));
g_assert(!gbinder_buffer_objects(NULL));
g_assert(!gbinder_buffer_io(NULL));
g_assert(!gbinder_buffer_data(NULL, NULL));
g_assert(!gbinder_buffer_data(NULL, &size));
g_assert(!gbinder_buffer_contents(NULL));
g_assert(!gbinder_buffer_contents_list_add(NULL, NULL));
g_assert(!gbinder_buffer_contents_list_dup(NULL));
g_assert(!size);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* list
*==========================================================================*/
static
void
test_list(
void)
{
static const guint8 data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
void* ptr = g_memdup(data, sizeof(data));
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderBuffer* buf = gbinder_buffer_new(driver, ptr, sizeof(data), NULL);
GBinderBufferContents* contents = gbinder_buffer_contents(buf);
GBinderBufferContentsList* list = gbinder_buffer_contents_list_add
(NULL, contents);
GBinderBufferContentsList* list2 = gbinder_buffer_contents_list_dup(list);
g_assert(contents);
g_assert(list);
g_assert(list2);
gbinder_buffer_free(buf);
gbinder_buffer_contents_list_free(list);
gbinder_buffer_contents_list_free(list2);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* parent
*==========================================================================*/
@ -112,12 +144,14 @@ test_parent(
*==========================================================================*/
#define TEST_PREFIX "/buffer/"
#define TEST_(t) TEST_PREFIX t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "parent", test_parent);
g_test_add_func(TEST_("null"), test_null);
g_test_add_func(TEST_("list"), test_list);
g_test_add_func(TEST_("parent"), test_parent);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@ -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:
*
@ -51,12 +51,12 @@ static TestOpt test_opt;
static
GBinderClient*
test_client_new(
guint handle,
guint h,
const char* iface)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, handle);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE);
GBinderClient* client = gbinder_client_new(obj, iface);
g_assert(client);
@ -99,7 +99,7 @@ test_basic(
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE);
const char* iface = "foo";
GBinderClient* client = gbinder_client_new(obj, iface);
@ -125,7 +125,7 @@ test_interfaces(
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE);
static const GBinderClientIfaceInfo ifaces[] = {
{"33", 33 }, { "11", 11 }, { "22", 22 }
};
@ -198,7 +198,7 @@ test_dead(
gbinder_remote_object_add_death_handler(obj, test_dead_done, loop);
test_binder_br_dead_binder(fd, handle);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(gbinder_remote_object_is_dead(obj));
@ -207,8 +207,9 @@ test_dead(
g_assert(!gbinder_client_transact(client, 0, 0, NULL, NULL, NULL, NULL));
gbinder_client_unref(client);
g_main_loop_unref(loop);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
/*==========================================================================*

View File

@ -76,6 +76,7 @@ test_basic(
g_assert(gbinder_driver_exit_looper(driver));
g_assert(!gbinder_driver_request_death_notification(driver, NULL));
g_assert(!gbinder_driver_clear_death_notification(driver, NULL));
g_assert(!gbinder_driver_dead_binder_done(NULL, NULL));
gbinder_driver_unref(driver);
g_assert(!gbinder_handler_transact(NULL, NULL, NULL, 0, 0, NULL));

View File

@ -103,13 +103,23 @@ test_null(
g_assert(!gbinder_object_registry_ref(NULL));
gbinder_object_registry_unref(NULL);
g_assert(!gbinder_object_registry_get_local(NULL, NULL));
g_assert(!gbinder_object_registry_get_remote(NULL, 0));
g_assert(!gbinder_object_registry_get_remote(NULL, 0, FALSE));
g_assert(!gbinder_ipc_find_local_object(NULL, NULL, NULL));
}
/*==========================================================================*
* basic
*==========================================================================*/
static
gboolean
test_basic_find_none(
GBinderLocalObject* obj,
void* user_data)
{
return FALSE;
}
static
void
test_basic(
@ -124,6 +134,9 @@ test_basic(
gbinder_ipc_cancel(ipc2, 0); /* not a valid transaction */
gbinder_ipc_unref(ipc2);
g_assert(!gbinder_ipc_find_local_object(NULL, test_basic_find_none, NULL));
g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL));
/* Second gbinder_ipc_new returns the same (default) object */
g_assert(gbinder_ipc_new(NULL) == ipc);
g_assert(gbinder_ipc_new("") == ipc);
@ -272,7 +285,8 @@ test_sync_reply_error(
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
const guint32 handle = 0;
const guint32 code = 1;
const gint expected_status = GBINDER_STATUS_FAILED;
const gint expected_status = (-EINVAL);
const gint unexpected_status = GBINDER_STATUS_FAILED;
int status = INT_MAX;
test_binder_br_noop(fd);
@ -281,7 +295,16 @@ test_sync_reply_error(
test_binder_br_reply_status(fd, expected_status);
g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
g_assert(status == expected_status);
g_assert_cmpint(status, == ,expected_status);
/* GBINDER_STATUS_FAILED gets replaced with -EFAULT */
test_binder_br_noop(fd);
test_binder_br_transaction_complete(fd);
test_binder_br_noop(fd);
test_binder_br_reply_status(fd, unexpected_status);
g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
g_assert_cmpint(status, == ,-EFAULT);
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
@ -790,6 +813,58 @@ test_transact_2way(
g_main_loop_unref(loop);
}
/*==========================================================================*
* transact_unhandled
*==========================================================================*/
static
void
test_transact_unhandled_done(
GBinderIpc* ipc,
GBinderRemoteReply* reply,
int status,
void* user_data)
{
g_assert(!reply);
g_assert_cmpint(status, == ,GBINDER_STATUS_DEAD_OBJECT);
test_quit_later((GMainLoop*)user_data);
}
static
void
test_transact_unhandled_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderDriver* driver = ipc->driver;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver);
int fd = gbinder_driver_fd(driver);
test_binder_set_passthrough(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
g_assert(gbinder_ipc_transact(ipc, 1 /* Non-existent object */,
gbinder_driver_protocol(driver)->ping_tx, 0, req,
test_transact_unhandled_done, NULL, loop));
gbinder_local_request_unref(req);
test_run(&test_opt, loop);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
static
void
test_transact_unhandled(
void)
{
test_run_in_context(&test_opt, test_transact_unhandled_run);
}
/*==========================================================================*
* transact_incoming
*==========================================================================*/
@ -819,7 +894,7 @@ test_transact_incoming_proc(
static
void
test_transact_incoming(
test_transact_incoming_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -844,9 +919,11 @@ test_transact_incoming(
test_binder_br_transaction(fd, obj, prot->ping_tx,
gbinder_local_request_data(ping)->bytes);
test_binder_br_transaction_complete(fd); /* For reply */
test_binder_br_transaction(fd, obj, 1,
gbinder_local_request_data(req)->bytes);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_br_transaction_complete(fd); /* For reply */
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
@ -863,6 +940,14 @@ test_transact_incoming(
g_main_loop_unref(loop);
}
static
void
test_transact_incoming(
void)
{
test_run_in_context(&test_opt, test_transact_incoming_run);
}
/*==========================================================================*
* transact_status_reply
*==========================================================================*/
@ -890,7 +975,7 @@ test_transact_status_reply_proc(
static
void
test_transact_status_reply(
test_transact_status_reply_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -912,7 +997,8 @@ test_transact_status_reply(
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_br_transaction_complete(fd); /* For reply */
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
@ -928,6 +1014,14 @@ test_transact_status_reply(
g_main_loop_unref(loop);
}
static
void
test_transact_status_reply(
void)
{
test_run_in_context(&test_opt, test_transact_status_reply_run);
}
/*==========================================================================*
* transact_async
*==========================================================================*/
@ -998,7 +1092,7 @@ test_transact_async_proc(
static
void
test_transact_async(
test_transact_async_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -1020,7 +1114,8 @@ test_transact_async(
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_br_transaction_complete(fd); /* For reply */
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
@ -1036,6 +1131,14 @@ test_transact_async(
g_main_loop_unref(loop);
}
static
void
test_transact_async(
void)
{
test_run_in_context(&test_opt, test_transact_async_run);
}
/*==========================================================================*
* transact_async_sync
*==========================================================================*/
@ -1072,7 +1175,7 @@ test_transact_async_sync_proc(
static
void
test_transact_async_sync(
test_transact_async_sync_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -1094,7 +1197,8 @@ test_transact_async_sync(
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_br_transaction_complete(fd); /* For reply */
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
@ -1110,6 +1214,14 @@ test_transact_async_sync(
g_main_loop_unref(loop);
}
static
void
test_transact_async_sync(
void)
{
test_run_in_context(&test_opt, test_transact_async_sync_run);
}
/*==========================================================================*
* drop_remote_refs
*==========================================================================*/
@ -1127,7 +1239,7 @@ test_drop_remote_refs_cb(
static
void
test_drop_remote_refs(
test_drop_remote_refs_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -1139,7 +1251,7 @@ test_drop_remote_refs(
test_drop_remote_refs_cb, loop);
test_binder_br_acquire(fd, obj);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(obj->strong_refs == 1);
@ -1153,6 +1265,14 @@ test_drop_remote_refs(
g_main_loop_unref(loop);
}
static
void
test_drop_remote_refs(
void)
{
test_run_in_context(&test_opt, test_drop_remote_refs_run);
}
/*==========================================================================*
* cancel_on_exit
*==========================================================================*/
@ -1218,6 +1338,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_("transact_cancel2"), test_transact_cancel2);
g_test_add_func(TEST_("transact_2way"), test_transact_2way);
g_test_add_func(TEST_("transact_incoming"), test_transact_incoming);
g_test_add_func(TEST_("transact_unhandled"), test_transact_unhandled);
g_test_add_func(TEST_("transact_status_reply"), test_transact_status_reply);
g_test_add_func(TEST_("transact_async"), test_transact_async);
g_test_add_func(TEST_("transact_async_sync"), test_transact_async_sync);

View File

@ -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:
*
@ -137,7 +137,7 @@ test_null(
g_assert(!gbinder_ipc_transact_custom(NULL, NULL, NULL, NULL, NULL));
gbinder_local_object_handle_increfs(NULL);
gbinder_local_object_handle_decrefs(NULL);
gbinder_local_object_handle_acquire(NULL);
gbinder_local_object_handle_acquire(NULL, NULL);
gbinder_local_object_handle_release(NULL);
}
@ -179,7 +179,7 @@ test_basic(
base_interface, -1) == GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED);
gbinder_local_object_handle_increfs(foo);
gbinder_local_object_handle_decrefs(foo);
gbinder_local_object_handle_acquire(foo);
gbinder_local_object_handle_acquire(foo, NULL);
gbinder_local_object_handle_release(foo);
gbinder_local_object_unref(foo);
@ -621,7 +621,7 @@ test_increfs_cb(
static
void
test_increfs(
test_increfs_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -635,7 +635,7 @@ test_increfs(
/* ipc is not an object, will be ignored */
test_binder_br_increfs(fd, ipc);
test_binder_br_increfs(fd, obj);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(obj->weak_refs == 1);
@ -646,6 +646,14 @@ test_increfs(
g_main_loop_unref(loop);
}
static
void
test_increfs(
void)
{
test_run_in_context(&test_opt, test_increfs_run);
}
/*==========================================================================*
* decrefs
*==========================================================================*/
@ -664,7 +672,7 @@ test_decrefs_cb(
static
void
test_decrefs(
test_decrefs_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -679,7 +687,7 @@ test_decrefs(
test_binder_br_decrefs(fd, ipc);
test_binder_br_increfs(fd, obj);
test_binder_br_decrefs(fd, obj);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(obj->weak_refs == 0);
@ -690,6 +698,14 @@ test_decrefs(
g_main_loop_unref(loop);
}
static
void
test_decrefs(
void)
{
test_run_in_context(&test_opt, test_decrefs_run);
}
/*==========================================================================*
* acquire
*==========================================================================*/
@ -707,7 +723,7 @@ test_acquire_cb(
static
void
test_acquire(
test_acquire_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -721,7 +737,7 @@ test_acquire(
/* ipc is not an object, will be ignored */
test_binder_br_acquire(fd, ipc);
test_binder_br_acquire(fd, obj);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(obj->strong_refs == 1);
@ -732,6 +748,14 @@ test_acquire(
g_main_loop_unref(loop);
}
static
void
test_acquire(
void)
{
test_run_in_context(&test_opt, test_acquire_run);
}
/*==========================================================================*
* release
*==========================================================================*/
@ -750,7 +774,7 @@ test_release_cb(
static
void
test_release(
test_release_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@ -764,7 +788,7 @@ test_release(
test_binder_br_release(fd, ipc);
test_binder_br_acquire(fd, obj);
test_binder_br_release(fd, obj);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(obj->strong_refs == 0);
@ -775,6 +799,14 @@ test_release(
g_main_loop_unref(loop);
}
static
void
test_release(
void)
{
test_run_in_context(&test_opt, test_release_run);
}
/*==========================================================================*
* Common
*==========================================================================*/

View File

@ -88,7 +88,8 @@ test_null(
gbinder_local_reply_init_writer(NULL, NULL);
gbinder_local_reply_init_writer(NULL, &writer);
g_assert(!gbinder_local_reply_data(NULL));
g_assert(!gbinder_local_reply_set_contents(NULL, NULL));
g_assert(!gbinder_local_reply_contents(NULL));
g_assert(!gbinder_local_reply_set_contents(NULL, NULL, NULL));
gbinder_local_reply_cleanup(NULL, NULL, &count);
gbinder_local_reply_cleanup(NULL, test_int_inc, &count);
@ -480,7 +481,7 @@ test_remote_reply(
/* Copy flat structures (no binder objects) */
buffer = test_buffer_from_bytes(driver, bytes);
req2 = gbinder_local_reply_new(io);
g_assert(gbinder_local_reply_set_contents(req2, buffer) == req2);
g_assert(gbinder_local_reply_set_contents(req2, buffer, NULL) == req2);
gbinder_buffer_free(buffer);
data2 = gbinder_local_reply_data(req2);

View File

@ -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:
*
@ -94,7 +94,7 @@ test_null(
g_assert(!gbinder_local_request_new(NULL, NULL));
g_assert(!gbinder_local_request_ref(NULL));
g_assert(!gbinder_local_request_new_from_data(NULL));
g_assert(!gbinder_local_request_new_from_data(NULL, NULL));
gbinder_local_request_unref(NULL);
gbinder_local_request_init_writer(NULL, NULL);
gbinder_local_request_init_writer(NULL, &writer);
@ -490,7 +490,7 @@ test_remote_request(
/* Copy flat structures (no binder objects) */
buffer = test_buffer_from_bytes(driver, bytes);
req2 = gbinder_local_request_new_from_data(buffer);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
@ -503,7 +503,7 @@ test_remote_request(
/* Same thing but with non-NULL (albeit empty) array of objects */
buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj);
req2 = gbinder_local_request_new_from_data(buffer);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
@ -572,7 +572,7 @@ test_remote_request_obj(
}
buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects);
req2 = gbinder_local_request_new_from_data(buffer);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
test_remote_request_obj_validate_data(gbinder_local_request_data(req2));

View File

@ -54,14 +54,25 @@ static TestOpt test_opt;
#define DEV2 "/dev/ybinder"
#define DEV2_PRIV DEV2 "-private"
#define TX_CODE (GBINDER_FIRST_CALL_TRANSACTION + 1)
#define TX_PARAM_REPLY 0x11111111
#define TX_PARAM_DONT_REPLY 0x22222222
#define TX_RESULT 0x33333333
enum test_tx_codes {
TX_CODE = GBINDER_FIRST_CALL_TRANSACTION,
TX_CODE2,
TX_CODE3
};
#define TX_PARAM1 0x11111111
#define TX_PARAM2 0x22222222
#define TX_PARAM3 0x33333333
#define TX_RESULT1 0x01010101
#define TX_RESULT2 0x02020202
#define TX_PARAM_REPLY 0x11110000
#define TX_PARAM_DONT_REPLY 0x22220000
#define TX_RESULT 0x03030303
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-proxy-XXXXXX";
const char TEST_IFACE[] = "test@1.0::ITest";
const char TEST_IFACE2[] = "test@1.0::ITest2";
static const char* TEST_IFACES[] = { TEST_IFACE, NULL };
static const char* TEST_IFACES2[] = { TEST_IFACE2, NULL };
static const char DEFAULT_CONFIG_DATA[] =
"[Protocol]\n"
"Default = hidl\n"
@ -172,7 +183,7 @@ test_basic_reply(
static
void
test_basic(
test_basic_run(
void)
{
TestConfig config;
@ -193,14 +204,15 @@ test_basic(
fd_obj = gbinder_driver_fd(ipc_obj->driver);
obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_basic_cb, &n);
remote_obj = gbinder_remote_object_new(ipc_proxy,
test_binder_register_object(fd_obj, obj, AUTO_HANDLE), FALSE);
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));
g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
remote_proxy = gbinder_remote_object_new(ipc_obj,
test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE),
FALSE);
REMOTE_OBJECT_CREATE_ALIVE);
proxy_client = gbinder_client_new(remote_proxy, TEST_IFACE);
test_binder_set_passthrough(fd_obj, TRUE);
@ -230,6 +242,14 @@ test_basic(
g_main_loop_unref(loop);
}
static
void
test_basic(
void)
{
test_run_in_context(&test_opt, test_basic_run);
}
/*==========================================================================*
* param
*==========================================================================*/
@ -324,7 +344,7 @@ test_param_reply(
static
void
test_param(
test_param_run(
void)
{
TestConfig config;
@ -350,14 +370,15 @@ test_param(
fd_obj = gbinder_driver_fd(ipc_obj->driver);
obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_param_cb, &n);
remote_obj = gbinder_remote_object_new(ipc_remote_obj,
test_binder_register_object(fd_obj, obj, AUTO_HANDLE), FALSE);
test_binder_register_object(fd_obj, obj, AUTO_HANDLE),
REMOTE_OBJECT_CREATE_ALIVE);
/* remote_proxy(DEV2_PRIV) => proxy (DEV2) => obj (DEV) => DEV_PRIV */
g_assert(!gbinder_proxy_object_new(NULL, remote_obj));
g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
remote_proxy = gbinder_remote_object_new(ipc_remote_proxy,
test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE),
FALSE);
REMOTE_OBJECT_CREATE_ALIVE);
proxy_client = gbinder_client_new(remote_proxy, TEST_IFACE);
test_binder_set_passthrough(fd_obj, TRUE);
@ -367,7 +388,7 @@ test_param(
/*
* Perform two transactions via proxy. First one never gets completed
* and eventually is cancelled, and the seconf one is replied to.
* and eventually is cancelled, and the second one is replied to.
*/
req = gbinder_client_new_request(proxy_client);
gbinder_local_request_append_int32(req, TX_PARAM_DONT_REPLY);
@ -382,7 +403,9 @@ test_param(
gbinder_local_request_unref(req);
test_run(&test_opt, loop);
g_assert_cmpint(n, == ,2);
/* Depending on how callbacks are scheduled, n could be 1 or 2 */
g_assert_cmpint(n, > ,0);
test_binder_unregister_objects(fd_obj);
test_binder_unregister_objects(fd_proxy);
@ -401,6 +424,255 @@ test_param(
g_main_loop_unref(loop);
}
static
void
test_param(
void)
{
test_run_in_context(&test_opt, test_param_run);
}
/*==========================================================================*
* obj
*==========================================================================*/
typedef struct test_obj_data {
GMainLoop* loop;
GBinderLocalObject* tmp_proxy;
gboolean obj_call_handled;
gboolean obj_call_finished;
gboolean obj2_call_handled;
gboolean obj2_call_finished;
} TestObj;
static
GBinderLocalReply*
test_obj2_cb(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
TestObj* test = user_data;
GBinderReader reader;
gint32 param = 0;
GDEBUG("Request 2 handled");
g_assert(!test->obj2_call_handled);
test->obj2_call_handled = TRUE;
g_assert(!flags);
g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE2));
g_assert(code == TX_CODE2);
/* TX_PARAM3 parameter is expected */
gbinder_remote_request_init_reader(req, &reader);
g_assert(gbinder_reader_read_int32(&reader, &param));
g_assert_cmpuint(param, == ,TX_PARAM3);
g_assert(gbinder_reader_at_end(&reader));
*status = GBINDER_STATUS_OK;
return gbinder_local_reply_append_int32
(gbinder_local_object_new_reply(obj), TX_RESULT2);
}
static
void
test_obj2_reply(
GBinderClient* client,
GBinderRemoteReply* reply,
int status,
void* data)
{
GBinderReader reader;
gint32 result = 0;
TestObj* test = data;
GDEBUG("Reply 2 received");
/* Make sure that result got delivered intact */
gbinder_remote_reply_init_reader(reply, &reader);
g_assert(gbinder_reader_read_int32(&reader, &result));
g_assert(gbinder_reader_at_end(&reader));
g_assert_cmpint(result, == ,TX_RESULT2);
g_assert(!test->obj2_call_finished);
test->obj2_call_finished = TRUE;
if (test->obj_call_finished) {
GDEBUG("Both calls are done");
g_main_loop_quit(test->loop);
}
}
static
GBinderLocalReply*
test_obj_cb(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
TestObj* test = user_data;
GBinderReader reader;
GBinderRemoteObject* obj2;
GBinderClient* client2;
GBinderLocalRequest* req2;
gint32 param = 0;
GDEBUG("Request 1 handled");
g_assert(!test->obj_call_handled);
test->obj_call_handled = TRUE;
g_assert(!flags);
g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE));
g_assert(code == TX_CODE);
/* Read parameters: TX_PARAM1, object, TX_PARAM2 */
gbinder_remote_request_init_reader(req, &reader);
g_assert(gbinder_reader_read_int32(&reader, &param));
g_assert_cmpuint(param, == ,TX_PARAM1);
g_assert((obj2 = gbinder_reader_read_object(&reader)));
g_assert(gbinder_reader_read_int32(&reader, &param));
g_assert_cmpuint(param, == ,TX_PARAM2);
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),
obj2->handle);
g_assert(test->tmp_proxy);
/* Call remote object */
client2 = gbinder_client_new(obj2, TEST_IFACE2);
req2 = gbinder_client_new_request(client2);
gbinder_local_request_append_int32(req2, TX_PARAM3);
gbinder_client_transact(client2, TX_CODE2, 0, req2, test_obj2_reply,
NULL, test);
gbinder_local_request_unref(req2);
gbinder_client_unref(client2);
gbinder_remote_object_unref(obj2);
*status = GBINDER_STATUS_OK;
return gbinder_local_reply_append_int32
(gbinder_local_object_new_reply(obj), TX_RESULT1);
}
static
void
test_obj_reply(
GBinderClient* client,
GBinderRemoteReply* reply,
int status,
void* data)
{
GBinderReader reader;
gint32 result = 0;
TestObj* test = data;
GDEBUG("Reply 1 received");
/* Make sure that result got delivered intact */
gbinder_remote_reply_init_reader(reply, &reader);
g_assert(gbinder_reader_read_int32(&reader, &result));
g_assert(gbinder_reader_at_end(&reader));
g_assert_cmpint(result, == ,TX_RESULT1);
g_assert(!test->obj_call_finished);
test->obj_call_finished = TRUE;
if (test->obj2_call_finished) {
GDEBUG("Both calls are done");
g_main_loop_quit(test->loop);
}
}
static
void
test_obj_run(
void)
{
TestConfig config;
TestObj test;
GBinderLocalObject* obj;
GBinderLocalObject* obj2;
GBinderProxyObject* proxy;
GBinderRemoteObject* remote_obj;
GBinderRemoteObject* remote_proxy;
GBinderClient* proxy_client;
GBinderIpc* ipc_obj;
GBinderIpc* ipc_proxy;
GBinderLocalRequest* req;
int fd_obj, fd_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);
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,
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));
g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
remote_proxy = gbinder_remote_object_new(ipc_obj,
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_obj, TRUE);
test_binder_set_passthrough(fd_proxy, TRUE);
test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
/* Pass object reference via proxy */
req = gbinder_client_new_request(proxy_client);
gbinder_local_request_append_int32(req, TX_PARAM1);
gbinder_local_request_append_local_object(req, obj2);
gbinder_local_request_append_int32(req, TX_PARAM2);
gbinder_client_transact(proxy_client, TX_CODE, 0, req, test_obj_reply,
NULL, &test);
gbinder_local_request_unref(req);
test_run(&test_opt, test.loop);
g_assert(test.obj_call_handled);
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);
test_binder_unregister_objects(fd_obj);
test_binder_unregister_objects(fd_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_obj);
gbinder_ipc_unref(ipc_proxy);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, test.loop);
test_config_deinit(&config);
g_main_loop_unref(test.loop);
}
static
void
test_obj(
void)
{
test_run_in_context(&test_opt, test_obj_run);
}
/*==========================================================================*
* Common
*==========================================================================*/
@ -413,6 +685,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);
g_test_add_func(TEST_("param"), test_param);
g_test_add_func(TEST_("obj"), test_obj);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@ -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:
*
@ -70,8 +70,8 @@ test_basic(
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg, 1);
GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg, 2);
GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg,1,TRUE);
GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg,2,TRUE);
g_assert(obj1);
g_assert(obj2);
@ -84,7 +84,7 @@ test_basic(
g_assert(gbinder_remote_object_ref(obj1) == obj1);
gbinder_remote_object_unref(obj1); /* Compensate the above reference */
g_assert(!gbinder_remote_object_add_death_handler(obj1, NULL, NULL));
g_assert(gbinder_ipc_get_remote_object(ipc, 1, TRUE) == obj1);
g_assert(gbinder_object_registry_get_remote(reg, 1, FALSE) == obj1);
gbinder_remote_object_unref(obj1); /* Compensate the above reference */
gbinder_remote_object_unref(obj1);
gbinder_remote_object_unref(obj2);
@ -107,20 +107,20 @@ test_dead_done(
static
void
test_dead(
test_dead_run(
void)
{
const guint handle = 1;
const guint h = 1;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderRemoteObject* obj = gbinder_ipc_get_remote_object
(ipc, handle, FALSE);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE);
gulong id = gbinder_remote_object_add_death_handler
(obj, test_dead_done, loop);
test_binder_br_dead_binder(fd, handle);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_br_dead_binder(fd, h);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
g_assert(gbinder_remote_object_is_dead(obj));
@ -132,6 +132,14 @@ test_dead(
g_main_loop_unref(loop);
}
static
void
test_dead(
void)
{
test_run_in_context(&test_opt, test_dead_run);
}
/*==========================================================================*
* Common
*==========================================================================*/

View File

@ -71,7 +71,8 @@ static
GBinderRemoteObject*
reg_dummy_get_remote(
GBinderObjectRegistry* reg,
guint32 handle)
guint32 handle,
REMOTE_REGISTRY_CREATE create)
{
return NULL;
}

View File

@ -38,6 +38,7 @@
#include "gbinder_remote_request_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_request_p.h"
#include "gbinder_object_converter.h"
#include "gbinder_output_data.h"
#include "gbinder_io.h"
@ -80,6 +81,7 @@ test_null(
g_assert(gbinder_reader_at_end(&reader));
g_assert(!gbinder_remote_request_interface(NULL));
g_assert(!gbinder_remote_request_copy_to_local(NULL));
g_assert(!gbinder_remote_request_convert_to_local(NULL, NULL));
g_assert(gbinder_remote_request_sender_pid(NULL) == (pid_t)(-1));
g_assert(gbinder_remote_request_sender_euid(NULL) == (uid_t)(-1));
g_assert(!gbinder_remote_request_read_int32(NULL, NULL));
@ -89,6 +91,7 @@ test_null(
g_assert(!gbinder_remote_request_read_string8(NULL));
g_assert(!gbinder_remote_request_read_string16(NULL));
g_assert(!gbinder_remote_request_read_object(NULL));
g_assert(!gbinder_object_converter_handle_to_local(NULL, 0));
}
/*==========================================================================*
@ -252,6 +255,15 @@ test_string16(
* to_local
*==========================================================================*/
static
GBinderLocalObject*
test_to_local_convert_none(
GBinderObjectConverter* convert,
guint32 handle)
{
return NULL;
}
static
void
test_to_local(
@ -277,12 +289,16 @@ test_to_local(
TEST_INT64_BYTES(0), /* handle */
TEST_INT64_BYTES(0) /* cookie */
};
static const GBinderObjectConverterFunctions convert_f = {
.handle_to_local = test_to_local_convert_none
};
const char* dev = GBINDER_DEFAULT_BINDER;
const char* dev2 = GBINDER_DEFAULT_HWBINDER;
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
GBinderDriver* driver2 = gbinder_driver_new(dev2, NULL);
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
gbinder_rpc_protocol_for_device(dev), 0, 0);
GBinderObjectConverter convert;
GBinderLocalRequest* req2;
GBinderOutputData* data;
const GByteArray* bytes;
@ -311,7 +327,7 @@ test_to_local(
gbinder_local_request_unref(req2);
/* The same with gbinder_remote_request_translate_to_local() */
req2 = gbinder_remote_request_translate_to_local(req, NULL);
req2 = gbinder_remote_request_convert_to_local(req, NULL);
data = gbinder_local_request_data(req2);
offsets = gbinder_output_data_offsets(data);
bytes = data->bytes;
@ -324,7 +340,11 @@ test_to_local(
gbinder_local_request_unref(req2);
/* Different driver actually requires translation */
req2 = gbinder_remote_request_translate_to_local(req, driver2);
memset(&convert, 0, sizeof(convert));
convert.f = &convert_f;
convert.io = gbinder_driver_io(driver2);
convert.protocol = gbinder_driver_protocol(driver2);
req2 = gbinder_remote_request_convert_to_local(req, &convert);
data = gbinder_local_request_data(req2);
offsets = gbinder_output_data_offsets(data);
bytes = data->bytes;

View File

@ -39,6 +39,7 @@
#include "gbinder_ipc.h"
#include "gbinder_local_object_p.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_object_registry.h"
#include "gbinder_rpc_protocol.h"
#include <gutil_strv.h>
@ -177,8 +178,9 @@ test_servicemanager_get_service(
if (gutil_strv_contains(self->services, name)) {
if (!self->remote) {
self->remote = gbinder_ipc_get_remote_object
(gbinder_client_ipc(sm->client), 1, TRUE);
self->remote = gbinder_object_registry_get_remote
(gbinder_ipc_object_registry(gbinder_client_ipc(sm->client)),
1, TRUE);
}
*status = GBINDER_STATUS_OK;
return gbinder_remote_object_ref(self->remote);
@ -491,6 +493,7 @@ test_basic(
gbinder_servicemanager_unref(sm);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
}
/*==========================================================================*
@ -533,6 +536,7 @@ test_legacy(
gbinder_ipc_unref(ipc);
gbinder_servicemanager_exit();
gbinder_ipc_exit();
}
/*==========================================================================*
@ -682,6 +686,7 @@ test_wait(
gbinder_servicemanager_remove_handler(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
}
/*==========================================================================*
@ -727,6 +732,7 @@ test_wait_long(
gbinder_servicemanager_remove_handler(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
}
/*==========================================================================*
@ -770,6 +776,7 @@ test_wait_async(
gbinder_servicemanager_remove_all_handlers(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@ -779,8 +786,7 @@ test_wait_async(
static
void
test_death(
void)
test_death_run()
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev);
@ -806,7 +812,7 @@ test_death(
/* Generate death notification (need looper for that) */
test_binder_br_dead_binder(fd, 0);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
/* No registrations must have occured */
@ -819,9 +825,17 @@ test_death(
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
static
void
test_death()
{
test_run_in_context(&test_opt, test_death_run);
}
/*==========================================================================*
* reanimate
*==========================================================================*/
@ -840,7 +854,7 @@ test_reanimate_quit(
/* Disable looper and reanimate the object */
GDEBUG("Reanimating...");
test_binder_set_looper_enabled(fd, FALSE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_DISABLE);
test_binder_br_transaction_complete(fd);
test_binder_br_reply(fd, 0, 0, NULL);
}
@ -877,7 +891,7 @@ test_reanimate(
/* Generate death notification (need looper for that) */
test_binder_br_dead_binder(fd, 0);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_run(&test_opt, loop);
/* No registrations must have occured */
@ -891,6 +905,7 @@ test_reanimate(
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
@ -948,6 +963,7 @@ test_reuse(
gbinder_ipc_unref(binder_ipc);
gbinder_ipc_unref(vndbinder_ipc);
gbinder_ipc_unref(hwbinder_ipc);
gbinder_ipc_exit();
}
/*==========================================================================*
@ -992,6 +1008,7 @@ test_notify_type(
gbinder_servicemanager_remove_handler(sm, id2);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
}
static
@ -1049,6 +1066,7 @@ test_list(
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@ -1112,6 +1130,7 @@ test_get(
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@ -1156,8 +1175,10 @@ test_add(
test_run(&test_opt, loop);
g_assert(gutil_strv_contains(test->services, "foo"));
gbinder_local_object_unref(obj);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@ -178,7 +178,7 @@ servicemanager_aidl_new(
self->handle_on_looper_thread = handle_on_looper_thread;
gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
servicemanager_aidl_handler, self);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_binder_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
@ -308,7 +308,7 @@ test_get_cb(
static
void
test_get()
test_get_run()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
@ -319,7 +319,6 @@ test_get()
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GMainContext* context = g_main_loop_get_context(loop);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
@ -346,25 +345,24 @@ test_get()
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
test_run(&test_opt, loop);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
static
void
test_get()
{
test_run_in_context(&test_opt, test_get_run);
}
/*==========================================================================*
* list
*==========================================================================*/
@ -392,7 +390,7 @@ test_list_cb(
static
void
test_list()
test_list_run()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
@ -402,7 +400,6 @@ test_list()
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
GMainContext* context;
TestList test;
memset(&test, 0, sizeof(test));
@ -434,20 +431,11 @@ test_list()
g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
g_assert_cmpstr(test.list[0], == ,name);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
context = g_main_loop_get_context(test.loop);
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, test.loop);
@ -456,6 +444,13 @@ test_list()
g_main_loop_unref(test.loop);
}
static
void
test_list()
{
test_run_in_context(&test_opt, test_list_run);
}
/*==========================================================================*
* notify
*==========================================================================*/
@ -474,7 +469,7 @@ test_notify_cb(
static
void
test_notify()
test_notify_run()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
@ -485,7 +480,6 @@ test_notify()
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GMainContext* context = g_main_loop_get_context(loop);
gulong id;
/* Set up binder simulator */
@ -508,32 +502,31 @@ test_notify()
test_run(&test_opt, loop);
gbinder_servicemanager_remove_handler(sm, id);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(svc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
static
void
test_notify()
{
test_run_in_context(&test_opt, test_notify_run);
}
/*==========================================================================*
* notify2
*==========================================================================*/
static
void
test_notify2()
test_notify2_run()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
@ -545,7 +538,6 @@ test_notify2()
const char* name1 = "name1";
const char* name2 = "name2";
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GMainContext* context = g_main_loop_get_context(loop);
gulong id1, id2;
/* Set up binder simulator */
@ -577,25 +569,24 @@ test_notify2()
gbinder_servicemanager_remove_handler(sm, id1);
gbinder_servicemanager_remove_handler(sm, id2);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
g_main_loop_unref(loop);
}
static
void
test_notify2()
{
test_run_in_context(&test_opt, test_notify2_run);
}
/*==========================================================================*
* Common
*==========================================================================*/

View File

@ -180,7 +180,7 @@ servicemanager_aidl2_new(
self->handle_on_looper_thread = handle_on_looper_thread;
gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
servicemanager_aidl2_handler, self);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
test_binder_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
@ -344,7 +344,7 @@ test_context_deinit(
static
void
test_get()
test_get_run()
{
TestContext test;
const char* name = "name";
@ -375,13 +375,20 @@ test_get()
test_context_deinit(&test);
}
static
void
test_get()
{
test_run_in_context(&test_opt, test_get_run);
}
/*==========================================================================*
* list
*==========================================================================*/
static
void
test_list()
test_list_run()
{
TestContext test;
const char* name = "name";
@ -413,6 +420,13 @@ test_list()
test_context_deinit(&test);
}
static
void
test_list()
{
test_run_in_context(&test_opt, test_list_run);
}
/*==========================================================================*
* Common
*==========================================================================*/

View File

@ -169,7 +169,7 @@ test_get_cb(
static
void
test_get()
test_get_run()
{
TestConfig config;
GBinderIpc* ipc;
@ -178,7 +178,6 @@ test_get()
int fd;
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GMainContext* context = g_main_loop_get_context(loop);
const char* name = "android.hidl.base@1.0::IBase/test";
test_config_init(&config, NULL);
@ -216,19 +215,11 @@ test_get()
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
test_run(&test_opt, loop);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, loop);
@ -236,6 +227,13 @@ test_get()
g_main_loop_unref(loop);
}
static
void
test_get()
{
test_run_in_context(&test_opt, test_get_run);
}
/*==========================================================================*
* list
*==========================================================================*/
@ -263,7 +261,7 @@ test_list_cb(
static
void
test_list()
test_list_run()
{
TestList test;
TestConfig config;
@ -272,7 +270,6 @@ test_list()
GBinderLocalObject* obj;
int fd;
GBinderServiceManager* sm;
GMainContext* context;
const char* name = "android.hidl.base@1.0::IBase/test";
memset(&test, 0, sizeof(test));
@ -311,20 +308,11 @@ test_list()
g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
g_assert_cmpstr(test.list[0], == ,name);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
context = g_main_loop_get_context(test.loop);
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, test.loop);
@ -334,6 +322,13 @@ test_list()
g_main_loop_unref(test.loop);
}
static
void
test_list()
{
test_run_in_context(&test_opt, test_list_run);
}
/*==========================================================================*
* notify
*==========================================================================*/
@ -394,7 +389,7 @@ test_notify_cb(
static
void
test_notify()
test_notify_run()
{
TestNotify test;
TestConfig config;
@ -402,7 +397,6 @@ test_notify()
GBinderLocalObject* obj;
int fd;
GBinderServiceManager* sm;
GMainContext* context;
const char* name = "android.hidl.base@1.0::IBase/test";
gulong id;
@ -443,20 +437,11 @@ test_notify()
test_run(&test_opt, test.loop);
gbinder_servicemanager_remove_handler(sm, id);
/*
* Synchronize deletion of objects with the threads that may
* still be running.
*/
context = g_main_loop_get_context(test.loop);
while (!g_main_context_acquire(context)) {
GDEBUG("Acquiring the context");
}
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(test.smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
g_main_context_release(context);
gbinder_ipc_exit();
test_binder_exit_wait(&test_opt, test.loop);
@ -464,6 +449,13 @@ test_notify()
g_main_loop_unref(test.loop);
}
static
void
test_notify()
{
test_run_in_context(&test_opt, test_notify_run);
}
/*==========================================================================*
* Common
*==========================================================================*/