diff --git a/Makefile b/Makefile index bae841c..d001939 100644 --- a/Makefile +++ b/Makefile @@ -105,6 +105,7 @@ SRC += \ gbinder_servicemanager_aidl.c \ gbinder_servicemanager_aidl2.c \ gbinder_servicemanager_aidl3.c \ + gbinder_servicemanager_aidl4.c \ gbinder_servicemanager_hidl.c SRC += \ diff --git a/src/gbinder_servicemanager.c b/src/gbinder_servicemanager.c index c5ab815..0acc52b 100644 --- a/src/gbinder_servicemanager.c +++ b/src/gbinder_servicemanager.c @@ -83,11 +83,12 @@ static const GBinderServiceManagerType gbinder_servicemanager_types[] = { { "aidl", gbinder_servicemanager_aidl_get_type }, { "aidl2", gbinder_servicemanager_aidl2_get_type }, { "aidl3", gbinder_servicemanager_aidl3_get_type }, + { "aidl4", gbinder_servicemanager_aidl4_get_type }, { "hidl", gbinder_servicemanager_hidl_get_type } }; #define SERVICEMANAGER_TYPE_AIDL (gbinder_servicemanager_types + 0) -#define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 3) +#define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 4) #define SERVICEMANAGER_TYPE_DEFAULT SERVICEMANAGER_TYPE_AIDL static GHashTable* gbinder_servicemanager_map = NULL; diff --git a/src/gbinder_servicemanager_aidl.h b/src/gbinder_servicemanager_aidl.h index c5b7c4f..0c48243 100644 --- a/src/gbinder_servicemanager_aidl.h +++ b/src/gbinder_servicemanager_aidl.h @@ -66,6 +66,13 @@ enum gbinder_servicemanager_aidl_calls { LIST_SERVICES_TRANSACTION }; +enum gbinder_stability_level { + UNDECLARED = 0, + VENDOR = 0b000011, + SYSTEM = 0b001100, + VINTF = 0b111111 +}; + #define DUMP_FLAG_PRIORITY_DEFAULT (0x08) #define DUMP_FLAG_PRIORITY_ALL (0x0f) diff --git a/src/gbinder_servicemanager_aidl3.c b/src/gbinder_servicemanager_aidl3.c index 82038fc..31e26b3 100644 --- a/src/gbinder_servicemanager_aidl3.c +++ b/src/gbinder_servicemanager_aidl3.c @@ -32,7 +32,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include "gbinder_servicemanager_aidl.h" +#include "gbinder_servicemanager_aidl_p.h" #include "gbinder_client_p.h" #include "gbinder_reader_p.h" @@ -50,14 +50,6 @@ G_DEFINE_TYPE(GBinderServiceManagerAidl3, #define PARENT_CLASS gbinder_servicemanager_aidl3_parent_class -enum gbinder_stability_level { - UNDECLARED = 0, - VENDOR = 0b000011, - SYSTEM = 0b001100, - VINTF = 0b111111 -}; - -static GBinderRemoteObject* gbinder_servicemanager_aidl3_get_service( GBinderServiceManager* self, @@ -84,28 +76,6 @@ gbinder_servicemanager_aidl3_get_service( return obj; } -static -GBinderLocalRequest* -gbinder_servicemanager_aidl3_add_service_req( - GBinderClient* client, - const char* name, - GBinderLocalObject* obj) -{ - GBinderLocalRequest* req = gbinder_client_new_request(client); - - gbinder_local_request_append_string16(req, name); - gbinder_local_request_append_local_object(req, obj); - /* - * Starting from Android 11, to add a service, Android framework requires - * an additional field `stability` when reading a strong binder. - */ - gbinder_local_request_append_int32(req, SYSTEM); - gbinder_local_request_append_int32(req, 0); - gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT); - return req; -} - -static char** gbinder_servicemanager_aidl3_list( GBinderServiceManager* manager, @@ -148,6 +118,27 @@ gbinder_servicemanager_aidl3_list( return (char**)g_ptr_array_free(list, FALSE); } +static +GBinderLocalRequest* +gbinder_servicemanager_aidl3_add_service_req( + GBinderClient* client, + const char* name, + GBinderLocalObject* obj) +{ + GBinderLocalRequest* req = gbinder_client_new_request(client); + + gbinder_local_request_append_string16(req, name); + gbinder_local_request_append_local_object(req, obj); + /* + * Starting from Android 11, to add a service, Android framework requires + * an additional field `stability` when reading a strong binder. + */ + gbinder_local_request_append_int32(req, SYSTEM); + gbinder_local_request_append_int32(req, 0); + gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT); + return req; +} + static void gbinder_servicemanager_aidl3_init( diff --git a/src/gbinder_servicemanager_aidl4.c b/src/gbinder_servicemanager_aidl4.c new file mode 100644 index 0000000..e33e761 --- /dev/null +++ b/src/gbinder_servicemanager_aidl4.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 Jolla Ltd. + * Copyright (C) 2020 Slava Monich + * Copyright (C) 2021 Gary Wang + * Copyright (C) 2021 Madhushan Nishantha + * + * 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 "gbinder_servicemanager_aidl_p.h" +#include "gbinder_client_p.h" +#include "gbinder_reader_p.h" +#include "binder.h" + +#include +#include + +/* Variant of AIDL servicemanager appeared in Android 12 (API level 31) */ + +typedef GBinderServiceManagerAidl GBinderServiceManagerAidl4; +typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl4Class; + +G_DEFINE_TYPE(GBinderServiceManagerAidl4, + gbinder_servicemanager_aidl4, + GBINDER_TYPE_SERVICEMANAGER_AIDL) + +#define PARENT_CLASS gbinder_servicemanager_aidl4_parent_class + +#define BINDER_WIRE_FORMAT_VERSION (1) + +static +GBinderLocalRequest* +gbinder_servicemanager_aidl4_add_service_req( + GBinderClient* client, + const char* name, + GBinderLocalObject* obj) +{ + GBinderLocalRequest* req = gbinder_client_new_request(client); + + gbinder_local_request_append_string16(req, name); + gbinder_local_request_append_local_object(req, obj); + /* + * When reading nullable strong binder, from Android 12, the format of + * the `stability` field passed on the wire was changed and evolved to + * `struct Category`, which consists of the following members with 4 bytes long. + * + * struct Category { + * uint8_t version; + * uint8_t reserved[2]; + * Level level; <- bitmask of Stability::Level + * } + */ + gbinder_local_request_append_int32(req, B_PACK_CHARS(SYSTEM, 0, 0, BINDER_WIRE_FORMAT_VERSION)); + gbinder_local_request_append_int32(req, 0); + gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT); + + return req; +} + +static +void +gbinder_servicemanager_aidl4_init( + GBinderServiceManagerAidl* self) +{ +} + +static +void +gbinder_servicemanager_aidl4_class_init( + GBinderServiceManagerAidl4Class* cls) +{ + GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(cls); + cls->add_service_req = gbinder_servicemanager_aidl4_add_service_req; + manager->list = gbinder_servicemanager_aidl3_list; + manager->get_service = gbinder_servicemanager_aidl3_get_service; +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/src/gbinder_servicemanager_aidl_p.h b/src/gbinder_servicemanager_aidl_p.h new file mode 100644 index 0000000..6a00ac9 --- /dev/null +++ b/src/gbinder_servicemanager_aidl_p.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich + * Copyright (C) 2021 Gary Wang + * + * You may use this file under the terms of BSD license as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GBINDER_SERVICEMANAGER_AIDL_PRIVATE_H +#define GBINDER_SERVICEMANAGER_AIDL_PRIVATE_H + +#include "gbinder_servicemanager_aidl.h" + +char** +gbinder_servicemanager_aidl3_list( + GBinderServiceManager* manager, + const GBinderIpcSyncApi* api) + GBINDER_INTERNAL; + +GBinderRemoteObject* +gbinder_servicemanager_aidl3_get_service( + GBinderServiceManager* manager, + const char* name, + int* status, + const GBinderIpcSyncApi* api) + GBINDER_INTERNAL; + +#endif /* GBINDER_SERVICEMANAGER_AIDL_PRIVATE_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/gbinder_servicemanager_p.h b/src/gbinder_servicemanager_p.h index 4b45836..8e02721 100644 --- a/src/gbinder_servicemanager_p.h +++ b/src/gbinder_servicemanager_p.h @@ -112,6 +112,7 @@ gbinder_servicemanager_exit( GType gbinder_servicemanager_aidl_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl2_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl3_get_type(void) GBINDER_INTERNAL; +GType gbinder_servicemanager_aidl4_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_hidl_get_type(void) GBINDER_INTERNAL; #endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */ diff --git a/unit/Makefile b/unit/Makefile index b5ef2a3..7fab42b 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -25,6 +25,7 @@ all: @$(MAKE) -C unit_servicemanager_aidl $* @$(MAKE) -C unit_servicemanager_aidl2 $* @$(MAKE) -C unit_servicemanager_aidl3 $* + @$(MAKE) -C unit_servicemanager_aidl4 $* @$(MAKE) -C unit_servicemanager_hidl $* @$(MAKE) -C unit_servicename $* @$(MAKE) -C unit_servicepoll $* diff --git a/unit/coverage/run b/unit/coverage/run index ab20d0a..fe6ff60 100755 --- a/unit/coverage/run +++ b/unit/coverage/run @@ -27,6 +27,7 @@ unit_servicemanager \ unit_servicemanager_aidl \ unit_servicemanager_aidl2 \ unit_servicemanager_aidl3 \ +unit_servicemanager_aidl4 \ unit_servicemanager_hidl \ unit_servicename \ unit_servicepoll \ diff --git a/unit/unit_servicemanager/unit_servicemanager.c b/unit/unit_servicemanager/unit_servicemanager.c index bc8d1d8..4dda717 100644 --- a/unit/unit_servicemanager/unit_servicemanager.c +++ b/unit/unit_servicemanager/unit_servicemanager.c @@ -399,6 +399,14 @@ gbinder_servicemanager_aidl3_get_type() return 0; } +GType +gbinder_servicemanager_aidl4_get_type() +{ + /* Dummy function to avoid pulling in gbinder_servicemanager_aidl4 */ + g_assert_not_reached(); + return 0; +} + /*==========================================================================* * null *==========================================================================*/ diff --git a/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c b/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c index 556f230..2e8bd97 100644 --- a/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c +++ b/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c @@ -71,6 +71,14 @@ gbinder_servicemanager_aidl3_get_type() return 0; } +GType +gbinder_servicemanager_aidl4_get_type() +{ + /* Dummy function to avoid pulling in gbinder_servicemanager_aidl4 */ + g_assert_not_reached(); + return 0; +} + /*==========================================================================* * Test service manager *==========================================================================*/ diff --git a/unit/unit_servicemanager_aidl4/Makefile b/unit/unit_servicemanager_aidl4/Makefile new file mode 100644 index 0000000..d9f6d59 --- /dev/null +++ b/unit/unit_servicemanager_aidl4/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_servicemanager_aidl4 + +include ../common/Makefile diff --git a/unit/unit_servicemanager_aidl4/unit_servicemanager_aidl4.c b/unit/unit_servicemanager_aidl4/unit_servicemanager_aidl4.c new file mode 100644 index 0000000..a0e0464 --- /dev/null +++ b/unit/unit_servicemanager_aidl4/unit_servicemanager_aidl4.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2020-2021 Jolla Ltd. + * Copyright (C) 2020-2021 Slava Monich + * + * You may use this file under the terms of BSD license as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#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 "gbinder_writer.h" + +#include +#include + +static TestOpt test_opt; +static const char TMP_DIR_TEMPLATE[] = + "gbinder-test-servicemanager_aidl4-XXXXXX"; + +enum gbinder_stability_level { + UNDECLARED = 0, + VENDOR = 0b000011, + SYSTEM = 0b001100, + VINTF = 0b111111 +}; + +GType +gbinder_servicemanager_hidl_get_type() +{ + /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ + g_assert_not_reached(); + 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 ServiceManagerAidl4Class; +typedef struct service_manager_aidl4 { + GBinderLocalObject parent; + GHashTable* objects; + gboolean handle_on_looper_thread; +} ServiceManagerAidl4; + +#define SERVICE_MANAGER_AIDL4_TYPE (service_manager_aidl4_get_type()) +#define SERVICE_MANAGER_AIDL4(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + SERVICE_MANAGER_AIDL4_TYPE, ServiceManagerAidl4)) +G_DEFINE_TYPE(ServiceManagerAidl4, service_manager_aidl4, \ + GBINDER_TYPE_LOCAL_OBJECT) + +/* This is the format of stability passed on the wire. */ +typedef struct category { + /* + * This is the version of the wire protocol associated with the host + * process of a particular binder. As the wire protocol changes, if + * sending a transaction to a binder with an old version, the Parcel + * class must write parcels according to the version documented here. + */ + gint8 version; + gint8 reserved[2]; + gint8 level; /* bitmask of Stability::Level */ +} Category; + +static +GBinderLocalReply* +servicemanager_aidl4_handler( + GBinderLocalObject* obj, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status, + void* user_data) +{ + ServiceManagerAidl4* self = user_data; + GBinderLocalReply* reply = NULL; + GBinderReader reader; + GBinderRemoteObject* remote_obj; + guint32 allow_isolated, dumpsys_priority; + char* str; + Category category; + + 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) { + GBinderWriter writer; + + GDEBUG("Found name '%s' => %p", str, remote_obj); + gbinder_local_reply_init_writer(reply, &writer); + gbinder_writer_append_int32(&writer, UNDECLARED); + gbinder_writer_append_remote_object(&writer, 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); + gbinder_reader_read_uint32(&reader, (guint32*)&category); + if (str && remote_obj && + category.level == SYSTEM && + category.version == 1 && + 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, &dumpsys_priority)) { + if (g_hash_table_size(self->objects) == 1) { + GList* keys = g_hash_table_get_keys(self->objects); + GList* l = g_list_nth(keys, 0); + gint32 srv_size = 1; + GBinderWriter writer; + + reply = gbinder_local_object_new_reply(obj); + gbinder_local_reply_init_writer(reply, &writer); + gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); + gbinder_writer_append_int32(&writer, srv_size); + gbinder_writer_append_string16(&writer, l->data); + g_list_free(keys); + *status = GBINDER_STATUS_OK; + } else { + GDEBUG("Incorrect number of services %u", + g_hash_table_size(self->objects)); + } + } + break; + default: + GDEBUG("Unhandled command %u", code); + break; + } + return reply; +} + +static +ServiceManagerAidl4* +servicemanager_aidl4_new( + const char* dev, + gboolean handle_on_looper_thread) +{ + ServiceManagerAidl4* self = g_object_new(SERVICE_MANAGER_AIDL4_TYPE, NULL); + GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); + GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); + 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_aidl4_handler, self); + test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE); + test_binder_register_object(fd, obj, SVCMGR_HANDLE); + gbinder_ipc_register_local_object(ipc, obj); + gbinder_ipc_unref(ipc); + return self; +} + +static +GBINDER_LOCAL_TRANSACTION_SUPPORT +service_manager_aidl4_can_handle_transaction( + GBinderLocalObject* object, + const char* iface, + guint code) +{ + ServiceManagerAidl4* self = SERVICE_MANAGER_AIDL4(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_aidl4_parent_class)-> + can_handle_transaction(object, iface, code); + } +} + +static +GBinderLocalReply* +service_manager_aidl4_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_aidl4_parent_class)-> + handle_transaction(object, req, code, flags, status); + } else { + return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl4_parent_class)-> + handle_looper_transaction(object, req, code, flags, status); + } +} + +static +void +service_manager_aidl4_finalize( + GObject* object) +{ + ServiceManagerAidl4* self = SERVICE_MANAGER_AIDL4(object); + + g_hash_table_destroy(self->objects); + G_OBJECT_CLASS(service_manager_aidl4_parent_class)->finalize(object); +} + +static +void +service_manager_aidl4_init( + ServiceManagerAidl4* 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_aidl4_class_init( + ServiceManagerAidl4Class* klass) +{ + GObjectClass* object = G_OBJECT_CLASS(klass); + GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass); + + object->finalize = service_manager_aidl4_finalize; + local_object->can_handle_transaction = + service_manager_aidl4_can_handle_transaction; + local_object->handle_looper_transaction = + service_manager_aidl4_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; + ServiceManagerAidl4* 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 = aidl3\n" + "/dev/binder = aidl3\n" + "[ServiceManager]\n" + "Default = aidl4\n" + "/dev/binder = aidl4\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, NULL); + 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_aidl4_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(&test_opt, NULL); + 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_run() +{ + 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); +} + +static +void +test_get() +{ + test_run_in_context(&test_opt, test_get_run); +} + +/*==========================================================================* + * list + *==========================================================================*/ + +static +void +test_list_run() +{ + 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); +} + +static +void +test_list() +{ + test_run_in_context(&test_opt, test_list_run); +} + +/*==========================================================================* + * Common + *==========================================================================*/ + +#define TEST_(t) "/servicemanager_aidl4/" t + +int main(int argc, char* argv[]) +{ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + g_type_init(); + G_GNUC_END_IGNORE_DEPRECATIONS; + 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: + */ diff --git a/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c b/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c index 1e5ea07..58d0712 100644 --- a/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c +++ b/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c @@ -82,6 +82,14 @@ gbinder_servicemanager_aidl3_get_type() return 0; } +GType +gbinder_servicemanager_aidl4_get_type() +{ + /* Dummy function to avoid pulling in gbinder_servicemanager_aidl4 */ + g_assert_not_reached(); + return 0; +} + /*==========================================================================* * Common *==========================================================================*/ diff --git a/unit/unit_servicename/unit_servicename.c b/unit/unit_servicename/unit_servicename.c index 45332f8..30dc387 100644 --- a/unit/unit_servicename/unit_servicename.c +++ b/unit/unit_servicename/unit_servicename.c @@ -240,6 +240,12 @@ gbinder_servicemanager_aidl3_get_type() return TEST_TYPE_SERVICEMANAGER; } +GType +gbinder_servicemanager_aidl4_get_type() +{ + return TEST_TYPE_SERVICEMANAGER; +} + GType gbinder_servicemanager_hidl_get_type() { diff --git a/unit/unit_servicepoll/unit_servicepoll.c b/unit/unit_servicepoll/unit_servicepoll.c index c7f79d6..285b1a6 100644 --- a/unit/unit_servicepoll/unit_servicepoll.c +++ b/unit/unit_servicepoll/unit_servicepoll.c @@ -206,6 +206,12 @@ gbinder_servicemanager_aidl3_get_type() return TEST_TYPE_SERVICEMANAGER; } +GType +gbinder_servicemanager_aidl4_get_type() +{ + return TEST_TYPE_SERVICEMANAGER; +} + GType gbinder_servicemanager_hidl_get_type() {