Compare commits

...

42 Commits

Author SHA1 Message Date
Slava Monich
7f72021075 Version 1.1.13 2021-10-28 14:33:18 +03:00
Slava Monich
846037073b Merge pull request #77 from monich/noprepare
Simplify writer a bit
2021-10-28 14:23:48 +03:00
Slava Monich
f0b55886b2 [gbinder] Simplify writer a bit. JB#42956 2021-10-28 02:25:55 +03:00
Slava Monich
165d386436 [test] Added ashmem-test. JB#42956 2021-10-27 19:53:00 +03:00
Slava Monich
a07e0f2a99 [gbinder] Add gbinder_writer_append_hidl_string_copy(). JB#42956 2021-10-22 20:59:08 +03:00
Slava Monich
bc172346e0 [gbinder] Drop pkgconfig requirement for devel package. JB#42956 2021-10-10 05:21:16 +03:00
Slava Monich
5801ed4f15 [gbinder] Added gbinder_writer_strdup(). JB#42956 2021-10-10 05:02:06 +03:00
Slava Monich
e8c5a0c0bb Version 1.1.12 2021-09-24 18:02:37 +03:00
Slava Monich
94b74ee948 Housekeeping 2021-09-24 17:58:42 +03:00
Slava Monich
1bfacab88d [test] Add binder-call to debian build. JB#55084 2021-09-23 12:47:57 +03:00
Slava Monich
704e7d011c Merge pull request #68 from krnlyng/jb55084
Add binder-call
2021-09-23 12:23:16 +03:00
Frajo Haider
3a4ae9a716 [test] Add binder-call. JB#55084 2021-09-23 11:18:00 +03:00
Slava Monich
5ddc9d94d6 Version 1.1.11 2021-09-02 12:34:24 +03:00
Slava Monich
159708d829 Merge pull request #72 from monich/transact_2way
Serialize execution of /ipc/transact_2way test
2021-09-02 12:23:40 +03:00
Slava Monich
5753cdab1a Merge pull request #74 from monich/musl
Threading fixes
2021-09-02 12:18:55 +03:00
Slava Monich
d4ea1261eb [gbinder] Fix occasional crashes in pthread_setname_np(). JB#42956
gbinder_ipc_looper_thread() may be invoked before looper->thread is set
by pthread_create(). This does happen quite often on musl-based systems.
2021-09-02 02:26:36 +03:00
Slava Monich
7e3ac0a761 [gbinder] Fix potential deadlock in gbinder_ipc_looper_free(). JB#42956
Joining self is never a good idea. This deadlock does indeed happen
quite often on musl-based systems.
2021-09-02 02:26:36 +03:00
Slava Monich
76494c3e3d [unit] Allow side-by-side linking with libglibutil 2021-09-02 02:14:47 +03:00
Slava Monich
dfbc8acd9e [unit] Serialize execution of /ipc/transact_2way 2021-08-31 19:34:16 +03:00
Slava Monich
158d33db5a [debian] Bump libglibutil requirement 2021-08-31 17:53:31 +03:00
Slava Monich
4b11769895 Acknowledge Bart's contribution 2021-08-31 17:50:33 +03:00
Slava Monich
4da96dd0b5 Merge pull request #70 from PureTryOut/ioctl
[unit] Pull in definition of _IOC_SIZE. JB#42254
2021-08-31 17:44:34 +03:00
Bart Ribbers
d9bfb9e200 Include more definitions of _IOC_SIZE
Continuation of https://github.com/mer-hybris/libgbinder/pull/64
2021-08-31 16:39:09 +02:00
Slava Monich
f088e6d1bb [unit] Make unit tests comptible with glib < 2.36
g_type_init() was required back then, whenever a GObject is touched.
2021-08-30 19:32:21 +03:00
Slava Monich
ca665ad3d1 Version 1.1.10 2021-05-10 03:19:18 +03:00
Slava Monich
6ed9082e69 [build] Tweaked build dependencies 2021-05-10 03:17:12 +03:00
Slava Monich
5332dd3482 [gbinder] Use gutil_memdup() instead of g_memdup()
g_memdup has been deprecated since glib 2.68
2021-05-10 02:49:59 +03:00
Slava Monich
a15f63621e Merge pull request #67 from monich/bc_release
Release dead binder nodes
2021-05-10 02:34:04 +03:00
Slava Monich
e4b5f081bd [gbinder] Release dead binder nodes. JB#54002
One has to be careful about BC_RELEASE and BC_ACQUIRE. Those
increment and decrements strong references to binder nodes in
the driver. Not releasing strong references may have various
severe consequences, from kernel memory leaks to missing death
notifications.
2021-05-09 20:20:19 +03:00
Slava Monich
5fab0bcdaa [gbinder] Housekeeping 2021-05-09 20:12:55 +03:00
Slava Monich
5c1b23f30e Version 1.1.9 2021-04-20 13:01:10 +03:00
Slava Monich
8f720e11de Acknowledge contributions 2021-04-20 13:00:55 +03:00
Slava Monich
3c2cd5d599 Merge pull request #64 from george-hopkins/ioc-size
[gbinder] Include definition of _IOC_SIZE. JB#42254
2021-04-20 12:49:15 +03:00
Slava Monich
8df1e70c0b Version 1.1.8 2021-04-16 19:12:35 +03:00
Slava Monich
3833a36693 Merge pull request #66 from monich/out-of-range
Handle out-of-range transaction codes
2021-04-16 18:54:05 +03:00
Slava Monich
38fd1e6dcb [gbinder] Handle out-of-range transaction codes. JB#42956 2021-04-16 17:04:34 +03:00
Slava Monich
5fa4cd03de Version 1.1.7 2021-03-31 23:12:03 +03:00
Slava Monich
d6c0cdd231 Merge pull request #65 from monich/invoke_later
Drop use of g_main_context_invoke_full
2021-03-31 23:01:58 +03:00
Slava Monich
c38843d9c9 [gbinder] Dropped use of g_main_context_invoke_full(). JB#53719
Replaced it with gbinder_idle_callback_invoke_later(), which should
work nicely with non-GLib event loops.
2021-03-31 13:29:52 +03:00
Slava Monich
a6a5df963f [unit] Test for gbinder_idle_callback_invoke_later(). JB#53719 2021-03-31 13:29:08 +03:00
Slava Monich
b60960d955 [gbinder] Added gbinder_idle_callback_invoke_later(). JB#53719
Non-cancellable callback mechanism, similar to g_main_context_invoke_full()
but not requiring GMainContext and GLib event loop.
2021-03-31 13:28:50 +03:00
George Hopkins
0adb80a9ed Include definition of _IOC_SIZE 2021-03-18 08:04:33 +01:00
42 changed files with 2480 additions and 194 deletions

View File

@@ -3,3 +3,6 @@ Matti Lehtimäki <matti.lehtimaki@gmail.com>
Franz-Josef Haider <franz.haider@jolla.com>
Juho Hämäläinen <juho.hamalainen@jolla.com>
Andrew Branson <andrew.branson@jolla.com>
Rinigus <rinigus.git@gmail.com>
George Hopkins <george-hopkins@null.net>
Bart Ribbers <bribbers@disroot.org>

View File

@@ -16,7 +16,7 @@
VERSION_MAJOR = 1
VERSION_MINOR = 1
VERSION_RELEASE = 6
VERSION_RELEASE = 13
# Version for pkg-config
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
@@ -157,6 +157,16 @@ DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
COVERAGE_OBJS = $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o)
DEBUG_SO = $(DEBUG_BUILD_DIR)/$(LIB_SO)
RELEASE_SO = $(RELEASE_BUILD_DIR)/$(LIB_SO)
DEBUG_LINK = $(DEBUG_BUILD_DIR)/$(LIB_SYMLINK1)
RELEASE_LINK = $(RELEASE_BUILD_DIR)/$(LIB_SYMLINK1)
DEBUG_DEV_LINK = $(DEBUG_BUILD_DIR)/$(LIB_DEV_SYMLINK)
RELEASE_DEV_LINK = $(RELEASE_BUILD_DIR)/$(LIB_DEV_SYMLINK)
DEBUG_LIB = $(DEBUG_BUILD_DIR)/$(LIB)
RELEASE_LIB = $(RELEASE_BUILD_DIR)/$(LIB)
COVERAGE_LIB = $(COVERAGE_BUILD_DIR)/$(LIB)
#
# Dependencies
#
@@ -173,20 +183,15 @@ $(DEBUG_OBJS) $(DEBUG_SO): | $(DEBUG_BUILD_DIR) $(DEBUG_DEPS)
$(RELEASE_OBJS) $(RELEASE_SO): | $(RELEASE_BUILD_DIR) $(RELEASE_DEPS)
$(COVERAGE_OBJS) $(COVERAGE_LIB): | $(COVERAGE_BUILD_DIR)
$(DEBUG_LINK): | $(DEBUG_LIB)
$(RELEASE_LINK): | $(RELEASE_LIB)
$(DEBUG_DEV_LINK): | $(DEBUG_LINK)
$(RELEASE_DEV_LINK): | $(RELEASE_LINK)
#
# Rules
#
DEBUG_SO = $(DEBUG_BUILD_DIR)/$(LIB_SO)
RELEASE_SO = $(RELEASE_BUILD_DIR)/$(LIB_SO)
DEBUG_LINK = $(DEBUG_BUILD_DIR)/$(LIB_SYMLINK1)
RELEASE_LINK = $(RELEASE_BUILD_DIR)/$(LIB_SYMLINK1)
DEBUG_DEV_LINK = $(DEBUG_BUILD_DIR)/$(LIB_DEV_SYMLINK)
RELEASE_DEV_LINK = $(RELEASE_BUILD_DIR)/$(LIB_DEV_SYMLINK)
DEBUG_LIB = $(DEBUG_BUILD_DIR)/$(LIB)
RELEASE_LIB = $(RELEASE_BUILD_DIR)/$(LIB)
COVERAGE_LIB = $(COVERAGE_BUILD_DIR)/$(LIB)
debug: $(DEBUG_SO) $(DEBUG_LINK) $(DEBUG_DEV_LINK)
release: $(RELEASE_SO) $(RELEASE_LINK) $(RELEASE_DEV_LINK)

49
debian/changelog vendored
View File

@@ -1,3 +1,52 @@
libgbinder (1.1.13) unstable; urgency=low
* Added gbinder_writer_strdup()
* Added gbinder_writer_append_hidl_string_copy()
* Dropped pkgconfig requirement for devel package
-- Slava Monich <slava.monich@jolla.com> Thu, 28 Oct 2021 14:31:01 +0300
libgbinder (1.1.12) unstable; urgency=low
* Added binder-call test tool
-- Slava Monich <slava.monich@jolla.com> Fri, 24 Sep 2021 16:46:05 +0300
libgbinder (1.1.11) unstable; urgency=low
* Fix potential deadlock in gbinder_ipc_looper_free()
* Fix occasional crashes in pthread_setname_np()
* Fix unit tests on some musl-based systems
* Make unit tests comptible with glib < 2.36
* Bump libglibutil requirement for debian build
-- Slava Monich <slava.monich@jolla.com> Thu, 02 Sep 2021 12:32:39 +0300
libgbinder (1.1.10) unstable; urgency=low
* Release dead binder nodes
* Use gutil_memdup() instead of g_memdup()
-- Slava Monich <slava.monich@jolla.com> Mon, 10 May 2021 02:36:43 +0300
libgbinder (1.1.9) unstable; urgency=low
* Include definition of _IOC_SIZE
-- Slava Monich <slava.monich@jolla.com> Tue, 20 Apr 2021 12:52:41 +0300
libgbinder (1.1.8) unstable; urgency=low
* Handle out-of-range transaction codes
-- Slava Monich <slava.monich@jolla.com> Fri, 16 Apr 2021 19:11:14 +0300
libgbinder (1.1.7) unstable; urgency=low
* Dropped use of g_main_context_invoke_full()
-- Slava Monich <slava.monich@jolla.com> Wed, 31 Mar 2021 23:10:37 +0300
libgbinder (1.1.6) unstable; urgency=low
* Implemented support for passing object over the bridge

4
debian/control vendored
View File

@@ -2,13 +2,13 @@ Source: libgbinder
Section: libs
Priority: optional
Maintainer: Slava Monich <slava.monich@jolla.com>
Build-Depends: debhelper (>= 8.1.3), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.35)
Build-Depends: debhelper (>= 8.1.3), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.52), flex, bison
Standards-Version: 3.8.4
Package: libgbinder
Section: libs
Architecture: any
Depends: libglibutil (>= 1.0.35), ${shlibs:Depends}, ${misc:Depends}
Depends: libglibutil (>= 1.0.52), ${shlibs:Depends}, ${misc:Depends}
Description: Binder client library
Package: libgbinder-dev

2
debian/rules vendored
View File

@@ -9,12 +9,14 @@ LIBDIR=usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
override_dh_auto_build:
dh_auto_build -- LIBDIR=$(LIBDIR) release pkgconfig debian/libgbinder.install debian/libgbinder-dev.install
dh_auto_build -- -C test/binder-bridge release
dh_auto_build -- -C test/binder-call release
dh_auto_build -- -C test/binder-list release
dh_auto_build -- -C test/binder-ping release
override_dh_auto_install:
dh_auto_install -- LIBDIR=$(LIBDIR) install-dev
dh_auto_install -- -C test/binder-bridge
dh_auto_install -- -C test/binder-call
dh_auto_install -- -C test/binder-list
dh_auto_install -- -C test/binder-ping

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 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:
*
@@ -14,8 +14,8 @@
* 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.
* 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
@@ -121,13 +121,13 @@ gbinder_writer_append_fd(
gsize
gbinder_writer_bytes_written(
GBinderWriter* writer); /* since 1.0.21 */
GBinderWriter* writer); /* Since 1.0.21 */
void
gbinder_writer_overwrite_int32(
GBinderWriter* writer,
gsize offset,
gint32 value); /* since 1.0.21 */
gint32 value); /* Since 1.0.21 */
guint
gbinder_writer_append_buffer_object_with_parent(
@@ -147,13 +147,18 @@ gbinder_writer_append_hidl_vec(
GBinderWriter* writer,
const void* base,
guint count,
guint elemsize); /* since 1.0.8 */
guint elemsize); /* Since 1.0.8 */
void
gbinder_writer_append_hidl_string(
GBinderWriter* writer,
const char* str);
void
gbinder_writer_append_hidl_string_copy(
GBinderWriter* writer,
const char* str); /* Since 1.1.13 */
void
gbinder_writer_append_hidl_string_vec(
GBinderWriter* writer,
@@ -174,17 +179,19 @@ void
gbinder_writer_append_byte_array(
GBinderWriter* writer,
const void* byte_array,
gint32 len); /* since 1.0.12 */
gint32 len); /* Since 1.0.12 */
/* Note: memory allocated by GBinderWriter is owned by GBinderWriter */
void*
gbinder_writer_malloc(
GBinderWriter* writer,
gsize size); /* since 1.0.19 */
gsize size); /* Since 1.0.19 */
void*
gbinder_writer_malloc0(
GBinderWriter* writer,
gsize size); /* since 1.0.19 */
gsize size); /* Since 1.0.19 */
#define gbinder_writer_new(writer,type) \
((type*) gbinder_writer_malloc(writer, sizeof(type)))
@@ -192,17 +199,22 @@ gbinder_writer_malloc0(
#define gbinder_writer_new0(writer,type) \
((type*) gbinder_writer_malloc0(writer, sizeof(type)))
void*
gbinder_writer_memdup(
GBinderWriter* writer,
const void* buf,
gsize size); /* since 1.0.19 */
void
gbinder_writer_add_cleanup(
GBinderWriter* writer,
GDestroyNotify destroy,
gpointer data); /* since 1.0.19 */
gpointer data); /* Since 1.0.19 */
void*
gbinder_writer_memdup(
GBinderWriter* writer,
const void* buf,
gsize size); /* Since 1.0.19 */
char*
gbinder_writer_strdup(
GBinderWriter* writer,
const char* str); /* Since 1.1.13 */
G_END_DECLS

View File

@@ -1,15 +1,19 @@
Name: libgbinder
Version: 1.1.6
Version: 1.1.13
Release: 0
Summary: Binder client library
License: BSD
URL: https://github.com/mer-hybris/libgbinder
Source: %{name}-%{version}.tar.bz2
%define libglibutil_version 1.0.49
%define libglibutil_version 1.0.52
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version}
BuildRequires: pkgconfig
BuildRequires: bison
BuildRequires: flex
Requires: libglibutil >= %{libglibutil_version}
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
@@ -20,7 +24,6 @@ C interfaces for Android binder
%package devel
Summary: Development library for %{name}
Requires: %{name} = %{version}
Requires: pkgconfig
%description devel
This package contains the development library for %{name}.
@@ -29,10 +32,11 @@ This package contains the development library for %{name}.
%setup -q
%build
make LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
make %{_smp_mflags} LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
make -C test/binder-bridge KEEP_SYMBOLS=1 release
make -C test/binder-list KEEP_SYMBOLS=1 release
make -C test/binder-ping KEEP_SYMBOLS=1 release
make -C test/binder-call KEEP_SYMBOLS=1 release
%install
rm -rf %{buildroot}
@@ -40,6 +44,7 @@ make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev
make -C test/binder-bridge DESTDIR=%{buildroot} install
make -C test/binder-list DESTDIR=%{buildroot} install
make -C test/binder-ping DESTDIR=%{buildroot} install
make -C test/binder-call DESTDIR=%{buildroot} install
%check
make -C unit test
@@ -72,3 +77,4 @@ Binder command line utilities
%{_bindir}/binder-bridge
%{_bindir}/binder-list
%{_bindir}/binder-ping
%{_bindir}/binder-call

View File

@@ -203,9 +203,15 @@ gbinder_client_transact_sync_reply2(
req = r->basic_req;
}
}
return api->sync_reply(obj->ipc, obj->handle, code, req, status);
if (req) {
return api->sync_reply(obj->ipc, obj->handle, code, req,
status);
} else {
GWARN("Unable to build empty request for tx code %u", code);
}
} else {
GDEBUG("Refusing to perform transaction with a dead object");
}
GDEBUG("Refusing to perform transaction with a dead object");
}
return NULL;
}
@@ -230,10 +236,15 @@ gbinder_client_transact_sync_oneway2(
req = r->basic_req;
}
}
return api->sync_oneway(obj->ipc, obj->handle, code, req);
if (req) {
return api->sync_oneway(obj->ipc, obj->handle, code, req);
} else {
GWARN("Unable to build empty request for tx code %u", code);
}
} else {
GDEBUG("Refusing to perform transaction with a dead object");
return (-ESTALE);
}
GDEBUG("Refusing to perform transaction with a dead object");
return (-ESTALE);
}
return (-EINVAL);
}
@@ -407,13 +418,6 @@ gbinder_client_transact(
GBinderRemoteObject* obj = self->remote;
if (G_LIKELY(!obj->dead)) {
GBinderClientTx* tx = g_slice_new0(GBinderClientTx);
tx->client = gbinder_client_ref(self);
tx->reply = reply;
tx->destroy = destroy;
tx->user_data = user_data;
if (!req) {
const GBinderClientIfaceRange* r = gbinder_client_find_range
(gbinder_client_cast(self), code);
@@ -423,12 +427,22 @@ gbinder_client_transact(
req = r->basic_req;
}
}
if (req) {
GBinderClientTx* tx = g_slice_new0(GBinderClientTx);
return gbinder_ipc_transact(obj->ipc, obj->handle, code,
flags, req, gbinder_client_transact_reply,
gbinder_client_transact_destroy, tx);
tx->client = gbinder_client_ref(self);
tx->reply = reply;
tx->destroy = destroy;
tx->user_data = user_data;
return gbinder_ipc_transact(obj->ipc, obj->handle, code,
flags, req, gbinder_client_transact_reply,
gbinder_client_transact_destroy, tx);
} else {
GWARN("Unable to build empty request for tx code %u", code);
}
} else {
GDEBUG("Refusing to perform transaction with a dead object");
}
GDEBUG("Refusing to perform transaction with a dead object");
}
return 0;
}

View File

@@ -61,6 +61,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/ioctl.h>
/* BINDER_VM_SIZE copied from native/libs/binder/ProcessState.cpp */
#define BINDER_VM_SIZE ((1024*1024) - sysconf(_SC_PAGE_SIZE)*2)
@@ -674,12 +675,11 @@ gbinder_driver_handle_command(
} else if (cmd == io->br.transaction) {
gbinder_driver_handle_transaction(self, context, data);
} else if (cmd == io->br.dead_binder) {
guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE];
guint64 handle = 0;
GBinderRemoteObject* obj;
io->decode_cookie(data, &handle);
GVERBOSE("> BR_DEAD_BINDER %llu", (long long unsigned int)handle);
GVERBOSE("> BR_DEAD_BINDER 0x%08llx", (long long unsigned int) handle);
obj = gbinder_object_registry_get_remote(reg, (guint32)handle,
REMOTE_REGISTRY_DONT_CREATE);
if (obj) {
@@ -687,13 +687,23 @@ gbinder_driver_handle_command(
gbinder_remote_object_handle_death_notification(obj);
gbinder_remote_object_unref(obj);
} else {
guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE];
/* This shouldn't normally happen. Just send the same data back. */
GVERBOSE("< BC_DEAD_BINDER_DONE %llu", (long long unsigned int)
GVERBOSE("< BC_DEAD_BINDER_DONE 0x%08llx", (long long unsigned int)
handle);
gbinder_driver_cmd_data(self, io->bc.dead_binder_done, data, buf);
}
} else if (cmd == io->br.clear_death_notification_done) {
GVERBOSE("> BR_CLEAR_DEATH_NOTIFICATION_DONE");
#if GUTIL_LOG_VERBOSE
if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) {
guint64 handle = 0;
io->decode_cookie(data, &handle);
GVERBOSE("> BR_CLEAR_DEATH_NOTIFICATION_DONE 0x%08llx",
(long long unsigned int) handle);
}
#endif /* GUTIL_LOG_VERBOSE */
} else {
#pragma message("TODO: handle more commands from the driver")
GWARN("Unexpected command 0x%08x", cmd);
@@ -1025,7 +1035,7 @@ gbinder_driver_dead_binder_done(
write.ptr = (uintptr_t)buf;
write.size = 4 + io->encode_cookie(data + 1, obj->handle);
GVERBOSE("< BC_DEAD_BINDER_DONE %u", obj->handle);
GVERBOSE("< BC_DEAD_BINDER_DONE 0x%08x", obj->handle);
return gbinder_driver_write(self, &write) >= 0;
} else {
return FALSE;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2020-2021 Jolla Ltd.
* Copyright (C) 2020-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -34,6 +34,13 @@
#include <gutil_macros.h>
typedef struct gbinder_idle_callback_data {
GBinderEventLoopCallback* cb;
GBinderEventLoopCallbackFunc func;
GDestroyNotify destroy;
gpointer data;
} GBinderIdleCallbackData;
#define GBINDER_DEFAULT_EVENTLOOP (&gbinder_eventloop_glib)
static const GBinderEventLoopIntegration gbinder_eventloop_glib;
@@ -220,6 +227,36 @@ static const GBinderEventLoopIntegration gbinder_eventloop_glib = {
gbinder_eventloop_glib_cleanup
};
/*==========================================================================*
* Implementation
*==========================================================================*/
static
void
gbinder_idle_callback_invoke_proc(
void* user_data)
{
GBinderIdleCallbackData* idle = user_data;
if (idle->func) {
idle->func(idle->data);
}
gbinder_idle_callback_unref(idle->cb);
}
static
void
gbinder_idle_callback_invoke_done(
void* user_data)
{
GBinderIdleCallbackData* idle = user_data;
if (idle->destroy) {
idle->destroy(idle->data);
}
gutil_slice_free(idle);
}
/*==========================================================================*
* Internal interface
*==========================================================================*/
@@ -325,6 +362,27 @@ gbinder_idle_callback_destroy(
}
}
/* Non-cancellable callback */
void
gbinder_idle_callback_invoke_later(
GBinderEventLoopCallbackFunc func,
gpointer data,
GDestroyNotify destroy)
{
GBinderIdleCallbackData* idle = g_slice_new(GBinderIdleCallbackData);
idle->func = func;
idle->data = data;
idle->destroy = destroy;
idle->cb = gbinder_idle_callback_new(gbinder_idle_callback_invoke_proc,
idle, gbinder_idle_callback_invoke_done);
gbinder_idle_callback_schedule(idle->cb);
}
/*==========================================================================*
* Public interface
*==========================================================================*/
void
gbinder_eventloop_set(
const GBinderEventLoopIntegration* loop)

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2020-2021 Jolla Ltd.
* Copyright (C) 2020-2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -41,12 +41,14 @@ gbinder_timeout_add(
guint millis,
GSourceFunc func,
gpointer data)
G_GNUC_WARN_UNUSED_RESULT
GBINDER_INTERNAL;
GBinderEventLoopTimeout*
gbinder_idle_add(
GSourceFunc func,
gpointer data)
G_GNUC_WARN_UNUSED_RESULT
GBINDER_INTERNAL;
void
@@ -59,6 +61,7 @@ gbinder_idle_callback_new(
GBinderEventLoopCallbackFunc func,
gpointer data,
GDestroyNotify destroy)
G_GNUC_WARN_UNUSED_RESULT
GBINDER_INTERNAL;
GBinderEventLoopCallback*
@@ -66,6 +69,7 @@ gbinder_idle_callback_schedule_new(
GBinderEventLoopCallbackFunc func,
gpointer data,
GDestroyNotify destroy)
G_GNUC_WARN_UNUSED_RESULT
GBINDER_INTERNAL;
GBinderEventLoopCallback*
@@ -93,6 +97,13 @@ gbinder_idle_callback_destroy(
GBinderEventLoopCallback* cb)
GBINDER_INTERNAL;
void
gbinder_idle_callback_invoke_later(
GBinderEventLoopCallbackFunc func,
gpointer data,
GDestroyNotify destroy)
GBINDER_INTERNAL;
#endif /* GBINDER_EVENTLOOP_PRIVATE_H */
/*

View File

@@ -429,7 +429,7 @@ void
gbinder_ipc_looper_free(
GBinderIpcLooper* looper)
{
if (!looper->joined) {
if (!looper->joined && looper->thread != pthread_self()) {
pthread_join(looper->thread, NULL);
}
close(looper->pipefd[0]);
@@ -766,13 +766,13 @@ gbinder_ipc_looper_thread(
GBinderIpcLooper* looper = data;
GBinderDriver* driver = looper->driver;
g_mutex_lock(&looper->mutex);
pthread_setname_np(looper->thread, looper->name);
if (gbinder_driver_enter_looper(driver)) {
struct pollfd pipefd;
int res;
GDEBUG("Looper %s running", looper->name);
g_mutex_lock(&looper->mutex);
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
@@ -837,7 +837,6 @@ gbinder_ipc_looper_thread(
GDEBUG("Looper %s is abandoned", looper->name);
}
} else {
g_mutex_lock(&looper->mutex);
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
@@ -869,6 +868,7 @@ gbinder_ipc_looper_new(
g_atomic_int_set(&looper->refcount, 1);
g_cond_init(&looper->start_cond);
g_mutex_init(&looper->mutex);
g_mutex_lock(&looper->mutex);
looper->name = g_strdup_printf("%s#%u", gbinder_ipc_name(ipc), id);
looper->handler.f = &handler_functions;
looper->ipc = ipc;
@@ -877,11 +877,13 @@ gbinder_ipc_looper_new(
looper)) {
/* gbinder_ipc_looper_thread() will release this reference: */
gbinder_ipc_looper_ref(looper);
g_mutex_unlock(&looper->mutex);
GDEBUG("Starting looper %s", looper->name);
return looper;
} else {
GERR("Failed to create looper thread %s", looper->name);
}
g_mutex_unlock(&looper->mutex);
gbinder_ipc_looper_unref(looper);
} else {
GERR("Failed to create looper pipe: %s", strerror(errno));

View File

@@ -38,6 +38,7 @@
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply_p.h"
#include "gbinder_remote_request.h"
#include "gbinder_eventloop_p.h"
#include "gbinder_writer.h"
#include "gbinder_log.h"
@@ -47,7 +48,6 @@
#include <errno.h>
struct gbinder_local_object_priv {
GMainContext* context;
char** ifaces;
GBinderLocalTransactFunc txproc;
void* user_data;
@@ -280,18 +280,16 @@ static
void
gbinder_local_object_handle_later(
GBinderLocalObject* self,
GSourceFunc function)
GBinderEventLoopCallbackFunc function)
{
if (G_LIKELY(self)) {
GBinderLocalObjectPriv* priv = self->priv;
g_main_context_invoke_full(priv->context, G_PRIORITY_DEFAULT, function,
gbinder_idle_callback_invoke_later(function,
gbinder_local_object_ref(self), g_object_unref);
}
}
static
gboolean
void
gbinder_local_object_increfs_proc(
gpointer user_data)
{
@@ -300,11 +298,10 @@ gbinder_local_object_increfs_proc(
self->weak_refs++;
g_signal_emit(self, gbinder_local_object_signals
[SIGNAL_WEAK_REFS_CHANGED], 0);
return G_SOURCE_REMOVE;
}
static
gboolean
void
gbinder_local_object_decrefs_proc(
gpointer user_data)
{
@@ -314,7 +311,6 @@ gbinder_local_object_decrefs_proc(
self->weak_refs--;
g_signal_emit(self, gbinder_local_object_signals
[SIGNAL_WEAK_REFS_CHANGED], 0);
return G_SOURCE_REMOVE;
}
static
@@ -330,7 +326,7 @@ gbinder_local_object_default_acquire(
}
static
gboolean
void
gbinder_local_object_acquire_proc(
gpointer user_data)
{
@@ -338,7 +334,6 @@ gbinder_local_object_acquire_proc(
GBinderLocalObject* self = data->object;
GBINDER_LOCAL_OBJECT_GET_CLASS(self)->acquire(self);
return G_SOURCE_REMOVE;
}
static
@@ -371,14 +366,13 @@ gbinder_local_object_default_release(
}
static
gboolean
void
gbinder_local_object_release_proc(
gpointer obj)
{
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(obj);
GBINDER_LOCAL_OBJECT_GET_CLASS(self)->release(self);
return G_SOURCE_REMOVE;
}
/*==========================================================================*
@@ -586,7 +580,6 @@ gbinder_local_object_handle_acquire(
GBinderBufferContentsList* bufs)
{
if (G_LIKELY(self)) {
GBinderLocalObjectPriv* priv = self->priv;
GBinderLocalObjectAcquireData* data =
g_slice_new(GBinderLocalObjectAcquireData);
@@ -604,9 +597,8 @@ gbinder_local_object_handle_acquire(
*/
data->object = gbinder_local_object_ref(self);
data->bufs = gbinder_buffer_contents_list_dup(bufs);
g_main_context_invoke_full(priv->context, G_PRIORITY_DEFAULT,
gbinder_local_object_acquire_proc, data,
gbinder_local_object_acquire_done);
gbinder_idle_callback_invoke_later(gbinder_local_object_acquire_proc,
data, gbinder_local_object_acquire_done);
}
}
@@ -629,7 +621,6 @@ gbinder_local_object_init(
GBinderLocalObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectPriv);
priv->context = g_main_context_default();
self->priv = priv;
}

View File

@@ -36,10 +36,10 @@
#include "gbinder_ipc.h"
#include "gbinder_remote_object_p.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_eventloop_p.h"
#include "gbinder_log.h"
struct gbinder_remote_object_priv {
GMainContext* context;
gboolean acquired;
};
@@ -67,15 +67,21 @@ static guint gbinder_remote_object_signals[SIGNAL_COUNT] = { 0 };
static
void
gbinder_remote_object_handle_death_on_main_thread(
GBinderRemoteObject* self)
gpointer user_data)
{
GBinderRemoteObject* self = THIS(user_data);
if (!self->dead) {
GBinderIpc* ipc = self->ipc;
GBinderDriver* driver = ipc->driver;
GBinderRemoteObjectPriv* priv = self->priv;
self->dead = TRUE;
priv->acquired = FALSE;
if (priv->acquired) {
priv->acquired = FALSE;
/* Release the dead node */
gbinder_driver_release(driver, self->handle);
}
/* ServiceManager always has the same handle, and can be reanimated. */
if (self->handle != GBINDER_SERVICEMANAGER_HANDLE) {
gbinder_ipc_invalidate_remote_handle(ipc, self->handle);
@@ -85,15 +91,6 @@ gbinder_remote_object_handle_death_on_main_thread(
}
}
static
gboolean
gbinder_remote_object_death_notification_proc(
gpointer self)
{
gbinder_remote_object_handle_death_on_main_thread(THIS(self));
return G_SOURCE_REMOVE;
}
/*==========================================================================*
* Internal interface
*==========================================================================*/
@@ -135,9 +132,9 @@ gbinder_remote_object_handle_death_notification(
/* This function is invoked from the looper thread, the caller has
* checked the object pointer */
GVERBOSE_("%p %u", self, self->handle);
g_main_context_invoke_full(self->priv->context, G_PRIORITY_DEFAULT,
gbinder_remote_object_death_notification_proc,
gbinder_remote_object_ref(self), g_object_unref);
gbinder_idle_callback_invoke_later
(gbinder_remote_object_handle_death_on_main_thread,
gbinder_remote_object_ref(self), g_object_unref);
}
void
@@ -147,10 +144,16 @@ gbinder_remote_object_commit_suicide(
/* This function is only invoked by GBinderProxyObject in context of
* the main thread, the object pointer is checked by the caller */
if (!self->dead) {
GBinderIpc* ipc = self->ipc;
GBinderDriver* driver = ipc->driver;
GBinderRemoteObjectPriv* priv = self->priv;
self->dead = TRUE;
priv->acquired = FALSE;
if (priv->acquired) {
priv->acquired = FALSE;
/* Release the dead node */
gbinder_driver_release(driver, self->handle);
}
GVERBOSE_("%p %u", self, self->handle);
gbinder_ipc_invalidate_remote_handle(self->ipc, self->handle);
/* Don't submit BC_DEAD_BINDER_DONE because this is a suicide */
@@ -267,7 +270,6 @@ gbinder_remote_object_init(
GBinderRemoteObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
THIS_TYPE, GBinderRemoteObjectPriv);
priv->context = g_main_context_default();
self->priv = priv;
}
@@ -294,9 +296,9 @@ gbinder_remote_object_finalize(
if (!self->dead) {
gbinder_driver_clear_death_notification(driver, self);
if (priv->acquired) {
gbinder_driver_release(driver, self->handle);
}
}
if (priv->acquired) {
gbinder_driver_release(driver, self->handle);
}
gbinder_ipc_unref(ipc);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);

View File

@@ -40,6 +40,7 @@
#include <gutil_intarray.h>
#include <gutil_macros.h>
#include <gutil_strv.h>
#include <gutil_misc.h>
#include <unistd.h>
#include <stdint.h>
@@ -154,30 +155,6 @@ gbinder_writer_data_record_offset(
gutil_int_array_append(data->offsets, offset);
}
static
void
gbinder_writer_data_write_buffer_object(
GBinderWriterData* data,
const void* ptr,
gsize size,
const GBinderParent* parent)
{
GByteArray* buf = data->bytes;
const guint offset = buf->len;
guint n;
/* Preallocate enough space */
g_byte_array_set_size(buf, offset + GBINDER_MAX_BUFFER_OBJECT_SIZE);
/* Write the object */
n = data->io->encode_buffer_object(buf->data + offset, ptr, size, parent);
/* Fix the data size */
g_byte_array_set_size(buf, offset + n);
/* Record the offset */
gbinder_writer_data_record_offset(data, offset);
/* The driver seems to require each buffer to be 8-byte aligned */
data->buffers_size += G_ALIGN8(size);
}
void
gbinder_writer_init(
GBinderWriter* self,
@@ -580,17 +557,6 @@ gbinder_writer_append_bytes(
}
}
static
guint
gbinder_writer_data_prepare(
GBinderWriterData* data)
{
if (!data->offsets) {
data->offsets = gutil_int_array_new();
}
return data->offsets->count;
}
static
void
gbinder_writer_data_close_fd(
@@ -681,9 +647,21 @@ gbinder_writer_data_append_buffer_object(
gsize size,
const GBinderParent* parent)
{
guint index = gbinder_writer_data_prepare(data);
const guint index = data->offsets ? data->offsets->count : 0;
GByteArray* buf = data->bytes;
const guint offset = buf->len;
guint n;
gbinder_writer_data_write_buffer_object(data, ptr, size, parent);
/* Preallocate enough space */
g_byte_array_set_size(buf, offset + GBINDER_MAX_BUFFER_OBJECT_SIZE);
/* Write the object */
n = data->io->encode_buffer_object(buf->data + offset, ptr, size, parent);
/* Fix the data size */
g_byte_array_set_size(buf, offset + n);
/* Record the offset */
gbinder_writer_data_record_offset(data, offset);
/* The driver seems to require each buffer to be 8-byte aligned */
data->buffers_size += G_ALIGN8(size);
return index;
}
@@ -699,6 +677,21 @@ gbinder_writer_append_hidl_string(
}
}
void
gbinder_writer_append_hidl_string_copy(
GBinderWriter* self,
const char* str) /* Since 1.1.13 */
{
GBinderWriterData* data = gbinder_writer_data(self);
if (G_LIKELY(data)) {
static const char empty[] = "";
gbinder_writer_data_append_hidl_string(data, str ? (str[0] ?
gbinder_writer_strdup(self, str) : empty) : NULL);
}
}
void
gbinder_writer_data_append_hidl_vec(
GBinderWriterData* data,
@@ -709,11 +702,7 @@ gbinder_writer_data_append_hidl_vec(
GBinderParent vec_parent;
GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
const gsize total = count * elemsize;
void* buf = g_memdup(base, total);
/* Prepare parent descriptor for the string data */
vec_parent.index = gbinder_writer_data_prepare(data);
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
void* buf = gutil_memdup(base, total);
/* Fill in the vector descriptor */
if (buf) {
@@ -725,8 +714,10 @@ gbinder_writer_data_append_hidl_vec(
data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, vec);
/* Every vector, even the one without data, requires two buffer objects */
gbinder_writer_data_write_buffer_object(data, vec, sizeof(*vec), NULL);
gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
vec_parent.index = gbinder_writer_data_append_buffer_object(data,
vec, sizeof(*vec), NULL);
gbinder_writer_data_append_buffer_object(data, buf, total, &vec_parent);
}
void
@@ -752,10 +743,6 @@ gbinder_writer_data_append_hidl_string(
GBinderHidlString* hidl_string = g_new0(GBinderHidlString, 1);
const gsize len = str ? strlen(str) : 0;
/* Prepare parent descriptor for the string data */
str_parent.index = gbinder_writer_data_prepare(data);
str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
/* Fill in the string descriptor and store it */
hidl_string->data.str = str;
hidl_string->len = len;
@@ -763,17 +750,18 @@ gbinder_writer_data_append_hidl_string(
data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, hidl_string);
/* Write the buffer object pointing to the string descriptor */
gbinder_writer_data_write_buffer_object(data, hidl_string,
sizeof(*hidl_string), NULL);
str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
str_parent.index = gbinder_writer_data_append_buffer_object(data,
hidl_string, sizeof(*hidl_string), NULL);
if (str) {
/* Write the buffer pointing to the string data including the
* NULL terminator, referencing string descriptor as a parent. */
gbinder_writer_data_write_buffer_object(data, str, len+1, &str_parent);
gbinder_writer_data_append_buffer_object(data, str, len+1, &str_parent);
GVERBOSE_("\"%s\" %u %u %u", str, (guint)len, (guint)str_parent.index,
(guint)data->buffers_size);
} else {
gbinder_writer_data_write_buffer_object(data, NULL, 0, &str_parent);
gbinder_writer_data_append_buffer_object(data, NULL, 0, &str_parent);
}
}
@@ -806,10 +794,6 @@ gbinder_writer_data_append_hidl_string_vec(
count = gutil_strv_length((char**)strv);
}
/* Prepare parent descriptor for the vector data */
vec_parent.index = gbinder_writer_data_prepare(data);
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
/* Fill in the vector descriptor */
if (count > 0) {
strings = g_new0(GBinderHidlString, count);
@@ -832,7 +816,10 @@ gbinder_writer_data_append_hidl_string_vec(
}
/* Write the vector object */
gbinder_writer_data_write_buffer_object(data, vec, sizeof(*vec), NULL);
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
vec_parent.index = gbinder_writer_data_append_buffer_object(data,
vec, sizeof(*vec), NULL);
if (strings) {
GBinderParent str_parent;
@@ -841,7 +828,7 @@ gbinder_writer_data_append_hidl_string_vec(
str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
/* Write the vector data (it's parent for the string data) */
gbinder_writer_data_write_buffer_object(data, strings,
gbinder_writer_data_append_buffer_object(data, strings,
sizeof(*strings) * count, &vec_parent);
/* Write the string data */
@@ -849,7 +836,7 @@ gbinder_writer_data_append_hidl_string_vec(
GBinderHidlString* hidl_str = strings + i;
if (hidl_str->data.str) {
gbinder_writer_data_write_buffer_object(data,
gbinder_writer_data_append_buffer_object(data,
hidl_str->data.str, hidl_str->len + 1, &str_parent);
GVERBOSE_("%d. \"%s\" %u %u %u", i + 1, hidl_str->data.str,
(guint)hidl_str->len, (guint)str_parent.index,
@@ -857,13 +844,13 @@ gbinder_writer_data_append_hidl_string_vec(
} else {
GVERBOSE_("%d. NULL %u %u %u", i + 1, (guint)hidl_str->len,
(guint)str_parent.index, (guint)data->buffers_size);
gbinder_writer_data_write_buffer_object(data, NULL, 0,
gbinder_writer_data_append_buffer_object(data, NULL, 0,
&str_parent);
}
str_parent.offset += sizeof(GBinderHidlString);
}
} else {
gbinder_writer_data_write_buffer_object(data, NULL, 0, &vec_parent);
gbinder_writer_data_append_buffer_object(data, NULL, 0, &vec_parent);
}
}
@@ -994,6 +981,14 @@ gbinder_writer_malloc0(
return gbinder_writer_alloc(self, size, g_malloc0, g_free);
}
char*
gbinder_writer_strdup(
GBinderWriter* writer,
const char* str) /* since 1.1.13 */
{
return str ? gbinder_writer_memdup(writer, str, strlen(str) + 1) : NULL;
}
void*
gbinder_writer_memdup(
GBinderWriter* self,

View File

@@ -8,4 +8,5 @@ all:
@$(MAKE) -C binder-list $*
@$(MAKE) -C binder-ping $*
@$(MAKE) -C binder-service $*
@$(MAKE) -C binder-call $*
@$(MAKE) -C rild-card-status $*

140
test/ashmem-test/Makefile Normal file
View File

@@ -0,0 +1,140 @@
# -*- Mode: makefile-gmake -*-
.PHONY: all debug release clean cleaner
.PHONY: libgbinder-release libgbinder-debug
#
# Required packages
#
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
#
# Default target
#
all: debug release
#
# Executable
#
EXE = ashmem-test
#
# Sources
#
SRC = $(EXE).c
#
# Directories
#
SRC_DIR = .
BUILD_DIR = build
LIB_DIR = ../..
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
#
# Tools and flags
#
CC ?= $(CROSS_COMPILE)gcc
LD = $(CC)
WARNINGS = -Wall
INCLUDES = -I$(LIB_DIR)/include
BASE_FLAGS = -fPIC
CFLAGS = $(BASE_FLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \
$(shell pkg-config --cflags $(PKGS))
LDFLAGS = $(BASE_FLAGS) $(shell pkg-config --libs $(PKGS))
QUIET_MAKE = make --no-print-directory
DEBUG_FLAGS = -g
RELEASE_FLAGS =
ifndef KEEP_SYMBOLS
KEEP_SYMBOLS = 0
endif
ifneq ($(KEEP_SYMBOLS),0)
RELEASE_FLAGS += -g
SUBMAKE_OPTS += KEEP_SYMBOLS=1
endif
DEBUG_LDFLAGS = $(LDFLAGS) $(DEBUG_FLAGS)
RELEASE_LDFLAGS = $(LDFLAGS) $(RELEASE_FLAGS)
DEBUG_CFLAGS = $(CFLAGS) $(DEBUG_FLAGS) -DDEBUG
RELEASE_CFLAGS = $(CFLAGS) $(RELEASE_FLAGS) -O2
#
# Files
#
DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
DEBUG_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_so)
RELEASE_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_so)
DEBUG_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_link)
RELEASE_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_link)
DEBUG_SO = $(LIB_DIR)/$(DEBUG_SO_FILE)
RELEASE_SO = $(LIB_DIR)/$(RELEASE_SO_FILE)
#
# Dependencies
#
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(DEPS)),)
-include $(DEPS)
endif
endif
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
#
# Rules
#
DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE)
RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE)
debug: libgbinder-debug $(DEBUG_EXE)
release: libgbinder-release $(RELEASE_EXE)
clean:
rm -f *~
rm -fr $(BUILD_DIR)
cleaner: clean
@make -C $(LIB_DIR) clean
$(DEBUG_BUILD_DIR):
mkdir -p $@
$(RELEASE_BUILD_DIR):
mkdir -p $@
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_EXE): $(DEBUG_SO) $(DEBUG_BUILD_DIR) $(DEBUG_OBJS)
$(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) $< -o $@
$(RELEASE_EXE): $(RELEASE_SO) $(RELEASE_BUILD_DIR) $(RELEASE_OBJS)
$(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) $< -o $@
ifeq ($(KEEP_SYMBOLS),0)
strip $@
endif
libgbinder-debug:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(DEBUG_SO_FILE) $(DEBUG_LINK_FILE)
libgbinder-release:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)

View File

@@ -0,0 +1,260 @@
/*
* 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 <gbinder.h>
#include <gutil_misc.h>
#include <gutil_log.h>
#include <sys/mman.h>
#define RET_OK (0)
#define RET_NOTFOUND (1)
#define RET_INVARG (2)
#define RET_ERR (3)
#define DEFAULT_BINDER GBINDER_DEFAULT_HWBINDER
#define ALLOCATOR_IFACE "android.hidl.allocator@1.0::IAllocator"
#define DEFAULT_FQNAME ALLOCATOR_IFACE "/ashmem"
#define TX_ALLOCATE GBINDER_FIRST_CALL_TRANSACTION
typedef struct app_options {
const char* fqname;
char* dev;
gsize size;
} AppOptions;
static
void
app_dumpmem(
const GBinderHidlMemory* mem)
{
const GBinderFds* fds = mem->data.fds;
GDEBUG("Name: %s", mem->name.data.str);
GDEBUG("Size: %" G_GUINT64_FORMAT " bytes", mem->size);
GASSERT(fds->version == GBINDER_HIDL_FDS_VERSION);
GDEBUG("Contains %u fd(s)", fds->num_fds);
if (fds->num_fds) {
guint i;
for (i = 0; i < fds->num_fds; i++) {
int fd = gbinder_fds_get_fd(fds, i);
guint8* ptr = mmap(NULL, mem->size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (ptr) {
gsize off = 0;
GDEBUG("fd %d => %p", fd, ptr);
while (off < mem->size) {
char line[GUTIL_HEXDUMP_BUFSIZE];
guint n = gutil_hexdump(line, ptr + off, mem->size - off);
GDEBUG("%04X: %s", (uint) off, line);
off += n;
}
munmap(ptr, mem->size);
} else {
GDEBUG("fd %d", fd);
}
}
}
}
static
int
app_allocate(
const AppOptions* opt,
GBinderClient* client)
{
GBinderLocalRequest* request = gbinder_client_new_request(client);
GBinderRemoteReply* reply;
int status, ret;
gbinder_local_request_append_int64(request, opt->size);
reply = gbinder_client_transact_sync_reply(client, TX_ALLOCATE,
request, &status);
if (reply) {
GBinderReader reader;
gint32 tx_status;
gboolean success;
gbinder_remote_reply_init_reader(reply, &reader);
if (gbinder_reader_read_int32(&reader, &tx_status) &&
gbinder_reader_read_bool(&reader, &success) &&
tx_status == GBINDER_STATUS_OK &&
success) {
const GBinderHidlMemory* mem = gbinder_reader_read_hidl_struct
(&reader, GBinderHidlMemory);
if (mem) {
GINFO("OK");
app_dumpmem(mem);
} else {
GINFO("OOPS");
}
} else {
GINFO("FAILED");
}
ret = RET_OK;
} else {
GERR("Call failed (%d)", status);
ret = RET_ERR;
}
gbinder_local_request_unref(request);
gbinder_remote_reply_unref(reply);
return ret;
}
static
int
app_run(
const AppOptions* opt)
{
int ret = RET_NOTFOUND;
GBinderServiceManager* sm = gbinder_servicemanager_new(opt->dev);
if (sm) {
int status = 0;
GBinderRemoteObject* remote = gbinder_servicemanager_get_service_sync
(sm, opt->fqname, &status);
if (remote) {
GBinderClient* client = gbinder_client_new(remote, ALLOCATOR_IFACE);
ret = app_allocate(opt, client);
gbinder_client_unref(client);
} else {
GERR("%s not found", opt->fqname);
}
gbinder_servicemanager_unref(sm);
} else {
GERR("No servicemanager at %s", opt->dev);
}
return ret;
}
static
gboolean
app_log_verbose(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = GLOG_LEVEL_VERBOSE;
return TRUE;
}
static
gboolean
app_log_quiet(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = GLOG_LEVEL_NONE;
return TRUE;
}
static
gboolean
app_init(
AppOptions* opt,
int argc,
char* argv[])
{
gboolean ok = FALSE;
GOptionEntry entries[] = {
{ "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_verbose, "Enable verbose output", NULL },
{ "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_quiet, "Be quiet", NULL },
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
"Binder device [" DEFAULT_BINDER "]", "DEVICE" },
{ NULL }
};
GError* error = NULL;
GOptionContext* options = g_option_context_new("[FQNAME]");
gutil_log_timestamp = FALSE;
gutil_log_default.level = GLOG_LEVEL_DEFAULT;
g_option_context_add_main_entries(options, entries, NULL);
if (g_option_context_parse(options, &argc, &argv, &error)) {
if (!opt->dev || !opt->dev[0]) {
opt->dev = g_strdup(DEFAULT_BINDER);
}
if (argc < 3) {
opt->fqname = ((argc == 2) ? argv[1] : DEFAULT_FQNAME);
opt->size = 64;
ok = TRUE;
} else {
char* help = g_option_context_get_help(options, TRUE, NULL);
fprintf(stderr, "%s", help);
g_free(help);
}
} else {
GERR("%s", error->message);
g_error_free(error);
}
g_option_context_free(options);
return ok;
}
int main(int argc, char* argv[])
{
AppOptions opt;
int ret = RET_INVARG;
memset(&opt, 0, sizeof(opt));
if (app_init(&opt, argc, argv)) {
ret = app_run(&opt);
}
g_free(opt.dev);
return ret;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

178
test/binder-call/Makefile Normal file
View File

@@ -0,0 +1,178 @@
# -*- Mode: makefile-gmake -*-
.PHONY: all debug release install clean cleaner
.PHONY: libgbinder-release libgbinder-debug
#
# Required packages
#
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
#
# Default target
#
all: debug release
#
# Executable
#
EXE = binder-call
#
# Sources
#
SRC = $(EXE).c
GEN_SRC = \
cmdline.tab.c \
lex.cmdline.c
#
# Directories
#
SRC_DIR = .
BUILD_DIR = build
GEN_DIR = $(BUILD_DIR)
LIB_DIR = ../..
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
#
# Tools and flags
#
CC ?= $(CROSS_COMPILE)gcc
LD = $(CC)
WARNINGS = -Wall
INCLUDES = -I$(LIB_DIR)/include -I$(GEN_DIR) -I$(SRC_DIR)
BASE_FLAGS = -fPIC
CFLAGS = $(BASE_FLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \
$(shell pkg-config --cflags $(PKGS))
LDFLAGS = $(BASE_FLAGS) $(shell pkg-config --libs $(PKGS))
QUIET_MAKE = make --no-print-directory
DEBUG_FLAGS = -g
RELEASE_FLAGS =
KEEP_SYMBOLS ?= 0
ifneq ($(KEEP_SYMBOLS),0)
RELEASE_FLAGS += -g
SUBMAKE_OPTS += KEEP_SYMBOLS=1
endif
DEBUG_LDFLAGS = $(LDFLAGS) $(DEBUG_FLAGS)
RELEASE_LDFLAGS = $(LDFLAGS) $(RELEASE_FLAGS)
DEBUG_CFLAGS = $(CFLAGS) $(DEBUG_FLAGS) -DDEBUG
RELEASE_CFLAGS = $(CFLAGS) $(RELEASE_FLAGS) -O2
#
# Files
#
DEBUG_OBJS = \
$(GEN_SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) \
$(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
RELEASE_OBJS = \
$(GEN_SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) \
$(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
DEBUG_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_so)
RELEASE_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_so)
DEBUG_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_link)
RELEASE_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_link)
DEBUG_SO = $(LIB_DIR)/$(DEBUG_SO_FILE)
RELEASE_SO = $(LIB_DIR)/$(RELEASE_SO_FILE)
GEN_FILES = $(GEN_SRC:%=$(GEN_DIR)/%)
.PRECIOUS: $(GEN_FILES)
#
# Dependencies
#
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(DEPS)),)
-include $(DEPS)
endif
endif
$(GEN_FILES): | $(GEN_DIR)
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
#
# Rules
#
DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE)
RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE)
debug: libgbinder-debug $(DEBUG_EXE)
release: libgbinder-release $(RELEASE_EXE)
clean:
rm -f *~
rm -fr $(BUILD_DIR)
cleaner: clean
@make -C $(LIB_DIR) clean
$(DEBUG_BUILD_DIR):
mkdir -p $@
$(RELEASE_BUILD_DIR):
mkdir -p $@
$(GEN_DIR):
mkdir -p $@
$(GEN_DIR)/%.tab.c : $(SRC_DIR)/%.y
bison -pcmdline -bcmdline -d -o $@ $<
$(GEN_DIR)/lex.%.c : $(SRC_DIR)/%.l
flex -o $@ $<
$(DEBUG_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_EXE): $(DEBUG_SO) $(DEBUG_BUILD_DIR) $(DEBUG_OBJS)
$(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) $< -o $@
$(RELEASE_EXE): $(RELEASE_SO) $(RELEASE_BUILD_DIR) $(RELEASE_OBJS)
$(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) $< -o $@
ifeq ($(KEEP_SYMBOLS),0)
strip $@
endif
libgbinder-debug:
@$(MAKE) $(SUBMAKE_OPTS) -C $(LIB_DIR) $(DEBUG_SO_FILE) $(DEBUG_LINK_FILE)
libgbinder-release:
@$(MAKE) $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)
#
# Install
#
INSTALL = install
INSTALL_BIN_DIR = $(DESTDIR)/usr/bin
install: release $(INSTALL_BIN_DIR)
$(INSTALL) -m 755 $(RELEASE_EXE) $(INSTALL_BIN_DIR)
$(INSTALL_BIN_DIR):
$(INSTALL) -d $@

View File

@@ -0,0 +1,814 @@
/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Franz-Josef Haider <franz.haider@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.h>
#include <gutil_log.h>
#include <unistd.h>
#include <binder-call.h>
#include <stdlib.h>
#define RET_OK (0)
#define RET_NOTFOUND (1)
#define RET_INVARG (2)
#define RET_ERR (3)
#define DEFAULT_DEVICE GBINDER_DEFAULT_BINDER
#define GBINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4)
#define GBINDER_INTERFACE_TRANSACTION GBINDER_TRANSACTION('N','T','F')
static const char pname[] = "binder-call";
struct transaction_and_reply* ast;
enum transaction_pass {
COMPUTE_SIZES = 0,
FILL_BUFFERS,
BUILD_TRANSACTION
};
enum reply_pass {
PRINT_REPLY = 0,
COMPUTE_SIZES_REPLY
};
static
int
go_through_transaction_ast(
App* app,
GList* node_list,
int parent_idx,
void* buf,
enum transaction_pass cur_pass,
int cont_offset)
{
GList* l;
int offset = cont_offset;
for (l = node_list; l; l = l->next) {
struct value_info* v = l->data;
switch(v->type) {
case INT8_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int8");
if (parent_idx == -1) {
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((unsigned char*)(((char*)buf)+offset)) =
*((unsigned char*)v->value);
}
offset++;
break;
case INT32_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int32");
if (parent_idx == -1) {
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((int*)(((char*)buf)+offset)) = *((int*)v->value);
}
offset += sizeof(gint32);
break;
case INT64_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int64");
if (parent_idx == -1) {
gbinder_writer_append_int64(&app->writer, *((gint64*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((gint64*)(((char*)buf)+offset)) = *((gint64*)v->value);
}
offset += sizeof(gint64);
break;
case FLOAT_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("float");
if (parent_idx == -1) {
gbinder_writer_append_float(&app->writer, *((float*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((float*)(((char*)buf)+offset)) = *((float*)v->value);
}
offset += sizeof(float);
break;
case DOUBLE_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("double");
if (parent_idx == -1) {
gbinder_writer_append_double(&app->writer,*((double*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((double*)(((char*)buf)+offset)) = *((double*)v->value);
}
offset += sizeof(double);
break;
case STRING8_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string8");
gbinder_writer_append_string8(&app->writer, v->value);
/* offset not incremented since it only makes sense for hidl */
break;
case STRING16_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string16");
gbinder_writer_append_string16(&app->writer, v->value);
/* offset not incremented since it only makes sense for hidl */
break;
case HSTRING_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("hstring");
if (parent_idx == -1) {
gbinder_writer_append_hidl_string(&app->writer, v->value);
} else {
GBinderHidlString* hidl_str = (GBinderHidlString*)
(((char*)buf)+offset);
if (cur_pass == FILL_BUFFERS) {
hidl_str->data.str = v->value;
hidl_str->len = strlen(v->value);
hidl_str->owns_buffer = TRUE;
} else if (cur_pass == BUILD_TRANSACTION) {
GBinderParent p;
p.index = parent_idx;
p.offset = offset;
gbinder_writer_append_buffer_object_with_parent
(&app->writer, hidl_str->data.str, hidl_str->len+1, &p);
}
}
offset += sizeof(GBinderHidlString);
break;
case STRUCT_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("struct");
if (!app->opt->aidl) {
if (parent_idx == -1) {
int s = go_through_transaction_ast(app, v->value, 0,
NULL, COMPUTE_SIZES, 0);
void* new_buf = gbinder_writer_malloc(&app->writer, s);
int new_parent_idx;
go_through_transaction_ast(app, v->value, 0, new_buf,
FILL_BUFFERS, 0);
new_parent_idx = gbinder_writer_append_buffer_object
(&app->writer, new_buf, s);
/*
* if parent_idx == -1 there is no need to update the
* offset, since we are processing the argument list
* and are not inside an argument.
*/
go_through_transaction_ast(app, v->value,
new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
} else {
if (cur_pass == FILL_BUFFERS) {
/* fill struct mode */
offset += go_through_transaction_ast(app,
v->value, 0, ((char*)buf)+offset, cur_pass, 0);
} else if (cur_pass == BUILD_TRANSACTION) {
int s = go_through_transaction_ast(app,
v->value, 0, NULL, COMPUTE_SIZES, 0);
go_through_transaction_ast(app, v->value, 0,
buf, FILL_BUFFERS, offset);
go_through_transaction_ast(app, v->value, parent_idx,
buf, BUILD_TRANSACTION, offset);
offset += s;
} else if (cur_pass == COMPUTE_SIZES) {
offset += go_through_transaction_ast(app,
v->value, 0, NULL, cur_pass, 0);
}
}
} else {
go_through_transaction_ast(app, v->value, -1, NULL,
cur_pass, 0);
}
if (cur_pass == BUILD_TRANSACTION) GDEBUG("structend");
break;
case VECTOR_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vector");
if (!app->opt->aidl) {
if (parent_idx == -1) {
GBinderHidlVec* vec;
int vs = go_through_transaction_ast(app,
v->value, 0, NULL, COMPUTE_SIZES, 0);
int es = go_through_transaction_ast(app,
g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
void* new_buf = gbinder_writer_malloc(&app->writer, vs);
int new_parent_idx;
GBinderParent vec_parent;
go_through_transaction_ast(app, v->value, 0, new_buf,
FILL_BUFFERS, 0);
vec = gbinder_writer_new0(&app->writer, GBinderHidlVec);
vec->data.ptr = new_buf;
vec->count = vs / es;
if (vec->count != g_list_length(v->value)) {
GERR("SEMANTIC ERROR VECTOR");
abort();
}
vec_parent.index = gbinder_writer_append_buffer_object
(&app->writer, vec, sizeof(*vec));
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
new_parent_idx =
gbinder_writer_append_buffer_object_with_parent
(&app->writer, new_buf, vs, &vec_parent);
go_through_transaction_ast(app, v->value,
new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
} else {
if (cur_pass == FILL_BUFFERS) {
/* fill struct mode */
int sl = go_through_transaction_ast(app,
v->value, 0, NULL, COMPUTE_SIZES, 0);
int es = go_through_transaction_ast(app,
g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
void* new_buf = gbinder_writer_malloc(&app->writer, sl);
GBinderHidlVec* vec = (GBinderHidlVec*)
(((char*)buf)+offset);
vec->data.ptr = new_buf;
vec->count = sl / es;
} else if (cur_pass == BUILD_TRANSACTION) {
int new_parent_idx;
int sl = go_through_transaction_ast(app,
v->value, 0, NULL, COMPUTE_SIZES, 0);
GBinderHidlVec* vec = (GBinderHidlVec*)
(((char*)buf)+offset);
GBinderParent p;
void* new_buf = (void*)vec->data.ptr;
go_through_transaction_ast(app, v->value, 0,
new_buf, FILL_BUFFERS, 0);
if (vec->count != g_list_length(v->value)) {
GERR("SEMANTIC ERROR VECTOR");
abort();
}
p.index = parent_idx;
p.offset = offset;
new_parent_idx =
gbinder_writer_append_buffer_object_with_parent
(&app->writer, new_buf, sl, &p);
go_through_transaction_ast(app, v->value,
new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
}
offset += sizeof(GBinderHidlVec);
}
} else {
int vl = g_list_length(v->value);
gbinder_writer_append_int32(&app->writer, vl);
go_through_transaction_ast(app, v->value, -1, NULL,
cur_pass, 0);
}
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vectorend");
break;
default:
GERR("unknown type: %d\n", v->type);
break;
}
}
return offset;
}
static
int
go_through_reply_ast(
App* app,
GList* node_list,
struct type_info* tt,
const void* buf,
enum reply_pass cur_pass)
{
GList* l;
int offset = 0;
for (l = node_list; l || tt; l = l->next) {
struct type_info* t = node_list ? l->data : tt;
switch(t->type) {
case INT8_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("int8");
if (cur_pass != COMPUTE_SIZES_REPLY) {
int val;
if (!buf) {
gbinder_reader_read_int32(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((unsigned char*)(((char*)buf)+offset));
}
printf("%d:8 ", val);
}
offset += 1;
break;
case INT32_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("int32");
if (cur_pass != COMPUTE_SIZES_REPLY) {
gint32 val;
if (!buf) {
gbinder_reader_read_int32(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((gint32*)(((char*)buf)+offset));
}
printf("%" G_GINT32_FORMAT " ", val);
}
offset += sizeof(gint32);
break;
case INT64_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("int64");
if (cur_pass != COMPUTE_SIZES_REPLY) {
gint64 val;
if (!buf) {
gbinder_reader_read_int64(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((gint64*)(((char*)buf)+offset));
}
printf("%" G_GINT64_FORMAT " ", val);
}
offset += sizeof(gint64);
break;
case FLOAT_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("float");
if (cur_pass != COMPUTE_SIZES_REPLY) {
float val;
if (!buf) {
gbinder_reader_read_float(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((float*)(((char*)buf)+offset));
}
printf("%f ", val);
}
offset += sizeof(float);
break;
case DOUBLE_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("double");
if (cur_pass != COMPUTE_SIZES_REPLY) {
double val;
if (!buf) {
gbinder_reader_read_double(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((double*)(((char*)buf)+offset));
}
printf("%lfL ", val);
}
offset += sizeof(double);
break;
case STRING8_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("string8");
printf("\"%s\" ", gbinder_reader_read_string8(&app->reader));
/* offset not incremented since it only makes sense for hidl */
break;
case STRING16_TYPE: {
char* val = gbinder_reader_read_string16(&app->reader);
if (cur_pass == PRINT_REPLY) GDEBUG("string16");
printf("\"%s\"U ", val);
g_free(val);
/* offset not incremented since it only makes sense for hidl */
break;
}
case HSTRING_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("hstring");
if (cur_pass != COMPUTE_SIZES_REPLY) {
char* val = NULL;
if (!buf) {
val = gbinder_reader_read_hidl_string(&app->reader);
} else {
GBinderHidlString* hidl_str = (GBinderHidlString*)
(((char*)buf)+offset);
val = strdup(hidl_str->data.str);
}
printf("\"%s\"H ", val);
g_free(val);
}
offset += sizeof(GBinderHidlString);
break;
case STRUCT_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("struct");
if (!app->opt->aidl) {
if (cur_pass == COMPUTE_SIZES_REPLY) {
offset += go_through_reply_ast(app, t->data, NULL, NULL,
COMPUTE_SIZES_REPLY);
} else {
printf("{ ");
if (!buf) {
int sl = go_through_reply_ast(app, t->data, NULL, NULL,
COMPUTE_SIZES_REPLY);
offset += go_through_reply_ast(app, t->data, NULL,
gbinder_reader_read_hidl_struct1(&app->reader, sl),
PRINT_REPLY);
} else {
offset += go_through_reply_ast(app, t->data, NULL,
((char*)buf) + offset, PRINT_REPLY);
}
printf("} ");
}
} else {
go_through_reply_ast(app, t->data, NULL, NULL, cur_pass);
}
if (cur_pass == PRINT_REPLY) GDEBUG("structend");
break;
case VECTOR_TYPE:
if (cur_pass == PRINT_REPLY) GDEBUG("vector");
if (!app->opt->aidl) {
if (cur_pass != COMPUTE_SIZES_REPLY) {
if (!buf) {
guint i;
gsize count, elemsize;
const void* new_buf = gbinder_reader_read_hidl_vec
(&app->reader, &count, &elemsize);
printf("[ ");
for (i = 0; i < count; i++) {
/* TODO: validate elemsize somehow? */
go_through_reply_ast(app, NULL, t->data,
new_buf + elemsize*i, cur_pass);
}
printf("] ");
} else {
guint i;
gsize count;
GBinderHidlVec* vec = (GBinderHidlVec*)
(((char*)buf) + offset);
int off;
count = vec->count;
printf("[ ");
off = 0;
for (i = 0; i < count; i++) {
off += go_through_reply_ast(app, NULL, t->data,
vec->data.ptr + off, cur_pass);
}
printf("] ");
}
}
offset += sizeof(GBinderHidlVec);
} else {
guint i;
gint32 vl;
gbinder_reader_read_int32(&app->reader, &vl);
printf("[ ");
for (i = 0; i < vl; i++) {
go_through_reply_ast(app, NULL, t->data, NULL, cur_pass);
}
printf("] ");
}
if (cur_pass == PRINT_REPLY) GDEBUG("vectorend");
break;
default:
GERR("unknown type: %d\n", t->type);
break;
}
if (tt) break;
}
return offset;
}
static
void
go_through_ast(
App* app,
struct transaction_and_reply* ast,
gboolean transaction)
{
if (ast) {
if (transaction && ast->tree_transaction) {
go_through_transaction_ast(app, ast->tree_transaction, -1, NULL,
BUILD_TRANSACTION, 0);
} else if (!transaction && ast->tree_reply) {
GDEBUG("REPLY:");
go_through_reply_ast(app, ast->tree_reply, NULL, NULL, PRINT_REPLY);
printf("\n");
}
}
}
static
void
free_ast_transaction_tree(
gpointer data)
{
struct value_info* v = data;
if (v->type == STRUCT_TYPE || v->type == VECTOR_TYPE) {
g_list_free_full(v->value, free_ast_transaction_tree);
} else {
g_free(v->value);
}
g_free(v);
}
static
void
free_ast_reply_tree(
gpointer data)
{
struct type_info* t = data;
if (t->type == VECTOR_TYPE) {
free_ast_reply_tree(t->data);
} else if (t->type == STRUCT_TYPE) {
g_list_free_full(t->data, free_ast_reply_tree);
}
g_free(t);
}
static
void
free_ast(
struct transaction_and_reply* ast)
{
if (ast) {
g_list_free_full(ast->tree_transaction, free_ast_transaction_tree);
g_list_free_full(ast->tree_reply, free_ast_reply_tree);
g_free(ast);
}
}
static
void
app_run(
App* app)
{
const AppOptions* opt = app->opt;
char* iface = opt->iface ? g_strdup(opt->iface) : NULL;
int status = 0;
int rargc = 1;
char* service = opt->argv[rargc++];
int code = atoi(opt->argv[rargc++]);
GBinderClient* client;
GBinderLocalRequest* req;
GBinderRemoteReply* reply;
GBinderRemoteObject* obj;
if (!code) {
GERR("Transaction code must be > GBINDER_FIRST_CALL_TRANSACTION(=1).");
return;
}
obj = gbinder_servicemanager_get_service_sync(app->sm,
service, &status);
if (!obj) {
GERR("No such service: %s", service);
g_free(iface);
return;
}
if (strstr(service, "/") != NULL) {
iface = g_strndup(service, strchr(service, '/') - service);
} else {
GBinderReader reader;
client = gbinder_client_new(obj, NULL);
req = gbinder_client_new_request(client);
reply = gbinder_client_transact_sync_reply(client,
GBINDER_INTERFACE_TRANSACTION, req, &status);
gbinder_remote_reply_init_reader(reply, &reader);
iface = gbinder_reader_read_string16(&reader);
gbinder_local_request_unref(req);
gbinder_remote_reply_unref(reply);
gbinder_client_unref(client);
}
if (!iface) {
GERR("Failed to get interface");
return;
}
GDEBUG("Got iface: %s", iface);
client = gbinder_client_new(obj, iface);
g_free(iface);
req = gbinder_client_new_request(client);
app->rargc = rargc;
app->code = code;
cmdline_parse(app);
gbinder_local_request_init_writer(req, &app->writer);
go_through_ast(app, ast, TRUE);
if (opt->oneway) {
gbinder_client_transact_sync_oneway(client, code, req);
reply = NULL;
} else {
reply = gbinder_client_transact_sync_reply(client, code, req, &status);
}
gbinder_local_request_unref(req);
if (!reply) {
printf("NO REPLY\n");
} else {
if (ast && !ast->tree_reply) {
guchar b;
gbinder_remote_reply_init_reader(reply, &app->reader);
printf("TRANSACTION BUFFER: 0x");
while (gbinder_reader_read_byte(&app->reader, &b)) {
printf("%02X", b);
}
printf("\n");
} else {
gbinder_remote_reply_init_reader(reply, &app->reader);
go_through_ast(app, ast, FALSE);
}
gbinder_remote_reply_unref(reply);
}
gbinder_client_unref(client);
free_ast(ast);
}
static
gboolean
app_log_verbose(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = (gutil_log_default.level < GLOG_LEVEL_DEBUG) ?
GLOG_LEVEL_DEBUG : GLOG_LEVEL_VERBOSE;
return TRUE;
}
static
gboolean
app_log_quiet(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = GLOG_LEVEL_ERR;
return TRUE;
}
static
gboolean
app_init(
AppOptions* opt,
int argc,
char* argv[])
{
gboolean ok = FALSE;
GOptionEntry entries[] = {
{ "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_verbose, "Enable verbose output", NULL },
{ "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_quiet, "Be quiet", NULL },
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
"Binder device [" DEFAULT_DEVICE "]", "DEVICE" },
{ "oneway", 'o', 0, G_OPTION_ARG_NONE, &opt->oneway,
"Use a oneway transaction", NULL },
{ "aidl", 'a', 0, G_OPTION_ARG_NONE, &opt->aidl,
"Treat types as aidl types (default: hidl)", NULL },
{ NULL }
};
GError* error = NULL;
GOptionContext* options = g_option_context_new("NAME CODE [[VALUE1] [VALUE2] ...] [reply [TYPE1] [TYPE2] ...]");
g_option_context_set_description(options,
"Performs binder transactions from the command line.\n\n"
"NAME is the name of the object to call, registered with servicemanager.\n"
"For example \"android.hardware.sensors@1.0::ISensors/default\".\n\n"
"CODE is the transaction id (must be >=1).\n\n"
"Optional transaction arguments follow the transaction code.\n"
"Possible arguments are:\n\n"
"\t[0-9]*:8 for an 8-bit integer\n"
"\t[0-9]* for a 32-bit integer\n"
"\t[0-9]*L for an 64-bit integer\n"
"\t[0-9]*.[0-9]* for a 32-bit float\n"
"\t[0-9]*.[0-9]*L for a 64-bit double\n"
"\t\"[.*]\" for an 8-bit aidl string\n"
"\t\"[.*]\"L for an utf16 aidl string\n"
"\t\"[.*]\"h for an 8-bit hidl string\n"
"\t{ VALUE1 VALUE2 ... VALUEN } for a struct containing VALUE1, VALUE2, etc., where\n"
"\t all of these values can be any of the possible values described here.\n"
"\t[ VALUE1 VALUE2 ... VALUEN ] for a vector of length N containing VALUE1, VALUE2, etc., where\n"
"\t all of these values can be one of the possible VALUES described here.\n"
"\t They must be of the same type.\n\n"
"The structure of the reply follows the \"reply\" keyword.\n"
"The following types are accepted:\n\n"
"\ti8 for an 8-bit integer\n"
"\ti32 for a 32-bit integer\n"
"\ti64 for a 64-bit integer\n"
"\ts8 for an 8-bit aidl string\n"
"\ts16 for an utf16 aidl string\n"
"\thstr for an 8-bit hidl string\n"
"\tf|float for a 32-bit float\n"
"\td|double for a 64-bit double\n"
"\t[ TYPE ] for a vector<TYPE> where TYPE can be any of the possible types decribed here\n"
"\t{ TYPE1 TYPE2 ... TYPEN } for a struct containing TYPE1, TYPE2, etc. where\n"
"\t all of the types can be any of the possible types decribed here.\n\n"
"The following example calls getSensorsList method on \"android.hardware.sensors@1.0::ISensors/default\"\n"
"service:\n\n"
"\tbinder-call -d /dev/hwbinder android.hardware.sensors@1.0::ISensors/default 1 reply i32 \"[ { i32 i32 hstr hstr i32 i32 hstr f f f i32 i32 i32 hstr i32 i32 } ]\"\n");
g_option_context_add_main_entries(options, entries, NULL);
memset(opt, 0, sizeof(*opt));
gutil_log_timestamp = FALSE;
gutil_log_set_type(GLOG_TYPE_STDERR, pname);
gutil_log_default.level = GLOG_LEVEL_DEFAULT;
if (g_option_context_parse(options, &argc, &argv, &error)) {
char* help;
if (argc > 2) {
opt->argc = argc;
opt->argv = argv;
ok = TRUE;
} else {
help = g_option_context_get_help(options, TRUE, NULL);
fprintf(stderr, "%s", help);
g_free(help);
}
} else {
GERR("%s", error->message);
g_error_free(error);
}
g_option_context_free(options);
return ok;
}
int main(int argc, char* argv[])
{
App app;
AppOptions opt;
memset(&app, 0, sizeof(app));
app.ret = RET_INVARG;
app.opt = &opt;
if (app_init(&opt, argc, argv)) {
app.sm = gbinder_servicemanager_new(opt.dev);
if (app.sm) {
app_run(&app);
gbinder_servicemanager_unref(app.sm);
} else {
GERR("servicemanager seems to be missing");
}
}
g_free(opt.iface);
g_free(opt.dev);
return app.ret;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2021 Jolla Ltd.
*
* 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 BINDER_CALL_H__
#define BINDER_CALL_H__
#include <gbinder.h>
typedef struct app_options {
char* dev;
char* iface;
gboolean oneway;
gboolean aidl;
gint transaction;
int argc;
char** argv;
} AppOptions;
typedef struct app {
const AppOptions* opt;
GMainLoop* loop;
GBinderServiceManager* sm;
GBinderWriter writer;
GBinderReader reader;
int code;
int rargc;
int ret;
} App;
enum TYPE_INFO {
INT8_TYPE = 0,
INT32_TYPE,
INT64_TYPE,
FLOAT_TYPE,
DOUBLE_TYPE,
STRING8_TYPE,
STRING16_TYPE,
HSTRING_TYPE,
STRUCT_TYPE,
VECTOR_TYPE
};
struct type_info {
enum TYPE_INFO type;
void* data;
};
struct value_info {
enum TYPE_INFO type;
void* value;
};
struct transaction_and_reply {
GList* tree_transaction;
GList* tree_reply;
};
int
cmdline_parse(
App* app);
int
cmdlinelex(
void* args);
extern struct transaction_and_reply* ast;
#endif

View File

@@ -0,0 +1,98 @@
D [0-9]
l "l"
L "L"
u "u"
U "U"
h "h"
H "H"
COLON ":"
EIGHT "8"
INT8_SUFFIX {COLON}{EIGHT}
INT64_SUFFIX [lL]
UTF16_SUFFIX [uU]
HSTRING_SUFFIX [hH]
%{
#include <glib.h>
#include "cmdline.tab.h"
#include "binder-call.h"
#define YY_SKIP_YYWRAP
int cmdlinewrap(App* app);
#undef yywrap
#define yywrap() cmdlinewrap( (App*) args )
#define YY_DECL int cmdlinelex( void* args )
char* handle_str(char* text) {
// extract str from "str"X
char* str = g_strndup(text + 1, strlen(text) - 3);
return str;
}
char* handle_str8(char* text) {
// extract str from "str"
char* str = g_strndup(text + 1, strlen(text) - 2);
return str;
}
%}
%option never-interactive noinput nounput
%%
"i8" { return(INT8); }
"i32" { return(INT32); }
"i64" { return(INT64); }
"s8" { return(STRING8); }
"s16" { return(STRING16); }
"float" { return(FLOAT); }
"double" { return(DOUBLE); }
"f" { return(FLOAT); }
"d" { return(DOUBLE); }
"hstr" { return(HSTRING); }
"{" { return('{'); }
"}" { return('}'); }
"[" { return('['); }
"]" { return(']'); }
{D}*{INT8_SUFFIX} { cmdlinelval.int8_value = atoi(yytext); return(INT8_VALUE); }
{D}*{INT64_SUFFIX} { cmdlinelval.int64_value = atol(yytext); return(INT64_VALUE); }
{D}* { cmdlinelval.int32_value = atoi(yytext); return(INT32_VALUE); }
{D}+"."{D}*{INT64_SUFFIX} { cmdlinelval.double_value = atof(yytext); return(DOUBLE_VALUE); }
{D}+"."{D}* { cmdlinelval.float_value = atof(yytext); return(FLOAT_VALUE); }
"reply" { return(REPLY); }
\".*\"{HSTRING_SUFFIX} { cmdlinelval.hstring_value = handle_str(yytext); return(HSTRING_VALUE); }
\".*\"{UTF16_SUFFIX} { cmdlinelval.string16_value = handle_str(yytext); return(STRING16_VALUE); }
\".*\" { cmdlinelval.string8_value = handle_str8(yytext); return(STRING8_VALUE); }
" " { /* eat */ }
. { fprintf(stderr, "Unrecognized character: '%c'\n", yytext[0]); }
%%
#include "binder-call.h"
int cmdlinewrap(App* app)
{
if (YY_CURRENT_BUFFER) {
yy_delete_buffer( YY_CURRENT_BUFFER );
}
if (app->rargc == app->opt->argc) {
return 1;
}
yy_scan_string(app->opt->argv[app->rargc++]);
return 0;
}
int cmdline_parse(App* app) {
if (app->opt->argc > app->rargc) {
cmdlinewrap(app);
} else {
return 1;
}
return cmdlineparse (app);
}

399
test/binder-call/cmdline.y Normal file
View File

@@ -0,0 +1,399 @@
%{
#include <glib.h>
#include "binder-call.h"
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply);
struct value_info* handle_int8(App* app, int value);
struct value_info* handle_int32(App* app, int value);
struct value_info* handle_int64(App* app, long value);
struct value_info* handle_float(App* app, float value);
struct value_info* handle_double(App* app, double value);
struct value_info* handle_string8(App* app, char* value);
struct value_info* handle_string16(App* app, char* value);
struct value_info* handle_hstring(App* app, char* value);
struct value_info* handle_vector(App* app, GList* values);
struct value_info* handle_struct(App* app, GList* values);
void cmdlineerror(App* app, char const* s);
struct type_info* handle_type_int8(App* app);
struct type_info* handle_type_int32(App* app);
struct type_info* handle_type_int64(App* app);
struct type_info* handle_type_float(App* app);
struct type_info* handle_type_double(App* app);
struct type_info* handle_type_string8(App* app);
struct type_info* handle_type_string16(App* app);
struct type_info* handle_type_hstring(App* app);
struct type_info* handle_type_vector(App* app, struct type_info* t);
struct type_info* handle_type_struct(App* app, GList* l);
%}
%union {
union {
int int8_value;
int int32_value;
long int64_value;
float float_value;
double double_value;
char* string8_value;
char* string16_value;
char* hstring_value;
};
struct value_info* value;
struct type_info* type;
GList* value_list;
GList* type_list;
GList* struct_type_list;
struct transaction_and_reply* trans_and_reply;
}
%parse-param { void* args }
%lex-param { void* args }
%token INT8 INT32 INT64 FLOAT DOUBLE STRING8 STRING16 HSTRING
%token INT8_VALUE INT32_VALUE INT64_VALUE FLOAT_VALUE DOUBLE_VALUE STRING8_VALUE STRING16_VALUE HSTRING_VALUE
%type <value> values
%type <value> struct_values
%type <value> vec_values
%type <value> value_specifiers
%type <trans_and_reply> translation_unit
%type <value_list> values_list
%type <type_list> specifiers_list
%type <type> specifiers
%type <type> type_specifier
%type <type> vec_specifier
%type <type> struct_specifier
%type <struct_type_list> struct_declaration_list
%token REPLY
%start translation_unit
%%
type_specifier
: INT8 { $$ = handle_type_int8(args); }
| INT32 { $$ = handle_type_int32(args); }
| INT64 { $$ = handle_type_int64(args); }
| STRING8 { $$ = handle_type_string8(args); }
| STRING16 { $$ = handle_type_string16(args); }
| FLOAT { $$ = handle_type_float(args); }
| DOUBLE { $$ = handle_type_double(args); }
| HSTRING { $$ = handle_type_hstring(args); }
;
values
: INT8_VALUE { $$ = handle_int8(args, cmdlinelval.int8_value); }
| INT32_VALUE { $$ = handle_int32(args, cmdlinelval.int32_value); }
| INT64_VALUE { $$ = handle_int64(args, cmdlinelval.int64_value); }
| STRING8_VALUE { $$ = handle_string8(args, cmdlinelval.string8_value); }
| STRING16_VALUE { $$ = handle_string16(args, cmdlinelval.string16_value); }
| HSTRING_VALUE { $$ = handle_hstring(args, cmdlinelval.hstring_value); }
| FLOAT_VALUE { $$ = handle_float(args, cmdlinelval.float_value); }
| DOUBLE_VALUE { $$ = handle_double(args, cmdlinelval.double_value); }
;
struct
: '{'
;
struct_end
: '}'
;
vec
: '['
;
vec_end
: ']'
;
struct_specifier
: struct struct_declaration_list struct_end { $$ = handle_type_struct(args, $2); }
;
vec_specifier
: vec specifiers vec_end { $$ = handle_type_vector(args, $2); }
;
struct_declaration_list
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| struct_declaration_list specifiers { $$ = g_list_append($$, $2); }
;
specifiers
: type_specifier
| struct_specifier
| vec_specifier
;
specifiers_list
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| specifiers_list specifiers { $$ = g_list_append($$, $2); }
;
struct_values
: struct values_list struct_end { $$ = handle_struct(args, $2); }
;
vec_values
: vec values_list vec_end { $$ = handle_vector(args, $2); }
;
value_specifiers
: values
| struct_values
| vec_values
;
values_list
: value_specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| values_list value_specifiers { $$ = g_list_append($$, $2); }
;
reply
: REPLY
;
translation_unit
: values_list reply specifiers_list { $$ = make_transaction_and_reply($1, $3); ast = $$; }
| values_list { $$ = make_transaction_and_reply($1, 0); ast = $$; }
| reply specifiers_list { $$ = make_transaction_and_reply(0, $2); ast = $$; }
;
%%
#include <stdio.h>
#include <glib.h>
#include <gutil_log.h>
#include <binder-call.h>
extern char yytext[];
struct value_info* handle_int8(App* app, int value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT8_TYPE;
v->value = g_new0(unsigned char, 1);
* ((unsigned char*)v->value) = value;
return v;
}
struct value_info* handle_int32(App* app, int value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT32_TYPE;
v->value = g_new0(int, 1);
* ((int*)v->value) = value;
return v;
}
struct value_info* handle_int64(App* app, long value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT64_TYPE;
v->value = g_new0(long, 1);
* ((long*)v->value) = value;
return v;
}
struct value_info* handle_float(App* app, float value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = FLOAT_TYPE;
v->value = g_new0(float, 1);
* ((float*)v->value) = value;
return v;
}
struct value_info* handle_double(App* app, double value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = DOUBLE_TYPE;
v->value = g_new0(double, 1);
* ((double*)v->value) = value;
return v;
}
struct value_info* handle_string8(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRING8_TYPE;
v->value = value;
return v;
}
struct value_info* handle_string16(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRING16_TYPE;
v->value = value;
return v;
}
struct value_info* handle_hstring(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = HSTRING_TYPE;
v->value = value;
return v;
}
struct value_info* handle_vector(App* app, GList* values)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = VECTOR_TYPE;
v->value = values;
return v;
}
struct value_info* handle_struct(App* app, GList* values)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRUCT_TYPE;
v->value = values;
return v;
}
struct type_info* handle_type_int8(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT8_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_int32(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT32_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_int64(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT64_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_float(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = FLOAT_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_double(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = DOUBLE_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_string8(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRING8_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_string16(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRING16_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_hstring(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = HSTRING_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_vector(App* app, struct type_info* t)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = VECTOR_TYPE;
info->data = t;
return info;
}
struct type_info* handle_type_struct(App* app, GList* l)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRUCT_TYPE;
info->data = l;
return info;
}
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply)
{
struct transaction_and_reply* tar = g_new0(struct transaction_and_reply, 1);
tar->tree_transaction = transaction;
tar->tree_reply = reply;
return tar;
}
void cmdlineerror(App* app, char const* s)
{
fprintf(stderr, "@%d %s: %s\n", app->rargc - 1, s, app->opt->argv[app->rargc - 1]);
}

View File

@@ -18,7 +18,31 @@ COMMON_SRC ?= test_binder.c test_main.c
# Required packages
#
PKGS += libglibutil glib-2.0 gobject-2.0
PKGS += glib-2.0 gobject-2.0
ifeq ($(LIBGLIBUTIL_PATH),)
# Assume that libglibutil devel package is installed
PKGS += libglibutil
else
# Side-by-side build
INCLUDES += -I$(LIBGLIBUTIL_PATH)/include
DEBUG_LIBS = -L$(LIBGLIBUTIL_PATH)/build/debug -lglibutil
RELEASE_LIBS = -L$(LIBGLIBUTIL_PATH)/build/release -lglibutil
DEBUG_DEPS = libglibutil_debug
RELEASE_DEPS = libglibutil_release
.PHONY: libglibutil_debug libglibutil_release
libglibutil_debug:
make -C $(LIBGLIBUTIL_PATH) debug
libglibutil_release:
make -C $(LIBGLIBUTIL_PATH) release
endif
#
# Default target
@@ -74,9 +98,9 @@ DEBUG_LIB = $(LIB_DIR)/$(DEBUG_LIB_FILE)
RELEASE_LIB = $(LIB_DIR)/$(RELEASE_LIB_FILE)
COVERAGE_LIB = $(LIB_DIR)/$(COVERAGE_LIB_FILE)
DEBUG_LIBS = $(DEBUG_LIB) $(LIBS)
RELEASE_LIBS = $(RELEASE_LIB) $(LIBS)
COVERAGE_LIBS = $(COVERAGE_LIB) $(LIBS)
DEBUG_LIBS += $(DEBUG_LIB) $(LIBS)
RELEASE_LIBS += $(RELEASE_LIB) $(LIBS)
COVERAGE_LIBS += $(COVERAGE_LIB) $(LIBS)
#
# Files
@@ -107,8 +131,8 @@ $(DEBUG_LIB): | debug_lib
$(RELEASE_LIB): | release_lib
$(COVERAGE_LIB): | coverage_lib
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR) $(DEBUG_DEPS)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR) $(RELEASE_DEPS)
$(COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR)
#

View File

@@ -47,6 +47,7 @@ GLOG_MODULE_DEFINE2("test_binder", gutil_log_default);
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <linux/ioctl.h>
#define gettid() ((int)syscall(SYS_gettid))

View File

@@ -1,4 +1,3 @@
#!/bin/bash
#
# This script requires lcov, dirname
@@ -65,9 +64,13 @@ for t in $TESTS ; do
popd
done
# Sometimes you need this, sometimes that :S
BASE_DIR="$TOP_DIR"
#BASE_DIR="$TOP_DIR/src"
FULL_COV="$COV_DIR/full.gcov"
LIB_COV="$COV_DIR/lib.gcov"
rm -f "$FULL_COV" "$LIB_COV"
lcov $LCOV_OPT -c -d "$TOP_DIR/build/coverage" -b "$TOP_DIR/src" -o "$FULL_COV" || exit 1
lcov $LCOV_OPT -e "$FULL_COV" "$TOP_DIR/src/*" -o "$LIB_COV" || exit 1
lcov $LCOV_OPT -c -d "$TOP_DIR/build/coverage" -b "$BASE_DIR" -o "$FULL_COV" || exit 1
lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/*" -o "$LIB_COV" || exit 1
genhtml $GENHTML_OPT "$LIB_COV" -t "libgbinder" --output-directory "$COV_DIR/report" || exit 1

View File

@@ -482,6 +482,9 @@ test_basic(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -139,6 +139,11 @@ test_interfaces(
g_assert_cmpstr(gbinder_client_interface2(client, 33), == ,"33");
g_assert(!gbinder_client_interface2(client, 34));
g_assert(!gbinder_client_new_request2(client, 34));
/* Those fail to allocate default request for out-of-range codes: */
g_assert(!gbinder_client_transact_sync_reply(client, 34, NULL, NULL));
g_assert_cmpint(gbinder_client_transact_sync_oneway(client, 34, NULL),
== ,-EINVAL);
g_assert(!gbinder_client_transact(client, 34, 0, NULL, NULL, NULL, NULL));
gbinder_client_unref(client);
/* Client with no interface info */
@@ -432,6 +437,9 @@ test_reply_ok3(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -48,6 +48,14 @@ test_unreached_proc(
return G_SOURCE_CONTINUE;
}
static
void
test_quit_cb(
gpointer data)
{
g_main_loop_quit((GMainLoop*)data);
}
/*==========================================================================*
* Test event loop integration
*==========================================================================*/
@@ -212,14 +220,6 @@ test_timeout(
* callback
*==========================================================================*/
static
void
test_quit_cb(
gpointer data)
{
g_main_loop_quit((GMainLoop*)data);
}
static
void
test_callback(
@@ -238,6 +238,28 @@ test_callback(
g_main_loop_unref(loop);
}
/*==========================================================================*
* invoke
*==========================================================================*/
static
void
test_invoke(
void)
{
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gbinder_eventloop_set(NULL);
gbinder_idle_callback_invoke_later(test_quit_cb, loop, NULL);
test_run(&test_opt, loop);
gbinder_eventloop_set(NULL);
gbinder_idle_callback_invoke_later(NULL, loop, test_quit_cb);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
@@ -251,6 +273,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_("idle"), test_idle);
g_test_add_func(TEST_("timeout"), test_timeout);
g_test_add_func(TEST_("callback"), test_callback);
g_test_add_func(TEST_("invoke"), test_invoke);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -756,7 +756,7 @@ test_transact_2way_incoming_proc(
static
void
test_transact_2way(
test_transact_2way_run(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
@@ -813,6 +813,14 @@ test_transact_2way(
g_main_loop_unref(loop);
}
static
void
test_transact_2way(
void)
{
test_run_in_context(&test_opt, test_transact_2way_run);
}
/*==========================================================================*
* transact_unhandled
*==========================================================================*/
@@ -1320,6 +1328,9 @@ test_cancel_on_exit(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -815,6 +815,9 @@ test_release(
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_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "basic", test_basic);

View File

@@ -504,6 +504,9 @@ test_remote_reply(
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_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "cleanup", test_cleanup);

View File

@@ -681,6 +681,9 @@ test_obj(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -1,6 +1,6 @@
/*
* 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:
*
@@ -2156,7 +2156,11 @@ int main(int argc, char* argv[])
{
guint i;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
g_type_init();
G_GNUC_END_IGNORE_DEPRECATIONS;
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("empty"), test_empty);
g_test_add_func(TEST_("byte"), test_byte);
g_test_add_func(TEST_("bool"), test_bool);

View File

@@ -148,6 +148,9 @@ test_dead(
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_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "basic", test_basic);

View File

@@ -1190,6 +1190,9 @@ test_add(
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_("null"), test_null);
g_test_add_func(TEST_("invalid"), test_invalid);

View File

@@ -595,6 +595,9 @@ test_notify2()
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);

View File

@@ -435,6 +435,9 @@ test_list()
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);

View File

@@ -464,6 +464,9 @@ test_notify()
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);

View File

@@ -520,6 +520,9 @@ test_cancel(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -477,6 +477,9 @@ test_already_there(
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_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);

View File

@@ -1,6 +1,6 @@
/*
* 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:
*
@@ -84,6 +84,7 @@ test_null(
gbinder_writer_append_bytes(&writer, NULL, 0);
gbinder_writer_append_hidl_vec(NULL, NULL, 0, 0);
gbinder_writer_append_hidl_string(NULL, NULL);
gbinder_writer_append_hidl_string_copy(NULL, NULL);
gbinder_writer_append_hidl_string(&writer, NULL);
gbinder_writer_append_hidl_string_vec(NULL, NULL, 0);
gbinder_writer_append_hidl_string_vec(&writer, NULL, 0);
@@ -107,6 +108,8 @@ test_null(
g_assert(!gbinder_writer_malloc0(NULL, 0));
g_assert(!gbinder_writer_memdup(&writer, NULL, 0));
g_assert(!gbinder_writer_memdup(NULL, &writer, 0));
g_assert(!gbinder_writer_strdup(&writer, NULL));
g_assert(!gbinder_writer_strdup(NULL, ""));
}
/*==========================================================================*
@@ -128,20 +131,24 @@ test_cleanup(
{
GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
GBinderWriter writer;
const int value = 42;
const char* str = "foo";
int cleanup_count = 0;
int value = 42;
int* zero;
int* copy;
char* scopy;
gbinder_local_request_init_writer(req, &writer);
zero = gbinder_writer_new0(&writer, int);
copy = gbinder_writer_memdup(&writer, &value, sizeof(value));
g_assert(*zero == 0);
g_assert(*copy == value);
scopy = gbinder_writer_strdup(&writer, str);
g_assert_cmpint(*zero, == ,0);
g_assert_cmpint(*copy, == ,value);
g_assert_cmpstr(scopy, == ,str);
gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count);
gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count);
gbinder_local_request_unref(req);
g_assert(cleanup_count == 2);
g_assert_cmpint(cleanup_count, == ,2);
}
/*==========================================================================*
@@ -173,11 +180,11 @@ test_int32(
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(value2));
g_assert(!memcmp(data->bytes->data, &value2, data->bytes->len));
// test overlap over the end of the buffer
gbinder_writer_overwrite_int32(&writer, 2, value2);
g_assert(data->bytes->len == sizeof(value2));
gbinder_local_request_unref(req);
}
@@ -551,12 +558,18 @@ static const TestHidlStringData test_hidl_string_tests[] = {
{ "32/null", &gbinder_io_32, NULL,
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
sizeof(GBinderHidlString) },
{ "32/empty", &gbinder_io_32, "",
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ },
{ "32/xxx", &gbinder_io_32, "xxx",
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ },
{ "64/null", &gbinder_io_64, NULL,
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
sizeof(GBinderHidlString) },
{ "64/empty", &gbinder_io_64, "",
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ },
{ "64/xxxxxxx", &gbinder_io_64, "xxxxxxx",
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }
@@ -564,10 +577,10 @@ static const TestHidlStringData test_hidl_string_tests[] = {
static
void
test_hidl_string(
gconstpointer test_data)
test_hidl_string_xxx(
const TestHidlStringData* test,
void (*append)(GBinderWriter* writer, const char* str))
{
const TestHidlStringData* test = test_data;
GBinderLocalRequest* req = gbinder_local_request_new(test->io, NULL);
GBinderOutputData* data;
GBinderWriter writer;
@@ -575,7 +588,7 @@ test_hidl_string(
guint i;
gbinder_local_request_init_writer(req, &writer);
gbinder_writer_append_hidl_string(&writer, test->str);
append(&writer, test->str);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
@@ -587,6 +600,22 @@ test_hidl_string(
gbinder_local_request_unref(req);
}
static
void
test_hidl_string(
gconstpointer test_data)
{
test_hidl_string_xxx(test_data, gbinder_writer_append_hidl_string);
}
static
void
test_hidl_string_copy(
gconstpointer test_data)
{
test_hidl_string_xxx(test_data, gbinder_writer_append_hidl_string_copy);
}
static
void
test_hidl_string2(
@@ -953,7 +982,7 @@ test_bytes_written(
g_assert(gbinder_writer_bytes_written(&writer) == 0);
gbinder_writer_append_int32(&writer, value);
g_assert(gbinder_writer_bytes_written(&writer) == sizeof(value));
gbinder_local_request_unref(req);
}
/*==========================================================================*
@@ -1006,9 +1035,12 @@ int main(int argc, char* argv[])
for (i = 0; i < G_N_ELEMENTS(test_hidl_string_tests); i++) {
const TestHidlStringData* test = test_hidl_string_tests + i;
char* path = g_strconcat(TEST_("hidl_string/"), test->name, NULL);
char* path2 = g_strconcat(TEST_("hidl_string_copy/"), test->name, NULL);
g_test_add_data_func(path, test, test_hidl_string);
g_test_add_data_func(path2, test, test_hidl_string_copy);
g_free(path);
g_free(path2);
}
for (i = 0; i < G_N_ELEMENTS(test_hidl_string_vec_tests); i++) {