diff --git a/rpm/libgbinder.spec b/rpm/libgbinder.spec index 722d0da..28d685b 100644 --- a/rpm/libgbinder.spec +++ b/rpm/libgbinder.spec @@ -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} diff --git a/src/gbinder_buffer.c b/src/gbinder_buffer.c index 4971fa6..89e72d3 100644 --- a/src/gbinder_buffer.c +++ b/src/gbinder_buffer.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018 Jolla Ltd. - * Copyright (C) 2018 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 *==========================================================================*/ diff --git a/src/gbinder_buffer_p.h b/src/gbinder_buffer_p.h index 000e565..cdad7d6 100644 --- a/src/gbinder_buffer_p.h +++ b/src/gbinder_buffer_p.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 */ /* diff --git a/src/gbinder_driver.c b/src/gbinder_driver.c index 633b89c..6984bba 100644 --- a/src/gbinder_driver.c +++ b/src/gbinder_driver.c @@ -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); - gbinder_local_object_unref(obj); - GVERBOSE("< BC_ACQUIRE_DONE %p", obj); - gbinder_driver_cmd_data(self, io->bc.acquire_done, data, buf); + 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); + } 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); + } 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); } - 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); } - txstatus = tx.status; - GASSERT(txstatus != (-EAGAIN)); - if (txstatus == (-EAGAIN)) txstatus = (-EFAULT); + /* + * 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; + 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; } diff --git a/src/gbinder_driver.h b/src/gbinder_driver.h index d1c7bf0..d46cb27 100644 --- a/src/gbinder_driver.h +++ b/src/gbinder_driver.h @@ -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 */ /* diff --git a/src/gbinder_io.c b/src/gbinder_io.c index 2aa9c84..c009c3e 100644 --- a/src/gbinder_io.c +++ b/src/gbinder_io.c @@ -90,19 +90,49 @@ 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 */ @@ -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) <= diff --git a/src/gbinder_io.h b/src/gbinder_io.h index a558eb5..bae1c3b 100644 --- a/src/gbinder_io.h +++ b/src/gbinder_io.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2019 Jolla Ltd. - * Copyright (C) 2018-2019 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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, diff --git a/src/gbinder_ipc.c b/src/gbinder_ipc.c index a543d28..affcf54 100644 --- a/src/gbinder_ipc.c +++ b/src/gbinder_ipc.c @@ -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,16 +1116,24 @@ 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)); - if (g_hash_table_size(priv->remote_objects) == 0) { - g_hash_table_unref(priv->remote_objects); - priv->remote_objects = NULL; + 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; + } } } } @@ -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,10 +1182,12 @@ 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_size(priv->local_objects) == 0) { - g_hash_table_unref(priv->local_objects); - priv->local_objects = NULL; + 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); @@ -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); } - g_hash_table_insert(priv->local_objects, obj, obj); + 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; diff --git a/src/gbinder_ipc.h b/src/gbinder_ipc.h index 09e7697..cef6557 100644 --- a/src/gbinder_ipc.h +++ b/src/gbinder_ipc.h @@ -59,6 +59,12 @@ struct gbinder_ipc_tx { void* user_data; }; +typedef +gboolean +(*GBinderIpcLocalObjectCheckFunc)( + GBinderLocalObject* obj, + void* user_data); + typedef void (*GBinderIpcReplyFunc)( @@ -109,7 +115,7 @@ gbinder_ipc_unref( void gbinder_ipc_looper_check( - GBinderIpc* ipc) + GBinderIpc* ipc) GBINDER_INTERNAL; GBinderObjectRegistry* @@ -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, diff --git a/src/gbinder_local_object.c b/src/gbinder_local_object.c index 9842c81..f05f19e 100644 --- a/src/gbinder_local_object.c +++ b/src/gbinder_local_object.c @@ -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 +#include #include @@ -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; - GASSERT(self->strong_refs > 0); - self->strong_refs--; - g_signal_emit(self, gbinder_local_object_signals - [SIGNAL_STRONG_REFS_CHANGED], 0); + 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] = diff --git a/src/gbinder_local_object_p.h b/src/gbinder_local_object_p.h index 37f91a7..1d49323 100644 --- a/src/gbinder_local_object_p.h +++ b/src/gbinder_local_object_p.h @@ -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 diff --git a/src/gbinder_local_reply.c b/src/gbinder_local_reply.c index 6163a03..c3144b2 100644 --- a/src/gbinder_local_reply.c +++ b/src/gbinder_local_reply.c @@ -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, diff --git a/src/gbinder_local_reply_p.h b/src/gbinder_local_reply_p.h index c5665c1..3942972 100644 --- a/src/gbinder_local_reply_p.h +++ b/src/gbinder_local_reply_p.h @@ -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 */ diff --git a/src/gbinder_local_request.c b/src/gbinder_local_request.c index 4e62861..4e2e086 100644 --- a/src/gbinder_local_request.c +++ b/src/gbinder_local_request.c @@ -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); } } diff --git a/src/gbinder_local_request_p.h b/src/gbinder_local_request_p.h index 407ebf1..f04da5c 100644 --- a/src/gbinder_local_request_p.h +++ b/src/gbinder_local_request_p.h @@ -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 */ diff --git a/src/gbinder_object_converter.h b/src/gbinder_object_converter.h new file mode 100644 index 0000000..2fa1fa4 --- /dev/null +++ b/src/gbinder_object_converter.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * 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: + */ diff --git a/src/gbinder_object_registry.h b/src/gbinder_object_registry.h index 482e66e..dc312d7 100644 --- a/src/gbinder_object_registry.h +++ b/src/gbinder_object_registry.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 */ diff --git a/src/gbinder_proxy_object.c b/src/gbinder_proxy_object.c index 445aa45..6b8b6b2 100644 --- a/src/gbinder_proxy_object.c +++ b/src/gbinder_proxy_object.c @@ -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 @@ -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,9 +359,18 @@ 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_proxy_tx_reply, gbinder_proxy_tx_destroy, tx); + 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; } else { @@ -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; } diff --git a/src/gbinder_remote_object.c b/src/gbinder_remote_object.c index 1047972..ec7029d 100644 --- a/src/gbinder_remote_object.c +++ b/src/gbinder_remote_object.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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) { - GBinderIpc* ipc = self->ipc; - GBinderDriver* driver = ipc->driver; - - GASSERT(!self->dead); if (!self->dead) { + GBinderIpc* ipc = self->ipc; + GBinderDriver* driver = ipc->driver; + GBinderRemoteObjectPriv* priv = self->priv; + 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 */ - gbinder_driver_acquire(ipc->driver, handle); + 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); - gbinder_driver_release(driver, self->handle); + 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 diff --git a/src/gbinder_remote_object_p.h b/src/gbinder_remote_object_p.h index 4fbe67c..6076dfe 100644 --- a/src/gbinder_remote_object_p.h +++ b/src/gbinder_remote_object_p.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 */ /* diff --git a/src/gbinder_remote_reply.c b/src/gbinder_remote_reply.c index 7349751..98dd458 100644 --- a/src/gbinder_remote_reply.c +++ b/src/gbinder_remote_reply.c @@ -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; diff --git a/src/gbinder_remote_reply_p.h b/src/gbinder_remote_reply_p.h index 161b174..9744c79 100644 --- a/src/gbinder_remote_reply_p.h +++ b/src/gbinder_remote_reply_p.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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, diff --git a/src/gbinder_remote_request.c b/src/gbinder_remote_request.c index 69c0baf..ba47b23 100644 --- a/src/gbinder_remote_request.c +++ b/src/gbinder_remote_request.c @@ -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; } } diff --git a/src/gbinder_remote_request_p.h b/src/gbinder_remote_request_p.h index 5f417d4..e476fc3 100644 --- a/src/gbinder_remote_request_p.h +++ b/src/gbinder_remote_request_p.h @@ -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 */ diff --git a/src/gbinder_servicemanager.c b/src/gbinder_servicemanager.c index 71b1aba..4d83066 100644 --- a/src/gbinder_servicemanager.c +++ b/src/gbinder_servicemanager.c @@ -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; diff --git a/src/gbinder_servicemanager_p.h b/src/gbinder_servicemanager_p.h index 82d0125..85734f4 100644 --- a/src/gbinder_servicemanager_p.h +++ b/src/gbinder_servicemanager_p.h @@ -39,9 +39,6 @@ #include -/* As a special case, ServiceManager's handle is zero */ -#define GBINDER_SERVICEMANAGER_HANDLE (0) - typedef struct gbinder_servicemanager_priv GBinderServiceManagerPriv; typedef struct gbinder_servicemanager { diff --git a/src/gbinder_types_p.h b/src/gbinder_types_p.h index 4d51acb..112d9e0 100644 --- a/src/gbinder_types_p.h +++ b/src/gbinder_types_p.h @@ -36,10 +36,12 @@ #include 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 */ /* diff --git a/src/gbinder_writer.c b/src/gbinder_writer.c index 4677876..5466855 100644 --- a/src/gbinder_writer.c +++ b/src/gbinder_writer.c @@ -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,62 +57,88 @@ 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 >= 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); + } - 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); + 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); + } } } diff --git a/src/gbinder_writer_p.h b/src/gbinder_writer_p.h index a8d5453..dc28880 100644 --- a/src/gbinder_writer_p.h +++ b/src/gbinder_writer_p.h @@ -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 diff --git a/test/binder-bridge/binder-bridge.c b/test/binder-bridge/binder-bridge.c index 8b523bc..71d2a42 100644 --- a/test/binder-bridge/binder-bridge.c +++ b/test/binder-bridge/binder-bridge.c @@ -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); diff --git a/unit/common/test_binder.c b/unit/common/test_binder.c index 962b313..e6ae321 100644 --- a/unit/common/test_binder.c +++ b/unit/common/test_binder.c @@ -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 */ - break; + 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; + } + } else { + can_read &= ~(READ_FLAG_TX_COMPLETION | + READ_FLAG_TX_INCOMING | READ_FLAG_TX_REPLY | + READ_FLAG_TX_ERROR); } - /* fallthrough */ + 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); } diff --git a/unit/common/test_binder.h b/unit/common/test_binder.h index 9ce5bec..e258341 100644 --- a/unit/common/test_binder.h +++ b/unit/common/test_binder.h @@ -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, diff --git a/unit/common/test_common.h b/unit/common/test_common.h index 1cad73e..c1c66a8 100644 --- a/unit/common/test_common.h +++ b/unit/common/test_common.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 */ diff --git a/unit/common/test_main.c b/unit/common/test_main.c index 8e20989..50e41e6 100644 --- a/unit/common/test_main.c +++ b/unit/common/test_main.c @@ -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, diff --git a/unit/unit_bridge/unit_bridge.c b/unit/unit_bridge/unit_bridge.c index 627f109..417d9c9 100644 --- a/unit/unit_bridge/unit_bridge.c +++ b/unit/unit_bridge/unit_bridge.c @@ -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); } /*==========================================================================* diff --git a/unit/unit_buffer/unit_buffer.c b/unit/unit_buffer/unit_buffer.c index 4ce1674..35092a1 100644 --- a/unit/unit_buffer/unit_buffer.c +++ b/unit/unit_buffer/unit_buffer.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018 Jolla Ltd. - * Copyright (C) 2018 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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(); } diff --git a/unit/unit_client/unit_client.c b/unit/unit_client/unit_client.c index edb60c8..694be13 100644 --- a/unit/unit_client/unit_client.c +++ b/unit/unit_client/unit_client.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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); } /*==========================================================================* diff --git a/unit/unit_driver/unit_driver.c b/unit/unit_driver/unit_driver.c index 82e7e4c..55a4bf2 100644 --- a/unit/unit_driver/unit_driver.c +++ b/unit/unit_driver/unit_driver.c @@ -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)); diff --git a/unit/unit_ipc/unit_ipc.c b/unit/unit_ipc/unit_ipc.c index 6b80e2e..fe3655d 100644 --- a/unit/unit_ipc/unit_ipc.c +++ b/unit/unit_ipc/unit_ipc.c @@ -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); diff --git a/unit/unit_local_object/unit_local_object.c b/unit/unit_local_object/unit_local_object.c index 8943f56..da22bdd 100644 --- a/unit/unit_local_object/unit_local_object.c +++ b/unit/unit_local_object/unit_local_object.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 *==========================================================================*/ diff --git a/unit/unit_local_reply/unit_local_reply.c b/unit/unit_local_reply/unit_local_reply.c index fdf2fb2..0c658ac 100644 --- a/unit/unit_local_reply/unit_local_reply.c +++ b/unit/unit_local_reply/unit_local_reply.c @@ -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); diff --git a/unit/unit_local_request/unit_local_request.c b/unit/unit_local_request/unit_local_request.c index 268f112..8349966 100644 --- a/unit/unit_local_request/unit_local_request.c +++ b/unit/unit_local_request/unit_local_request.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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)); diff --git a/unit/unit_proxy_object/unit_proxy_object.c b/unit/unit_proxy_object/unit_proxy_object.c index 98b487a..c6cf18d 100644 --- a/unit/unit_proxy_object/unit_proxy_object.c +++ b/unit/unit_proxy_object/unit_proxy_object.c @@ -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, ¶m)); + 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, ¶m)); + g_assert_cmpuint(param, == ,TX_PARAM1); + g_assert((obj2 = gbinder_reader_read_object(&reader))); + g_assert(gbinder_reader_read_int32(&reader, ¶m)); + 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(); } diff --git a/unit/unit_remote_object/unit_remote_object.c b/unit/unit_remote_object/unit_remote_object.c index a4b8a2d..df25af0 100644 --- a/unit/unit_remote_object/unit_remote_object.c +++ b/unit/unit_remote_object/unit_remote_object.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018-2020 Jolla Ltd. - * Copyright (C) 2018-2020 Slava Monich + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich * * 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 *==========================================================================*/ diff --git a/unit/unit_remote_reply/unit_remote_reply.c b/unit/unit_remote_reply/unit_remote_reply.c index f0e30f3..a54323d 100644 --- a/unit/unit_remote_reply/unit_remote_reply.c +++ b/unit/unit_remote_reply/unit_remote_reply.c @@ -71,7 +71,8 @@ static GBinderRemoteObject* reg_dummy_get_remote( GBinderObjectRegistry* reg, - guint32 handle) + guint32 handle, + REMOTE_REGISTRY_CREATE create) { return NULL; } diff --git a/unit/unit_remote_request/unit_remote_request.c b/unit/unit_remote_request/unit_remote_request.c index 434dc7b..3737cc9 100644 --- a/unit/unit_remote_request/unit_remote_request.c +++ b/unit/unit_remote_request/unit_remote_request.c @@ -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; diff --git a/unit/unit_servicemanager/unit_servicemanager.c b/unit/unit_servicemanager/unit_servicemanager.c index 48ada68..a111936 100644 --- a/unit/unit_servicemanager/unit_servicemanager.c +++ b/unit/unit_servicemanager/unit_servicemanager.c @@ -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 @@ -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); } diff --git a/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c b/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c index 08a1822..749186d 100644 --- a/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c +++ b/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c @@ -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 *==========================================================================*/ diff --git a/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c b/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c index e3bda57..2149f55 100644 --- a/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c +++ b/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c @@ -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 *==========================================================================*/ diff --git a/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c b/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c index 2410731..e462cb9 100644 --- a/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c +++ b/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c @@ -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 *==========================================================================*/