Merge pull request #16 from monich/poll

Implement service polling for old servicemanager
This commit is contained in:
Slava Monich
2018-12-05 11:57:29 +02:00
committed by GitHub
9 changed files with 949 additions and 6 deletions

View File

@@ -62,6 +62,7 @@ SRC = \
gbinder_remote_reply.c \
gbinder_remote_request.c \
gbinder_rpc_protocol.c \
gbinder_servicepoll.c \
gbinder_writer.c
SRC += \

View File

@@ -32,6 +32,7 @@
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_servicepoll.h"
#include "gbinder_log.h"
#include <gbinder_client.h>
@@ -43,13 +44,31 @@
#include <errno.h>
#include <pthread.h>
typedef GBinderServiceManager GBinderDefaultServiceManager;
typedef struct gbinder_defaultservicemanager_watch {
GBinderServicePoll* poll;
char* name;
gulong handler_id;
guint notify_id;
} GBinderDefaultServiceManagerWatch;
typedef GBinderServiceManagerClass GBinderDefaultServiceManagerClass;
typedef struct gbinder_defaultservicemanager {
GBinderServiceManager manager;
GBinderServicePoll* poll;
GHashTable* watch_table;
} GBinderDefaultServiceManager;
G_DEFINE_TYPE(GBinderDefaultServiceManager,
gbinder_defaultservicemanager,
GBINDER_TYPE_SERVICEMANAGER)
#define PARENT_CLASS gbinder_defaultservicemanager_parent_class
#define GBINDER_TYPE_DEFAULTSERVICEMANAGER \
gbinder_defaultservicemanager_get_type()
#define GBINDER_DEFAULTSERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_DEFAULTSERVICEMANAGER, \
GBinderDefaultServiceManager)
enum gbinder_defaultservicemanager_calls {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
@@ -66,7 +85,76 @@ gbinder_defaultservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type
(gbinder_defaultservicemanager_get_type(), dev);
(GBINDER_TYPE_DEFAULTSERVICEMANAGER, dev);
}
static
void
gbinder_defaultservicemanager_watch_proc(
GBinderServicePoll* poll,
const char* name_added,
void* user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
if (!g_strcmp0(name_added, watch->name)) {
GBinderServiceManager* manager =
gbinder_servicepoll_manager(watch->poll);
if (watch->notify_id) {
g_source_remove(watch->notify_id);
watch->notify_id = 0;
}
gbinder_servicemanager_service_registered(manager, name_added);
}
}
static
gboolean
gbinder_defaultservicemanager_watch_notify(
gpointer user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll);
char* name = g_strdup(watch->name);
GASSERT(watch->notify_id);
watch->notify_id = 0;
gbinder_servicemanager_service_registered(manager, name);
g_free(name);
return G_SOURCE_REMOVE;
}
static
void
gbinder_defaultservicemanager_watch_free(
gpointer user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
if (watch->notify_id) {
g_source_remove(watch->notify_id);
}
gbinder_servicepoll_remove_handler(watch->poll, watch->handler_id);
gbinder_servicepoll_unref(watch->poll);
g_free(watch->name);
g_slice_free(GBinderDefaultServiceManagerWatch, watch);
}
static
GBinderDefaultServiceManagerWatch*
gbinder_defaultservicemanager_watch_new(
GBinderDefaultServiceManager* manager,
const char* name)
{
GBinderDefaultServiceManagerWatch* watch =
g_slice_new0(GBinderDefaultServiceManagerWatch);
watch->name = g_strdup(name);
watch->poll = gbinder_servicepoll_new(&manager->manager, &manager->poll);
watch->handler_id = gbinder_servicepoll_add_handler(watch->poll,
gbinder_defaultservicemanager_watch_proc, watch);
return watch;
}
static
@@ -157,9 +245,35 @@ gbinder_defaultservicemanager_check_name(
GBinderServiceManager* self,
const char* name)
{
/* Old servicemanager doesn't support notifications, those would
* have to be emulated with polling. Is it necessary though? */
return GBINDER_SERVICEMANAGER_NAME_INVALID;
return GBINDER_SERVICEMANAGER_NAME_OK;
}
static
gboolean
gbinder_defaultservicemanager_watch(
GBinderServiceManager* manager,
const char* name)
{
GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(manager);
GBinderDefaultServiceManagerWatch* watch =
gbinder_defaultservicemanager_watch_new(self, name);
g_hash_table_replace(self->watch_table, watch->name, watch);
if (gbinder_servicepoll_is_known_name(watch->poll, name)) {
watch->notify_id =
g_idle_add(gbinder_defaultservicemanager_watch_notify, watch);
}
return TRUE;
}
static
void
gbinder_defaultservicemanager_unwatch(
GBinderServiceManager* manager,
const char* name)
{
g_hash_table_remove(GBINDER_DEFAULTSERVICEMANAGER(manager)->watch_table,
name);
}
static
@@ -167,6 +281,19 @@ void
gbinder_defaultservicemanager_init(
GBinderDefaultServiceManager* self)
{
self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, gbinder_defaultservicemanager_watch_free);
}
static
void
gbinder_defaultservicemanager_finalize(
GObject* object)
{
GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(object);
g_hash_table_destroy(self->watch_table);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
static
@@ -183,7 +310,10 @@ gbinder_defaultservicemanager_class_init(
klass->get_service = gbinder_defaultservicemanager_get_service;
klass->add_service = gbinder_defaultservicemanager_add_service;
klass->check_name = gbinder_defaultservicemanager_check_name;
/* No need for other watch callbacks */
/* normalize_name is not needed */
klass->watch = gbinder_defaultservicemanager_watch;
klass->unwatch = gbinder_defaultservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = gbinder_defaultservicemanager_finalize;
}
/*

268
src/gbinder_servicepoll.c Normal file
View File

@@ -0,0 +1,268 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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 "gbinder_servicepoll.h"
#include "gbinder_servicemanager.h"
#include <gutil_strv.h>
#include <glib-object.h>
/* This is configurable mostly so that unit testing doesn't take too long */
guint gbinder_servicepoll_interval_ms = 2000;
typedef GObjectClass GBinderServicePollClass;
struct gbinder_servicepoll {
GObject object;
GBinderServiceManager* manager;
char** list;
gulong list_id;
guint timer_id;
};
G_DEFINE_TYPE(GBinderServicePoll, gbinder_servicepoll, G_TYPE_OBJECT)
#define GBINDER_TYPE_SERVICEPOLL (gbinder_servicepoll_get_type())
#define GBINDER_SERVICEPOLL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GBINDER_TYPE_SERVICEPOLL, GBinderServicePoll))
enum gbinder_servicepoll_signal {
SIGNAL_NAME_ADDED,
SIGNAL_COUNT
};
static const char SIGNAL_NAME_ADDED_NAME[] = "servicepoll-name-added";
static guint gbinder_servicepoll_signals[SIGNAL_COUNT] = { 0 };
/*==========================================================================*
* Implementation
*==========================================================================*/
/* GBinderServiceManagerListFunc callback returns TRUE to keep the services
* list, otherwise the caller will deallocate it. */
gboolean
gbinder_servicepoll_list(
GBinderServiceManager* sm,
char** services,
void* user_data)
{
GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
gbinder_servicepoll_ref(self);
self->list_id = 0;
if (services) {
const GStrV* ptr_new;
ptr_new = services = gutil_strv_sort(services, TRUE);
if (self->list) {
const GStrV* ptr_old = self->list;
while (*ptr_new && *ptr_old) {
const int i = gutil_strv_find(ptr_old, *ptr_new);
if (i < 0) {
/* New name */
g_signal_emit(self, gbinder_servicepoll_signals
[SIGNAL_NAME_ADDED], 0, *ptr_new);
} else {
int k;
/* If some names have disappeared, then i may be > 0 */
for (k = 0; k < i; k ++) ptr_old++;
ptr_old++;
}
ptr_new++;
}
}
while (*ptr_new) {
g_signal_emit(self, gbinder_servicepoll_signals
[SIGNAL_NAME_ADDED], 0, *ptr_new);
ptr_new++;
}
}
g_strfreev(self->list);
self->list = services;
gbinder_servicepoll_unref(self);
return TRUE;
}
static
gboolean
gbinder_servicepoll_timer(
gpointer user_data)
{
GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
if (!self->list_id) {
self->list_id = gbinder_servicemanager_list(self->manager,
gbinder_servicepoll_list, self);
}
return G_SOURCE_CONTINUE;
}
static
GBinderServicePoll*
gbinder_servicepoll_create(
GBinderServiceManager* manager)
{
GBinderServicePoll* self = g_object_new(GBINDER_TYPE_SERVICEPOLL, NULL);
self->manager = gbinder_servicemanager_ref(manager);
self->list_id = gbinder_servicemanager_list(manager,
gbinder_servicepoll_list, self);
return self;
}
/*==========================================================================*
* API
*==========================================================================*/
GBinderServicePoll*
gbinder_servicepoll_new(
GBinderServiceManager* manager,
GBinderServicePoll** weakptr)
{
if (weakptr) {
if (*weakptr) {
gbinder_servicepoll_ref(*weakptr);
} else {
*weakptr = gbinder_servicepoll_create(manager);
g_object_add_weak_pointer(G_OBJECT(*weakptr), (gpointer*)weakptr);
}
return *weakptr;
} else {
return gbinder_servicepoll_create(manager);
}
}
GBinderServicePoll*
gbinder_servicepoll_ref(
GBinderServicePoll* self)
{
if (G_LIKELY(self)) {
g_object_ref(GBINDER_SERVICEPOLL(self));
return self;
} else {
return NULL;
}
}
void
gbinder_servicepoll_unref(
GBinderServicePoll* self)
{
if (G_LIKELY(self)) {
g_object_unref(GBINDER_SERVICEPOLL(self));
}
}
GBinderServiceManager*
gbinder_servicepoll_manager(
GBinderServicePoll* self)
{
return G_LIKELY(self) ? self->manager : NULL;
}
gboolean
gbinder_servicepoll_is_known_name(
GBinderServicePoll* self,
const char* name)
{
return G_LIKELY(self) && gutil_strv_contains(self->list, name);
}
gulong
gbinder_servicepoll_add_handler(
GBinderServicePoll* self,
GBinderServicePollFunc fn,
void* user_data)
{
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
SIGNAL_NAME_ADDED_NAME, G_CALLBACK(fn), user_data) : 0;
}
void
gbinder_servicepoll_remove_handler(
GBinderServicePoll* self,
gulong id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
g_signal_handler_disconnect(self, id);
}
}
/*==========================================================================*
* Internals
*==========================================================================*/
static
void
gbinder_servicepoll_init(
GBinderServicePoll* self)
{
self->timer_id = g_timeout_add(gbinder_servicepoll_interval_ms,
gbinder_servicepoll_timer, self);
}
static
void
gbinder_servicepoll_finalize(
GObject* object)
{
GBinderServicePoll* self = GBINDER_SERVICEPOLL(object);
g_source_remove(self->timer_id);
gbinder_servicemanager_cancel(self->manager, self->list_id);
gbinder_servicemanager_unref(self->manager);
g_strfreev(self->list);
}
static
void
gbinder_servicepoll_class_init(
GBinderServicePollClass* klass)
{
G_OBJECT_CLASS(klass)->finalize = gbinder_servicepoll_finalize;
gbinder_servicepoll_signals[SIGNAL_NAME_ADDED] =
g_signal_new(SIGNAL_NAME_ADDED_NAME, G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
1, G_TYPE_STRING);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

88
src/gbinder_servicepoll.h Normal file
View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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 GBINDER_SERVICEPOLL_H
#define GBINDER_SERVICEPOLL_H
#include "gbinder_types_p.h"
extern guint gbinder_servicepoll_interval_ms;
typedef
void
(*GBinderServicePollFunc)(
GBinderServicePoll* poll,
const char* name_added,
void* user_data);
GBinderServicePoll*
gbinder_servicepoll_new(
GBinderServiceManager* manager,
GBinderServicePoll** weakptr);
GBinderServicePoll*
gbinder_servicepoll_ref(
GBinderServicePoll* poll);
void
gbinder_servicepoll_unref(
GBinderServicePoll* poll);
GBinderServiceManager*
gbinder_servicepoll_manager(
GBinderServicePoll* poll);
gboolean
gbinder_servicepoll_is_known_name(
GBinderServicePoll* poll,
const char* name);
gulong
gbinder_servicepoll_add_handler(
GBinderServicePoll* poll,
GBinderServicePollFunc func,
void* user_data);
void
gbinder_servicepoll_remove_handler(
GBinderServicePoll* poll,
gulong id);
#endif /* GBINDER_SERVICEPOLL_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -44,6 +44,7 @@ typedef struct gbinder_ipc GBinderIpc;
typedef struct gbinder_object_registry GBinderObjectRegistry;
typedef struct gbinder_output_data GBinderOutputData;
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
typedef struct gbinder_servicepoll GBinderServicePoll;
typedef struct hidl_vec {
union {

View File

@@ -15,6 +15,7 @@ all:
@$(MAKE) -C unit_remote_reply $*
@$(MAKE) -C unit_remote_request $*
@$(MAKE) -C unit_servicemanager $*
@$(MAKE) -C unit_servicepoll $*
@$(MAKE) -C unit_writer $*
clean: unitclean

View File

@@ -17,6 +17,7 @@ unit_remote_object \
unit_remote_reply \
unit_remote_request \
unit_servicemanager \
unit_servicepoll \
unit_writer"
function err() {

View File

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

View File

@@ -0,0 +1,448 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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_common.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_servicepoll.h"
#include "gbinder_rpc_protocol.h"
#include <gutil_strv.h>
#include <gutil_log.h>
#include <errno.h>
static TestOpt test_opt;
/*==========================================================================*
* TestServiceManager
*==========================================================================*/
typedef GBinderServiceManagerClass TestServiceManagerClass;
typedef struct test_servicemanager {
GBinderServiceManager manager;
GMutex mutex;
char** services;
} TestServiceManager;
G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
GBINDER_TYPE_SERVICEMANAGER)
#define TEST_SERVICEMANAGER_HANDLE (0)
#define TEST_SERVICEMANAGER_IFACE "android.os.IServiceManager"
#define TEST_TYPE_SERVICEMANAGER (test_servicemanager_get_type())
#define TEST_SERVICEMANAGER(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), \
TEST_TYPE_SERVICEMANAGER, TestServiceManager)
static
char**
test_servicemanager_list(
GBinderServiceManager* manager)
{
char** ret;
TestServiceManager* self = TEST_SERVICEMANAGER(manager);
g_mutex_lock(&self->mutex);
ret = g_strdupv(self->services);
GDEBUG("%u", gutil_strv_length(ret));
g_mutex_unlock(&self->mutex);
return ret;
}
static
GBinderRemoteObject*
test_servicemanager_get_service(
GBinderServiceManager* manager,
const char* name,
int* status)
{
*status = (-ENOENT);
return NULL;
}
static
int
test_servicemanager_add_service(
GBinderServiceManager* manager,
const char* name,
GBinderLocalObject* obj)
{
TestServiceManager* self = TEST_SERVICEMANAGER(manager);
g_mutex_lock(&self->mutex);
if (!gutil_strv_contains(self->services, name)) {
self->services = gutil_strv_add(self->services, name);
}
g_mutex_unlock(&self->mutex);
return GBINDER_STATUS_OK;
}
static
GBINDER_SERVICEMANAGER_NAME_CHECK
test_servicemanager_check_name(
GBinderServiceManager* manager,
const char* name)
{
return name ?
GBINDER_SERVICEMANAGER_NAME_INVALID :
GBINDER_SERVICEMANAGER_NAME_OK;
}
static
gboolean
test_servicemanager_watch(
GBinderServiceManager* manager,
const char* name)
{
return TRUE;
}
static
void
test_servicemanager_unwatch(
GBinderServiceManager* manager,
const char* name)
{
}
static
void
test_servicemanager_init(
TestServiceManager* self)
{
g_mutex_init(&self->mutex);
}
static
void
test_servicemanager_finalize(
GObject* object)
{
TestServiceManager* self = TEST_SERVICEMANAGER(object);
g_mutex_clear(&self->mutex);
g_strfreev(self->services);
G_OBJECT_CLASS(test_servicemanager_parent_class)->finalize(object);
}
static
void
test_servicemanager_class_init(
TestServiceManagerClass* klass)
{
klass->handle = TEST_SERVICEMANAGER_HANDLE;
klass->iface = TEST_SERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
klass->check_name = test_servicemanager_check_name;
klass->watch = test_servicemanager_watch;
klass->unwatch = test_servicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
}
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new(dev);
}
/*==========================================================================*
* null
*==========================================================================*/
static
void
test_null(
void)
{
g_assert(!gbinder_servicepoll_ref(NULL));
g_assert(!gbinder_servicepoll_manager(NULL));
g_assert(!gbinder_servicepoll_is_known_name(NULL, ""));
g_assert(!gbinder_servicepoll_add_handler(NULL, NULL, NULL));
gbinder_servicepoll_remove_handler(NULL, 0);
gbinder_servicepoll_unref(NULL);
}
/*==========================================================================*
* basic
*==========================================================================*/
static
void
test_basic(
void)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
GBinderServicePoll* poll = gbinder_servicepoll_new(manager, NULL);
g_assert(poll);
g_assert(gbinder_servicepoll_manager(poll) == manager);
g_assert(!gbinder_servicepoll_is_known_name(poll, "foo"));
g_assert(!gbinder_servicepoll_add_handler(poll, NULL, NULL));
gbinder_servicepoll_remove_handler(poll, 0); /* this does nothing */
gbinder_servicepoll_unref(poll);
poll = gbinder_servicepoll_new(manager, &weakptr);
g_assert(poll == weakptr);
g_assert(poll == gbinder_servicepoll_new(manager, &weakptr));
gbinder_servicepoll_unref(poll);
gbinder_servicepoll_unref(poll);
gbinder_servicemanager_unref(manager);
}
/*==========================================================================*
* notify1
*==========================================================================*/
static
void
test_notify_proc(
GBinderServicePoll* poll,
const char* name_added,
void* user_data)
{
GDEBUG("\"%s\" added", name_added);
if (!g_strcmp0(name_added, "foo")) {
test_quit_later((GMainLoop*)user_data);
}
}
static
gboolean
test_notify1_foo(
gpointer user_data)
{
TestServiceManager* test = user_data;
g_mutex_lock(&test->mutex);
GDEBUG("adding \"foo\"");
test->services = gutil_strv_add(test->services, "foo");
g_mutex_unlock(&test->mutex);
return G_SOURCE_REMOVE;
}
static
gboolean
test_notify1_bar(
gpointer user_data)
{
TestServiceManager* test = user_data;
g_mutex_lock(&test->mutex);
GDEBUG("adding \"bar\"");
test->services = gutil_strv_add(test->services, "bar");
g_mutex_unlock(&test->mutex);
return G_SOURCE_REMOVE;
}
static
void
test_notify1(
void)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* poll;
gulong id;
gbinder_servicepoll_interval_ms = 100;
poll = gbinder_servicepoll_new(manager, &weakptr);
g_timeout_add(2 * gbinder_servicepoll_interval_ms,
test_notify1_bar, test);
g_timeout_add(4 * gbinder_servicepoll_interval_ms,
test_notify1_foo, test);
id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop);
g_assert(id);
test_run(&test_opt, loop);
g_assert(gbinder_servicepoll_is_known_name(poll, "foo"));
g_assert(gbinder_servicepoll_is_known_name(poll, "bar"));
gbinder_servicepoll_remove_handler(poll, id);
gbinder_servicepoll_unref(poll);
g_assert(!weakptr);
gbinder_servicemanager_unref(manager);
g_main_loop_unref(loop);
}
/*==========================================================================*
* notify2
*==========================================================================*/
static
gboolean
test_notify2_foo(
gpointer user_data)
{
TestServiceManager* test = user_data;
g_mutex_lock(&test->mutex);
GDEBUG("services = [\"bar\",\"foo\"]");
g_strfreev(test->services);
test->services = g_strsplit("bar,bar3,foo", ",", -1);
g_mutex_unlock(&test->mutex);
return G_SOURCE_REMOVE;
}
static
gboolean
test_notify2_bar(
gpointer user_data)
{
TestServiceManager* test = user_data;
g_mutex_lock(&test->mutex);
GDEBUG("services = [\"bar1\",\"bar2\",\"bar3\"]");
g_strfreev(test->services);
test->services = g_strsplit("bar1,bar2,bar3", ",", -1);
g_mutex_unlock(&test->mutex);
return G_SOURCE_REMOVE;
}
static
void
test_notify2(
void)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* poll;
gulong id;
gbinder_servicepoll_interval_ms = 100;
poll = gbinder_servicepoll_new(manager, &weakptr);
g_timeout_add(2 * gbinder_servicepoll_interval_ms,
test_notify2_bar, test);
g_timeout_add(4 * gbinder_servicepoll_interval_ms,
test_notify2_foo, test);
/* Reusing test_notify_proc */
id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop);
g_assert(id);
test_run(&test_opt, loop);
g_assert(gbinder_servicepoll_is_known_name(poll, "foo"));
g_assert(gbinder_servicepoll_is_known_name(poll, "bar"));
g_assert(gbinder_servicepoll_is_known_name(poll, "bar3"));
g_assert(!gbinder_servicepoll_is_known_name(poll, "bar1"));
g_assert(!gbinder_servicepoll_is_known_name(poll, "bar2"));
gbinder_servicepoll_remove_handler(poll, id);
gbinder_servicepoll_unref(poll);
g_assert(!weakptr);
gbinder_servicemanager_unref(manager);
g_main_loop_unref(loop);
}
/*==========================================================================*
* already_there
*==========================================================================*/
static
void
test_already_there_proc(
GBinderServicePoll* poll,
const char* name_added,
void* user_data)
{
g_assert(!g_strcmp0(name_added, "foo"));
test_quit_later((GMainLoop*)user_data);
}
static
void
test_already_there(
void)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
GBinderServicePoll* poll = gbinder_servicepoll_new(manager, &weakptr);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id;
test->services = gutil_strv_add(test->services, "foo");
id = gbinder_servicepoll_add_handler(poll, test_already_there_proc, loop);
g_assert(id);
test_run(&test_opt, loop);
gbinder_servicepoll_remove_handler(poll, id);
gbinder_servicepoll_unref(poll);
g_assert(!weakptr);
gbinder_servicemanager_unref(manager);
g_main_loop_unref(loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicepoll/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);
g_test_add_func(TEST_("notify1"), test_notify1);
g_test_add_func(TEST_("notify2"), test_notify2);
g_test_add_func(TEST_("already_there"), test_already_there);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/