From d6d6b76fa50f4a060c4de712071a93745abfc59b Mon Sep 17 00:00:00 2001 From: Slava Monich <slava.monich@jolla.com> Date: Wed, 23 Dec 2020 21:44:53 +0200 Subject: [PATCH] [unit] Added unit_servicemanager_aidl2 test. JB#42956 --- unit/Makefile | 1 + unit/coverage/run | 1 + unit/unit_servicemanager_aidl2/Makefile | 5 + .../unit_servicemanager_aidl2.c | 437 ++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 unit/unit_servicemanager_aidl2/Makefile create mode 100644 unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c diff --git a/unit/Makefile b/unit/Makefile index 796cabc..e6b371f 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -20,6 +20,7 @@ all: @$(MAKE) -C unit_remote_request $* @$(MAKE) -C unit_servicemanager $* @$(MAKE) -C unit_servicemanager_aidl $* + @$(MAKE) -C unit_servicemanager_aidl2 $* @$(MAKE) -C unit_servicename $* @$(MAKE) -C unit_servicepoll $* @$(MAKE) -C unit_writer $* diff --git a/unit/coverage/run b/unit/coverage/run index dfa8b63..2ba250d 100755 --- a/unit/coverage/run +++ b/unit/coverage/run @@ -23,6 +23,7 @@ unit_remote_reply \ unit_remote_request \ unit_servicemanager \ unit_servicemanager_aidl \ +unit_servicemanager_aidl2 \ unit_servicename \ unit_servicepoll \ unit_writer" diff --git a/unit/unit_servicemanager_aidl2/Makefile b/unit/unit_servicemanager_aidl2/Makefile new file mode 100644 index 0000000..881b957 --- /dev/null +++ b/unit/unit_servicemanager_aidl2/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_servicemanager_aidl2 + +include ../common/Makefile diff --git a/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c b/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c new file mode 100644 index 0000000..5839def --- /dev/null +++ b/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c @@ -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: + */ -- GitLab