Skip to content
Snippets Groups Projects
Commit 1ae7c269 authored by Slava Monich's avatar Slava Monich
Browse files

[unit] Added servicemanager_hidl test. JB#42956

parent 536143c1
No related branches found
No related tags found
No related merge requests found
......@@ -21,6 +21,7 @@ all:
@$(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 $*
......
/*
* 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:
*
......@@ -86,12 +86,14 @@ 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;
TestBinder* binder;
TestBinderNode* other;
gboolean looper_enabled;
} TestBinderNode;
};
typedef struct test_binder_fd {
int fd;
......@@ -138,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;
......@@ -166,6 +173,8 @@ typedef struct binder_object_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)
......@@ -353,6 +362,7 @@ test_io_passthough_write_64(
gssize bytes_written;
guint extra;
guint32* cmd;
guint32 bytes_to_really_write;
void* data;
/* Just ignore some codes for now */
......@@ -374,6 +384,7 @@ test_io_passthough_write_64(
tx = data;
break;
case BC_TRANSACTION_64:
case BC_TRANSACTION_SG_64:
*cmd = BR_TRANSACTION_64;
tx = data;
break;
......@@ -382,6 +393,7 @@ test_io_passthough_write_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;
......@@ -399,8 +411,8 @@ test_io_passthough_write_64(
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);
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
......@@ -410,9 +422,11 @@ test_io_passthough_write_64(
if (*data_ptr == BINDER_TYPE_BINDER) {
BinderObject64* object = (BinderObject64*) data_ptr;
object->type = BINDER_TYPE_HANDLE;
object->object = test_io_passthough_fix_handle(binder,
object->object);
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;
}
}
......@@ -422,9 +436,13 @@ test_io_passthough_write_64(
tx->data_buffer = GPOINTER_TO_SIZE(data_buffer);
tx->data_offsets = GPOINTER_TO_SIZE(data_offsets);
}
bytes_written = write(fd->fd, cmd, bytes_to_write);
/* 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);
return bytes_written;
g_assert(bytes_written == bytes_to_really_write || bytes_written <= 0);
return (bytes_written >= 0) ? bytes_to_write : bytes_written;
}
static
......@@ -484,15 +502,18 @@ test_io_handle_write_read_64(
}
is_looper = g_private_get(&test_looper) ? TRUE : FALSE;
if (node->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(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) {
/* 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);
......@@ -622,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
......@@ -1055,6 +1076,7 @@ gbinder_system_open(
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);
}
......
/*
* 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:
*/
/*
* 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:
*/
......@@ -24,6 +24,7 @@ unit_remote_request \
unit_servicemanager \
unit_servicemanager_aidl \
unit_servicemanager_aidl2 \
unit_servicemanager_hidl \
unit_servicename \
unit_servicepoll \
unit_writer"
......
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_hidl
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
include ../common/Makefile
/*
* 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:
*/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment