Compare commits

...

24 Commits
1.1.0 ... 1.1.4

Author SHA1 Message Date
Slava Monich
66f08bf0db Version 1.1.4 2021-01-21 03:38:18 +02:00
Slava Monich
90384e921f Merge pull request #59 from monich/ipc_api
Fix a threading issue + assorted changes
2021-01-21 03:31:56 +02:00
Slava Monich
1ae7c2697c [unit] Added servicemanager_hidl test. JB#42956 2021-01-21 02:13:15 +02:00
Slava Monich
536143c1d7 [gbinder] Fixed a threading issue. JB#42956
Make sure that transaction handlers don't get invoked on a worker thread.
It was possible in cases if an incoming transaction arrives while we are
waiting for response from an asynchronous call to servicemanager (which
is actually a synchronous call made on a worker thread).

Invoking callbacks on a worker thread would completely break libgbinder's
synchronization model. It was a pretty unlikely scenario, though.
2021-01-20 19:32:40 +02:00
Slava Monich
6025950aab [gbinder] Decode NULL object reference. JB#52557 2021-01-19 02:19:44 +02:00
Slava Monich
4d69940b96 [gbinder] Added new basic HIDL types. JB#52557
GBinderFds - native_handle_t
GBinderHidlHandle - struct hidl_handle
GBinderHidlMemory - struct hidl_memory
2021-01-12 20:00:26 +02:00
Slava Monich
b1f49cae13 [gbinder] Set TF_ACCEPT_FDS transaction flag. JB#52557
Except for one-way transactions where it would meaningless.
2021-01-12 13:58:23 +02:00
Slava Monich
80e9be5343 Version 1.1.3 2020-12-23 21:50:41 +02:00
Slava Monich
d6d6b76fa5 [unit] Added unit_servicemanager_aidl2 test. JB#42956 2020-12-23 21:44:53 +02:00
Slava Monich
d18a352a82 [gbinder] Set function attributes. JB#42956
For better compile time diagnostics.
2020-12-23 21:43:28 +02:00
Slava Monich
5fbaabb47e [unit] Improved gbinder_servicemanager_aidl coverage. JB#42956
Also made ServiceManagerAidl a GBinderLocalObject-derived class to prepare
for possibly making it (deriving classes from GBinderLocalObject) a public
functionality.
2020-12-23 21:38:17 +02:00
Slava Monich
83cc13b817 [gbinder] Fixed some typos. JB#42956 2020-12-23 21:37:46 +02:00
Slava Monich
1679cda4c5 [gbinder] Allow to derive objects from GBinderLocalObject. JB#42956
It's something I've always had in mind. It's still an internal API
but it's quite likely that at some point it will be made public.
For now it's only going to be used by unit tests.
2020-12-23 18:17:51 +02:00
Slava Monich
e5f11aafc1 Version 1.1.2 2020-12-23 12:40:10 +02:00
Slava Monich
f262c77b17 Merge pull request #58 from monich/unit-wait
Fix random unit text failures
2020-12-23 12:29:35 +02:00
Slava Monich
3ef00effc5 [unit] Call gbinder_ipc_exit() when appropriate. Fixes JB#52595
If a test calls test_binder_set_looper_enabled(fd, TRUE) to enable
processing of incoming data by the looper thread, the same thread
may get picked up by the next test and swallow the reply before the
transaction (for which the reply was intended) has been submitted.
Which may cause that next test to either fail or (if the transaction
was synchronous) block forever, stalling the build.

Calling gbinder_ipc_exit() makes sure that looper thread terminates
before the next test starts.
2020-12-22 23:50:37 +02:00
Slava Monich
1ce13bea91 [unit] Speed up test_binder_exit_wait()
Use condition variable to signal closure of the last descriptor.
2020-12-22 23:40:56 +02:00
Slava Monich
eaab366dcb Version 1.1.1 2020-12-22 15:19:03 +02:00
Slava Monich
784f06c415 Merge pull request #57 from monich/sm-test
A few minor tweaks and another unit test
2020-12-22 15:13:09 +02:00
Slava Monich
4b07e80a8f [gbinder] Added couple more looper checks
We must have looper running in order to receive death notifications.
2020-12-19 01:42:42 +02:00
Slava Monich
9f7fac407d [unit] Added unit_servicemanager_aidl. JB#42956
And refactored binder simulator, it can now simulate a lot
more sophisticated scenarious.
2020-12-08 05:03:15 +02:00
Slava Monich
47b6668876 [gbinder] Added internal gbinder_ipc_set_max_threads(). JB#42956
It's needed by unit tests to make sure that asynchronous requests
get executed sequentially. Our binder simulation is too primitive
to handle truely overlapping requests.
2020-12-08 05:02:57 +02:00
Slava Monich
951977961b [gbinder] Pass 0x0f priority to service list request. JB#42956
Based on examination of servicemanager source code, it seems to be
a better idea because it's interpreted as a bitmask.
2020-12-07 18:56:04 +02:00
Slava Monich
f069c3a595 [gbinder] Corner cases for abandoned loopers. JB#42956
If the last GBinderIpc reference was dropped by the looper thread,
the exit flag wasn't set by gbinder_ipc_looper_stop() and such
looper thread kept hanging forever. Also, that flag wasn't checked
by gbinder_driver_read(), and even though it's even less likely
to cause any trouble in real life, let's fix that too.
2020-12-07 05:19:14 +02:00
41 changed files with 3100 additions and 385 deletions

View File

@@ -1,5 +1,5 @@
Copyright (C) 2018-2020 Jolla Ltd.
Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
Copyright (C) 2018-2021 Jolla Ltd.
Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:

View File

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

31
debian/changelog vendored
View File

@@ -1,3 +1,34 @@
libgbinder (1.1.4) unstable; urgency=low
* Fixed a threading issue
* Decode NULL object reference
* Added new basic HIDL types
* Set TF_ACCEPT_FDS transaction flag
* Added servicemanager_hidl unit test
-- Slava Monich <slava.monich@jolla.com> Thu, 21 Jan 2021 03:34:45 +0200
libgbinder (1.1.3) unstable; urgency=low
* Improved unit test coverage
-- Slava Monich <slava.monich@jolla.com> Wed, 23 Dec 2020 21:48:27 +0200
libgbinder (1.1.2) unstable; urgency=low
* Fixed random unit text failures
-- Slava Monich <slava.monich@jolla.com> Wed, 23 Dec 2020 12:39:22 +0200
libgbinder (1.1.1) unstable; urgency=low
* Handle corner cases for abandoned loopers
* Pass 0x0f priority to aidl2 service list request.
* Improved binder simulation for unit tests
* Added servicemanager_aidl unit test
-- Slava Monich <slava.monich@jolla.com> Tue, 22 Dec 2020 15:15:10 +0200
libgbinder (1.1.0) unstable; urgency=low
* Made RPC protocol configurable per binder device

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -93,14 +93,16 @@ gbinder_servicemanager_new_local_object(
GBinderServiceManager* sm,
const char* iface,
GBinderLocalTransactFunc handler,
void* user_data);
void* user_data)
G_GNUC_WARN_UNUSED_RESULT;
GBinderLocalObject*
gbinder_servicemanager_new_local_object2(
GBinderServiceManager* sm,
const char* const* ifaces,
GBinderLocalTransactFunc handler,
void* user_data); /* Since 1.0.29 */
void* user_data) /* Since 1.0.29 */
G_GNUC_WARN_UNUSED_RESULT;
GBinderServiceManager*
gbinder_servicemanager_ref(
@@ -127,7 +129,9 @@ gbinder_servicemanager_list(
char**
gbinder_servicemanager_list_sync(
GBinderServiceManager* sm);
GBinderServiceManager* sm)
G_GNUC_WARN_UNUSED_RESULT
G_GNUC_MALLOC;
gulong
gbinder_servicemanager_get_service(

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -76,16 +76,20 @@ typedef struct gbinder_parent GBinderParent;
/* Basic HIDL types */
#define GBINDER_ALIGNED(x) __attribute__ ((aligned(x)))
typedef struct gbinder_hidl_vec {
union {
guint64 value;
const void* ptr;
} data;
guint32 count;
guint32 owns_buffer;
guint8 owns_buffer;
guint8 pad[3];
} GBinderHidlVec;
#define GBINDER_HIDL_VEC_BUFFER_OFFSET (0)
G_STATIC_ASSERT(sizeof(GBinderHidlVec) == 16);
typedef struct gbinder_hidl_string {
union {
@@ -93,10 +97,51 @@ typedef struct gbinder_hidl_string {
const char* str;
} data;
guint32 len;
guint32 owns_buffer;
guint8 owns_buffer;
guint8 pad[3];
} GBinderHidlString;
#define GBINDER_HIDL_STRING_BUFFER_OFFSET (0)
G_STATIC_ASSERT(sizeof(GBinderHidlString) == 16);
typedef struct gbinder_fds {
guint32 version GBINDER_ALIGNED(4);
guint32 num_fds GBINDER_ALIGNED(4);
guint32 num_ints GBINDER_ALIGNED(4);
} GBINDER_ALIGNED(4) GBinderFds; /* Since 1.1.4 */
/* Actual fds immediately follow GBinderFds: */
#define gbinder_fds_get_fd(fds,i) (((const int*)((fds) + 1))[i])
#define GBINDER_HIDL_FDS_VERSION (12)
G_STATIC_ASSERT(sizeof(GBinderFds) == GBINDER_HIDL_FDS_VERSION);
typedef struct gbinder_hidl_handle {
union {
guint64 value;
const GBinderFds* fds;
} data;
guint8 owns_handle;
guint8 pad[7];
} GBinderHidlHandle; /* Since 1.1.4 */
#define GBINDER_HIDL_HANDLE_VALUE_OFFSET (0)
G_STATIC_ASSERT(sizeof(GBinderHidlHandle) == 16);
typedef struct gbinder_hidl_memory {
union {
guint64 value;
const GBinderFds* fds;
} data;
guint8 owns_buffer;
guint8 pad[7];
guint64 size;
GBinderHidlString name;
} GBinderHidlMemory; /* Since 1.1.4 */
#define GBINDER_HIDL_MEMORY_PTR_OFFSET (0)
#define GBINDER_HIDL_MEMORY_NAME_OFFSET (24)
G_STATIC_ASSERT(sizeof(GBinderHidlMemory) == 40);
/*
* Each RPC call is identified by the interface name returned

View File

@@ -1,5 +1,5 @@
Name: libgbinder
Version: 1.1.0
Version: 1.1.4
Release: 0
Summary: Binder client library
License: BSD

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -178,6 +178,66 @@ gbinder_client_transact_destroy(
g_slice_free(GBinderClientTx, tx);
}
/*==========================================================================*
* Internal interface
*==========================================================================*/
GBinderRemoteReply*
gbinder_client_transact_sync_reply2(
GBinderClient* self,
guint32 code,
GBinderLocalRequest* req,
int* status,
const GBinderIpcSyncApi* api)
{
if (G_LIKELY(self)) {
GBinderRemoteObject* obj = self->remote;
if (G_LIKELY(!obj->dead)) {
if (!req) {
const GBinderClientIfaceRange* r = gbinder_client_find_range
(gbinder_client_cast(self), code);
/* Default empty request (just the header, no parameters) */
if (r) {
req = r->basic_req;
}
}
return api->sync_reply(obj->ipc, obj->handle, code, req, status);
}
GDEBUG("Refusing to perform transaction with a dead object");
}
return NULL;
}
int
gbinder_client_transact_sync_oneway2(
GBinderClient* self,
guint32 code,
GBinderLocalRequest* req,
const GBinderIpcSyncApi* api)
{
if (G_LIKELY(self)) {
GBinderRemoteObject* obj = self->remote;
if (G_LIKELY(!obj->dead)) {
if (!req) {
const GBinderClientIfaceRange* r = gbinder_client_find_range
(gbinder_client_cast(self), code);
/* Default empty request (just the header, no parameters) */
if (r) {
req = r->basic_req;
}
}
return api->sync_oneway(obj->ipc, obj->handle, code, req);
}
GDEBUG("Refusing to perform transaction with a dead object");
return (-ESTALE);
}
return (-EINVAL);
}
/*==========================================================================*
* Interface
*==========================================================================*/
@@ -319,25 +379,8 @@ gbinder_client_transact_sync_reply(
GBinderLocalRequest* req,
int* status)
{
if (G_LIKELY(self)) {
GBinderRemoteObject* obj = self->remote;
if (G_LIKELY(!obj->dead)) {
if (!req) {
const GBinderClientIfaceRange* r = gbinder_client_find_range
(gbinder_client_cast(self), code);
/* Default empty request (just the header, no parameters) */
if (r) {
req = r->basic_req;
}
}
return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
code, req, status);
}
GDEBUG("Refusing to perform transaction with a dead object");
}
return NULL;
return gbinder_client_transact_sync_reply2(self, code, req, status,
&gbinder_ipc_sync_main);
}
int
@@ -346,26 +389,8 @@ gbinder_client_transact_sync_oneway(
guint32 code,
GBinderLocalRequest* req)
{
if (G_LIKELY(self)) {
GBinderRemoteObject* obj = self->remote;
if (G_LIKELY(!obj->dead)) {
if (!req) {
const GBinderClientIfaceRange* r = gbinder_client_find_range
(gbinder_client_cast(self), code);
/* Default empty request (just the header, no parameters) */
if (r) {
req = r->basic_req;
}
}
return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
code, req);
}
GDEBUG("Refusing to perform transaction with a dead object");
return (-ESTALE);
}
return (-EINVAL);
return gbinder_client_transact_sync_oneway2(self, code, req,
&gbinder_ipc_sync_main);
}
gulong

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -41,6 +41,24 @@ struct gbinder_client {
GBinderRemoteObject* remote;
};
GBinderRemoteReply*
gbinder_client_transact_sync_reply2(
GBinderClient* self,
guint32 code,
GBinderLocalRequest* req,
int* status,
const GBinderIpcSyncApi* api)
G_GNUC_WARN_UNUSED_RESULT
GBINDER_INTERNAL;
int
gbinder_client_transact_sync_oneway2(
GBinderClient* self,
guint32 code,
GBinderLocalRequest* req,
const GBinderIpcSyncApi* api)
GBINDER_INTERNAL;
#define gbinder_client_ipc(client) ((client)->remote->ipc)
#endif /* GBINDER_CLIENT_PRIVATE_H */

View File

@@ -713,7 +713,7 @@ gbinder_driver_new(
max_threads, strerror(errno));
}
/* Choose the protocol based on the device name
* if none is explicitely specified */
* if none is explicitly specified */
self->protocol = protocol ? protocol :
gbinder_rpc_protocol_for_device(dev);
return self;
@@ -953,7 +953,7 @@ gbinder_driver_read(
if (ret >= 0) {
/* Loop until we have handled all the incoming commands */
gbinder_driver_handle_commands(self, reg, handler, &rb);
while (rb.buf.consumed) {
while (rb.buf.consumed && gbinder_handler_can_loop(handler)) {
ret = gbinder_driver_write_read(self, NULL, &rb.buf);
if (ret >= 0) {
gbinder_driver_handle_commands(self, reg, handler, &rb);

View File

@@ -36,6 +36,7 @@
#include "gbinder_types_p.h"
typedef struct gbinder_handler_functions {
gboolean (*can_loop)(GBinderHandler* handler);
GBinderLocalReply* (*transact)(GBinderHandler* handler,
GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code,
guint flags, int* status);
@@ -47,6 +48,14 @@ struct gbinder_handler {
/* Inline wrappers */
GBINDER_INLINE_FUNC
gboolean
gbinder_handler_can_loop(
GBinderHandler* self)
{
return self && self->f->can_loop && self->f->can_loop(self);
}
GBINDER_INLINE_FUNC
GBinderLocalReply*
gbinder_handler_transact(

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -118,7 +118,7 @@ GBINDER_IO_FN(encode_pointer)(
return sizeof(*dest);
}
/* Encodes flat_buffer_object */
/* Encodes flat_binder_object */
static
guint
GBINDER_IO_FN(encode_local_object)(
@@ -163,12 +163,12 @@ GBINDER_IO_FN(encode_fd_object)(
void* out,
int fd)
{
struct flat_binder_object* dest = out;
struct binder_fd_object* dest = out;
memset(dest, 0, sizeof(*dest));
dest->hdr.type = BINDER_TYPE_FD;
dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
dest->handle = fd;
dest->pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
dest->fd = fd;
return sizeof(*dest);
}
@@ -226,9 +226,7 @@ GBINDER_IO_FN(fill_transaction_data)(
tr->code = code;
tr->data_size = payload->len;
tr->data.ptr.buffer = (uintptr_t)payload->data;
if (flags & GBINDER_TX_FLAG_ONEWAY) {
tr->flags |= TF_ONE_WAY;
}
tr->flags |= (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS;
if (offsets && offsets->count) {
guint i;
binder_size_t* tx_offsets = g_new(binder_size_t, offsets->count);
@@ -399,6 +397,15 @@ GBINDER_IO_FN(decode_binder_object)(
*out = gbinder_object_registry_get_remote(reg, obj->handle);
}
return sizeof(*obj);
case BINDER_TYPE_BINDER:
if (!obj->binder) {
/* That's a NULL reference */
if (out) {
*out = NULL;
}
return sizeof(*obj);
}
/* fallthrough */
default:
GERR("Unsupported binder object type 0x%08x", obj->hdr.type);
break;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -210,6 +210,23 @@ GBinderIpcLooper*
gbinder_ipc_looper_new(
GBinderIpc* ipc);
static
GBinderRemoteReply*
gbinder_ipc_transact_sync_reply_worker(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status);
static
int
gbinder_ipc_transact_sync_oneway_worker(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req);
/*==========================================================================*
* Utilities
*==========================================================================*/
@@ -606,6 +623,16 @@ gbinder_ipc_looper_count_primary(
return n;
}
static
gboolean
gbinder_ipc_looper_can_loop(
GBinderHandler* handler)
{
GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler);
return !g_atomic_int_get(&looper->exit);
}
static
GBinderLocalReply*
gbinder_ipc_looper_transact(
@@ -824,6 +851,7 @@ gbinder_ipc_looper_new(
/* Note: this call can actually fail */
if (!pipe(fd)) {
static const GBinderHandlerFunctions handler_functions = {
.can_loop = gbinder_ipc_looper_can_loop,
.transact = gbinder_ipc_looper_transact
};
GError* error = NULL;
@@ -896,13 +924,15 @@ gbinder_ipc_looper_stop(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
if (looper->thread) {
GDEBUG("Stopping looper %s", looper->name);
g_atomic_int_set(&looper->exit, TRUE);
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
if (looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
}
}
}
}
@@ -1420,29 +1450,15 @@ void
gbinder_ipc_tx_internal_exec(
GBinderIpcTxPriv* priv)
{
static const GBinderHandlerFunctions handler_fn = {
gbinder_ipc_tx_handler_transact
};
GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv);
GBinderIpcTx* pub = &priv->pub;
GBinderIpc* self = pub->ipc;
GBinderObjectRegistry* reg = &self->priv->object_registry;
GBinderHandler handler = { &handler_fn };
GBinderIpc* ipc = priv->pub.ipc;
/* Perform synchronous transaction */
if (tx->flags & GBINDER_TX_FLAG_ONEWAY) {
tx->status = gbinder_driver_transact(self->driver, reg, &handler,
tx->handle, tx->code, tx->req, NULL);
tx->status = gbinder_ipc_transact_sync_oneway_worker(ipc, tx->handle,
tx->code, tx->req);
} else {
tx->reply = gbinder_remote_reply_new(&self->priv->object_registry);
tx->status = gbinder_driver_transact(self->driver, reg, &handler,
tx->handle, tx->code, tx->req, tx->reply);
if (tx->status != GBINDER_STATUS_OK &&
gbinder_remote_reply_is_empty(tx->reply)) {
/* Drop useless reply */
gbinder_remote_reply_unref(tx->reply);
tx->reply = NULL;
}
tx->reply = gbinder_ipc_transact_sync_reply_worker(ipc, tx->handle,
tx->code, tx->req, &tx->status);
}
}
@@ -1565,6 +1581,128 @@ gbinder_ipc_tx_proc(
gbinder_idle_callback_schedule(tx->completion);
}
/*==========================================================================*
* GBinderIpcSyncApi for worker threads
*==========================================================================*/
static
GBinderRemoteReply*
gbinder_ipc_transact_sync_reply_worker(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status)
{
/* Must be invoked on worker thread */
if (G_LIKELY(self)) {
static const GBinderHandlerFunctions handler_fn = {
.can_loop = NULL,
.transact = gbinder_ipc_tx_handler_transact
};
GBinderHandler handler = { &handler_fn };
GBinderIpcPriv* priv = self->priv;
GBinderObjectRegistry* reg = &priv->object_registry;
GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
int ret = gbinder_driver_transact(self->driver, reg, &handler,
handle, code, req, reply);
if (status) *status = ret;
if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
return reply;
} else {
gbinder_remote_reply_unref(reply);
}
} else {
if (status) *status = (-EINVAL);
}
return NULL;
}
static
int
gbinder_ipc_transact_sync_oneway_worker(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req)
{
/* Must be invoked on worker thread */
if (G_LIKELY(self)) {
static const GBinderHandlerFunctions handler_fn = {
.can_loop = NULL,
.transact = gbinder_ipc_tx_handler_transact
};
GBinderHandler handler = { &handler_fn };
GBinderIpcPriv* priv = self->priv;
return gbinder_driver_transact(self->driver, &priv->object_registry,
&handler, handle, code, req, NULL);
} else {
return (-EINVAL);
}
}
const GBinderIpcSyncApi gbinder_ipc_sync_worker = {
.sync_reply = gbinder_ipc_transact_sync_reply_worker,
.sync_oneway = gbinder_ipc_transact_sync_oneway_worker
};
/*==========================================================================*
* GBinderIpcSyncApi for the main thread
*==========================================================================*/
static
GBinderRemoteReply*
gbinder_ipc_transact_sync_reply(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status)
{
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
GBinderObjectRegistry* reg = &priv->object_registry;
GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
code, req, reply);
if (status) *status = ret;
if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
return reply;
} else {
gbinder_remote_reply_unref(reply);
}
} else {
if (status) *status = (-EINVAL);
}
return NULL;
}
static
int
gbinder_ipc_transact_sync_oneway(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req)
{
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
return gbinder_driver_transact(self->driver, &priv->object_registry,
NULL, handle, code, req, NULL);
} else {
return (-EINVAL);
}
}
const GBinderIpcSyncApi gbinder_ipc_sync_main = {
.sync_reply = gbinder_ipc_transact_sync_reply,
.sync_oneway = gbinder_ipc_transact_sync_oneway
};
/*==========================================================================*
* Interface
*==========================================================================*/
@@ -1638,50 +1776,6 @@ gbinder_ipc_object_registry(
return G_LIKELY(self) ? &self->priv->object_registry : NULL;
}
GBinderRemoteReply*
gbinder_ipc_transact_sync_reply(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status)
{
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
GBinderObjectRegistry* reg = &priv->object_registry;
GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
code, req, reply);
if (status) *status = ret;
if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
return reply;
} else {
gbinder_remote_reply_unref(reply);
}
} else {
if (status) *status = (-EINVAL);
}
return NULL;
}
int
gbinder_ipc_transact_sync_oneway(
GBinderIpc* self,
guint32 handle,
guint32 code,
GBinderLocalRequest* req)
{
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
return gbinder_driver_transact(self->driver, &priv->object_registry,
NULL, handle, code, req, NULL);
} else {
return (-EINVAL);
}
}
gulong
gbinder_ipc_transact(
GBinderIpc* self,
@@ -1749,6 +1843,14 @@ gbinder_ipc_cancel(
}
}
gboolean
gbinder_ipc_set_max_threads(
GBinderIpc* self,
gint max)
{
return g_thread_pool_set_max_threads(self->priv->tx_pool, max, NULL);
}
/*==========================================================================*
* Internals
*==========================================================================*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -67,6 +67,31 @@ void
int status,
void* user_data);
typedef
GBinderRemoteReply*
(*GBinderIpcSyncReplyFunc)(
GBinderIpc* ipc,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status);
typedef
int
(*GBinderIpcSyncOnewayFunc)(
GBinderIpc* ipc,
guint32 handle,
guint32 code,
GBinderLocalRequest* req);
struct gbinder_ipc_sync_api {
GBinderIpcSyncReplyFunc sync_reply;
GBinderIpcSyncOnewayFunc sync_oneway;
};
extern const GBinderIpcSyncApi gbinder_ipc_sync_main GBINDER_INTERNAL;
extern const GBinderIpcSyncApi gbinder_ipc_sync_worker GBINDER_INTERNAL;
GBinderIpc*
gbinder_ipc_new(
const char* dev)
@@ -111,23 +136,6 @@ gbinder_ipc_invalidate_remote_handle(
guint32 handle)
GBINDER_INTERNAL;
GBinderRemoteReply*
gbinder_ipc_transact_sync_reply(
GBinderIpc* ipc,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
int* status)
GBINDER_INTERNAL;
int
gbinder_ipc_transact_sync_oneway(
GBinderIpc* ipc,
guint32 handle,
guint32 code,
GBinderLocalRequest* req)
GBINDER_INTERNAL;
gulong
gbinder_ipc_transact(
GBinderIpc* ipc,
@@ -169,6 +177,13 @@ gbinder_ipc_remote_object_disposed(
GBinderRemoteObject* obj)
GBINDER_INTERNAL;
/* Needed by unit tests */
gboolean
gbinder_ipc_set_max_threads(
GBinderIpc* self,
gint max_threads)
GBINDER_INTERNAL;
/* Declared for unit tests */
void
gbinder_ipc_exit(

View File

@@ -339,38 +339,50 @@ gbinder_local_object_new(
if (G_LIKELY(ipc)) {
GBinderLocalObject* self = g_object_new
(GBINDER_TYPE_LOCAL_OBJECT, NULL);
GBinderLocalObjectPriv* priv = self->priv;
guint i = 0, n = gutil_strv_length((char**)ifaces);
gboolean append_base_interface;
if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
append_base_interface = TRUE;
n++;
} else {
append_base_interface = FALSE;
}
priv->ifaces = g_new(char*, n + 1);
if (ifaces) {
while (*ifaces) {
priv->ifaces[i++] = g_strdup(*ifaces++);
}
}
if (append_base_interface) {
priv->ifaces[i++] = g_strdup(hidl_base_interface);
}
priv->ifaces[i] = NULL;
self->ipc = gbinder_ipc_ref(ipc);
self->ifaces = (const char**)priv->ifaces;
priv->txproc = txproc;
priv->user_data = user_data;
gbinder_local_object_init_base(self, ipc, ifaces, txproc, user_data);
gbinder_ipc_register_local_object(ipc, self);
return self;
}
return NULL;
}
void
gbinder_local_object_init_base(
GBinderLocalObject* self,
GBinderIpc* ipc,
const char* const* ifaces,
GBinderLocalTransactFunc txproc,
void* user_data)
{
GBinderLocalObjectPriv* priv = self->priv;
guint i = 0, n = gutil_strv_length((char**)ifaces);
gboolean append_base_interface;
if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
append_base_interface = TRUE;
n++;
} else {
append_base_interface = FALSE;
}
priv->ifaces = g_new(char*, n + 1);
if (ifaces) {
while (*ifaces) {
priv->ifaces[i++] = g_strdup(*ifaces++);
}
}
if (append_base_interface) {
priv->ifaces[i++] = g_strdup(hidl_base_interface);
}
priv->ifaces[i] = NULL;
self->ipc = gbinder_ipc_ref(ipc);
self->ifaces = (const char**)priv->ifaces;
priv->txproc = txproc;
priv->user_data = user_data;
}
GBinderLocalObject*
gbinder_local_object_ref(
GBinderLocalObject* self)

View File

@@ -83,10 +83,21 @@ GType gbinder_local_object_get_type(void) GBINDER_INTERNAL;
#define GBINDER_TYPE_LOCAL_OBJECT (gbinder_local_object_get_type())
#define GBINDER_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObject))
#define GBINDER_LOCAL_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), \
GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectClass)
#define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
#define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
void
gbinder_local_object_init_base(
GBinderLocalObject* self,
GBinderIpc* ipc,
const char* const* ifaces,
GBinderLocalTransactFunc txproc,
void* user_data)
GBINDER_INTERNAL;
gulong
gbinder_local_object_add_weak_refs_changed_handler(
GBinderLocalObject* obj,

View File

@@ -115,6 +115,7 @@ gbinder_remote_object_reanimate(
if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
/* 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);
}
@@ -151,6 +152,7 @@ gbinder_remote_object_new(
self->ipc = gbinder_ipc_ref(ipc);
self->handle = handle;
if (!(self->dead = dead)) {
gbinder_ipc_looper_check(self->ipc); /* For death notifications */
gbinder_driver_acquire(ipc->driver, handle);
gbinder_driver_request_death_notification(ipc->driver, self);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -199,7 +199,8 @@ gbinder_servicemanager_list_tx_exec(
{
GBinderServiceManagerListTxData* data = tx->user_data;
data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->list(data->sm);
data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
list(data->sm, &gbinder_ipc_sync_worker);
}
static
@@ -243,8 +244,9 @@ gbinder_servicemanager_get_service_tx_exec(
{
GBinderServiceManagerGetServiceTxData* data = tx->user_data;
data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->get_service
(data->sm, data->name, &data->status);
data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
get_service(data->sm, data->name, &data->status,
&gbinder_ipc_sync_worker);
}
static
@@ -286,8 +288,8 @@ gbinder_servicemanager_add_service_tx_exec(
{
GBinderServiceManagerAddServiceTxData* data = tx->user_data;
data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->add_service
(data->sm, data->name, data->obj);
data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
add_service(data->sm, data->name, data->obj, &gbinder_ipc_sync_worker);
}
static
@@ -776,7 +778,8 @@ gbinder_servicemanager_list_sync(
GBinderServiceManager* self)
{
if (G_LIKELY(self)) {
return GBINDER_SERVICEMANAGER_GET_CLASS(self)->list(self);
return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
list(self, &gbinder_ipc_sync_main);
}
return NULL;
}
@@ -815,8 +818,8 @@ gbinder_servicemanager_get_service_sync(
GBinderRemoteObject* obj = NULL;
if (G_LIKELY(self) && name) {
obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->get_service
(self, name, status);
obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->
get_service(self, name, status, &gbinder_ipc_sync_main);
if (obj) {
GBinderServiceManagerPriv* priv = self->priv;
@@ -866,8 +869,8 @@ gbinder_servicemanager_add_service_sync(
GBinderLocalObject* obj)
{
if (G_LIKELY(self) && name && obj) {
return GBINDER_SERVICEMANAGER_GET_CLASS(self)->add_service
(self, name, obj);
return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
add_service(self, name, obj, &gbinder_ipc_sync_main);
} else {
return (-EINVAL);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -35,9 +35,9 @@
#include "gbinder_servicemanager_aidl.h"
#include "gbinder_servicepoll.h"
#include "gbinder_eventloop_p.h"
#include "gbinder_client_p.h"
#include "gbinder_log.h"
#include <gbinder_client.h>
#include <gbinder_local_request.h>
#include <gbinder_remote_reply.h>
@@ -172,7 +172,8 @@ gbinder_servicemanager_aidl_add_service_req(
static
char**
gbinder_servicemanager_aidl_list(
GBinderServiceManager* manager)
GBinderServiceManager* manager,
const GBinderIpcSyncApi* api)
{
GPtrArray* list = g_ptr_array_new();
GBinderClient* client = manager->client;
@@ -181,8 +182,8 @@ gbinder_servicemanager_aidl_list(
GBinderLocalRequest* req = klass->list_services_req(client, 0);
GBinderRemoteReply* reply;
while ((reply = gbinder_client_transact_sync_reply(client,
LIST_SERVICES_TRANSACTION, req, NULL)) != NULL) {
while ((reply = gbinder_client_transact_sync_reply2(client,
LIST_SERVICES_TRANSACTION, req, NULL, api)) != NULL) {
char* service = gbinder_remote_reply_read_string16(reply);
gbinder_remote_reply_unref(reply);
@@ -205,15 +206,16 @@ GBinderRemoteObject*
gbinder_servicemanager_aidl_get_service(
GBinderServiceManager* self,
const char* name,
int* status)
int* status,
const GBinderIpcSyncApi* api)
{
GBinderRemoteObject* obj;
GBinderRemoteReply* reply;
GBinderLocalRequest* req = gbinder_client_new_request(self->client);
gbinder_local_request_append_string16(req, name);
reply = gbinder_client_transact_sync_reply(self->client,
CHECK_SERVICE_TRANSACTION, req, status);
reply = gbinder_client_transact_sync_reply2(self->client,
CHECK_SERVICE_TRANSACTION, req, status, api);
obj = gbinder_remote_reply_read_object(reply);
gbinder_remote_reply_unref(reply);
@@ -226,14 +228,15 @@ int
gbinder_servicemanager_aidl_add_service(
GBinderServiceManager* manager,
const char* name,
GBinderLocalObject* obj)
GBinderLocalObject* obj,
const GBinderIpcSyncApi* api)
{
int status;
GBinderClient* client = manager->client;
GBinderLocalRequest* req = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS
(manager)->add_service_req(client, name, obj);
GBinderRemoteReply* reply = gbinder_client_transact_sync_reply(client,
ADD_SERVICE_TRANSACTION, req, &status);
GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2(client,
ADD_SERVICE_TRANSACTION, req, &status, api);
gbinder_remote_reply_unref(reply);
gbinder_local_request_unref(req);

View File

@@ -45,7 +45,8 @@ G_DEFINE_TYPE(GBinderServiceManagerAidl2,
GBINDER_TYPE_SERVICEMANAGER_AIDL)
#define PARENT_CLASS gbinder_servicemanager_aidl2_parent_class
#define DUMP_FLAG_PRIORITY_DEFAULT (8)
#define DUMP_FLAG_PRIORITY_DEFAULT (0x08)
#define DUMP_FLAG_PRIORITY_ALL (0x0f)
static
GBinderLocalRequest*
@@ -56,7 +57,7 @@ gbinder_servicemanager_aidl2_list_services_req(
GBinderLocalRequest* req = gbinder_client_new_request(client);
gbinder_local_request_append_int32(req, index);
gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT);
gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL);
return req;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -31,9 +31,9 @@
*/
#include "gbinder_servicemanager_p.h"
#include "gbinder_client_p.h"
#include "gbinder_log.h"
#include <gbinder_client.h>
#include <gbinder_local_object.h>
#include <gbinder_local_request.h>
#include <gbinder_remote_reply.h>
@@ -147,11 +147,12 @@ gbinder_servicemanager_hidl_notification(
static
char**
gbinder_servicemanager_hidl_list(
GBinderServiceManager* self)
GBinderServiceManager* self,
const GBinderIpcSyncApi* api)
{
GBinderLocalRequest* req = gbinder_client_new_request(self->client);
GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
(self->client, LIST_TRANSACTION, req, NULL);
GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2
(self->client, LIST_TRANSACTION, req, NULL, api);
gbinder_local_request_unref(req);
if (reply) {
@@ -178,7 +179,8 @@ GBinderRemoteObject*
gbinder_servicemanager_hidl_get_service(
GBinderServiceManager* self,
const char* fqinstance,
int* status)
int* status,
const GBinderIpcSyncApi* api)
{
/* e.g. "android.hardware.radio@1.1::IRadio/slot1" */
const char* sep = strchr(fqinstance, '/');
@@ -193,8 +195,8 @@ gbinder_servicemanager_hidl_get_service(
gbinder_local_request_append_hidl_string(req, fqname);
gbinder_local_request_append_hidl_string(req, name);
reply = gbinder_client_transact_sync_reply(self->client,
GET_TRANSACTION, req, status);
reply = gbinder_client_transact_sync_reply2(self->client,
GET_TRANSACTION, req, status, api);
if (reply) {
GBinderReader reader;
@@ -225,7 +227,8 @@ int
gbinder_servicemanager_hidl_add_service(
GBinderServiceManager* self,
const char* name,
GBinderLocalObject* obj)
GBinderLocalObject* obj,
const GBinderIpcSyncApi* api)
{
int status;
GBinderRemoteReply* reply;
@@ -235,8 +238,8 @@ gbinder_servicemanager_hidl_add_service(
gbinder_local_request_append_hidl_string(req, name);
gbinder_local_request_append_local_object(req, obj);
reply = gbinder_client_transact_sync_reply(self->client,
ADD_TRANSACTION, req, &status);
reply = gbinder_client_transact_sync_reply2(self->client,
ADD_TRANSACTION, req, &status, api);
gbinder_remote_reply_unref(reply);
gbinder_local_request_unref(req);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -66,12 +66,11 @@ typedef struct gbinder_servicemanager_class {
const char* default_device;
/* Methods (synchronous) */
char** (*list)(GBinderServiceManager* self);
GBinderRemoteObject* (*get_service)
(GBinderServiceManager* self, const char* name, int* status);
int (*add_service)
(GBinderServiceManager* self, const char* name,
GBinderLocalObject* obj);
char** (*list)(GBinderServiceManager* self, const GBinderIpcSyncApi* api);
GBinderRemoteObject* (*get_service)(GBinderServiceManager* self,
const char* name, int* status, const GBinderIpcSyncApi* api);
int (*add_service)(GBinderServiceManager* self, const char* name,
GBinderLocalObject* obj, const GBinderIpcSyncApi* api);
/* Checking/normalizing watch names */
GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name)

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -45,6 +45,7 @@ typedef struct gbinder_output_data GBinderOutputData;
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
typedef struct gbinder_servicepoll GBinderServicePoll;
typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi;
#define GBINDER_INLINE_FUNC static inline
#define GBINDER_INTERNAL G_GNUC_INTERNAL

View File

@@ -19,6 +19,9 @@ all:
@$(MAKE) -C unit_remote_reply $*
@$(MAKE) -C unit_remote_request $*
@$(MAKE) -C unit_servicemanager $*
@$(MAKE) -C unit_servicemanager_aidl $*
@$(MAKE) -C unit_servicemanager_aidl2 $*
@$(MAKE) -C unit_servicemanager_hidl $*
@$(MAKE) -C unit_servicename $*
@$(MAKE) -C unit_servicepoll $*
@$(MAKE) -C unit_writer $*

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -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
@@ -30,6 +30,8 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_local_object.h"
#include "gbinder_system.h"
#define GLOG_MODULE_NAME test_binder_log
@@ -47,19 +49,27 @@ static GHashTable* test_fd_map = NULL;
static GHashTable* test_node_map = NULL;
static GPrivate test_looper;
#define public_fd fd[0]
#define private_fd fd[1]
G_LOCK_DEFINE_STATIC(test_binder);
static GCond test_node_map_cond;
#define PUBLIC (0)
#define PRIVATE (1)
#define public_fd node[PUBLIC].fd
#define private_fd node[PRIVATE].fd
#define BINDER_VERSION _IOWR('b', 9, gint32)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, guint32)
#define B_TYPE_LARGE 0x85
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', B_TYPE_LARGE)
#define BINDER_TYPE_HANDLE GBINDER_FOURCC('s', 'h', '*', B_TYPE_LARGE)
#define TF_ONE_WAY 0x01
#define TF_ROOT_OBJECT 0x04
#define TF_STATUS_CODE 0x08
#define TF_ACCEPT_FDS 0x10
typedef struct test_binder_io TestBinderIo;
typedef struct test_binder TestBinder;
typedef
void
@@ -76,24 +86,36 @@ typedef struct test_binder_submit_thread {
TestBinder* binder;
} TestBinderSubmitThread;
typedef struct test_binder_node {
typedef struct test_binder_node TestBinderNode;
struct test_binder_node {
int fd;
char* path;
int refcount;
const TestBinderIo* io;
TestBinder* binder;
TestBinderNode* other;
gboolean looper_enabled;
};
typedef struct test_binder_fd {
int fd;
GHashTable* destroy_map;
} TestBinderNode;
TestBinderNode* node;
} TestBinderFd;
typedef struct test_binder {
TestBinderNode* node;
gint refcount;
const TestBinderIo* io;
GHashTable* object_map; /* GBinderLocalObject* => handle */
GHashTable* handle_map; /* handle => GBinderLocalObject* */
TestBinderSubmitThread* submit_thread;
gboolean looper_enabled;
int fd[2];
GMutex mutex;
gboolean passthrough;
TestBinderNode node[2];
} TestBinder;
struct test_binder_io {
int version;
int write_read_request;
int (*handle_write_read)(TestBinder* binder, void* data);
int (*handle_write_read)(TestBinderFd* fd, void* data);
};
typedef struct binder_write_read_64 {
@@ -118,6 +140,11 @@ typedef struct binder_transaction_data_64 {
guint64 data_offsets;
} BinderTransactionData64;
typedef struct binder_transaction_data_sg_64 {
BinderTransactionData64 tx;
guint64 buffers_size;
} BinderTransactionDataSg64;
typedef struct binder_pre_cookie_64 {
guint64 ptr;
guint64 cookie;
@@ -128,6 +155,13 @@ typedef struct binder_handle_cookie_64 {
guint64 cookie;
} __attribute__((packed)) BinderHandleCookie64;
typedef struct binder_object_64 {
guint32 type;
guint32 flags;
guint64 object;
guint64 cookie;
} BinderObject64;
#define BC_TRANSACTION_64 _IOW('c', 0, BinderTransactionData64)
#define BC_REPLY_64 _IOW('c', 1, BinderTransactionData64)
#define BC_FREE_BUFFER_64 _IOW('c', 3, guint64)
@@ -139,6 +173,8 @@ typedef struct binder_handle_cookie_64 {
#define BC_EXIT_LOOPER _IO('c', 13)
#define BC_REQUEST_DEATH_NOTIFICATION_64 _IOW('c', 14, BinderHandleCookie64)
#define BC_CLEAR_DEATH_NOTIFICATION_64 _IOW('c', 15, BinderHandleCookie64)
#define BC_TRANSACTION_SG_64 _IOW('c', 17, BinderTransactionDataSg64)
#define BC_REPLY_SG_64 _IOW('c', 18, BinderTransactionDataSg64)
#define BR_TRANSACTION_64 _IOR('r', 2, BinderTransactionData64)
#define BR_REPLY_64 _IOR('r', 3, BinderTransactionData64)
@@ -259,74 +295,205 @@ test_binder_submit_later(
static
void
test_io_free_buffer(
TestBinder* binder,
TestBinderFd* fd,
void* ptr)
{
if (ptr) {
TestBinderNode* node = binder->node;
GDestroyNotify destroy = g_hash_table_lookup(node->destroy_map, ptr);
GDestroyNotify destroy;
G_LOCK(test_binder);
destroy = g_hash_table_lookup(fd->destroy_map, ptr);
if (destroy) {
g_hash_table_remove(node->destroy_map, ptr);
g_hash_table_remove(fd->destroy_map, ptr);
destroy(ptr);
} else {
g_free(ptr);
}
G_UNLOCK(test_binder);
}
}
void
test_binder_exit_wait(
void)
{
G_LOCK(test_binder);
while (test_node_map) {
GDEBUG("Waiting for loopers to exit...");
g_cond_wait(&test_node_map_cond, &G_LOCK_NAME(test_binder));
}
G_UNLOCK(test_binder);
}
static
guint64
test_io_passthough_fix_handle(
TestBinder* binder,
guint64 handle)
{
gpointer key = GSIZE_TO_POINTER(handle);
/* Invoked under lock */
if (g_hash_table_contains(binder->object_map, key)) {
gpointer value = g_hash_table_lookup(binder->object_map, key);
handle = GPOINTER_TO_SIZE(value);
GDEBUG("Object %p => handle %u", key, (guint) handle);
} else if (g_hash_table_contains(binder->handle_map, key)) {
gpointer obj = g_hash_table_lookup(binder->handle_map, key);
GDEBUG("Handle %u => object %p", (guint) handle, obj);
handle = GPOINTER_TO_SIZE(obj);
}
return handle;
}
static
gssize
test_io_passthough_write_64(
TestBinderFd* fd,
const void* bytes,
gsize bytes_to_write)
{
const guint code = *(guint32*)bytes;
TestBinderNode* node = fd->node;
TestBinder* binder = node->binder;
BinderTransactionData64* tx = NULL;
gssize bytes_written;
guint extra;
guint32* cmd;
guint32 bytes_to_really_write;
void* data;
/* Just ignore some codes for now */
switch (code) {
case BC_ACQUIRE:
case BC_RELEASE:
case BC_REQUEST_DEATH_NOTIFICATION_64:
case BC_CLEAR_DEATH_NOTIFICATION_64:
return bytes_to_write;
default:
break;
}
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;
break;
case BR_REPLY_64:
*cmd = BC_REPLY_64;
tx = data;
break;
case BC_REPLY_64:
case BC_REPLY_SG_64:
extra = BR_TRANSACTION_COMPLETE;
write(fd->fd, &extra, sizeof(extra));
*cmd = BR_REPLY_64;
tx = data;
break;
}
if (tx) {
guint32* data_buffer = g_memdup(GSIZE_TO_POINTER(tx->data_buffer),
tx->data_size);
void* data_offsets = g_memdup(GSIZE_TO_POINTER(tx->data_offsets),
tx->offsets_size);
G_LOCK(test_binder);
tx->handle = test_io_passthough_fix_handle(binder, tx->handle);
if (data_buffer && tx->data_size > sizeof(BinderObject64)) {
/* Replace BINDER_TYPE_BINDER with BINDER_TYPE_HANDLE */
guint32* data_ptr = data_buffer;
const guint32* data_end = data_buffer + ((tx->data_size -
sizeof(BinderObject64))/sizeof(guint32) + 1);
/*
* Objects are supposed to be aligned at 32-bit boundary, so we
* can scan the data buffer with 4-byte step.
*/
for (data_ptr = data_buffer; data_ptr < data_end; data_ptr++) {
if (*data_ptr == BINDER_TYPE_BINDER) {
BinderObject64* object = (BinderObject64*) data_ptr;
if (object->object) {
object->type = BINDER_TYPE_HANDLE;
object->object = test_io_passthough_fix_handle(binder,
object->object);
}
data_ptr += sizeof(object)/sizeof(guint32) - 1;
}
}
}
g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
G_UNLOCK(test_binder);
tx->data_buffer = GPOINTER_TO_SIZE(data_buffer);
tx->data_offsets = GPOINTER_TO_SIZE(data_offsets);
}
/* Real number of bytes to write may have changed */
bytes_to_really_write = sizeof(guint32) + _IOC_SIZE(*cmd);
bytes_written = write(fd->fd, cmd, bytes_to_really_write);
g_free(cmd);
g_assert(bytes_written == bytes_to_really_write || bytes_written <= 0);
return (bytes_written >= 0) ? bytes_to_write : bytes_written;
}
static
int
test_io_handle_write_read_64(
TestBinder* binder,
TestBinderFd* fd,
void* data)
{
TestBinderNode* node = fd->node;
TestBinder* binder = node->binder;
BinderWriteRead64* wr = data;
gssize bytes_left = wr->write_size - wr->write_consumed;
const guint8* write_ptr = (void*)(gsize)
(wr->write_buffer + wr->write_consumed);
const guint8* write_ptr = (void*)(gsize)(wr->write_buffer +
wr->write_consumed);
gboolean is_looper;
while (bytes_left >= sizeof(guint32)) {
const guint cmd = *(guint32*)write_ptr;
const guint cmdsize = _IOC_SIZE(cmd);
const void* cmddata = write_ptr + sizeof(guint32);
const gsize bytes_to_write = sizeof(guint32) + cmdsize;
GASSERT(bytes_left >= (sizeof(guint32) + cmdsize));
if (bytes_left >= (sizeof(guint32) + cmdsize)) {
wr->write_consumed += sizeof(guint32);
write_ptr += sizeof(guint32);
bytes_left -= sizeof(guint32);
GASSERT(bytes_left >= bytes_to_write);
if (bytes_left >= bytes_to_write) {
gssize bytes_written = bytes_to_write;
switch (cmd) {
case BC_TRANSACTION_64:
case BC_REPLY_64:
/* Is there anything special about transactions and replies? */
break;
case BC_FREE_BUFFER_64:
test_io_free_buffer(binder,
GSIZE_TO_POINTER(*(guint64*)write_ptr));
test_io_free_buffer(fd, GSIZE_TO_POINTER(*(guint64*)cmddata));
break;
case BC_ENTER_LOOPER:
g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
break;
g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
break;
case BC_EXIT_LOOPER:
g_private_set(&test_looper, NULL);
break;
case BC_REQUEST_DEATH_NOTIFICATION_64:
case BC_CLEAR_DEATH_NOTIFICATION_64:
case BC_INCREFS:
case BC_ACQUIRE:
case BC_RELEASE:
case BC_DECREFS:
g_private_set(&test_looper, NULL);
break;
default:
#pragma message("TODO: implement more BINDER_WRITE_READ commands")
GDEBUG("Unhandled command 0x%08x", cmd);
if (binder->passthrough) {
bytes_written = test_io_passthough_write_64(fd,
write_ptr, bytes_to_write);
}
break;
}
wr->write_consumed += cmdsize;
write_ptr += cmdsize;
bytes_left -= cmdsize;
if (bytes_written >= 0) {
wr->write_consumed += bytes_written;
write_ptr += bytes_written;
bytes_left -= bytes_written;
} else {
GDEBUG("Write failed, %s", strerror(errno));
return -1;
}
} else {
/* Partial command in the buffer */
errno = EINVAL;
@@ -335,17 +502,20 @@ test_io_handle_write_read_64(
}
is_looper = g_private_get(&test_looper) ? TRUE : FALSE;
if (binder->looper_enabled || !is_looper) {
/* Now read the data from the socket */
if ((node->looper_enabled || !is_looper) &&
(wr->read_size > wr->read_consumed)) {
/* Do we have anything to read? */
int bytes_available = 0;
int err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
int err = ioctl(fd->fd, FIONREAD, &bytes_available);
if (err >= 0) {
/* Re-check the looper_enabled flag */
if (err >= 0 && (node->looper_enabled || !is_looper)) {
int bytes_read = 0;
if (bytes_available >= 4) {
bytes_read = read(binder->public_fd,
(void*)(gsize)(wr->read_buffer + wr->read_consumed),
/* Read the data from the socket */
bytes_read = read(fd->fd, (void*)(gsize)
(wr->read_buffer + wr->read_consumed),
wr->read_size - wr->read_consumed);
} else {
struct timespec wait;
@@ -387,26 +557,26 @@ test_binder_ioctl_version(
TestBinder* binder,
int* version)
{
*version = binder->node->io->version;
*version = binder->io->version;
return 0;
}
static
void
test_binder_node_unref(
TestBinderNode* node)
TestBinderFd*
test_binder_fd_from_fd(
int fd)
{
node->refcount--;
if (!node->refcount) {
g_hash_table_remove(test_node_map, node->path);
g_hash_table_destroy(node->destroy_map);
g_free(node->path);
g_free(node);
}
if (!g_hash_table_size(test_node_map)) {
g_hash_table_unref(test_node_map);
test_node_map = NULL;
TestBinderFd* binder_fd = NULL;
G_LOCK(test_binder);
GASSERT(test_fd_map);
if (test_fd_map) {
binder_fd = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
GASSERT(binder_fd);
}
G_UNLOCK(test_binder);
return binder_fd;
}
static
@@ -414,13 +584,9 @@ TestBinder*
test_binder_from_fd(
int fd)
{
TestBinder* binder = NULL;
GASSERT(test_fd_map);
if (test_fd_map) {
binder = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
GASSERT(binder);
}
return binder;
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
return binder_fd ? binder_fd->node->binder : NULL;
}
static
@@ -435,11 +601,22 @@ void
test_binder_set_looper_enabled(
int fd,
gboolean enabled)
{
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
g_assert(binder_fd);
binder_fd->node->looper_enabled = enabled;
}
void
test_binder_set_passthrough(
int fd,
gboolean passthrough)
{
TestBinder* binder = test_binder_from_fd(fd);
g_assert(binder);
binder->looper_enabled = enabled;
binder->passthrough = passthrough;
}
void
@@ -448,13 +625,13 @@ test_binder_set_destroy(
gpointer ptr,
GDestroyNotify destroy)
{
TestBinder* binder = test_binder_from_fd(fd);
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
if (binder) {
TestBinderNode* node = binder->node;
g_hash_table_replace(node->destroy_map, ptr,
if (binder_fd) {
G_LOCK(test_binder);
g_hash_table_replace(binder_fd->destroy_map, ptr,
destroy ? destroy : test_io_destroy_none);
G_UNLOCK(test_binder);
}
}
@@ -466,10 +643,10 @@ test_binder_push_data(
{
const guint32* cmd = data;
const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
TestBinder* binder = test_binder_from_fd(fd);
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
TestBinderNode* node = binder_fd->node;
g_assert(binder);
g_assert(write(binder->private_fd, data, len) == len);
g_assert(write(node->other->fd, data, len) == len);
}
static
@@ -704,44 +881,221 @@ test_binder_br_reply_status_later(
test_binder_br_reply_status1(fd, status, test_binder_push_data_later);
}
static
void
test_binder_node_clear(
TestBinderNode* node)
{
GDEBUG("Done with %s", node->path);
g_hash_table_remove(test_node_map, node->path);
if (!g_hash_table_size(test_node_map)) {
g_hash_table_unref(test_node_map);
g_cond_broadcast(&test_node_map_cond);
test_node_map = NULL;
}
close(node->fd);
g_free(node->path);
}
static
TestBinder*
test_binder_ref(
TestBinder* binder)
{
if (binder) {
g_atomic_int_inc(&binder->refcount);
}
return binder;
}
static
void
test_binder_unregister_objects_internal(
TestBinder* binder,
gboolean need_lock)
{
GSList* objects = NULL;
GHashTableIter it;
gpointer value;
if (need_lock) {
G_LOCK(test_binder);
}
g_assert(binder);
g_hash_table_iter_init(&it, binder->handle_map);
while (g_hash_table_iter_next(&it, NULL, &value)) {
objects = g_slist_append(objects, value);
}
g_hash_table_remove_all(binder->object_map);
g_hash_table_remove_all(binder->handle_map);
if (need_lock) {
G_UNLOCK(test_binder);
}
/* Unref GBinderLocalObjects outside the lock */
g_slist_free_full(objects, (GDestroyNotify) gbinder_local_object_unref);
}
static
void
test_binder_unref_internal(
TestBinder* binder,
gboolean need_lock)
{
if (binder && g_atomic_int_dec_and_test(&binder->refcount)) {
if (need_lock) {
G_LOCK(test_binder);
}
test_binder_node_clear(binder->node + 0);
test_binder_node_clear(binder->node + 1);
if (need_lock) {
G_UNLOCK(test_binder);
}
test_binder_submit_thread_free(binder->submit_thread);
test_binder_unregister_objects_internal(binder, need_lock);
g_hash_table_destroy(binder->object_map);
g_hash_table_destroy(binder->handle_map);
g_mutex_clear(&binder->mutex);
g_free(binder);
}
}
guint
test_binder_register_object(
int fd,
GBinderLocalObject* obj,
guint h)
{
TestBinder* binder = test_binder_from_fd(fd);
g_assert(binder);
g_assert(obj);
G_LOCK(test_binder);
g_assert(!g_hash_table_contains(binder->object_map, obj));
g_assert(!g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)));
if (h == AUTO_HANDLE) {
h = 1;
while (g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)) ||
g_hash_table_contains(binder->object_map, GINT_TO_POINTER(h))) {
h++;
}
}
GDEBUG("Object %p <=> handle %u", obj, h);
g_hash_table_insert(binder->handle_map, GINT_TO_POINTER(h), obj);
g_hash_table_insert(binder->object_map, obj, GINT_TO_POINTER(h));
G_UNLOCK(test_binder);
gbinder_local_object_ref(obj);
return h;
}
void
test_binder_unregister_objects(
int fd)
{
test_binder_unregister_objects_internal(test_binder_from_fd(fd), TRUE);
}
static
void
test_fd_map_free(
gpointer entry)
{
TestBinderFd* binder_fd = entry;
GHashTableIter it;
gpointer key, value;
g_hash_table_iter_init(&it, binder_fd->destroy_map);
while (g_hash_table_iter_next(&it, &key, &value)) {
if (value) {
((GDestroyNotify)value)(key);
} else {
g_free(key);
}
}
g_hash_table_destroy(binder_fd->destroy_map);
test_binder_unref_internal(binder_fd->node->binder, FALSE);
close(binder_fd->fd);
g_free(binder_fd);
}
static
TestBinderFd*
test_binder_fd_new(
TestBinderNode* node)
{
TestBinderFd* binder_fd = g_new0(TestBinderFd, 1);
test_binder_ref(node->binder);
binder_fd->node = node;
binder_fd->fd = dup(node->fd);
binder_fd->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
return binder_fd;
}
int
gbinder_system_open(
const char* path,
int flags)
{
if (path && g_str_has_prefix(path, "/dev") &&
g_str_has_suffix(path, "binder")) {
TestBinderNode* node = NULL;
TestBinder* binder = NULL;
int fd;
static const char binder_suffix[] = "binder";
static const char binder_private_suffix[] = "binder-private";
static const char private_suffix[] = "-private";
if (path && g_str_has_prefix(path, "/dev/") &&
(g_str_has_suffix(path, binder_suffix) ||
g_str_has_suffix(path, binder_private_suffix))) {
TestBinderFd* fd;
TestBinderNode* node;
G_LOCK(test_binder);
node = test_node_map ? g_hash_table_lookup(test_node_map, path) : NULL;
if (!node) {
int i, fds[2];
TestBinder* binder = g_new0(TestBinder, 1);
binder->io = &test_io_64;
g_mutex_init(&binder->mutex);
g_assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
if (g_str_has_suffix(path, binder_suffix)) {
node = binder->node + PUBLIC;
node->path = g_strdup(path);
binder->node[PRIVATE].path = g_strconcat(path,
private_suffix, NULL);
} else {
node = binder->node + PRIVATE;
node->path = g_strdup(path);
binder->node[PUBLIC].path = g_strndup(path,
strlen(path) - strlen(private_suffix) - 1);
}
if (test_node_map) {
node = g_hash_table_lookup(test_node_map, path);
}
if (node) {
node->refcount++;
} else {
node = g_new0(TestBinderNode, 1);
node->path = g_strdup(path);
node->refcount = 1;
node->io = &test_io_64;
node->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
if (!test_node_map) {
test_node_map = g_hash_table_new(g_str_hash, g_str_equal);
}
g_hash_table_replace(test_node_map, node->path, node);
for (i = 0; i < 2; i++) {
binder->node[i].binder = binder;
binder->node[i].fd = fds[i];
binder->node[i].other = binder->node + ((i + 1) % 2);
g_hash_table_replace(test_node_map, binder->node[i].path,
binder->node + i);
}
binder->object_map = g_hash_table_new
(g_direct_hash, g_direct_equal);
binder->handle_map = g_hash_table_new
(g_direct_hash, g_direct_equal);
GDEBUG("Created %s <=> %s binder", binder->node[0].path,
binder->node[1].path);
}
binder = g_new0(TestBinder, 1);
binder->node = node;
socketpair(AF_UNIX, SOCK_STREAM, 0, binder->fd);
fd = binder->public_fd;
fd = test_binder_fd_new(node);
if (!test_fd_map) {
test_fd_map = g_hash_table_new(g_direct_hash, g_direct_equal);
test_fd_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, test_fd_map_free);
}
g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd), binder);
return fd;
g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd->fd), fd);
G_UNLOCK(test_binder);
return fd->fd;
} else {
errno = ENOENT;
return -1;
@@ -752,23 +1106,22 @@ int
gbinder_system_close(
int fd)
{
TestBinder* binder = test_binder_from_fd(fd);
int ret;
if (binder) {
g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd));
G_LOCK(test_binder);
if (g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd))) {
if (!g_hash_table_size(test_fd_map)) {
g_hash_table_unref(test_fd_map);
test_fd_map = NULL;
}
test_binder_submit_thread_free(binder->submit_thread);
test_binder_node_unref(binder->node);
close(binder->public_fd);
close(binder->private_fd);
g_free(binder);
return 0;
ret = 0;
} else {
errno = EBADF;
ret = -1;
}
errno = EBADF;
return -1;
G_UNLOCK(test_binder);
return ret;
}
int
@@ -777,9 +1130,11 @@ gbinder_system_ioctl(
int request,
void* data)
{
TestBinder* binder = test_binder_from_fd(fd);
if (binder) {
const TestBinderIo* io = binder->node->io;
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
if (binder_fd) {
TestBinder* binder = binder_fd->node->binder;
const TestBinderIo* io = binder->io;
switch (request) {
case BINDER_VERSION:
@@ -788,7 +1143,7 @@ gbinder_system_ioctl(
return 0;
default:
if (request == io->write_read_request) {
return io->handle_write_read(binder, data);
return io->handle_write_read(binder_fd, data);
} else {
errno = EINVAL;
return -1;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -35,6 +35,8 @@
#include "test_common.h"
typedef struct test_binder TestBinder;
void
test_binder_br_noop(
int fd);
@@ -116,12 +118,33 @@ test_binder_set_looper_enabled(
int fd,
gboolean enabled);
void
test_binder_set_passthrough(
int fd,
gboolean passthrough);
guint
test_binder_register_object(
int fd,
GBinderLocalObject* obj,
guint handle);
#define AUTO_HANDLE ((guint)-1)
void
test_binder_unregister_objects(
int fd);
void
test_binder_set_destroy(
int fd,
gpointer ptr,
GDestroyNotify destroy);
void
test_binder_exit_wait(
void);
#endif /* TEST_BINDER_H */
/*

View File

@@ -0,0 +1,421 @@
/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_servicemanager_hidl.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_local_request.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object_p.h"
#include "gbinder_reader.h"
#include "gbinder_writer.h"
#include "gbinder_cleanup.h"
#include "gbinder_client_p.h"
#include "gbinder_driver.h"
#include "gbinder_ipc.h"
#include <gutil_log.h>
#include <gutil_strv.h>
/*==========================================================================*
* Test service manager
*==========================================================================*/
#define SVCMGR_IFACE "android.hidl.manager@1.0::IServiceManager"
#define NOTIFICATION_IFACE "android.hidl.manager@1.0::IServiceNotification"
const char* const servicemanager_hidl_ifaces[] = { SVCMGR_IFACE, NULL };
enum servicemanager_hidl_tx {
GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
ADD_TRANSACTION,
GET_TRANSPORT_TRANSACTION,
LIST_TRANSACTION,
LIST_BY_INTERFACE_TRANSACTION,
REGISTER_FOR_NOTIFICATIONS_TRANSACTION,
DEBUG_DUMP_TRANSACTION,
REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
};
enum servicemanager_hidl_notify_tx {
ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
};
typedef GBinderLocalObjectClass TestServiceManagerHidlClass;
struct test_servicemanager_hidl {
GBinderLocalObject parent;
GHashTable* objects;
GPtrArray* watchers;
GBinderCleanup* cleanup;
gboolean handle_on_looper_thread;
};
#define THIS_TYPE test_servicemanager_hidl_get_type()
#define PARENT_CLASS test_servicemanager_hidl_parent_class
#define THIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), THIS_TYPE, \
TestServiceManagerHidl))
G_DEFINE_TYPE(TestServiceManagerHidl, test_servicemanager_hidl, \
GBINDER_TYPE_LOCAL_OBJECT)
static
GBinderLocalReply*
test_servicemanager_hidl_get(
TestServiceManagerHidl* self,
GBinderRemoteRequest* req)
{
GBinderRemoteObject* remote_obj;
GBinderLocalReply* reply =
gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
GBinderReader reader;
GBinderWriter writer;
const char* ifname;
const char* instance;
char* fqinstance;
gbinder_remote_request_init_reader(req, &reader);
g_assert((ifname = gbinder_reader_read_hidl_string_c(&reader)));
g_assert((instance = gbinder_reader_read_hidl_string_c(&reader)));
fqinstance = g_strconcat(ifname, "/", instance, NULL);
remote_obj = g_hash_table_lookup(self->objects, fqinstance);
gbinder_local_reply_init_writer(reply, &writer);
gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
if (remote_obj) {
GDEBUG("Found name '%s' => %p", fqinstance, remote_obj);
} else {
GDEBUG("Name '%s' not found", fqinstance);
}
gbinder_local_reply_append_remote_object(reply, remote_obj);
g_free(fqinstance);
return reply;
}
static
GBinderLocalReply*
test_servicemanager_hidl_add(
TestServiceManagerHidl* self,
GBinderRemoteRequest* req)
{
GBinderRemoteObject* remote_obj;
GBinderLocalReply* reply =
gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
GBinderReader reader;
const char* fqname;
gboolean success;
gbinder_remote_request_init_reader(req, &reader);
fqname = gbinder_reader_read_hidl_string_c(&reader);
remote_obj = gbinder_reader_read_object(&reader);
if (fqname && remote_obj) {
const char* sep = strchr(fqname, '/');
GPtrArray* watchers = self->watchers;
guint i;
GDEBUG("Adding '%s'", fqname);
g_hash_table_replace(self->objects, g_strdup(fqname), remote_obj);
/* For unit test purposes, just always notify all watchers */
for (i = 0; i < watchers->len; i++) {
char* iface = g_strndup(fqname, sep - fqname);
char* name = g_strdup(sep + 1);
GBinderClient* client = watchers->pdata[i];
GBinderLocalRequest* notify = gbinder_client_new_request(client);
GBinderWriter writer;
gbinder_local_request_init_writer(notify, &writer);
gbinder_writer_append_hidl_string(&writer, iface);
gbinder_writer_append_hidl_string(&writer, name);
gbinder_writer_append_bool(&writer, FALSE);
gbinder_writer_add_cleanup(&writer, g_free, iface);
gbinder_writer_add_cleanup(&writer, g_free, name);
gbinder_client_transact(client, ON_REGISTRATION_TRANSACTION,
GBINDER_TX_FLAG_ONEWAY, notify, NULL, NULL, NULL);
/* Keep it alive longer than libgbinder needs it */
self->cleanup = gbinder_cleanup_add(self->cleanup, (GDestroyNotify)
gbinder_local_request_unref, notify);
}
success = TRUE;
} else {
gbinder_remote_object_unref(remote_obj);
success = FALSE;
}
gbinder_local_reply_append_bool(reply, success);
return reply;
}
static
GBinderLocalReply*
test_servicemanager_hidl_list(
TestServiceManagerHidl* self,
GBinderRemoteRequest* req)
{
GHashTableIter it;
GBinderReader reader;
GBinderWriter writer;
GBinderLocalReply* reply =
gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
gpointer key;
char** list = NULL;
gbinder_remote_request_init_reader(req, &reader);
g_assert(gbinder_reader_at_end(&reader));
g_hash_table_iter_init(&it, self->objects);
while (g_hash_table_iter_next(&it, &key, NULL)) {
list = gutil_strv_add(list, key);
}
gbinder_local_reply_init_writer(reply, &writer);
gbinder_writer_append_int32(&writer, 0);
gbinder_writer_append_hidl_string_vec(&writer, (const char**) list, -1);
gbinder_writer_add_cleanup(&writer, (GDestroyNotify) g_strfreev, list);
return reply;
}
static
GBinderLocalReply*
test_servicemanager_hidl_register_for_notifications(
TestServiceManagerHidl* self,
GBinderRemoteRequest* req)
{
GBinderRemoteObject* watcher;
GBinderLocalReply* reply =
gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
GBinderReader reader;
const char* fqname;
const char* instance;
gboolean success;
gbinder_remote_request_init_reader(req, &reader);
fqname = gbinder_reader_read_hidl_string_c(&reader);
instance = gbinder_reader_read_hidl_string_c(&reader);
watcher = gbinder_reader_read_object(&reader);
if (watcher) {
GDEBUG("Registering watcher %s/%s", fqname, instance);
g_ptr_array_add(self->watchers, gbinder_client_new(watcher,
NOTIFICATION_IFACE));
gbinder_remote_object_unref(watcher); /* Client keeps the reference */
success = TRUE;
} else {
success = FALSE;
}
gbinder_local_reply_append_int32(reply, 0);
gbinder_local_reply_append_bool(reply, success);
return reply;
}
static
GBinderLocalReply*
test_servicemanager_hidl_handler(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
TestServiceManagerHidl* self = THIS(user_data);
GBinderLocalReply* reply = NULL;
g_assert(!flags);
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
*status = -1;
gbinder_cleanup_reset(self->cleanup);
switch (code) {
case GET_TRANSACTION:
reply = test_servicemanager_hidl_get(self, req);
break;
case ADD_TRANSACTION:
reply = test_servicemanager_hidl_add(self, req);
break;
case LIST_TRANSACTION:
reply = test_servicemanager_hidl_list(self, req);
break;
case REGISTER_FOR_NOTIFICATIONS_TRANSACTION:
reply = test_servicemanager_hidl_register_for_notifications(self, req);
break;
default:
GDEBUG("Unhandled command %u", code);
break;
}
/* If we don't defer deallocation of the reply, its buffers may get
* deallocated too early. */
if (reply) {
self->cleanup = gbinder_cleanup_add(self->cleanup, (GDestroyNotify)
gbinder_local_reply_unref, gbinder_local_reply_ref(reply));
}
return reply;
}
/*==========================================================================*
* Interface
*==========================================================================*/
TestServiceManagerHidl*
test_servicemanager_hidl_new(
GBinderIpc* ipc,
gboolean handle_on_looper_thread)
{
TestServiceManagerHidl* self = g_object_new(THIS_TYPE, NULL);
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
self->handle_on_looper_thread = handle_on_looper_thread;
gbinder_local_object_init_base(obj, ipc, servicemanager_hidl_ifaces,
test_servicemanager_hidl_handler, self);
gbinder_ipc_register_local_object(ipc, obj);
return self;
}
void
test_servicemanager_hidl_free(
TestServiceManagerHidl* self)
{
gbinder_cleanup_reset(self->cleanup);
gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
}
GBinderIpc*
test_servicemanager_hidl_ipc(
TestServiceManagerHidl* self)
{
return self ? self->parent.ipc : 0;
}
guint
test_servicemanager_hidl_object_count(
TestServiceManagerHidl* self)
{
return self ? g_hash_table_size(self->objects) : 0;
}
GBinderRemoteObject*
test_servicemanager_hidl_lookup(
TestServiceManagerHidl* self,
const char* name)
{
return self ? g_hash_table_lookup(self->objects, name) : NULL;
}
/*==========================================================================*
* Internals
*==========================================================================*/
static
GBINDER_LOCAL_TRANSACTION_SUPPORT
test_servicemanager_hidl_can_handle_transaction(
GBinderLocalObject* object,
const char* iface,
guint code)
{
TestServiceManagerHidl* self = THIS(object);
if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
return GBINDER_LOCAL_TRANSACTION_LOOPER;
} else {
return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
can_handle_transaction(object, iface, code);
}
}
static
GBinderLocalReply*
test_servicemanager_hidl_handle_looper_transaction(
GBinderLocalObject* object,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status)
{
if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
handle_transaction(object, req, code, flags, status);
} else {
return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
handle_looper_transaction(object, req, code, flags, status);
}
}
static
void
test_servicemanager_hidl_finalize(
GObject* object)
{
TestServiceManagerHidl* self = THIS(object);
gbinder_cleanup_free(self->cleanup);
g_hash_table_destroy(self->objects);
g_ptr_array_free(self->watchers, TRUE);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
static
void
test_servicemanager_hidl_init(
TestServiceManagerHidl* self)
{
self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify) gbinder_remote_object_unref);
self->watchers = g_ptr_array_new_with_free_func((GDestroyNotify)
gbinder_client_unref);
}
static
void
test_servicemanager_hidl_class_init(
TestServiceManagerHidlClass* klass)
{
GObjectClass* object = G_OBJECT_CLASS(klass);
GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
object->finalize = test_servicemanager_hidl_finalize;
local_object->can_handle_transaction =
test_servicemanager_hidl_can_handle_transaction;
local_object->handle_looper_transaction =
test_servicemanager_hidl_handle_looper_transaction;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TEST_SERVICEMANAGER_HIDL_H
#define TEST_SERVICEMANAGER_HIDL_H
#include <gbinder_types.h>
typedef struct test_servicemanager_hidl TestServiceManagerHidl;
TestServiceManagerHidl*
test_servicemanager_hidl_new(
GBinderIpc* ipc,
gboolean handle_on_looper_thread);
void
test_servicemanager_hidl_free(
TestServiceManagerHidl* sm);
GBinderIpc*
test_servicemanager_hidl_ipc(
TestServiceManagerHidl* self);
guint
test_servicemanager_hidl_object_count(
TestServiceManagerHidl* self);
GBinderRemoteObject*
test_servicemanager_hidl_lookup(
TestServiceManagerHidl* self,
const char* name);
#endif /* TEST_SERVICEMANAGER_HIDL_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -1,3 +1,4 @@
#!/bin/bash
#
# This script requires lcov, dirname
@@ -21,6 +22,9 @@ unit_remote_object \
unit_remote_reply \
unit_remote_request \
unit_servicemanager \
unit_servicemanager_aidl \
unit_servicemanager_aidl2 \
unit_servicemanager_hidl \
unit_servicename \
unit_servicepoll \
unit_writer"

View File

@@ -208,6 +208,7 @@ test_dead(
gbinder_client_unref(client);
g_main_loop_unref(loop);
gbinder_ipc_exit();
}
/*==========================================================================*

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -84,10 +84,16 @@ test_null(
g_assert(!gbinder_ipc_ref(null));
gbinder_ipc_unref(null);
g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, NULL));
g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, &status));
g_assert(status == (-EINVAL));
g_assert(gbinder_ipc_transact_sync_oneway(null, 0, 0, NULL) == (-EINVAL));
g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, NULL));
g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, &status));
g_assert_cmpint(status, == ,-EINVAL);
g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, NULL));
g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, &status));
g_assert_cmpint(status, == ,-EINVAL);
g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(null, 0, 0, NULL), == ,
-EINVAL);
g_assert_cmpint(gbinder_ipc_sync_worker.sync_oneway(null, 0, 0, NULL), == ,
-EINVAL);
g_assert(!gbinder_ipc_transact(null, 0, 0, 0, NULL, NULL, NULL, NULL));
g_assert(!gbinder_ipc_transact_custom(null, NULL, NULL, NULL, NULL));
g_assert(!gbinder_ipc_object_registry(null));
@@ -129,6 +135,7 @@ test_basic(
g_assert(!gbinder_ipc_new("invalid path"));
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -186,11 +193,11 @@ test_sync_oneway(
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
test_binder_br_transaction_complete(fd);
g_assert(gbinder_ipc_transact_sync_oneway(ipc, 0, 1, req) ==
GBINDER_STATUS_OK);
g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0);
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -223,7 +230,7 @@ test_sync_reply_ok_status(
test_binder_br_noop(fd);
test_binder_br_reply(fd, handle, code, data->bytes);
tx_reply = gbinder_ipc_transact_sync_reply(ipc, handle, code, req, status);
tx_reply = gbinder_ipc_sync_main.sync_reply(ipc, handle, code, req, status);
g_assert(tx_reply);
result_out = gbinder_remote_reply_read_string16(tx_reply);
@@ -235,6 +242,7 @@ test_sync_reply_ok_status(
gbinder_local_reply_unref(reply);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
static
@@ -272,12 +280,13 @@ test_sync_reply_error(
test_binder_br_noop(fd);
test_binder_br_reply_status(fd, expected_status);
g_assert(!gbinder_ipc_transact_sync_reply(ipc, handle, code, req, &status));
g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
g_assert(status == expected_status);
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -348,6 +357,7 @@ test_transact_ok(
gbinder_local_reply_unref(reply);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -395,6 +405,7 @@ test_transact_dead(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -442,6 +453,7 @@ test_transact_failed(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -491,6 +503,7 @@ test_transact_status(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -523,6 +536,7 @@ test_transact_custom(
gbinder_ipc_exit();
gbinder_ipc_unref(ipc);
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
@@ -554,6 +568,7 @@ test_transact_custom2(
gbinder_ipc_exit();
gbinder_ipc_unref(ipc);
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
@@ -586,6 +601,7 @@ test_transact_custom3(
/* Reference to GBinderIpc is released by test_transact_custom3_exec */
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -635,6 +651,7 @@ test_transact_cancel(
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -680,6 +697,7 @@ test_transact_cancel2(
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -769,6 +787,7 @@ test_transact_2way(
gbinder_ipc_exit();
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
@@ -840,6 +859,7 @@ test_transact_incoming(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -904,6 +924,7 @@ test_transact_status_reply(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1011,6 +1032,7 @@ test_transact_async(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1084,6 +1106,7 @@ test_transact_async_sync(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1126,6 +1149,7 @@ test_drop_remote_refs(
/* gbinder_ipc_exit will drop the remote reference */
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1163,6 +1187,7 @@ test_cancel_on_exit(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}

View File

@@ -642,6 +642,7 @@ test_increfs(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -685,6 +686,7 @@ test_decrefs(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -726,6 +728,7 @@ test_acquire(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -768,6 +771,7 @@ test_release(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -128,6 +128,7 @@ test_dead(
gbinder_remote_object_remove_handler(obj, 0); /* has no effect */
gbinder_remote_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -157,7 +157,8 @@ typedef struct test_servicemanager {
static
char**
test_servicemanager_list(
GBinderServiceManager* sm)
GBinderServiceManager* sm,
const GBinderIpcSyncApi* api)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
@@ -169,7 +170,8 @@ GBinderRemoteObject*
test_servicemanager_get_service(
GBinderServiceManager* sm,
const char* name,
int* status)
int* status,
const GBinderIpcSyncApi* api)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
@@ -191,7 +193,8 @@ int
test_servicemanager_add_service(
GBinderServiceManager* sm,
const char* name,
GBinderLocalObject* obj)
GBinderLocalObject* obj,
const GBinderIpcSyncApi* api)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
@@ -815,6 +818,7 @@ test_death(
gbinder_servicemanager_remove_all_handlers(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -886,6 +890,7 @@ test_reanimate(
gbinder_servicemanager_remove_all_handlers(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_aidl
include ../common/Makefile

View File

@@ -0,0 +1,581 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_driver.h"
#include "gbinder_ipc.h"
#include "gbinder_reader.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object.h"
#include <gutil_strv.h>
#include <gutil_log.h>
static TestOpt test_opt;
GType
gbinder_servicemanager_hidl_get_type()
{
/* Avoid pulling in gbinder_servicemanager_hidl object */
return 0;
}
GType
gbinder_servicemanager_aidl2_get_type()
{
/* Avoid pulling in gbinder_servicemanager_aidl2 object */
return 0;
}
/*==========================================================================*
* Test service manager
*==========================================================================*/
#define SVCMGR_HANDLE (0)
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
enum servicemanager_aidl_tx {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION
};
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
typedef GBinderLocalObjectClass ServiceManagerAidlClass;
typedef struct service_manager_aidl {
GBinderLocalObject parent;
GHashTable* objects;
gboolean handle_on_looper_thread;
} ServiceManagerAidl;
#define SERVICE_MANAGER_AIDL_TYPE (service_manager_aidl_get_type())
#define SERVICE_MANAGER_AIDL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
SERVICE_MANAGER_AIDL_TYPE, ServiceManagerAidl))
G_DEFINE_TYPE(ServiceManagerAidl, service_manager_aidl, \
GBINDER_TYPE_LOCAL_OBJECT)
static
GBinderLocalReply*
servicemanager_aidl_handler(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
ServiceManagerAidl* self = user_data;
GBinderLocalReply* reply = NULL;
GBinderReader reader;
GBinderRemoteObject* remote_obj;
guint32 num;
char* str;
g_assert(!flags);
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
*status = -1;
switch (code) {
case GET_SERVICE_TRANSACTION:
case CHECK_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
if (str) {
reply = gbinder_local_object_new_reply(obj);
remote_obj = g_hash_table_lookup(self->objects, str);
if (remote_obj) {
GDEBUG("Found name '%s' => %p", str, remote_obj);
gbinder_local_reply_append_remote_object(reply, remote_obj);
} else {
GDEBUG("Name '%s' not found", str);
gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
}
g_free(str);
}
break;
case ADD_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
remote_obj = gbinder_reader_read_object(&reader);
if (str && remote_obj && gbinder_reader_read_uint32(&reader, &num)) {
GDEBUG("Adding '%s'", str);
g_hash_table_replace(self->objects, str, remote_obj);
remote_obj = NULL;
str = NULL;
reply = gbinder_local_object_new_reply(obj);
*status = GBINDER_STATUS_OK;
}
g_free(str);
gbinder_remote_object_unref(remote_obj);
break;
case LIST_SERVICES_TRANSACTION:
if (gbinder_remote_request_read_uint32(req, &num)) {
if (num < g_hash_table_size(self->objects)) {
GList* keys = g_hash_table_get_keys(self->objects);
GList* l = g_list_nth(keys, num);
reply = gbinder_local_object_new_reply(obj);
gbinder_local_reply_append_string16(reply, l->data);
g_list_free(keys);
*status = GBINDER_STATUS_OK;
} else {
GDEBUG("Index %u out of bounds", num);
}
}
break;
default:
GDEBUG("Unhandled command %u", code);
break;
}
return reply;
}
static
ServiceManagerAidl*
servicemanager_aidl_new(
const char* dev,
gboolean handle_on_looper_thread)
{
ServiceManagerAidl* self = g_object_new(SERVICE_MANAGER_AIDL_TYPE, NULL);
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
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_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
return self;
}
static
void
servicemanager_aidl_free(
ServiceManagerAidl* self)
{
gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
}
static
GBINDER_LOCAL_TRANSACTION_SUPPORT
service_manager_aidl_can_handle_transaction(
GBinderLocalObject* object,
const char* iface,
guint code)
{
ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
return GBINDER_LOCAL_TRANSACTION_LOOPER;
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
can_handle_transaction(object, iface, code);
}
}
static
GBinderLocalReply*
service_manager_aidl_handle_looper_transaction(
GBinderLocalObject* object,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status)
{
if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
handle_transaction(object, req, code, flags, status);
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
handle_looper_transaction(object, req, code, flags, status);
}
}
static
void
service_manager_aidl_finalize(
GObject* object)
{
ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
g_hash_table_destroy(self->objects);
G_OBJECT_CLASS(service_manager_aidl_parent_class)->finalize(object);
}
static
void
service_manager_aidl_init(
ServiceManagerAidl* self)
{
self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify) gbinder_remote_object_unref);
}
static
void
service_manager_aidl_class_init(
ServiceManagerAidlClass* klass)
{
GObjectClass* object = G_OBJECT_CLASS(klass);
GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
object->finalize = service_manager_aidl_finalize;
local_object->can_handle_transaction =
service_manager_aidl_can_handle_transaction;
local_object->handle_looper_transaction =
service_manager_aidl_handle_looper_transaction;
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_add_cb(
GBinderServiceManager* sm,
int status,
void* user_data)
{
g_assert(status == GBINDER_STATUS_OK);
if (user_data) {
g_main_loop_quit(user_data);
}
}
static
void
test_get_none_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(!obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
/* Query the object (it's not there yet) and wait for completion */
GDEBUG("Querying '%s'", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
loop));
test_run(&test_opt, loop);
/* Register object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, loop));
test_run(&test_opt, loop);
g_assert_cmpuint(g_hash_table_size(smsvc->objects), == ,1);
g_assert(g_hash_table_contains(smsvc->objects, name));
/* Query the object (this time it must be there) and wait for completion */
GDEBUG("Querying '%s' again", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
test_run(&test_opt, loop);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* list
*==========================================================================*/
typedef struct test_list {
char** list;
GMainLoop* loop;
} TestList;
static
gboolean
test_list_cb(
GBinderServiceManager* sm,
char** services,
void* user_data)
{
TestList* test = user_data;
GDEBUG("Got %u name(s)", gutil_strv_length(services));
g_strfreev(test->list);
test->list = services;
g_main_loop_quit(test->loop);
return TRUE;
}
static
void
test_list()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
TestList test;
memset(&test, 0, sizeof(test));
test.loop = g_main_loop_new(NULL, FALSE);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
/* Request the list and wait for completion */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* There's nothing there yet */
g_assert(test.list);
g_assert(!test.list[0]);
/* Register object and wait for completion */
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, test.loop));
test_run(&test_opt, test.loop);
/* Request the list again */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* Now the name must be there */
g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
g_assert_cmpstr(test.list[0], == ,name);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_strfreev(test.list);
g_main_loop_unref(test.loop);
}
/*==========================================================================*
* notify
*==========================================================================*/
static
void
test_notify_cb(
GBinderServiceManager* sm,
const char* name,
void* user_data)
{
g_assert(name);
GDEBUG("'%s' is registered", name);
g_main_loop_quit(user_data);
}
static
void
test_notify()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* svc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id;
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
gbinder_ipc_set_max_threads(ipc, 1);
/* Start watching */
id = gbinder_servicemanager_add_registration_handler(sm, name,
test_notify_cb, loop);
g_assert(id);
/* Register the object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, NULL));
/* test_notify_cb will stop the loop */
test_run(&test_opt, loop);
gbinder_servicemanager_remove_handler(sm, id);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(svc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* notify2
*==========================================================================*/
static
void
test_notify2()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, TRUE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderServiceManager* sm;
const char* name1 = "name1";
const char* name2 = "name2";
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id1, id2;
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
gbinder_ipc_set_max_threads(ipc, 1);
/* Register the object synchronously (twice)*/
GDEBUG("Registering object '%s' => %p", name1, obj);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name1,obj),==,0);
g_assert(gbinder_servicemanager_get_service_sync(sm, name1, NULL));
GDEBUG("Registering object '%s' => %p", name2, obj);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name2,obj),==,0);
g_assert(gbinder_servicemanager_get_service_sync(sm, name2, NULL));
/* Watch for the first name to create internal name watcher */
id1 = gbinder_servicemanager_add_registration_handler(sm, name1,
test_notify_cb, loop);
g_assert(id1);
test_run(&test_opt, loop);
/* Now watch for the second name */
id2 = gbinder_servicemanager_add_registration_handler(sm, name2,
test_notify_cb, loop);
g_assert(id2);
test_run(&test_opt, loop);
gbinder_servicemanager_remove_handler(sm, id1);
gbinder_servicemanager_remove_handler(sm, id2);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager_aidl/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("list"), test_list);
g_test_add_func(TEST_("notify"), test_notify);
g_test_add_func(TEST_("notify2"), test_notify2);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_aidl2
include ../common/Makefile

View File

@@ -0,0 +1,437 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_driver.h"
#include "gbinder_config.h"
#include "gbinder_ipc.h"
#include "gbinder_reader.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object.h"
#include <gutil_strv.h>
#include <gutil_log.h>
static TestOpt test_opt;
static const char TMP_DIR_TEMPLATE[] =
"gbinder-test-servicemanager_aidl2-XXXXXX";
GType
gbinder_servicemanager_hidl_get_type()
{
/* Avoid pulling in gbinder_servicemanager_hidl object */
return 0;
}
/*==========================================================================*
* Test service manager
*==========================================================================*/
#define SVCMGR_HANDLE (0)
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
enum servicemanager_aidl_tx {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION
};
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
typedef GBinderLocalObjectClass ServiceManagerAidl2Class;
typedef struct service_manager_aidl2 {
GBinderLocalObject parent;
GHashTable* objects;
gboolean handle_on_looper_thread;
} ServiceManagerAidl2;
#define SERVICE_MANAGER_AIDL2_TYPE (service_manager_aidl2_get_type())
#define SERVICE_MANAGER_AIDL2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
SERVICE_MANAGER_AIDL2_TYPE, ServiceManagerAidl2))
G_DEFINE_TYPE(ServiceManagerAidl2, service_manager_aidl2, \
GBINDER_TYPE_LOCAL_OBJECT)
static
GBinderLocalReply*
servicemanager_aidl2_handler(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
ServiceManagerAidl2* self = user_data;
GBinderLocalReply* reply = NULL;
GBinderReader reader;
GBinderRemoteObject* remote_obj;
guint32 num, allow_isolated, dumpsys_priority;
char* str;
g_assert(!flags);
GDEBUG("%s %u", gbinder_remote_request_interface(req), code);
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
*status = -1;
switch (code) {
case GET_SERVICE_TRANSACTION:
case CHECK_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
if (str) {
reply = gbinder_local_object_new_reply(obj);
remote_obj = g_hash_table_lookup(self->objects, str);
if (remote_obj) {
GDEBUG("Found name '%s' => %p", str, remote_obj);
gbinder_local_reply_append_remote_object(reply, remote_obj);
} else {
GDEBUG("Name '%s' not found", str);
gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
}
g_free(str);
}
break;
case ADD_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
remote_obj = gbinder_reader_read_object(&reader);
if (str && remote_obj &&
gbinder_reader_read_uint32(&reader, &allow_isolated) &&
gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
GDEBUG("Adding '%s'", str);
g_hash_table_replace(self->objects, str, remote_obj);
remote_obj = NULL;
str = NULL;
reply = gbinder_local_object_new_reply(obj);
*status = GBINDER_STATUS_OK;
}
g_free(str);
gbinder_remote_object_unref(remote_obj);
break;
case LIST_SERVICES_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
if (gbinder_reader_read_uint32(&reader, &num) &&
gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
if (num < g_hash_table_size(self->objects)) {
GList* keys = g_hash_table_get_keys(self->objects);
GList* l = g_list_nth(keys, num);
/* Ignore dumpsys_priority */
reply = gbinder_local_object_new_reply(obj);
gbinder_local_reply_append_string16(reply, l->data);
g_list_free(keys);
*status = GBINDER_STATUS_OK;
} else {
GDEBUG("Index %u out of bounds", num);
}
}
break;
default:
GDEBUG("Unhandled command %u", code);
break;
}
return reply;
}
static
ServiceManagerAidl2*
servicemanager_aidl2_new(
const char* dev,
gboolean handle_on_looper_thread)
{
ServiceManagerAidl2* self = g_object_new(SERVICE_MANAGER_AIDL2_TYPE, NULL);
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
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_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
return self;
}
static
GBINDER_LOCAL_TRANSACTION_SUPPORT
service_manager_aidl2_can_handle_transaction(
GBinderLocalObject* object,
const char* iface,
guint code)
{
ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
return GBINDER_LOCAL_TRANSACTION_LOOPER;
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
can_handle_transaction(object, iface, code);
}
}
static
GBinderLocalReply*
service_manager_aidl2_handle_looper_transaction(
GBinderLocalObject* object,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status)
{
if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
handle_transaction(object, req, code, flags, status);
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
handle_looper_transaction(object, req, code, flags, status);
}
}
static
void
service_manager_aidl2_finalize(
GObject* object)
{
ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
g_hash_table_destroy(self->objects);
G_OBJECT_CLASS(service_manager_aidl2_parent_class)->finalize(object);
}
static
void
service_manager_aidl2_init(
ServiceManagerAidl2* self)
{
self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify) gbinder_remote_object_unref);
}
static
void
service_manager_aidl2_class_init(
ServiceManagerAidl2Class* klass)
{
GObjectClass* object = G_OBJECT_CLASS(klass);
GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
object->finalize = service_manager_aidl2_finalize;
local_object->can_handle_transaction =
service_manager_aidl2_can_handle_transaction;
local_object->handle_looper_transaction =
service_manager_aidl2_handle_looper_transaction;
}
/*==========================================================================*
* Test context
*==========================================================================*/
typedef struct test_context {
const char* default_config_dir;
const char* default_config_file;
char* config_dir;
char* config_subdir;
char* config_file;
GBinderLocalObject* object;
ServiceManagerAidl2* service;
GBinderServiceManager* client;
int fd;
} TestContext;
static
void
test_context_init(
TestContext* test)
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
/*
* Also set defaults so that both /dev/binder and /dev/binder-private
* use the same protocol.
*/
const char* config =
"[Protocol]\n"
"Default = aidl2\n"
"/dev/binder = aidl2\n"
"[ServiceManager]\n"
"Default = aidl2\n"
"/dev/binder = aidl2\n";
GBinderIpc* ipc;
memset(test, 0, sizeof(*test));
test->default_config_dir = gbinder_config_dir;
test->default_config_file = gbinder_config_file;
test->config_dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
test->config_subdir = g_build_filename(test->config_dir, "d", NULL);
test->config_file = g_build_filename(test->config_dir, "test.conf", NULL);
g_assert(g_file_set_contents(test->config_file, config, -1, NULL));
GDEBUG("Config file %s", test->config_file);
gbinder_config_dir = test->config_subdir; /* Doesn't exist */
gbinder_config_file = test->config_file;
ipc = gbinder_ipc_new(dev);
test->fd = gbinder_driver_fd(ipc->driver);
test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL);
/* Set up binder simulator */
test_binder_register_object(test->fd, test->object, AUTO_HANDLE);
test_binder_set_passthrough(test->fd, TRUE);
test->service = servicemanager_aidl2_new(other_dev, TRUE);
test->client = gbinder_servicemanager_new(dev);
gbinder_ipc_unref(ipc);
}
static
void
test_context_deinit(
TestContext* test)
{
test_binder_unregister_objects(test->fd);
gbinder_local_object_unref(test->object);
gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(test->service));
gbinder_servicemanager_unref(test->client);
gbinder_ipc_exit();
test_binder_exit_wait();
remove(test->config_file);
remove(test->config_dir);
g_free(test->config_file);
g_free(test->config_subdir);
g_free(test->config_dir);
gbinder_config_dir = test->default_config_dir;
gbinder_config_file = test->default_config_file;
gbinder_config_exit();
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_get()
{
TestContext test;
const char* name = "name";
int status = -1;
test_context_init(&test);
/* Query the object (it's not there yet) */
GDEBUG("Querying '%s'", name);
g_assert(!gbinder_servicemanager_get_service_sync(test.client,
name, &status));
g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
/* Register object */
GDEBUG("Registering object '%s' => %p", name, test.object);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
name, test.object), == ,GBINDER_STATUS_OK);
g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1);
g_assert(g_hash_table_contains(test.service->objects, name));
/* Query the object (this time it must be there) */
GDEBUG("Querying '%s' again", name);
g_assert(gbinder_servicemanager_get_service_sync(test.client, name,
&status));
g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
test_context_deinit(&test);
}
/*==========================================================================*
* list
*==========================================================================*/
static
void
test_list()
{
TestContext test;
const char* name = "name";
char** list;
test_context_init(&test);
/* Request the list */
list = gbinder_servicemanager_list_sync(test.client);
/* There's nothing there yet */
g_assert(list);
g_assert(!list[0]);
g_strfreev(list);
/* Register object */
GDEBUG("Registering object '%s' => %p", name, test.object);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
name, test.object), == ,GBINDER_STATUS_OK);
/* Request the list again */
list = gbinder_servicemanager_list_sync(test.client);
/* Now the name must be there */
g_assert_cmpuint(gutil_strv_length(list), == ,1);
g_assert_cmpstr(list[0], == ,name);
g_strfreev(list);
test_context_deinit(&test);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager_aidl2/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("list"), test_list);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,6 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_hidl
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
include ../common/Makefile

View File

@@ -0,0 +1,470 @@
/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "test_servicemanager_hidl.h"
#include "gbinder_ipc.h"
#include "gbinder_cleanup.h"
#include "gbinder_config.h"
#include "gbinder_client_p.h"
#include "gbinder_driver.h"
#include "gbinder_reader.h"
#include "gbinder_writer.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_local_request.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object_p.h"
#include <gutil_log.h>
#include <gutil_strv.h>
static TestOpt test_opt;
#define MAIN_DEV GBINDER_DEFAULT_HWBINDER
#define OTHER_DEV GBINDER_DEFAULT_HWBINDER "-private"
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-svcmgr-hidl-XXXXXX";
static const char DEFAULT_CONFIG_DATA[] =
"[Protocol]\n"
MAIN_DEV " = hidl\n"
OTHER_DEV " = hidl\n"
"[ServiceManager]\n"
MAIN_DEV " = hidl\n";
typedef struct test_config {
char* dir;
char* file;
} TestConfig;
GType
gbinder_servicemanager_aidl_get_type()
{
/* Avoid pulling in gbinder_servicemanager_aidl object */
return 0;
}
GType
gbinder_servicemanager_aidl2_get_type()
{
/* Avoid pulling in gbinder_servicemanager_aidl2 object */
return 0;
}
/*==========================================================================*
* Common
*==========================================================================*/
static
void
test_config_init(
TestConfig* config,
char* config_data)
{
config->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
config->file = g_build_filename(config->dir, "test.conf", NULL);
g_assert(g_file_set_contents(config->file, config_data ? config_data :
DEFAULT_CONFIG_DATA, -1, NULL));
gbinder_config_exit();
gbinder_config_dir = config->dir;
gbinder_config_file = config->file;
GDEBUG("Wrote config to %s", config->file);
}
static
void
test_config_deinit(
TestConfig* config)
{
gbinder_config_exit();
remove(config->file);
g_free(config->file);
remove(config->dir);
g_free(config->dir);
}
static
TestServiceManagerHidl*
test_servicemanager_impl_new(
const char* dev,
gboolean handle_on_looper_thread)
{
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
TestServiceManagerHidl* sm =
test_servicemanager_hidl_new(ipc, handle_on_looper_thread);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm),
GBINDER_SERVICEMANAGER_HANDLE);
gbinder_ipc_unref(ipc);
return sm;
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_add_cb(
GBinderServiceManager* sm,
int status,
void* user_data)
{
GDEBUG("Name added");
g_assert(status == GBINDER_STATUS_OK);
if (user_data) {
g_main_loop_quit(user_data);
}
}
static
void
test_get_none_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(!obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get()
{
TestConfig config;
GBinderIpc* ipc;
TestServiceManagerHidl* smsvc;
GBinderLocalObject* obj;
int fd;
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
const char* name = "android.hidl.base@1.0::IBase/test";
test_config_init(&config, NULL);
ipc = gbinder_ipc_new(MAIN_DEV);
smsvc = test_servicemanager_impl_new(OTHER_DEV, FALSE);
obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
fd = gbinder_driver_fd(ipc->driver);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(MAIN_DEV);
/* This one fails because of unexpected name format */
g_assert(!gbinder_servicemanager_get_service_sync(sm, "test", NULL));
/* Query the object (it's not there yet) and wait for completion */
GDEBUG("Querying '%s'", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
loop));
test_run(&test_opt, loop);
/* Register object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, loop));
test_run(&test_opt, loop);
g_assert_cmpuint(test_servicemanager_hidl_object_count(smsvc), == ,1);
g_assert(test_servicemanager_hidl_lookup(smsvc, name));
/* Query the object (this time it must be there) and wait for completion */
GDEBUG("Querying '%s' again", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
test_run(&test_opt, loop);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
test_config_deinit(&config);
g_main_loop_unref(loop);
}
/*==========================================================================*
* list
*==========================================================================*/
typedef struct test_list {
char** list;
GMainLoop* loop;
} TestList;
static
gboolean
test_list_cb(
GBinderServiceManager* sm,
char** services,
void* user_data)
{
TestList* test = user_data;
GDEBUG("Got %u name(s)", gutil_strv_length(services));
g_strfreev(test->list);
test->list = services;
g_main_loop_quit(test->loop);
return TRUE;
}
static
void
test_list()
{
TestList test;
TestConfig config;
GBinderIpc* ipc;
TestServiceManagerHidl* smsvc;
GBinderLocalObject* obj;
int fd;
GBinderServiceManager* sm;
const char* name = "android.hidl.base@1.0::IBase/test";
memset(&test, 0, sizeof(test));
test.loop = g_main_loop_new(NULL, FALSE);
test_config_init(&config, NULL);
ipc = gbinder_ipc_new(MAIN_DEV);
smsvc = test_servicemanager_impl_new(OTHER_DEV, FALSE);
obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
fd = gbinder_driver_fd(ipc->driver);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(MAIN_DEV);
/* Request the list and wait for completion */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* There's nothing there yet */
g_assert(test.list);
g_assert(!test.list[0]);
/* Register object and wait for completion */
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, test.loop));
test_run(&test_opt, test.loop);
/* Request the list again */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* Now the name must be there */
g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
g_assert_cmpstr(test.list[0], == ,name);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
test_config_deinit(&config);
g_strfreev(test.list);
g_main_loop_unref(test.loop);
}
/*==========================================================================*
* notify
*==========================================================================*/
typedef struct test_notify {
GMainLoop* loop;
TestServiceManagerHidl* smsvc;
int notify_count;
gboolean name_added;
} TestNotify;
static
void
test_notify_never(
GBinderServiceManager* sm,
const char* name,
void* user_data)
{
g_assert_not_reached();
}
static
void
test_notify_add_cb(
GBinderServiceManager* sm,
int status,
void* user_data)
{
TestNotify* test = user_data;
GDEBUG("Name added");
g_assert(status == GBINDER_STATUS_OK);
g_assert(!test->name_added);
test->name_added = TRUE;
if (test->notify_count) {
g_main_loop_quit(test->loop);
}
}
static
void
test_notify_cb(
GBinderServiceManager* sm,
const char* name,
void* user_data)
{
TestNotify* test = user_data;
GBinderIpc* ipc = test_servicemanager_hidl_ipc(test->smsvc);
int fd = gbinder_driver_fd(ipc->driver);
g_assert(name);
GDEBUG("'%s' is registered", name);
g_assert_cmpint(test->notify_count, == ,0);
test->notify_count++;
/* We want BR_TRANSACTION_COMPLETE to be handled by the transaction
* thread, disable the looper before pushing the data. */
test_binder_set_looper_enabled(fd, FALSE);
test_binder_br_transaction_complete(fd);
/* Exit the loop after both things happen */
if (test->name_added) {
g_main_loop_quit(test->loop);
}
}
static
void
test_notify()
{
TestNotify test;
TestConfig config;
GBinderIpc* ipc;
GBinderLocalObject* obj;
int fd;
GBinderServiceManager* sm;
const char* name = "android.hidl.base@1.0::IBase/test";
gulong id;
memset(&test, 0, sizeof(test));
test.loop = g_main_loop_new(NULL, FALSE);
test_config_init(&config, NULL);
ipc = gbinder_ipc_new(MAIN_DEV);
test.smsvc = test_servicemanager_impl_new(OTHER_DEV, TRUE);
obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
fd = gbinder_driver_fd(ipc->driver);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(MAIN_DEV);
/* This one fails because of invalid names */
g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL,
test_notify_never, NULL));
g_assert(!gbinder_servicemanager_add_registration_handler(sm, "",
test_notify_never, NULL));
g_assert(!gbinder_servicemanager_add_registration_handler(sm, ",",
test_notify_never, NULL));
/* Start watching */
id = gbinder_servicemanager_add_registration_handler(sm, name,
test_notify_cb, &test);
g_assert(id);
/* Register the object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_notify_add_cb, &test));
/* The loop quits after the name is added and notification is received */
test_run(&test_opt, test.loop);
gbinder_servicemanager_remove_handler(sm, id);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
test_servicemanager_hidl_free(test.smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
test_config_deinit(&config);
g_main_loop_unref(test.loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager_hidl/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("list"), test_list);
g_test_add_func(TEST_("notify"), test_notify);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2019-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2019-2021 Jolla Ltd.
* Copyright (C) 2019-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -101,7 +101,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
static
char**
test_servicemanager_list(
GBinderServiceManager* manager)
GBinderServiceManager* manager,
const GBinderIpcSyncApi* api)
{
char** ret;
TestServiceManager* self = TEST_SERVICEMANAGER(manager);
@@ -118,7 +119,8 @@ GBinderRemoteObject*
test_servicemanager_get_service(
GBinderServiceManager* manager,
const char* name,
int* status)
int* status,
const GBinderIpcSyncApi* api)
{
*status = (-ENOENT);
return NULL;
@@ -129,7 +131,8 @@ int
test_servicemanager_add_service(
GBinderServiceManager* manager,
const char* name,
GBinderLocalObject* obj)
GBinderLocalObject* obj,
const GBinderIpcSyncApi* api)
{
TestServiceManager* self = TEST_SERVICEMANAGER(manager);
@@ -345,6 +348,7 @@ test_present(
gbinder_ipc_unref(ipc);
test_run(&test_opt, loop);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -474,6 +478,7 @@ test_cancel(
gbinder_ipc_unref(ipc);
test_run(&test_opt, loop);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2021 Jolla Ltd.
* Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -79,7 +79,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
static
char**
test_servicemanager_list(
GBinderServiceManager* manager)
GBinderServiceManager* manager,
const GBinderIpcSyncApi* api)
{
char** ret;
TestServiceManager* self = TEST_SERVICEMANAGER(manager);
@@ -96,7 +97,8 @@ GBinderRemoteObject*
test_servicemanager_get_service(
GBinderServiceManager* manager,
const char* name,
int* status)
int* status,
const GBinderIpcSyncApi* api)
{
*status = (-ENOENT);
return NULL;
@@ -107,7 +109,8 @@ int
test_servicemanager_add_service(
GBinderServiceManager* manager,
const char* name,
GBinderLocalObject* obj)
GBinderLocalObject* obj,
const GBinderIpcSyncApi* api)
{
TestServiceManager* self = TEST_SERVICEMANAGER(manager);