367 lines
9.6 KiB
C
367 lines
9.6 KiB
C
/*
|
|
* Copyright (C) 2021 Jolla Ltd.
|
|
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
|
|
*
|
|
* You may use this file under the terms of the 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.
|
|
*
|
|
* The views and conclusions contained in the software and documentation
|
|
* are those of the authors and should not be interpreted as representing
|
|
* any official policies, either expressed or implied.
|
|
*/
|
|
|
|
#include "test_gbinder.h"
|
|
|
|
#include <gutil_log.h>
|
|
|
|
typedef struct test_gbinder_client_tx {
|
|
GBinderClient* client;
|
|
guint32 code;
|
|
guint32 flags;
|
|
GBinderLocalRequest* req;
|
|
GBinderClientReplyFunc reply;
|
|
GDestroyNotify destroy;
|
|
void* user_data;
|
|
} TestGBinderClientTx;
|
|
|
|
typedef struct test_gbinder_client_iface_range {
|
|
char* iface;
|
|
guint32 last_code;
|
|
} TestGBinderClientIfaceRange;
|
|
|
|
struct gbinder_client {
|
|
guint32 refcount;
|
|
GBinderRemoteObject* remote;
|
|
TestGBinderClientIfaceRange* ranges;
|
|
guint nr;
|
|
};
|
|
|
|
static
|
|
void
|
|
test_gbinder_client_free(
|
|
GBinderClient* self)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < self->nr; i++) {
|
|
g_free(self->ranges[i].iface);
|
|
}
|
|
g_free(self->ranges);
|
|
gbinder_remote_object_unref(self->remote);
|
|
g_free(self);
|
|
}
|
|
|
|
static
|
|
void
|
|
test_gbinder_client_init_range(
|
|
TestGBinderClientIfaceRange* r,
|
|
const GBinderClientIfaceInfo* info)
|
|
{
|
|
r->iface = g_strdup(info->iface);
|
|
r->last_code = info->last_code;
|
|
}
|
|
|
|
static
|
|
int
|
|
test_gbinder_client_sort_ranges(
|
|
const void* p1,
|
|
const void* p2)
|
|
{
|
|
const TestGBinderClientIfaceRange* r1 = p1;
|
|
const TestGBinderClientIfaceRange* r2 = p2;
|
|
|
|
return (r1->last_code < r2->last_code) ? (-1) :
|
|
(r1->last_code > r2->last_code) ? 1 : 0;
|
|
}
|
|
|
|
static
|
|
const TestGBinderClientIfaceRange*
|
|
test_gbinder_client_find_range(
|
|
GBinderClient* self,
|
|
guint32 code)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < self->nr; i++) {
|
|
const TestGBinderClientIfaceRange* r = self->ranges + i;
|
|
|
|
if (r->last_code >= code) {
|
|
return r;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
GBinderRemoteReply*
|
|
test_gbinder_client_transact(
|
|
GBinderClient* self,
|
|
guint32 code,
|
|
guint32 flags,
|
|
GBinderLocalRequest* req,
|
|
int* status)
|
|
{
|
|
GBinderLocalObject* obj = test_gbinder_remote_object_to_local(self->remote);
|
|
GBinderRemoteRequest* remote_req = test_gbinder_remote_request_new(req);
|
|
GBinderLocalReply* reply = test_gbinder_local_object_handle_tx(obj,
|
|
remote_req, code, flags, status);
|
|
GBinderRemoteReply* remote_reply = test_gbinder_remote_reply_new(reply);
|
|
|
|
gbinder_remote_request_unref(remote_req);
|
|
gbinder_local_reply_unref(reply);
|
|
return remote_reply;
|
|
}
|
|
|
|
static
|
|
GBinderRemoteReply*
|
|
test_gbinder_client_transact_sync(
|
|
GBinderClient* self,
|
|
guint32 code,
|
|
guint32 flags,
|
|
GBinderLocalRequest* req,
|
|
int* status)
|
|
{
|
|
GBinderRemoteReply* reply = NULL;
|
|
|
|
if (self) {
|
|
GBinderRemoteObject* obj = self->remote;
|
|
|
|
if (!test_gbinder_remote_object_dead(obj)) {
|
|
GBinderLocalRequest* tmp = NULL;
|
|
|
|
if (!req) {
|
|
const TestGBinderClientIfaceRange* r =
|
|
test_gbinder_client_find_range(self, code);
|
|
|
|
if (r) {
|
|
req = tmp = test_gbinder_local_request_new(r->iface);
|
|
}
|
|
}
|
|
if (req) {
|
|
reply = test_gbinder_client_transact(self, code, flags, req,
|
|
status);
|
|
}
|
|
gbinder_local_request_unref(tmp);
|
|
} else {
|
|
GDEBUG("Refusing to perform transaction with a dead object");
|
|
}
|
|
}
|
|
return reply;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
test_gbinder_client_tx_handle(
|
|
gpointer data)
|
|
{
|
|
int status = -1;
|
|
TestGBinderClientTx* tx = data;
|
|
GBinderRemoteReply* reply = test_gbinder_client_transact
|
|
(tx->client, tx->code, tx->flags, tx->req, &status);
|
|
|
|
tx->reply(tx->client, reply, status, tx->user_data);
|
|
gbinder_remote_reply_unref(reply);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static
|
|
void
|
|
test_gbinder_client_tx_destroy(
|
|
gpointer data)
|
|
{
|
|
TestGBinderClientTx* tx = data;
|
|
|
|
if (tx->destroy) {
|
|
tx->destroy(tx->user_data);
|
|
}
|
|
gbinder_local_request_unref(tx->req);
|
|
gbinder_client_unref(tx->client);
|
|
g_free(tx);
|
|
}
|
|
|
|
/*==========================================================================*
|
|
* libgbinder API
|
|
*==========================================================================*/
|
|
|
|
GBinderClient*
|
|
gbinder_client_new2(
|
|
GBinderRemoteObject* remote,
|
|
const GBinderClientIfaceInfo* ifaces,
|
|
gsize count)
|
|
{
|
|
if (remote) {
|
|
GBinderClient* self = g_new0(GBinderClient, 1);
|
|
|
|
g_atomic_int_set(&self->refcount, 1);
|
|
self->remote = gbinder_remote_object_ref(remote);
|
|
if (count > 0) {
|
|
gsize i;
|
|
|
|
self->nr = count;
|
|
self->ranges = g_new(TestGBinderClientIfaceRange, self->nr);
|
|
for (i = 0; i < count; i++) {
|
|
test_gbinder_client_init_range(self->ranges + i, ifaces + i);
|
|
}
|
|
qsort(self->ranges, count, sizeof(TestGBinderClientIfaceRange),
|
|
test_gbinder_client_sort_ranges);
|
|
} else {
|
|
/* No interface info */
|
|
self->nr = 1;
|
|
self->ranges = g_new0(TestGBinderClientIfaceRange, 1);
|
|
self->ranges[0].last_code = UINT_MAX;
|
|
}
|
|
return self;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GBinderClient*
|
|
gbinder_client_ref(
|
|
GBinderClient* self)
|
|
{
|
|
if (self) {
|
|
g_assert_cmpint(self->refcount, > ,0);
|
|
g_atomic_int_inc(&self->refcount);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
void
|
|
gbinder_client_unref(
|
|
GBinderClient* self)
|
|
{
|
|
if (self) {
|
|
g_assert_cmpint(self->refcount, > ,0);
|
|
if (g_atomic_int_dec_and_test(&self->refcount)) {
|
|
test_gbinder_client_free(self);
|
|
}
|
|
}
|
|
}
|
|
|
|
GBinderLocalRequest*
|
|
gbinder_client_new_request2(
|
|
GBinderClient* self,
|
|
guint32 code)
|
|
{
|
|
if (self) {
|
|
const TestGBinderClientIfaceRange* r =
|
|
test_gbinder_client_find_range(self, code);
|
|
|
|
if (r) {
|
|
return test_gbinder_local_request_new(r->iface);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GBinderRemoteReply*
|
|
gbinder_client_transact_sync_reply(
|
|
GBinderClient* self,
|
|
guint32 code,
|
|
GBinderLocalRequest* req,
|
|
int* status)
|
|
{
|
|
return test_gbinder_client_transact_sync(self, code, 0, req, status);
|
|
}
|
|
|
|
int
|
|
gbinder_client_transact_sync_oneway(
|
|
GBinderClient* self,
|
|
guint32 code,
|
|
GBinderLocalRequest* req)
|
|
{
|
|
int status = -1;
|
|
|
|
g_assert(!test_gbinder_client_transact_sync(self, code,
|
|
GBINDER_TX_FLAG_ONEWAY, req, &status));
|
|
return status;
|
|
}
|
|
|
|
gulong
|
|
gbinder_client_transact(
|
|
GBinderClient* self,
|
|
guint32 code,
|
|
guint32 flags,
|
|
GBinderLocalRequest* req,
|
|
GBinderClientReplyFunc reply,
|
|
GDestroyNotify destroy,
|
|
void* user_data)
|
|
{
|
|
gulong id = 0;
|
|
|
|
if (self) {
|
|
GBinderRemoteObject* obj = self->remote;
|
|
|
|
if (!test_gbinder_remote_object_dead(obj)) {
|
|
GBinderLocalRequest* tmp = NULL;
|
|
|
|
if (!req) {
|
|
const TestGBinderClientIfaceRange* r =
|
|
test_gbinder_client_find_range(self, code);
|
|
|
|
if (r) {
|
|
req = tmp = test_gbinder_local_request_new(r->iface);
|
|
}
|
|
}
|
|
if (req) {
|
|
TestGBinderClientTx* tx = g_new0(TestGBinderClientTx, 1);
|
|
|
|
tx->client = gbinder_client_ref(self);
|
|
tx->code = code;
|
|
tx->flags = flags;
|
|
tx->req = gbinder_local_request_ref(req);
|
|
tx->reply = reply;
|
|
tx->destroy = destroy;
|
|
tx->user_data = user_data;
|
|
id = g_idle_add_full(G_PRIORITY_DEFAULT,
|
|
test_gbinder_client_tx_handle, tx,
|
|
test_gbinder_client_tx_destroy);
|
|
}
|
|
gbinder_local_request_unref(tmp);
|
|
} else {
|
|
GDEBUG("Refusing to perform transaction with a dead object");
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void
|
|
gbinder_client_cancel(
|
|
GBinderClient* self,
|
|
gulong id)
|
|
{
|
|
g_source_remove((guint)id);
|
|
}
|
|
|
|
/*
|
|
* Local Variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|