Compare commits
	
		
			87 Commits
		
	
	
		
			1.1.7
			...
			upgrade-4.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e196c66264 | ||
| 
						 | 
					8f62f4d65c | ||
| 
						 | 
					8583a72d11 | ||
| 
						 | 
					381446eb4f | ||
| 
						 | 
					eabf5683f4 | ||
| 
						 | 
					f4712be3b5 | ||
| 
						 | 
					08f701c96b | ||
| 
						 | 
					8c5618aaab | ||
| 
						 | 
					1bc2efd724 | ||
| 
						 | 
					066464f3af | ||
| 
						 | 
					f5b399d775 | ||
| 
						 | 
					6e127bd184 | ||
| 
						 | 
					95277d1b3d | ||
| 
						 | 
					e0c56c1226 | ||
| 
						 | 
					5e3df46da9 | ||
| 
						 | 
					393006f051 | ||
| 
						 | 
					e10e3df1eb | ||
| 
						 | 
					86ba34dc67 | ||
| 
						 | 
					b0b5595f20 | ||
| 
						 | 
					757908cd6c | ||
| 
						 | 
					e6ca4b50ec | ||
| 
						 | 
					97831a65da | ||
| 
						 | 
					5798434ead | ||
| 
						 | 
					54b1149a75 | ||
| 
						 | 
					e9f873b3a4 | ||
| 
						 | 
					8b621fe7d3 | ||
| 
						 | 
					e5542027c2 | ||
| 
						 | 
					8539f6e944 | ||
| 
						 | 
					fa53d0cf70 | ||
| 
						 | 
					d6c3ddb4e3 | ||
| 
						 | 
					fd081601a3 | ||
| 
						 | 
					7b319ba822 | ||
| 
						 | 
					d439bd467a | ||
| 
						 | 
					c99df04daf | ||
| 
						 | 
					81b6b7f087 | ||
| 
						 | 
					91bc5253e4 | ||
| 
						 | 
					ddf67e83eb | ||
| 
						 | 
					fc257ec23c | ||
| 
						 | 
					8148f03ae6 | ||
| 
						 | 
					a1616163e7 | ||
| 
						 | 
					7f12f1a476 | ||
| 
						 | 
					f3cf738265 | ||
| 
						 | 
					f8916148a9 | ||
| 
						 | 
					fe2d441e25 | ||
| 
						 | 
					85c7d8f311 | ||
| 
						 | 
					597fb84367 | ||
| 
						 | 
					30242d7124 | ||
| 
						 | 
					14327ed5f1 | ||
| 
						 | 
					36e1fed889 | ||
| 
						 | 
					cecdea76bc | ||
| 
						 | 
					7f72021075 | ||
| 
						 | 
					846037073b | ||
| 
						 | 
					f0b55886b2 | ||
| 
						 | 
					165d386436 | ||
| 
						 | 
					a07e0f2a99 | ||
| 
						 | 
					bc172346e0 | ||
| 
						 | 
					5801ed4f15 | ||
| 
						 | 
					e8c5a0c0bb | ||
| 
						 | 
					94b74ee948 | ||
| 
						 | 
					1bfacab88d | ||
| 
						 | 
					704e7d011c | ||
| 
						 | 
					3a4ae9a716 | ||
| 
						 | 
					5ddc9d94d6 | ||
| 
						 | 
					159708d829 | ||
| 
						 | 
					5753cdab1a | ||
| 
						 | 
					d4ea1261eb | ||
| 
						 | 
					7e3ac0a761 | ||
| 
						 | 
					76494c3e3d | ||
| 
						 | 
					dfbc8acd9e | ||
| 
						 | 
					158d33db5a | ||
| 
						 | 
					4b11769895 | ||
| 
						 | 
					4da96dd0b5 | ||
| 
						 | 
					d9bfb9e200 | ||
| 
						 | 
					f088e6d1bb | ||
| 
						 | 
					ca665ad3d1 | ||
| 
						 | 
					6ed9082e69 | ||
| 
						 | 
					5332dd3482 | ||
| 
						 | 
					a15f63621e | ||
| 
						 | 
					e4b5f081bd | ||
| 
						 | 
					5fab0bcdaa | ||
| 
						 | 
					5c1b23f30e | ||
| 
						 | 
					8f720e11de | ||
| 
						 | 
					3c2cd5d599 | ||
| 
						 | 
					8df1e70c0b | ||
| 
						 | 
					3833a36693 | ||
| 
						 | 
					38fd1e6dcb | ||
| 
						 | 
					0adb80a9ed | 
							
								
								
									
										4
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -3,3 +3,7 @@ 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>
 | 
			
		||||
Gary Wang <gary.wang@canonical.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 | 
			
		||||
You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Makefile
									
									
									
									
									
								
							@@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 1
 | 
			
		||||
VERSION_RELEASE = 7
 | 
			
		||||
VERSION_RELEASE = 18
 | 
			
		||||
 | 
			
		||||
# Version for pkg-config
 | 
			
		||||
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
 | 
			
		||||
@@ -82,6 +82,7 @@ SRC = \
 | 
			
		||||
  gbinder_config.c \
 | 
			
		||||
  gbinder_driver.c \
 | 
			
		||||
  gbinder_eventloop.c \
 | 
			
		||||
  gbinder_fmq.c \
 | 
			
		||||
  gbinder_io_32.c \
 | 
			
		||||
  gbinder_io_64.c \
 | 
			
		||||
  gbinder_ipc.c \
 | 
			
		||||
@@ -103,6 +104,7 @@ SRC += \
 | 
			
		||||
  gbinder_servicemanager.c \
 | 
			
		||||
  gbinder_servicemanager_aidl.c \
 | 
			
		||||
  gbinder_servicemanager_aidl2.c \
 | 
			
		||||
  gbinder_servicemanager_aidl3.c \
 | 
			
		||||
  gbinder_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
SRC += \
 | 
			
		||||
@@ -157,6 +159,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 +185,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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README
									
									
									
									
									
								
							@@ -5,7 +5,7 @@ Key features:
 | 
			
		||||
1. Integration with GLib event loop
 | 
			
		||||
2. Detection of 32 vs 64 bit kernel at runtime
 | 
			
		||||
3. Asynchronous transactions that don't block the event thread
 | 
			
		||||
4. Stable service manager and low-level transation APIs
 | 
			
		||||
4. Stable service manager and low-level transaction APIs
 | 
			
		||||
 | 
			
		||||
Android keeps changing both low-level RPC and service manager
 | 
			
		||||
protocols from version to version. To counter that, libgbinder
 | 
			
		||||
@@ -22,8 +22,8 @@ directory is scanned for .conf files, the file list is sorted, files are
 | 
			
		||||
loaded one by one, overwriting the entries loaded from /etc/gbinder.conf
 | 
			
		||||
or from the previously processed file.
 | 
			
		||||
 | 
			
		||||
Known protocol and service manager variants are aidl, aidl2 and hidl.
 | 
			
		||||
This list is expected to expand further in the future. The default
 | 
			
		||||
Known protocol and service manager variants are aidl, aidl2, aidl3 and
 | 
			
		||||
hidl. This list is expected to expand further in the future. The default
 | 
			
		||||
configuration is as follows:
 | 
			
		||||
 | 
			
		||||
  [Protocol]
 | 
			
		||||
@@ -36,9 +36,11 @@ configuration is as follows:
 | 
			
		||||
  /dev/binder = aidl
 | 
			
		||||
  /dev/hwbinder = hidl
 | 
			
		||||
 | 
			
		||||
Alternatively, one can specify the desired Android API level:
 | 
			
		||||
Alternatively and preferably, one can specify the desired Android API
 | 
			
		||||
level:
 | 
			
		||||
 | 
			
		||||
  [General]
 | 
			
		||||
  ApiLevel = 29
 | 
			
		||||
 | 
			
		||||
and let libgbinder pick the appropriate preset.
 | 
			
		||||
and let libgbinder pick the appropriate preset. Full list of presets can
 | 
			
		||||
be found in src/gbinder_config.c
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										82
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,85 @@
 | 
			
		||||
libgbinder (1.1.18) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Disassociate auto-created proxies to stop them from piling up
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Sat, 08 Jan 2022 15:35:56 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.17) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Don't release remote proxy handle too early (sometimes hever)
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 07 Jan 2022 14:43:51 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.16) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Make sure stale object pointers don't hang around
 | 
			
		||||
  * Properly shut down remote object inside the proxy
 | 
			
		||||
  * Read ref_count from GObject atomically
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 03 Jan 2022 13:58:44 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.15) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added readers and writers for int8 and int16
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 27 Dec 2021 15:13:23 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.14) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Support for FMQ (Fast Message Queues)
 | 
			
		||||
  * Support for Android 11 (API level 30)
 | 
			
		||||
  * Made GBinderReader API slightly more NULL tolerant
 | 
			
		||||
  * Added gbinder_client_rpc_header()
 | 
			
		||||
  * Added gbinder_reader_get_data()
 | 
			
		||||
  * Added gbinder_writer_get_data()
 | 
			
		||||
  * Added gbinder_servicemanager_device()
 | 
			
		||||
  * Added gbinder_local_reply_append_fd()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 24 Nov 2021 17:15:48 +0200
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 | 
			
		||||
You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@
 | 
			
		||||
#include "gbinder_bridge.h"
 | 
			
		||||
#include "gbinder_buffer.h"
 | 
			
		||||
#include "gbinder_client.h"
 | 
			
		||||
#include "gbinder_fmq.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -78,6 +78,11 @@ gbinder_client_interface2(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    guint32 code); /* since 1.0.42 */
 | 
			
		||||
 | 
			
		||||
GBytes*
 | 
			
		||||
gbinder_client_rpc_header(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    guint32 code); /* since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_client_new_request(
 | 
			
		||||
    GBinderClient* client);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										154
									
								
								include/gbinder_fmq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								include/gbinder_fmq.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 GBINDER_FMQ_H
 | 
			
		||||
#define GBINDER_FMQ_H
 | 
			
		||||
 | 
			
		||||
#include <gbinder_types.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
/* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
typedef enum gbinder_fmq_type {
 | 
			
		||||
    GBINDER_FMQ_TYPE_SYNC_READ_WRITE = 1,
 | 
			
		||||
    GBINDER_FMQ_TYPE_UNSYNC_WRITE
 | 
			
		||||
} GBINDER_FMQ_TYPE;
 | 
			
		||||
 | 
			
		||||
typedef enum gbinder_fmq_flags {
 | 
			
		||||
    GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG = 0x1,
 | 
			
		||||
    GBINDER_FMQ_FLAG_NO_RESET_POINTERS    = 0x2
 | 
			
		||||
} GBINDER_FMQ_FLAGS;
 | 
			
		||||
 | 
			
		||||
GBinderFmq*
 | 
			
		||||
gbinder_fmq_new(
 | 
			
		||||
    gsize item_size,
 | 
			
		||||
    gsize max_num_items,
 | 
			
		||||
    GBINDER_FMQ_TYPE type,
 | 
			
		||||
    GBINDER_FMQ_FLAGS flags,
 | 
			
		||||
    gint fd,
 | 
			
		||||
    gsize buffer_size);
 | 
			
		||||
 | 
			
		||||
GBinderFmq*
 | 
			
		||||
gbinder_fmq_ref(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_unref(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
/* Functions for checking how many items are available in queue */
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_read(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_write(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_read_contiguous(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_write_contiguous(
 | 
			
		||||
    GBinderFmq* fmq);
 | 
			
		||||
 | 
			
		||||
/* Functions for obtaining data pointer for zero copy read/write */
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_fmq_begin_read(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_fmq_begin_write(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
/* Functions for ending zero copy read/write
 | 
			
		||||
 * The number of items must match the value provided to gbinder_fmq_begin_read
 | 
			
		||||
 * or gbinder_fmq_begin_write */
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_end_read(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_end_write(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
/* Regular read/write functions (non-zero-copy) */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_fmq_read(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    void* data,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_fmq_write(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    const void* data,
 | 
			
		||||
    gsize items);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Functions for waiting and waking message queue.
 | 
			
		||||
 * Requires configured event flag in message queue.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
gbinder_fmq_wait_timeout(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    guint32 bit_mask,
 | 
			
		||||
    guint32* state,
 | 
			
		||||
    int timeout_ms);
 | 
			
		||||
 | 
			
		||||
#define gbinder_fmq_try_wait(fmq, mask, state) \
 | 
			
		||||
    gbinder_fmq_wait_timeout(fmq, mask, state, 0)
 | 
			
		||||
 | 
			
		||||
#define gbinder_fmq_wait(fmq, mask, state) \
 | 
			
		||||
    gbinder_fmq_wait_timeout(fmq, mask, state, -1)
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_fmq_wake(
 | 
			
		||||
    GBinderFmq* fmq,
 | 
			
		||||
    guint32 bit_mask);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_FMQ_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -13,9 +13,9 @@
 | 
			
		||||
 *   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 name of Jolla Ltd nor the names of its contributors may
 | 
			
		||||
 *      be used to endorse or promote products derived from this software
 | 
			
		||||
 *      without specific prior written permission.
 | 
			
		||||
 *   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
 | 
			
		||||
@@ -112,6 +112,11 @@ gbinder_local_reply_append_remote_object(
 | 
			
		||||
    GBinderLocalReply* reply,
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_append_fd(
 | 
			
		||||
    GBinderLocalReply* reply,
 | 
			
		||||
    int fd); /* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_LOCAL_OBJECT_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -65,6 +65,26 @@ gbinder_reader_read_bool(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gboolean* value);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int8(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gint8* value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_uint8(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint8* value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gint16* value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_uint16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint16* value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int32(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
@@ -201,6 +221,11 @@ gbinder_reader_read_byte_array(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* len); /* Since 1.0.12 */
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_get_data(
 | 
			
		||||
    const GBinderReader* reader,
 | 
			
		||||
    gsize* size); /* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_read(
 | 
			
		||||
    const GBinderReader* reader);
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -112,6 +112,10 @@ void
 | 
			
		||||
gbinder_servicemanager_unref(
 | 
			
		||||
    GBinderServiceManager* sm);
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_servicemanager_device(
 | 
			
		||||
    GBinderServiceManager* sm); /* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_is_present(
 | 
			
		||||
    GBinderServiceManager* sm); /* Since 1.0.25 */
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,7 @@ G_BEGIN_DECLS
 | 
			
		||||
typedef struct gbinder_bridge GBinderBridge; /* Since 1.1.5 */
 | 
			
		||||
typedef struct gbinder_buffer GBinderBuffer;
 | 
			
		||||
typedef struct gbinder_client GBinderClient;
 | 
			
		||||
typedef struct gbinder_fmq GBinderFmq;  /* Since 1.1.14 */
 | 
			
		||||
typedef struct gbinder_ipc GBinderIpc;
 | 
			
		||||
typedef struct gbinder_local_object GBinderLocalObject;
 | 
			
		||||
typedef struct gbinder_local_reply GBinderLocalReply;
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -55,6 +55,16 @@ struct gbinder_parent {
 | 
			
		||||
    guint32 offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int8(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    guint8 value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int16(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    guint16 value); /* Since 1.1.15 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int32(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
@@ -119,15 +129,11 @@ gbinder_writer_append_fd(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    int fd); /* Since 1.0.18 */
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_writer_bytes_written(
 | 
			
		||||
    GBinderWriter* writer); /* since 1.0.21 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_overwrite_int32(
 | 
			
		||||
gbinder_writer_append_fds(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    gint32 value); /* since 1.0.21 */
 | 
			
		||||
    const GBinderFds* fds,
 | 
			
		||||
    const GBinderParent* parent); /* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
gbinder_writer_append_buffer_object_with_parent(
 | 
			
		||||
@@ -147,13 +153,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 +185,39 @@ void
 | 
			
		||||
gbinder_writer_append_byte_array(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const void* byte_array,
 | 
			
		||||
    gint32 len); /* since 1.0.12 */
 | 
			
		||||
    gint32 len); /* Since 1.0.12 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_fmq_descriptor(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const GBinderFmq* queue); /* since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_writer_get_data(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize* size); /* Since 1.1.14 */
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_writer_bytes_written(
 | 
			
		||||
    GBinderWriter* writer); /* Since 1.0.21 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_overwrite_int32(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    gint32 value); /* Since 1.0.21 */
 | 
			
		||||
 | 
			
		||||
/* 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 +225,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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,24 @@
 | 
			
		||||
Name: libgbinder
 | 
			
		||||
Version: 1.1.7
 | 
			
		||||
 | 
			
		||||
Version: 1.1.18
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# license macro requires rpm >= 4.11
 | 
			
		||||
BuildRequires: pkgconfig(rpm)
 | 
			
		||||
%define license_support %(pkg-config --exists 'rpm >= 4.11'; echo $?)
 | 
			
		||||
 | 
			
		||||
Requires: libglibutil >= %{libglibutil_version}
 | 
			
		||||
Requires(post): /sbin/ldconfig
 | 
			
		||||
Requires(postun): /sbin/ldconfig
 | 
			
		||||
@@ -20,7 +29,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 +37,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 +49,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
 | 
			
		||||
@@ -51,6 +61,9 @@ make -C unit test
 | 
			
		||||
%files
 | 
			
		||||
%defattr(-,root,root,-)
 | 
			
		||||
%{_libdir}/%{name}.so.*
 | 
			
		||||
%if %{license_support} == 0
 | 
			
		||||
%license LICENSE
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%files devel
 | 
			
		||||
%defattr(-,root,root,-)
 | 
			
		||||
@@ -72,3 +85,4 @@ Binder command line utilities
 | 
			
		||||
%{_bindir}/binder-bridge
 | 
			
		||||
%{_bindir}/binder-list
 | 
			
		||||
%{_bindir}/binder-ping
 | 
			
		||||
%{_bindir}/binder-call
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
@@ -340,6 +351,22 @@ gbinder_client_interface2(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBytes*
 | 
			
		||||
gbinder_client_rpc_header(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code) /* since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        const GBinderClientIfaceRange* r =
 | 
			
		||||
            gbinder_client_find_range(gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
        if (r) {
 | 
			
		||||
            return r->rpc_header;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_client_new_request(
 | 
			
		||||
    GBinderClient* self)
 | 
			
		||||
@@ -407,13 +434,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 +443,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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -113,9 +113,30 @@ static const GBinderConfigPresetGroup gbinder_config_29[] = {
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* API level 30 */
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetEntry gbinder_config_30_protocol[] = {
 | 
			
		||||
    { "/dev/binder", "aidl3" },
 | 
			
		||||
    { "/dev/vndbinder", "aidl3" },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetEntry gbinder_config_30_servicemanager[] = {
 | 
			
		||||
    { "/dev/binder", "aidl3" },
 | 
			
		||||
    { "/dev/vndbinder", "aidl3" },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetGroup gbinder_config_30[] = {
 | 
			
		||||
    { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_30_protocol },
 | 
			
		||||
    { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_30_servicemanager },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Presets sorted by API level in descending order */
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPreset gbinder_config_presets[] = {
 | 
			
		||||
    { 30, gbinder_config_30 },
 | 
			
		||||
    { 29, gbinder_config_29 },
 | 
			
		||||
    { 28, gbinder_config_28 }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -81,6 +82,7 @@ struct gbinder_driver {
 | 
			
		||||
    void* vm;
 | 
			
		||||
    gsize vmsize;
 | 
			
		||||
    char* dev;
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const GBinderIo* io;
 | 
			
		||||
    const GBinderRpcProtocol* protocol;
 | 
			
		||||
};
 | 
			
		||||
@@ -168,6 +170,14 @@ gbinder_driver_verbose_transaction_data(
 | 
			
		||||
                    GVERBOSE("> %s %d (%u bytes, %u objects)", name,
 | 
			
		||||
                        tx->status, (guint)tx->size, n);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (tx->code) {
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p 0x%08x (%u bytes, %u objects)", name,
 | 
			
		||||
                        tx->target, tx->code, (guint)tx->size, n);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s 0x%08x (%u bytes, %u objects)", name,
 | 
			
		||||
                        tx->code, (guint)tx->size, n);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p (%u bytes, %u objects)", name,
 | 
			
		||||
@@ -186,6 +196,14 @@ gbinder_driver_verbose_transaction_data(
 | 
			
		||||
                    GVERBOSE("> %s %d (%u bytes)", name, tx->status, (guint)
 | 
			
		||||
                        tx->size);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (tx->code) {
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p 0x%08x (%u bytes)", name,
 | 
			
		||||
                        tx->target, tx->code, (guint)tx->size);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s 0x%08x (%u bytes)", name,
 | 
			
		||||
                        tx->code, (guint)tx->size);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p (%u bytes)", name, tx->target, (guint)
 | 
			
		||||
@@ -548,7 +566,8 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
                tx.flags, &txstatus);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GWARN("Unhandled transaction %s 0x%08x", iface, tx.code);
 | 
			
		||||
        GWARN("Unhandled transaction %s 0x%08x from %s", iface, tx.code,
 | 
			
		||||
            self->name);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -674,12 +693,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 +705,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);
 | 
			
		||||
@@ -869,6 +897,9 @@ gbinder_driver_new(
 | 
			
		||||
                    self->vm = vm;
 | 
			
		||||
                    self->vmsize = vmsize;
 | 
			
		||||
                    self->dev = g_strdup(dev);
 | 
			
		||||
                    self->name = self->dev + /* Shorter version for logging */
 | 
			
		||||
                        (g_str_has_prefix(self->dev, "/dev/") ? 5 : 0);
 | 
			
		||||
 | 
			
		||||
                    if (gbinder_system_ioctl(fd, BINDER_SET_MAX_THREADS,
 | 
			
		||||
                        &max_threads) < 0) {
 | 
			
		||||
                        GERR("%s failed to set max threads (%u): %s", dev,
 | 
			
		||||
@@ -1025,7 +1056,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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										699
									
								
								src/gbinder_fmq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										699
									
								
								src/gbinder_fmq.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,699 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_fmq_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <linux/futex.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
 | 
			
		||||
/* Grantor data positions */
 | 
			
		||||
enum {
 | 
			
		||||
    READ_PTR_POS = 0,
 | 
			
		||||
    WRITE_PTR_POS,
 | 
			
		||||
    DATA_PTR_POS,
 | 
			
		||||
    EVENT_FLAG_PTR_POS
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_fmq {
 | 
			
		||||
    GBinderMQDescriptor* desc;
 | 
			
		||||
    guint8* ring;
 | 
			
		||||
    guint64* read_ptr;
 | 
			
		||||
    guint64* write_ptr;
 | 
			
		||||
    guint32* event_flag_ptr;
 | 
			
		||||
    guint32 refcount;
 | 
			
		||||
} GBinderFmq;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderFmqGrantorDescriptor*
 | 
			
		||||
gbinder_fmq_get_grantor_descriptor(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gint index)
 | 
			
		||||
{
 | 
			
		||||
    return (GBinderFmqGrantorDescriptor*)(self->desc->grantors.data.ptr) +
 | 
			
		||||
        index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_read_bytes(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gboolean contiguous)
 | 
			
		||||
{
 | 
			
		||||
    const guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_ACQUIRE);
 | 
			
		||||
    const gsize available_total = __atomic_load_n(self->write_ptr,
 | 
			
		||||
        __ATOMIC_ACQUIRE) - read_ptr;
 | 
			
		||||
 | 
			
		||||
    if (contiguous) {
 | 
			
		||||
        /*
 | 
			
		||||
         * The number of bytes that can be read contiguously from
 | 
			
		||||
         * read offset without wrapping around the ring buffer.
 | 
			
		||||
         */
 | 
			
		||||
        const gsize size = gbinder_fmq_get_grantor_descriptor(self,
 | 
			
		||||
            DATA_PTR_POS)->extent;
 | 
			
		||||
        const gsize available_contiguous = size - (read_ptr % size);
 | 
			
		||||
 | 
			
		||||
        return (available_contiguous < available_total) ?
 | 
			
		||||
            available_contiguous : available_total;
 | 
			
		||||
    } else {
 | 
			
		||||
        return available_total;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_write_bytes(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gboolean contiguous)
 | 
			
		||||
{
 | 
			
		||||
    const guint32 size = gbinder_fmq_get_grantor_descriptor(self,
 | 
			
		||||
        DATA_PTR_POS)->extent;
 | 
			
		||||
    const gsize available_total = size -
 | 
			
		||||
        gbinder_fmq_available_to_read_bytes(self, FALSE);
 | 
			
		||||
 | 
			
		||||
    if (contiguous) {
 | 
			
		||||
        /*
 | 
			
		||||
         * The number of bytes that can be written contiguously starting from
 | 
			
		||||
         * write_offset without wrapping around the ring buffer.
 | 
			
		||||
         */
 | 
			
		||||
        const guint64 write_ptr = __atomic_load_n(self->write_ptr,
 | 
			
		||||
            __ATOMIC_RELAXED);
 | 
			
		||||
        const gsize available_contiguous = size - (write_ptr % size);
 | 
			
		||||
 | 
			
		||||
        return (available_contiguous < available_total) ?
 | 
			
		||||
            available_contiguous : available_total;
 | 
			
		||||
    } else {
 | 
			
		||||
        return available_total;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderFmqGrantorDescriptor*
 | 
			
		||||
gbinder_fmq_create_grantors(
 | 
			
		||||
    gsize queue_size_bytes,
 | 
			
		||||
    gsize num_fds,
 | 
			
		||||
    gboolean configure_event_flag)
 | 
			
		||||
{
 | 
			
		||||
    const gsize num_grantors = configure_event_flag ?
 | 
			
		||||
        (EVENT_FLAG_PTR_POS + 1) : (DATA_PTR_POS + 1);
 | 
			
		||||
    GBinderFmqGrantorDescriptor* grantors =
 | 
			
		||||
        g_new0(GBinderFmqGrantorDescriptor, num_grantors);
 | 
			
		||||
    gsize pos, offset;
 | 
			
		||||
    gsize mem_sizes[] = {
 | 
			
		||||
        sizeof(guint64),  /* read pointer counter */
 | 
			
		||||
        sizeof(guint64),  /* write pointer counter */
 | 
			
		||||
        queue_size_bytes, /* data buffer */
 | 
			
		||||
        sizeof(guint32)   /* event flag pointer */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (pos = 0, offset = 0; pos < num_grantors; pos++) {
 | 
			
		||||
        GBinderFmqGrantorDescriptor* grantor = grantors + pos;
 | 
			
		||||
        guint32 grantor_fd_index;
 | 
			
		||||
        gsize grantor_offset;
 | 
			
		||||
 | 
			
		||||
        if (pos == DATA_PTR_POS && num_fds == 2) {
 | 
			
		||||
            grantor_fd_index = 1;
 | 
			
		||||
            grantor_offset = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            grantor_fd_index = 0;
 | 
			
		||||
            grantor_offset = offset;
 | 
			
		||||
            offset += mem_sizes[pos];
 | 
			
		||||
        }
 | 
			
		||||
        grantor->fd_index = grantor_fd_index;
 | 
			
		||||
        grantor->offset = (guint32)G_ALIGN8(grantor_offset);
 | 
			
		||||
        grantor->extent = mem_sizes[pos];
 | 
			
		||||
    }
 | 
			
		||||
    return grantors;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void*
 | 
			
		||||
gbinder_fmq_map_grantor_descriptor(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    guint32 index)
 | 
			
		||||
{
 | 
			
		||||
    if (index < self->desc->grantors.count) {
 | 
			
		||||
        const GBinderFmqGrantorDescriptor* desc =
 | 
			
		||||
            gbinder_fmq_get_grantor_descriptor(self, index);
 | 
			
		||||
        /* Offset for mmap must be a multiple of PAGE_SIZE */
 | 
			
		||||
        const guint32 map_offset = (desc->offset & ~(getpagesize()-1));
 | 
			
		||||
        const guint32 map_length = desc->offset - map_offset + desc->extent;
 | 
			
		||||
        const GBinderFds* fds = self->desc->data.fds;
 | 
			
		||||
        void* address = mmap(0, map_length, PROT_READ | PROT_WRITE, MAP_SHARED,
 | 
			
		||||
            gbinder_fds_get_fd(fds, desc->fd_index), map_offset);
 | 
			
		||||
 | 
			
		||||
        if (address != MAP_FAILED) {
 | 
			
		||||
            return (guint8*)address + (desc->offset - map_offset);
 | 
			
		||||
        } else {
 | 
			
		||||
            GWARN("mmap failed: %d", errno);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_unmap_grantor_descriptor(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    void* address,
 | 
			
		||||
    guint index)
 | 
			
		||||
{
 | 
			
		||||
    if (index < self->desc->grantors.count && address) {
 | 
			
		||||
        const GBinderFmqGrantorDescriptor* desc =
 | 
			
		||||
            gbinder_fmq_get_grantor_descriptor(self, index);
 | 
			
		||||
        const gsize remainder = desc->offset & (getpagesize() - 1);
 | 
			
		||||
 | 
			
		||||
        munmap((guint8*)address - remainder, remainder + desc->extent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_free(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    if (self->desc) {
 | 
			
		||||
        if (self->desc->flags == GBINDER_FMQ_TYPE_UNSYNC_WRITE) {
 | 
			
		||||
            g_free(self->read_ptr);
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_fmq_unmap_grantor_descriptor(self, self->read_ptr,
 | 
			
		||||
                READ_PTR_POS);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_fmq_unmap_grantor_descriptor(self, self->write_ptr,
 | 
			
		||||
            WRITE_PTR_POS);
 | 
			
		||||
        gbinder_fmq_unmap_grantor_descriptor(self, self->ring,
 | 
			
		||||
            DATA_PTR_POS);
 | 
			
		||||
        gbinder_fmq_unmap_grantor_descriptor(self, self->event_flag_ptr,
 | 
			
		||||
            EVENT_FLAG_PTR_POS);
 | 
			
		||||
 | 
			
		||||
        g_free((GBinderFmqGrantorDescriptor*)self->desc->grantors.data.ptr);
 | 
			
		||||
        g_free((GBinderFds*)self->desc->data.fds);
 | 
			
		||||
 | 
			
		||||
        g_free(self->desc);
 | 
			
		||||
    }
 | 
			
		||||
    g_slice_free(GBinderFmq, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Private API */
 | 
			
		||||
 | 
			
		||||
GBinderMQDescriptor*
 | 
			
		||||
gbinder_fmq_get_descriptor(
 | 
			
		||||
    const GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    return self->desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Public API */
 | 
			
		||||
 | 
			
		||||
GBinderFmq*
 | 
			
		||||
gbinder_fmq_new(
 | 
			
		||||
    gsize item_size,
 | 
			
		||||
    gsize num_items,
 | 
			
		||||
    GBINDER_FMQ_TYPE type,
 | 
			
		||||
    GBINDER_FMQ_FLAGS flags,
 | 
			
		||||
    gint fd,
 | 
			
		||||
    gsize buffer_size)
 | 
			
		||||
{
 | 
			
		||||
    if (item_size <= 0) {
 | 
			
		||||
        GWARN("Incorrect item size");
 | 
			
		||||
    } else if (num_items <= 0) {
 | 
			
		||||
        GWARN("Empty queue requested");
 | 
			
		||||
    } else if (num_items > SIZE_MAX / item_size) {
 | 
			
		||||
        GWARN("Requested message queue size too large");
 | 
			
		||||
    } else if (fd != -1 && num_items * item_size > buffer_size) {
 | 
			
		||||
        GWARN("The size needed for items (%"G_GSIZE_FORMAT") is larger "
 | 
			
		||||
            "than the supplied buffer size (%"G_GSIZE_FORMAT")",
 | 
			
		||||
            num_items * item_size, buffer_size);
 | 
			
		||||
    } else {
 | 
			
		||||
        GBinderFmq* self = g_slice_new0(GBinderFmq);
 | 
			
		||||
        gboolean configure_event_flag =
 | 
			
		||||
            (flags & GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG) != 0;
 | 
			
		||||
        gsize queue_size_bytes = num_items * item_size;
 | 
			
		||||
        gsize meta_data_size;
 | 
			
		||||
        gsize shmem_size;
 | 
			
		||||
        int shmem_fd;
 | 
			
		||||
 | 
			
		||||
        meta_data_size = 2 * sizeof(guint64);
 | 
			
		||||
        if (configure_event_flag) {
 | 
			
		||||
            meta_data_size += sizeof(guint32);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Allocate shared memory */
 | 
			
		||||
        if (fd != -1) {
 | 
			
		||||
            /* User-supplied ringbuffer memory provided,
 | 
			
		||||
             * allocating memory only for meta data */
 | 
			
		||||
            shmem_size = (meta_data_size + getpagesize() - 1) &
 | 
			
		||||
                ~(getpagesize() - 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Allocate ringbuffer, read counter and write counter */
 | 
			
		||||
            shmem_size = (G_ALIGN8(queue_size_bytes) +
 | 
			
		||||
                meta_data_size + getpagesize() - 1) & ~(getpagesize() - 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        shmem_fd = syscall(__NR_memfd_create, "MessageQueue", MFD_CLOEXEC);
 | 
			
		||||
 | 
			
		||||
        if (shmem_fd >= 0 && ftruncate(shmem_fd, shmem_size) == 0) {
 | 
			
		||||
            GBinderFmqGrantorDescriptor* grantors;
 | 
			
		||||
            gsize num_fds = (fd != -1) ? 2 : 1;
 | 
			
		||||
            gsize fds_size = sizeof(GBinderFds) + sizeof(int) * num_fds;
 | 
			
		||||
            GBinderFds* fds = (GBinderFds*)g_malloc0(fds_size);
 | 
			
		||||
 | 
			
		||||
            fds->version = fds_size;
 | 
			
		||||
            fds->num_fds = num_fds;
 | 
			
		||||
 | 
			
		||||
            (((int*)((fds) + 1))[0]) = shmem_fd;
 | 
			
		||||
 | 
			
		||||
            if (fd != -1) {
 | 
			
		||||
                /* Use user-supplied file descriptor for fd_index 1 */
 | 
			
		||||
                (((int*)((fds) + 1))[1]) = fd;
 | 
			
		||||
            }
 | 
			
		||||
            grantors = gbinder_fmq_create_grantors(queue_size_bytes,
 | 
			
		||||
                num_fds, configure_event_flag);
 | 
			
		||||
 | 
			
		||||
            /* Fill FMQ descriptor */
 | 
			
		||||
            self->desc = g_new0(GBinderMQDescriptor, 1);
 | 
			
		||||
            self->desc->data.fds = fds;
 | 
			
		||||
            self->desc->quantum = item_size;
 | 
			
		||||
            self->desc->flags = type;
 | 
			
		||||
            self->desc->grantors.data.ptr = grantors;
 | 
			
		||||
            self->desc->grantors.count = configure_event_flag ?
 | 
			
		||||
                (EVENT_FLAG_PTR_POS + 1) : (DATA_PTR_POS + 1);
 | 
			
		||||
            self->desc->grantors.owns_buffer = TRUE;
 | 
			
		||||
 | 
			
		||||
            /* Initialize memory pointers */
 | 
			
		||||
            if (type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) {
 | 
			
		||||
                self->read_ptr = gbinder_fmq_map_grantor_descriptor(self,
 | 
			
		||||
                    READ_PTR_POS);
 | 
			
		||||
            } else {
 | 
			
		||||
                /*
 | 
			
		||||
                 * Unsynchronized write FMQs may have multiple readers and
 | 
			
		||||
                 * each reader would have their own read pointer counter.
 | 
			
		||||
                 */
 | 
			
		||||
                self->read_ptr = g_new0(guint64, 1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!self->read_ptr) {
 | 
			
		||||
                GWARN("Read pointer is null");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self->write_ptr = gbinder_fmq_map_grantor_descriptor(self,
 | 
			
		||||
                WRITE_PTR_POS);
 | 
			
		||||
            if (!self->write_ptr) {
 | 
			
		||||
                GWARN("Write pointer is null");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!(flags & GBINDER_FMQ_FLAG_NO_RESET_POINTERS)) {
 | 
			
		||||
                __atomic_store_n(self->read_ptr, 0, __ATOMIC_RELEASE);
 | 
			
		||||
                __atomic_store_n(self->write_ptr, 0, __ATOMIC_RELEASE);
 | 
			
		||||
            } else if (type != GBINDER_FMQ_TYPE_SYNC_READ_WRITE) {
 | 
			
		||||
                /* Always reset the read pointer */
 | 
			
		||||
                __atomic_store_n(self->read_ptr, 0, __ATOMIC_RELEASE);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self->ring = gbinder_fmq_map_grantor_descriptor(self,
 | 
			
		||||
                DATA_PTR_POS);
 | 
			
		||||
            if (!self->ring) {
 | 
			
		||||
                GWARN("Ring buffer pointer is null");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (self->desc->grantors.count > EVENT_FLAG_PTR_POS) {
 | 
			
		||||
                self->event_flag_ptr = gbinder_fmq_map_grantor_descriptor(self,
 | 
			
		||||
                    EVENT_FLAG_PTR_POS);
 | 
			
		||||
                if (!self->event_flag_ptr) {
 | 
			
		||||
                    GWARN("Event flag pointer is null");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            g_atomic_int_set(&self->refcount, 1);
 | 
			
		||||
            return self;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GWARN("Failed to allocate shared memory: %s", strerror(errno));
 | 
			
		||||
        gbinder_fmq_free(self);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderFmq*
 | 
			
		||||
gbinder_fmq_ref(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
        g_atomic_int_inc(&self->refcount);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_unref(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
        if (g_atomic_int_dec_and_test(&self->refcount)) {
 | 
			
		||||
            gbinder_fmq_free(self);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_read(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? (gbinder_fmq_available_to_read_bytes(self, FALSE) /
 | 
			
		||||
        self->desc->quantum) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_write(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? (gbinder_fmq_available_to_write_bytes(self, FALSE) /
 | 
			
		||||
        self->desc->quantum) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_read_contiguous(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? (gbinder_fmq_available_to_read_bytes(self, TRUE) /
 | 
			
		||||
        self->desc->quantum) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_fmq_available_to_write_contiguous(
 | 
			
		||||
    GBinderFmq* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? (gbinder_fmq_available_to_write_bytes(self, TRUE) /
 | 
			
		||||
        self->desc->quantum) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_fmq_begin_read(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    void* ptr = NULL;
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(items > 0)) {
 | 
			
		||||
        gsize size = gbinder_fmq_get_grantor_descriptor(self,
 | 
			
		||||
            DATA_PTR_POS)->extent;
 | 
			
		||||
        gsize item_size = self->desc->quantum;
 | 
			
		||||
        gsize bytes_desired = items * item_size;
 | 
			
		||||
        guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_ACQUIRE);
 | 
			
		||||
        guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_RELAXED);
 | 
			
		||||
 | 
			
		||||
        if ((write_ptr % item_size) || (read_ptr % item_size)) {
 | 
			
		||||
            GWARN("Unable to write data because of misaligned pointer");
 | 
			
		||||
        } else if (write_ptr - read_ptr > size) {
 | 
			
		||||
            __atomic_store_n(self->read_ptr, write_ptr, __ATOMIC_RELEASE);
 | 
			
		||||
        } else if (write_ptr - read_ptr < bytes_desired) {
 | 
			
		||||
            /* Not enough data to read in FMQ. */
 | 
			
		||||
        } else {
 | 
			
		||||
            ptr = self->ring + (read_ptr % size);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_fmq_begin_write(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    void* ptr = NULL;
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(items > 0)) {
 | 
			
		||||
        const gsize item_size = self->desc->quantum;
 | 
			
		||||
        const gsize size = gbinder_fmq_get_grantor_descriptor(self,
 | 
			
		||||
            DATA_PTR_POS)->extent;
 | 
			
		||||
 | 
			
		||||
        if ((self->desc->flags == GBINDER_FMQ_TYPE_SYNC_READ_WRITE &&
 | 
			
		||||
            (gbinder_fmq_available_to_write(self) < items)) ||
 | 
			
		||||
            items > size / item_size) {
 | 
			
		||||
            /* Incorrect parameters */
 | 
			
		||||
        } else {
 | 
			
		||||
            guint64 write_ptr = __atomic_load_n(self->write_ptr,
 | 
			
		||||
                __ATOMIC_RELAXED);
 | 
			
		||||
 | 
			
		||||
            if (write_ptr % item_size) {
 | 
			
		||||
                GWARN("The write pointer has become misaligned.");
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                ptr = self->ring + (write_ptr % size);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_end_read(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(items > 0)) {
 | 
			
		||||
        gsize size = gbinder_fmq_get_grantor_descriptor(self,
 | 
			
		||||
            DATA_PTR_POS)->extent;
 | 
			
		||||
        guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_RELAXED);
 | 
			
		||||
        guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_ACQUIRE);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * If queue type is unsynchronized, it is possible that a write
 | 
			
		||||
         * overflow may have occurred.
 | 
			
		||||
         */
 | 
			
		||||
        if (write_ptr - read_ptr > size) {
 | 
			
		||||
            __atomic_store_n(self->read_ptr, write_ptr, __ATOMIC_RELEASE);
 | 
			
		||||
        } else {
 | 
			
		||||
            read_ptr += items * self->desc->quantum;
 | 
			
		||||
            __atomic_store_n(self->read_ptr, read_ptr, __ATOMIC_RELEASE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_fmq_end_write(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(items > 0)) {
 | 
			
		||||
        guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_RELAXED);
 | 
			
		||||
 | 
			
		||||
        write_ptr += items * self->desc->quantum;
 | 
			
		||||
        __atomic_store_n(self->write_ptr, write_ptr, __ATOMIC_RELEASE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_fmq_read(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    void* data,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(data) && G_LIKELY(items > 0)) {
 | 
			
		||||
        const void* in_data = gbinder_fmq_begin_read(self, items);
 | 
			
		||||
 | 
			
		||||
        if (in_data) {
 | 
			
		||||
            /*
 | 
			
		||||
             * The number of messages that can be read contiguously without
 | 
			
		||||
             * wrapping around the ring buffer.
 | 
			
		||||
             */
 | 
			
		||||
            const gsize contiguous_messages =
 | 
			
		||||
                gbinder_fmq_available_to_read_contiguous(self);
 | 
			
		||||
            const gsize item_size = self->desc->quantum;
 | 
			
		||||
 | 
			
		||||
            if (contiguous_messages < items) {
 | 
			
		||||
                /* A wrap around is required */
 | 
			
		||||
                memcpy(data, in_data, contiguous_messages * item_size);
 | 
			
		||||
                memcpy((char*)data + contiguous_messages * item_size,
 | 
			
		||||
                    self->ring, (items - contiguous_messages) * item_size);
 | 
			
		||||
            } else {
 | 
			
		||||
                /* A wrap around is not required */
 | 
			
		||||
                memcpy(data, in_data, items * item_size);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            gbinder_fmq_end_read(self, items);
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_fmq_write(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    const void* data,
 | 
			
		||||
    gsize items)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(data) && G_LIKELY(items > 0)) {
 | 
			
		||||
        void *out_data = gbinder_fmq_begin_write(self, items);
 | 
			
		||||
 | 
			
		||||
        if (out_data) {
 | 
			
		||||
            /*
 | 
			
		||||
             * The number of messages that can be written contiguously without
 | 
			
		||||
             * wrapping around the ring buffer.
 | 
			
		||||
             */
 | 
			
		||||
            const gsize contiguous_messages =
 | 
			
		||||
                gbinder_fmq_available_to_write_contiguous(self);
 | 
			
		||||
            const gsize item_size = self->desc->quantum;
 | 
			
		||||
 | 
			
		||||
            if (contiguous_messages < items) {
 | 
			
		||||
                /* A wrap around is required. */
 | 
			
		||||
                memcpy(out_data, data, contiguous_messages * item_size);
 | 
			
		||||
                memcpy(self->ring, (char *)data + contiguous_messages *
 | 
			
		||||
                    item_size / sizeof(char),
 | 
			
		||||
                    (items - contiguous_messages) * item_size);
 | 
			
		||||
            } else {
 | 
			
		||||
                /* A wrap around is not required to write items */
 | 
			
		||||
                memcpy(out_data, data, items * item_size);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            gbinder_fmq_end_write(self, items);
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_fmq_wait_timeout(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    guint32 bit_mask,
 | 
			
		||||
    guint32* state,
 | 
			
		||||
    int timeout_ms)
 | 
			
		||||
{
 | 
			
		||||
    if (G_UNLIKELY(!state) || G_UNLIKELY(!self)) {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    } else if (!self->event_flag_ptr) {
 | 
			
		||||
        return (-ENOSYS);
 | 
			
		||||
    } else if (!bit_mask) {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    } else {
 | 
			
		||||
        guint32 old_value = __atomic_fetch_and(self->event_flag_ptr, ~bit_mask,
 | 
			
		||||
            __ATOMIC_SEQ_CST);
 | 
			
		||||
        guint32 set_bits = old_value & bit_mask;
 | 
			
		||||
 | 
			
		||||
        /* Check if any of the bits was already set */
 | 
			
		||||
        if (set_bits != 0) {
 | 
			
		||||
            *state = set_bits;
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else if (!timeout_ms) {
 | 
			
		||||
            return (-ETIMEDOUT);
 | 
			
		||||
        } else {
 | 
			
		||||
            int ret;
 | 
			
		||||
 | 
			
		||||
            if (timeout_ms > 0) {
 | 
			
		||||
                struct timespec deadline;
 | 
			
		||||
                const guint64 ms = 1000000;
 | 
			
		||||
                const guint64 sec = 1000 * ms;
 | 
			
		||||
 | 
			
		||||
                clock_gettime(CLOCK_MONOTONIC, &deadline);
 | 
			
		||||
                deadline.tv_sec += timeout_ms / 1000;
 | 
			
		||||
                deadline.tv_nsec += (timeout_ms % 1000) * ms;
 | 
			
		||||
                if (deadline.tv_nsec >= sec) {
 | 
			
		||||
                    deadline.tv_sec++;
 | 
			
		||||
                    deadline.tv_nsec -= sec;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ret = syscall(__NR_futex, self->event_flag_ptr,
 | 
			
		||||
                    FUTEX_WAIT_BITSET, old_value, &deadline, NULL, bit_mask);
 | 
			
		||||
            } else {
 | 
			
		||||
                ret = syscall(__NR_futex, self->event_flag_ptr,
 | 
			
		||||
                    FUTEX_WAIT_BITSET, old_value, NULL, NULL, bit_mask);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ret == -1) {
 | 
			
		||||
                return errno ? (-errno) : -EFAULT;
 | 
			
		||||
            } else {
 | 
			
		||||
                old_value = __atomic_fetch_and(self->event_flag_ptr, ~bit_mask,
 | 
			
		||||
                    __ATOMIC_SEQ_CST);
 | 
			
		||||
                *state = old_value & bit_mask;
 | 
			
		||||
                return (*state) ? 0 : (-EAGAIN);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_fmq_wake(
 | 
			
		||||
    GBinderFmq* self,
 | 
			
		||||
    guint32 bit_mask)
 | 
			
		||||
{
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        if (!self->event_flag_ptr) {
 | 
			
		||||
            /* Event flag is not configured */
 | 
			
		||||
            ret = -ENOSYS;
 | 
			
		||||
        } else if (!bit_mask) {
 | 
			
		||||
            /* Ignore zero bit mask */
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Set bit mask only if needed */
 | 
			
		||||
            guint32 old_value = __atomic_fetch_or(self->event_flag_ptr,
 | 
			
		||||
                bit_mask, __ATOMIC_SEQ_CST);
 | 
			
		||||
 | 
			
		||||
            if (~old_value & bit_mask) {
 | 
			
		||||
                ret = syscall(__NR_futex, self->event_flag_ptr,
 | 
			
		||||
                    FUTEX_WAKE_BITSET, G_MAXUINT32, NULL, NULL, bit_mask);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ret == -1) {
 | 
			
		||||
                /* Report error code */
 | 
			
		||||
                ret = -errno;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* !GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
#pragma message("Not compiling FMQ")
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										102
									
								
								src/gbinder_fmq_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/gbinder_fmq_p.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 GBINDER_FMQ_PRIVATE_H
 | 
			
		||||
#define GBINDER_FMQ_PRIVATE_H
 | 
			
		||||
 | 
			
		||||
#include <gbinder_fmq.h>
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
/* FMQ functionality requires __NR_memfd_create syscall */
 | 
			
		||||
#include <sys/syscall.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __NR_memfd_create
 | 
			
		||||
#  define GBINDER_FMQ_SUPPORTED 1
 | 
			
		||||
#else
 | 
			
		||||
#  define GBINDER_FMQ_SUPPORTED 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * From linux/memfd.h
 | 
			
		||||
 */
 | 
			
		||||
#ifndef MFD_CLOEXEC
 | 
			
		||||
#  define MFD_CLOEXEC 0x0001U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FMQ types
 | 
			
		||||
 */
 | 
			
		||||
typedef struct gbinder_fmq_grantor_descriptor {
 | 
			
		||||
    guint32 flags GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 fd_index GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 offset GBINDER_ALIGNED(4);
 | 
			
		||||
    guint64 extent GBINDER_ALIGNED(8);
 | 
			
		||||
} GBinderFmqGrantorDescriptor;
 | 
			
		||||
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, flags) == 0);
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, fd_index) == 4);
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, offset) == 8);
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, extent) == 16);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderFmqGrantorDescriptor) == 24);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_mq_descriptor {
 | 
			
		||||
    GBinderHidlVec grantors;
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const GBinderFds* fds;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 quantum;
 | 
			
		||||
    guint32 flags;
 | 
			
		||||
} GBinderMQDescriptor;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET (0)
 | 
			
		||||
#define GBINDER_MQ_DESCRIPTOR_FDS_OFFSET (16)
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderMQDescriptor, grantors) ==
 | 
			
		||||
    GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET);
 | 
			
		||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderMQDescriptor, data) ==
 | 
			
		||||
    GBINDER_MQ_DESCRIPTOR_FDS_OFFSET);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderMQDescriptor) == 32);
 | 
			
		||||
 | 
			
		||||
GBinderMQDescriptor*
 | 
			
		||||
gbinder_fmq_get_descriptor(
 | 
			
		||||
    const GBinderFmq* self)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_FMQ_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -215,6 +215,23 @@ GBINDER_IO_FN(encode_fd_object)(
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_fda_object)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    const GBinderFds *fds,
 | 
			
		||||
    const GBinderParent* parent)
 | 
			
		||||
{
 | 
			
		||||
    struct binder_fd_array_object* dest = out;
 | 
			
		||||
 | 
			
		||||
    memset(dest, 0, sizeof(*dest));
 | 
			
		||||
    dest->hdr.type = BINDER_TYPE_FDA;
 | 
			
		||||
    dest->num_fds = fds->num_fds;
 | 
			
		||||
    dest->parent = parent->index;
 | 
			
		||||
    dest->parent_offset = parent->offset;
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes binder_buffer_object */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
@@ -637,6 +654,7 @@ const GBinderIo GBINDER_IO_PREFIX = {
 | 
			
		||||
    .encode_local_object = GBINDER_IO_FN(encode_local_object),
 | 
			
		||||
    .encode_remote_object = GBINDER_IO_FN(encode_remote_object),
 | 
			
		||||
    .encode_fd_object = GBINDER_IO_FN(encode_fd_object),
 | 
			
		||||
    .encode_fda_object = GBINDER_IO_FN(encode_fda_object),
 | 
			
		||||
    .encode_buffer_object = GBINDER_IO_FN(encode_buffer_object),
 | 
			
		||||
    .encode_handle_cookie = GBINDER_IO_FN(encode_handle_cookie),
 | 
			
		||||
    .encode_ptr_cookie = GBINDER_IO_FN(encode_ptr_cookie),
 | 
			
		||||
@@ -663,6 +681,10 @@ const GBinderIo GBINDER_IO_PREFIX = {
 | 
			
		||||
G_STATIC_ASSERT(GBINDER_POINTER_SIZE <= GBINDER_MAX_POINTER_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct flat_binder_object) <=
 | 
			
		||||
    GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_fd_object) <=
 | 
			
		||||
    GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_fd_array_object) <=
 | 
			
		||||
    GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_buffer_object) <=
 | 
			
		||||
    GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_handle_cookie) <=
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,8 @@ struct gbinder_io {
 | 
			
		||||
    guint (*encode_local_object)(void* out, GBinderLocalObject* obj);
 | 
			
		||||
    guint (*encode_remote_object)(void* out, GBinderRemoteObject* obj);
 | 
			
		||||
    guint (*encode_fd_object)(void* out, int fd);
 | 
			
		||||
    guint (*encode_fda_object)(void* out, const GBinderFds *fds,
 | 
			
		||||
        const GBinderParent* parent);
 | 
			
		||||
 | 
			
		||||
    /* Encode binder_buffer_object */
 | 
			
		||||
#define GBINDER_MAX_BUFFER_OBJECT_SIZE (40)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -207,9 +207,6 @@ typedef struct gbinder_ipc_tx_custom {
 | 
			
		||||
    GDestroyNotify fn_custom_destroy;
 | 
			
		||||
} GBinderIpcTxCustom;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
 | 
			
		||||
    { return self->priv->name; }
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderIpcLooper*
 | 
			
		||||
gbinder_ipc_looper_new(
 | 
			
		||||
@@ -429,7 +426,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 +763,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 +834,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 +865,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 +874,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));
 | 
			
		||||
@@ -1113,6 +1112,23 @@ gbinder_ipc_tx_handler_transact(
 | 
			
		||||
 * GBinderObjectRegistry
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_local_object_locked(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    if (priv->local_objects && g_hash_table_remove(priv->local_objects, obj)) {
 | 
			
		||||
        GVERBOSE_("%p %s", obj, gbinder_ipc_name(self));
 | 
			
		||||
        if (g_hash_table_size(priv->local_objects) == 0) {
 | 
			
		||||
            g_hash_table_unref(priv->local_objects);
 | 
			
		||||
            priv->local_objects = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_remote_handle_locked(
 | 
			
		||||
@@ -1138,6 +1154,20 @@ gbinder_ipc_invalidate_remote_handle_locked(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_local_object(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->local_objects_mutex);
 | 
			
		||||
    gbinder_ipc_invalidate_local_object_locked(self, obj);
 | 
			
		||||
    g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
@@ -1181,14 +1211,8 @@ gbinder_ipc_local_object_disposed(
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->local_objects_mutex);
 | 
			
		||||
    if (obj->object.ref_count == 1 && priv->local_objects) {
 | 
			
		||||
        if (g_hash_table_remove(priv->local_objects, obj)) {
 | 
			
		||||
            GVERBOSE_("%p %s", obj, gbinder_ipc_name(self));
 | 
			
		||||
            if (g_hash_table_size(priv->local_objects) == 0) {
 | 
			
		||||
                g_hash_table_unref(priv->local_objects);
 | 
			
		||||
                priv->local_objects = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    if (g_atomic_int_get(&obj->object.ref_count) == 1) {
 | 
			
		||||
        gbinder_ipc_invalidate_local_object_locked(self, obj);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
@@ -1201,9 +1225,32 @@ gbinder_ipc_remote_object_disposed(
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Check of ref_count for 1 makes it possible (albeit quite unlikely)
 | 
			
		||||
     * that GBinderRemoteObject still remains in remote_objects table
 | 
			
		||||
     * when it's being finalized.
 | 
			
		||||
     *
 | 
			
		||||
     * For this to happen, other thread must re-reference GBinderRemoteObject
 | 
			
		||||
     * right before we grab the lock here (making ref_count greater than 1)
 | 
			
		||||
     * and then release that reference before g_object_unref() re-checks the
 | 
			
		||||
     * refcount.
 | 
			
		||||
     *
 | 
			
		||||
     * That's why another gbinder_ipc_invalidate_remote_handle() call from
 | 
			
		||||
     * gbinder_remote_object_finalize() is necessary to make sure that stale
 | 
			
		||||
     * object pointer isn't stored in the hashtable.
 | 
			
		||||
     *
 | 
			
		||||
     * We still have to invalidate the handle here because it's the last
 | 
			
		||||
     * point when GObject can be legitimately re-referenced and brought
 | 
			
		||||
     * back to life. Which means that GBinderIpc mutex has to acquired
 | 
			
		||||
     * twice during GBinderRemoteObject destruction.
 | 
			
		||||
     *
 | 
			
		||||
     * The same applies to GBinderLocalObject too, except that it calls
 | 
			
		||||
     * gbinder_ipc_invalidate_local_object() from its finalize() handler.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->remote_objects_mutex);
 | 
			
		||||
    if (obj->object.ref_count == 1) {
 | 
			
		||||
    if (g_atomic_int_get(&obj->object.ref_count) == 1) {
 | 
			
		||||
        gbinder_ipc_invalidate_remote_handle_locked(self, obj->handle);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->remote_objects_mutex);
 | 
			
		||||
@@ -1248,10 +1295,10 @@ gbinder_ipc_priv_get_local_object(
 | 
			
		||||
            if (obj) {
 | 
			
		||||
                gbinder_local_object_ref(obj);
 | 
			
		||||
            } else {
 | 
			
		||||
                GWARN("Unknown local object %p", pointer);
 | 
			
		||||
                GWARN("Unknown local object %p %s", pointer, priv->name);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            GWARN("Unknown local object %p", pointer);
 | 
			
		||||
            GWARN("Unknown local object %p %s", pointer, priv->name);
 | 
			
		||||
        }
 | 
			
		||||
        g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
        /* Unlock */
 | 
			
		||||
@@ -1295,6 +1342,8 @@ gbinder_ipc_priv_get_remote_object(
 | 
			
		||||
        }
 | 
			
		||||
        GVERBOSE_("%p handle %u %s", obj, handle, gbinder_ipc_name(self));
 | 
			
		||||
        g_hash_table_replace(priv->remote_objects, key, obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        GWARN("Unknown handle %u %s", handle, priv->name);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->remote_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
@@ -1854,6 +1903,13 @@ gbinder_ipc_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_ipc_name(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->priv->name : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderObjectRegistry*
 | 
			
		||||
gbinder_ipc_object_registry(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -113,6 +113,11 @@ gbinder_ipc_unref(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_ipc_name(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_looper_check(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
@@ -149,7 +154,7 @@ gbinder_ipc_register_local_object(
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_get_service_manager(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
@@ -196,21 +201,27 @@ gbinder_ipc_cancel(
 | 
			
		||||
/* Internal for GBinderLocalObject */
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_local_object_disposed(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_local_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Internal for GBinderRemoteObject */
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_remote_object_disposed(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Needed by unit tests */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_set_max_threads(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    gint max_threads)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -644,6 +644,7 @@ gbinder_local_object_finalize(
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!self->strong_refs);
 | 
			
		||||
    gbinder_ipc_invalidate_local_object(self->ipc, self);
 | 
			
		||||
    gbinder_ipc_unref(self->ipc);
 | 
			
		||||
    g_strfreev(priv->ifaces);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
 
 | 
			
		||||
@@ -307,6 +307,17 @@ gbinder_local_reply_append_remote_object(
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_append_fd(
 | 
			
		||||
    GBinderLocalReply* self,
 | 
			
		||||
    int fd) /* Since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        gbinder_writer_data_append_fd(&self->data, fd);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2021-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -60,11 +60,9 @@ struct gbinder_proxy_tx {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gbinder_proxy_object_priv {
 | 
			
		||||
    gulong remote_death_id;
 | 
			
		||||
    gboolean acquired;
 | 
			
		||||
    gboolean dropped;
 | 
			
		||||
    GBinderProxyTx* tx;
 | 
			
		||||
    GMutex mutex; /* Protects the hashtable below */
 | 
			
		||||
    GHashTable* subproxies;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
 | 
			
		||||
@@ -76,79 +74,12 @@ G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
 | 
			
		||||
#define THIS_TYPE GBINDER_TYPE_PROXY_OBJECT
 | 
			
		||||
#define PARENT_CLASS gbinder_proxy_object_parent_class
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_subproxy_gone(
 | 
			
		||||
    gpointer proxy,
 | 
			
		||||
    GObject* subproxy)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(proxy);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->mutex);
 | 
			
		||||
    g_hash_table_remove(priv->subproxies, subproxy);
 | 
			
		||||
    if (g_hash_table_size(priv->subproxies) == 0) {
 | 
			
		||||
        g_hash_table_unref(priv->subproxies);
 | 
			
		||||
        priv->subproxies = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_drop_subproxies(
 | 
			
		||||
    GBinderProxyObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
    GSList* list = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->mutex);
 | 
			
		||||
    if (priv->subproxies) {
 | 
			
		||||
        GHashTableIter it;
 | 
			
		||||
        gpointer value;
 | 
			
		||||
 | 
			
		||||
        g_hash_table_iter_init(&it, priv->subproxies);
 | 
			
		||||
        while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
            list = g_slist_append(list, gbinder_local_object_ref(value));
 | 
			
		||||
            g_object_weak_unref(G_OBJECT(value),
 | 
			
		||||
                gbinder_proxy_object_subproxy_gone, self);
 | 
			
		||||
        }
 | 
			
		||||
        g_hash_table_destroy(priv->subproxies);
 | 
			
		||||
        priv->subproxies = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
 | 
			
		||||
    /* Drop (and possibly destroy) the objects outside of the lock */
 | 
			
		||||
    g_slist_free_full(list, (GDestroyNotify) gbinder_local_object_drop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_remote_death_proc(
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    void* proxy)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(proxy);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Remote object %u died on %s", obj->handle, obj->ipc->dev);
 | 
			
		||||
    gbinder_remote_object_remove_handler(obj, priv->remote_death_id);
 | 
			
		||||
    priv->remote_death_id = 0;
 | 
			
		||||
    /* Drop the implicit reference */
 | 
			
		||||
    gbinder_local_object_unref(&self->parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Converter
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_proxy_object_converter {
 | 
			
		||||
    GBinderObjectConverter pub;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
    GBinderIpc* remote;
 | 
			
		||||
    GBinderIpc* local;
 | 
			
		||||
} GBinderProxyObjectConverter;
 | 
			
		||||
@@ -182,8 +113,6 @@ gbinder_proxy_object_converter_handle_to_local(
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObjectConverter* c = gbinder_proxy_object_converter_cast(pub);
 | 
			
		||||
    GBinderProxyObject* proxy = c->proxy;
 | 
			
		||||
    GBinderProxyObjectPriv* priv = proxy->priv;
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(c->remote);
 | 
			
		||||
    GBinderRemoteObject* remote = gbinder_object_registry_get_remote(reg,
 | 
			
		||||
        handle, REMOTE_REGISTRY_CAN_CREATE /* but don't acquire */);
 | 
			
		||||
@@ -192,32 +121,7 @@ gbinder_proxy_object_converter_handle_to_local(
 | 
			
		||||
 | 
			
		||||
    if (!local && !remote->dead) {
 | 
			
		||||
        /* GBinderProxyObject will reference GBinderRemoteObject */
 | 
			
		||||
        GBinderProxyObject* subp = gbinder_proxy_object_new(c->local, remote);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Auto-created proxies may get spontaneously destroyed and
 | 
			
		||||
         * not necessarily on the UI thread.
 | 
			
		||||
         */
 | 
			
		||||
        subp->priv->remote_death_id = gbinder_remote_object_add_death_handler
 | 
			
		||||
            (remote, gbinder_proxy_remote_death_proc, subp);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Remote keeps an implicit reference to this auto-created
 | 
			
		||||
         * proxy. The reference gets released when the remote object
 | 
			
		||||
         * dies, i.e. by gbinder_proxy_remote_death_proc().
 | 
			
		||||
         */
 | 
			
		||||
        gbinder_local_object_ref(local = GBINDER_LOCAL_OBJECT(subp));
 | 
			
		||||
 | 
			
		||||
        /* Lock */
 | 
			
		||||
        g_mutex_lock(&priv->mutex);
 | 
			
		||||
        if (!priv->subproxies) {
 | 
			
		||||
            priv->subproxies = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
        }
 | 
			
		||||
        g_hash_table_insert(priv->subproxies, subp, subp);
 | 
			
		||||
        g_object_weak_ref(G_OBJECT(subp),
 | 
			
		||||
            gbinder_proxy_object_subproxy_gone, proxy);
 | 
			
		||||
        g_mutex_unlock(&priv->mutex);
 | 
			
		||||
        /* Unlock */
 | 
			
		||||
        local = &gbinder_proxy_object_new(c->local, remote)->parent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Release the reference returned by gbinder_object_registry_get_remote */
 | 
			
		||||
@@ -241,7 +145,6 @@ gbinder_proxy_object_converter_init(
 | 
			
		||||
    GBinderIpc* dest = proxy->parent.ipc;
 | 
			
		||||
 | 
			
		||||
    memset(convert, 0, sizeof(*convert));
 | 
			
		||||
    convert->proxy = proxy;
 | 
			
		||||
    convert->remote = remote;
 | 
			
		||||
    convert->local = local;
 | 
			
		||||
    pub->f = &gbinder_converter_fn;
 | 
			
		||||
@@ -346,7 +249,7 @@ gbinder_proxy_object_handle_transaction(
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
    if (!priv->dropped && !gbinder_remote_object_is_dead(remote)) {
 | 
			
		||||
    if (!priv->dropped && !remote->dead) {
 | 
			
		||||
        GBinderLocalRequest* fwd;
 | 
			
		||||
        GBinderProxyTx* tx = g_slice_new0(GBinderProxyTx);
 | 
			
		||||
        GBinderProxyObjectConverter convert;
 | 
			
		||||
@@ -374,6 +277,7 @@ gbinder_proxy_object_handle_transaction(
 | 
			
		||||
        gbinder_local_request_unref(fwd);
 | 
			
		||||
        *status = GBINDER_STATUS_OK;
 | 
			
		||||
    } else {
 | 
			
		||||
        GVERBOSE_("dropped: %d dead:%d", priv->dropped, remote->dead);
 | 
			
		||||
        *status = (-EBADMSG);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -397,33 +301,16 @@ gbinder_proxy_object_acquire(
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
    if (priv->remote_death_id && !object->strong_refs) {
 | 
			
		||||
        GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
        /* First strong ref, acquire the attached remote object */
 | 
			
		||||
    if (!remote->dead && !priv->dropped && !priv->acquired) {
 | 
			
		||||
        /* Not acquired yet */
 | 
			
		||||
        priv->acquired = TRUE;
 | 
			
		||||
        gbinder_driver_acquire(remote->ipc->driver, remote->handle);
 | 
			
		||||
    }
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->acquire(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_release(
 | 
			
		||||
    GBinderLocalObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    if (priv->remote_death_id && object->strong_refs == 1) {
 | 
			
		||||
        GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
        /* Last strong ref, release the attached remote object */
 | 
			
		||||
        gbinder_driver_release(remote->ipc->driver, remote->handle);
 | 
			
		||||
    }
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->release(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_drop(
 | 
			
		||||
@@ -433,7 +320,6 @@ gbinder_proxy_object_drop(
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    priv->dropped = TRUE;
 | 
			
		||||
    gbinder_proxy_object_drop_subproxies(self);
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->drop(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -459,6 +345,8 @@ gbinder_proxy_object_new(
 | 
			
		||||
        if (object) {
 | 
			
		||||
            GBinderProxyObject* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
            GDEBUG("Proxy %p %s => %u %s created", self, gbinder_ipc_name(src),
 | 
			
		||||
                remote->handle, gbinder_ipc_name(remote->ipc));
 | 
			
		||||
            self->remote = gbinder_remote_object_ref(remote);
 | 
			
		||||
            return self;
 | 
			
		||||
        }
 | 
			
		||||
@@ -477,11 +365,25 @@ gbinder_proxy_object_finalize(
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderLocalObject* local = &self->parent;
 | 
			
		||||
    GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
    gbinder_proxy_object_drop_subproxies(self);
 | 
			
		||||
    gbinder_remote_object_remove_handler(self->remote, priv->remote_death_id);
 | 
			
		||||
    gbinder_remote_object_unref(self->remote);
 | 
			
		||||
    g_mutex_clear(&priv->mutex);
 | 
			
		||||
    /*
 | 
			
		||||
     * gbinder_local_object_finalize() will also try to do the same thing
 | 
			
		||||
     * i.e. invalidate self but proxy objects need to do it before releasing
 | 
			
		||||
     * the handle, to leave no room for race conditions. That's not very good
 | 
			
		||||
     * because it grabs ipc-wide mutex but shouldn'd have much of an impact
 | 
			
		||||
     * on the performance because finalizing a proxy is not supposed to be a
 | 
			
		||||
     * frequent operation.
 | 
			
		||||
     */
 | 
			
		||||
    gbinder_ipc_invalidate_local_object(local->ipc, local);
 | 
			
		||||
    if (priv->acquired) {
 | 
			
		||||
        gbinder_driver_release(remote->ipc->driver, remote->handle);
 | 
			
		||||
    }
 | 
			
		||||
    GDEBUG("Proxy %p %s => %u %s gone", self,
 | 
			
		||||
        gbinder_ipc_name(self->parent.ipc), remote->handle,
 | 
			
		||||
        gbinder_ipc_name(remote->ipc));
 | 
			
		||||
    gbinder_remote_object_unref(remote);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -494,7 +396,6 @@ gbinder_proxy_object_init(
 | 
			
		||||
        THIS_TYPE, GBinderProxyObjectPriv);
 | 
			
		||||
 | 
			
		||||
    self->priv = priv;
 | 
			
		||||
    g_mutex_init(&priv->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -509,7 +410,6 @@ gbinder_proxy_object_class_init(
 | 
			
		||||
    klass->can_handle_transaction = gbinder_proxy_object_can_handle_transaction;
 | 
			
		||||
    klass->handle_transaction = gbinder_proxy_object_handle_transaction;
 | 
			
		||||
    klass->acquire = gbinder_proxy_object_acquire;
 | 
			
		||||
    klass->release = gbinder_proxy_object_release;
 | 
			
		||||
    klass->drop = gbinder_proxy_object_drop;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -90,7 +90,7 @@ gbinder_reader_at_end(
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->ptr >= p->end;
 | 
			
		||||
    return !p || p->ptr >= p->end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -138,6 +138,62 @@ gbinder_reader_read_bool(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int8(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gint8* value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_reader_read_uint8(reader, (guint8*)value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_uint8(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint8* value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    /* Primitive values are supposed to be padded to 4-byte boundary */
 | 
			
		||||
    if (value) {
 | 
			
		||||
        guint32 padded;
 | 
			
		||||
 | 
			
		||||
        if (gbinder_reader_read_uint32(reader, &padded)) {
 | 
			
		||||
            *value = (guint8)padded;
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return gbinder_reader_read_uint32(reader, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gint16* value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_reader_read_uint16(reader, (guint16*)value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_uint16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint16* value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    /* Primitive values are supposed to be padded to 4-byte boundary */
 | 
			
		||||
    if (value) {
 | 
			
		||||
        guint32 padded;
 | 
			
		||||
 | 
			
		||||
        if (gbinder_reader_read_uint32(reader, &padded)) {
 | 
			
		||||
            *value = (guint16)padded;
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return gbinder_reader_read_uint32(reader, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_int32(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
@@ -680,13 +736,38 @@ gbinder_reader_read_byte_array(
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_get_data(
 | 
			
		||||
    const GBinderReader* reader,
 | 
			
		||||
    gsize* size) /* Since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    if (p) {
 | 
			
		||||
        const GBinderReaderData* data = p->data;
 | 
			
		||||
 | 
			
		||||
        if (data && data->buffer) {
 | 
			
		||||
            if (size) {
 | 
			
		||||
                *size = data->buffer->size;
 | 
			
		||||
            }
 | 
			
		||||
            return data->buffer->data;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* No data */
 | 
			
		||||
    if (size) {
 | 
			
		||||
        *size = 0;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_read(
 | 
			
		||||
    const GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->ptr - p->start;
 | 
			
		||||
    return p ? (p->ptr - p->start) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
@@ -695,7 +776,7 @@ gbinder_reader_bytes_remaining(
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->end - p->ptr;
 | 
			
		||||
    return p ? (p->end - p->ptr) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -703,8 +784,11 @@ gbinder_reader_copy(
 | 
			
		||||
    GBinderReader* dest,
 | 
			
		||||
    const GBinderReader* src)
 | 
			
		||||
{
 | 
			
		||||
    /* It's actually quite simple :) */
 | 
			
		||||
    memcpy(dest, src, sizeof(*dest));
 | 
			
		||||
    if (src) {
 | 
			
		||||
        memcpy(dest, src, sizeof(*dest));
 | 
			
		||||
    } else {
 | 
			
		||||
        memset(dest, 0, sizeof(*dest));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -77,7 +77,11 @@ gbinder_remote_object_handle_death_on_main_thread(
 | 
			
		||||
        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);
 | 
			
		||||
@@ -140,10 +144,17 @@ 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;
 | 
			
		||||
        gbinder_driver_clear_death_notification(driver, self);
 | 
			
		||||
        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 */
 | 
			
		||||
@@ -284,11 +295,12 @@ gbinder_remote_object_finalize(
 | 
			
		||||
    GBinderIpc* ipc = self->ipc;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_invalidate_remote_handle(ipc, self->handle);
 | 
			
		||||
    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);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,9 @@
 | 
			
		||||
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
 | 
			
		||||
#define UNSET_WORK_SOURCE (-1)
 | 
			
		||||
 | 
			
		||||
#define BINDER_VND_HEADER GBINDER_FOURCC('V', 'N', 'D', 'R')
 | 
			
		||||
#define BINDER_SYS_HEADER GBINDER_FOURCC('S', 'Y', 'S', 'T')
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcProtocol callbacks (see Parcel::writeInterfaceToken in Android)
 | 
			
		||||
 * Note that there are two slightly different kinds of Parcels:
 | 
			
		||||
@@ -173,6 +176,50 @@ static const GBinderRpcProtocol gbinder_rpc_protocol_aidl2 = {
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_aidl2_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * AIDL protocol appeared in Android 11 (API level 30)
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_aidl3_write_rpc_header(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_writer_append_int32(writer, BINDER_RPC_FLAGS);
 | 
			
		||||
    gbinder_writer_append_int32(writer, UNSET_WORK_SOURCE);
 | 
			
		||||
    gbinder_writer_append_int32(writer, BINDER_SYS_HEADER);
 | 
			
		||||
    gbinder_writer_append_string16(writer, iface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_aidl3_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
{
 | 
			
		||||
    if (txcode > GBINDER_TRANSACTION(0,0,0)) {
 | 
			
		||||
        *iface = NULL;
 | 
			
		||||
    } else if (gbinder_reader_read_int32(reader, NULL) /* flags */ &&
 | 
			
		||||
        gbinder_reader_read_int32(reader, NULL) /* work source */ &&
 | 
			
		||||
        gbinder_reader_read_int32(reader, NULL) /* sys header */) {
 | 
			
		||||
        *iface = gbinder_reader_read_string16(reader);
 | 
			
		||||
    } else {
 | 
			
		||||
        *iface = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *iface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GBinderRpcProtocol gbinder_rpc_protocol_aidl3 = {
 | 
			
		||||
    .name = "aidl3",
 | 
			
		||||
    .ping_tx = GBINDER_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_aidl_write_ping, /* no payload */
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_aidl3_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_aidl3_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * The original /dev/hwbinder protocol.
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -225,6 +272,7 @@ static const GBinderRpcProtocol gbinder_rpc_protocol_hidl = {
 | 
			
		||||
static const GBinderRpcProtocol* gbinder_rpc_protocol_list[] = {
 | 
			
		||||
    &gbinder_rpc_protocol_aidl,
 | 
			
		||||
    &gbinder_rpc_protocol_aidl2,
 | 
			
		||||
    &gbinder_rpc_protocol_aidl3,
 | 
			
		||||
    &gbinder_rpc_protocol_hidl
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -82,11 +82,12 @@ typedef struct gbinder_servicemanager_type {
 | 
			
		||||
static const GBinderServiceManagerType gbinder_servicemanager_types[] = {
 | 
			
		||||
    { "aidl", gbinder_servicemanager_aidl_get_type },
 | 
			
		||||
    { "aidl2", gbinder_servicemanager_aidl2_get_type },
 | 
			
		||||
    { "aidl3", gbinder_servicemanager_aidl3_get_type },
 | 
			
		||||
    { "hidl", gbinder_servicemanager_hidl_get_type }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SERVICEMANAGER_TYPE_AIDL (gbinder_servicemanager_types + 0)
 | 
			
		||||
#define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 2)
 | 
			
		||||
#define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 3)
 | 
			
		||||
#define SERVICEMANAGER_TYPE_DEFAULT SERVICEMANAGER_TYPE_AIDL
 | 
			
		||||
 | 
			
		||||
static GHashTable* gbinder_servicemanager_map = NULL;
 | 
			
		||||
@@ -698,6 +699,13 @@ gbinder_servicemanager_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_servicemanager_device(
 | 
			
		||||
    GBinderServiceManager* self) /* Since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->dev : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_is_present(
 | 
			
		||||
    GBinderServiceManager* self) /* Since 1.0.25 */
 | 
			
		||||
 
 | 
			
		||||
@@ -61,16 +61,6 @@ G_DEFINE_TYPE(GBinderServiceManagerAidl,
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_AIDL(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
 | 
			
		||||
    GBinderServiceManagerAidl)
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) \
 | 
			
		||||
    G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
 | 
			
		||||
    GBinderServiceManagerAidlClass)
 | 
			
		||||
 | 
			
		||||
enum gbinder_servicemanager_aidl_calls {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SERVICEMANAGER_AIDL_IFACE  "android.os.IServiceManager"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,20 @@ typedef struct gbinder_servicemanager_aidl_class {
 | 
			
		||||
    G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
 | 
			
		||||
    GBinderServiceManagerAidlClass)
 | 
			
		||||
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) \
 | 
			
		||||
    G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
 | 
			
		||||
    GBinderServiceManagerAidlClass)
 | 
			
		||||
 | 
			
		||||
enum gbinder_servicemanager_aidl_calls {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_DEFAULT (0x08)
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_ALL     (0x0f)
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_AIDL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -64,3 +78,4 @@ typedef struct gbinder_servicemanager_aidl_class {
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										176
									
								
								src/gbinder_servicemanager_aidl3.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/gbinder_servicemanager_aidl3.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2021 Gary Wang <gary.wang@canonical.com>
 | 
			
		||||
 * Copyright (C) 2021 Madhushan Nishantha <jlmadushan@gmail.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_servicemanager_aidl.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_reader_p.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
 | 
			
		||||
/* Variant of AIDL servicemanager appeared in Android 11 (API level 30) */
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerAidl GBinderServiceManagerAidl3;
 | 
			
		||||
typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl3Class;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderServiceManagerAidl3,
 | 
			
		||||
    gbinder_servicemanager_aidl3,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER_AIDL)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_servicemanager_aidl3_parent_class
 | 
			
		||||
 | 
			
		||||
enum gbinder_stability_level {
 | 
			
		||||
    UNDECLARED = 0,
 | 
			
		||||
    VENDOR = 0b000011,
 | 
			
		||||
    SYSTEM = 0b001100,
 | 
			
		||||
    VINTF = 0b111111
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_servicemanager_aidl3_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GBinderClient* client = self->client;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
    GBinderRemoteObject* obj;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        CHECK_SERVICE_TRANSACTION, req, status, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    gbinder_reader_read_int32(&reader, NULL /* stability */);
 | 
			
		||||
    obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_aidl3_add_service_req(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj);
 | 
			
		||||
    /*
 | 
			
		||||
     * Starting from Android 11, to add a service, Android framework requires
 | 
			
		||||
     * an additional field `stability` when reading a strong binder.
 | 
			
		||||
     */
 | 
			
		||||
    gbinder_local_request_append_int32(req, SYSTEM);
 | 
			
		||||
    gbinder_local_request_append_int32(req, 0);
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_aidl3_list(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
    GBinderClient* client = manager->client;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Starting from Android 11, no `index` field is required but
 | 
			
		||||
     * only with `dump priority` field to request to list services.
 | 
			
		||||
     * As a result, a vector of strings which stands for service
 | 
			
		||||
     * list is given in the binder response.
 | 
			
		||||
     */
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        LIST_SERVICES_TRANSACTION, req, NULL, api);
 | 
			
		||||
 | 
			
		||||
    if (reply) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
        gint32 count;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
        gbinder_reader_read_int32(&reader, NULL /* status */);
 | 
			
		||||
        if (gbinder_reader_read_int32(&reader, &count)) {
 | 
			
		||||
            int i;
 | 
			
		||||
 | 
			
		||||
            /* Iterate each service name */
 | 
			
		||||
            for (i = 0; i < count; i++) {
 | 
			
		||||
                g_ptr_array_add(list, gbinder_reader_read_string16(&reader));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_remote_reply_unref(reply);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_ptr_array_add(list, NULL);
 | 
			
		||||
    return (char**)g_ptr_array_free(list, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl3_init(
 | 
			
		||||
    GBinderServiceManagerAidl3* self)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl3_class_init(
 | 
			
		||||
    GBinderServiceManagerAidl3Class* klass)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    klass->add_service_req = gbinder_servicemanager_aidl3_add_service_req;
 | 
			
		||||
    manager->list = gbinder_servicemanager_aidl3_list;
 | 
			
		||||
    manager->get_service = gbinder_servicemanager_aidl3_get_service;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -110,6 +110,7 @@ gbinder_servicemanager_exit(
 | 
			
		||||
 | 
			
		||||
GType gbinder_servicemanager_aidl_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
GType gbinder_servicemanager_aidl2_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
GType gbinder_servicemanager_aidl3_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
GType gbinder_servicemanager_hidl_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_writer_p.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_fmq_p.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_object_converter.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
@@ -40,6 +41,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 +156,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,
 | 
			
		||||
@@ -187,12 +165,35 @@ gbinder_writer_init(
 | 
			
		||||
    gbinder_writer_cast(self)->data = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_writer_get_data(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    gsize* size) /* Since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
 | 
			
		||||
        if (size) {
 | 
			
		||||
            *size = buf->len;
 | 
			
		||||
        }
 | 
			
		||||
        return buf->data;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (size) {
 | 
			
		||||
            *size = 0;
 | 
			
		||||
        }
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_writer_bytes_written(
 | 
			
		||||
    GBinderWriter* self) /* since 1.0.21 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
    return data->bytes->len;
 | 
			
		||||
 | 
			
		||||
    return G_LIKELY(data) ? data->bytes->len : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -212,12 +213,34 @@ gbinder_writer_data_append_bool(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    gboolean value)
 | 
			
		||||
{
 | 
			
		||||
    guint8 padded[4];
 | 
			
		||||
    /* Primitive values are padded to 4-byte boundary */
 | 
			
		||||
    gbinder_writer_data_append_int32(data, value != FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Boolean values are padded to 4-byte boundary */
 | 
			
		||||
    padded[0] = (value != FALSE);
 | 
			
		||||
    padded[1] = padded[2] = padded[3] = 0;
 | 
			
		||||
    g_byte_array_append(data->bytes, padded, sizeof(padded));
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int8(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    guint8 value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        /* Primitive values are padded to 4-byte boundary */
 | 
			
		||||
        gbinder_writer_data_append_int32(data, value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int16(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    guint16 value) /* Since 1.1.15 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        /* Primitive values are padded to 4-byte boundary */
 | 
			
		||||
        gbinder_writer_data_append_int32(data, value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -580,17 +603,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(
 | 
			
		||||
@@ -603,7 +615,6 @@ gbinder_writer_data_close_fd(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fd(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
@@ -645,6 +656,50 @@ gbinder_writer_append_fd(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fda_object(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const GBinderFds *fds,
 | 
			
		||||
    const GBinderParent* parent)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    guint written;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
 | 
			
		||||
    written = data->io->encode_fda_object(buf->data + offset, fds, parent);
 | 
			
		||||
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(buf, offset + written);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fds(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const GBinderFds *fds,
 | 
			
		||||
    const GBinderParent* parent)
 | 
			
		||||
{
 | 
			
		||||
    if (fds) {
 | 
			
		||||
        /* Size, fds data buffer and fd_array_object */
 | 
			
		||||
        const gsize fds_total = sizeof(GBinderFds) +
 | 
			
		||||
            sizeof(int) * (fds->num_fds + fds->num_ints);
 | 
			
		||||
        GBinderParent fds_parent;
 | 
			
		||||
 | 
			
		||||
        gbinder_writer_data_append_int64(data, fds_total);
 | 
			
		||||
        fds_parent.index = gbinder_writer_data_append_buffer_object(data,
 | 
			
		||||
            fds, fds_total, parent);
 | 
			
		||||
        fds_parent.offset = sizeof(GBinderFds);
 | 
			
		||||
        gbinder_writer_data_append_fda_object(data, fds, &fds_parent);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* If the pointer is null only write zero size */
 | 
			
		||||
        gbinder_writer_data_append_int64(data, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
gbinder_writer_append_buffer_object_with_parent(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
@@ -681,9 +736,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 +766,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 +791,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 +803,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 +832,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 +839,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 +883,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 +905,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 +917,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 +925,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 +933,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -940,6 +1016,67 @@ gbinder_writer_append_byte_array(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fmq_descriptor(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const GBinderFmq* queue)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent parent;
 | 
			
		||||
    GBinderMQDescriptor* desc = gbinder_fmq_get_descriptor(queue);
 | 
			
		||||
    GBinderMQDescriptor* mqdesc = gutil_memdup(desc,
 | 
			
		||||
        sizeof(GBinderMQDescriptor));
 | 
			
		||||
 | 
			
		||||
    const gsize vec_total =
 | 
			
		||||
        desc->grantors.count * sizeof(GBinderFmqGrantorDescriptor);
 | 
			
		||||
    void* vec_buf = gutil_memdup(desc->grantors.data.ptr, vec_total);
 | 
			
		||||
 | 
			
		||||
    const gsize fds_total = sizeof(GBinderFds) +
 | 
			
		||||
        sizeof(int) * (desc->data.fds->num_fds + desc->data.fds->num_ints);
 | 
			
		||||
    GBinderFds* fds = gutil_memdup(desc->data.fds, fds_total);
 | 
			
		||||
 | 
			
		||||
    mqdesc->data.fds = fds;
 | 
			
		||||
    data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, fds);
 | 
			
		||||
 | 
			
		||||
    /* Fill in the grantor vector descriptor */
 | 
			
		||||
    if (vec_buf) {
 | 
			
		||||
        mqdesc->grantors.count = desc->grantors.count;
 | 
			
		||||
        mqdesc->grantors.data.ptr = vec_buf;
 | 
			
		||||
        mqdesc->grantors.owns_buffer = TRUE;
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, vec_buf);
 | 
			
		||||
    }
 | 
			
		||||
    data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, mqdesc);
 | 
			
		||||
 | 
			
		||||
    /* Write the FMQ descriptor object */
 | 
			
		||||
    parent.index = gbinder_writer_data_append_buffer_object(data,
 | 
			
		||||
        mqdesc, sizeof(*mqdesc), NULL);
 | 
			
		||||
 | 
			
		||||
    /* Write the vector data buffer */
 | 
			
		||||
    parent.offset = GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET;
 | 
			
		||||
    gbinder_writer_data_append_buffer_object(data, vec_buf, vec_total,
 | 
			
		||||
        &parent);
 | 
			
		||||
 | 
			
		||||
    /* Write the fds */
 | 
			
		||||
    parent.offset = GBINDER_MQ_DESCRIPTOR_FDS_OFFSET;
 | 
			
		||||
    gbinder_writer_data_append_fds(data, mqdesc->data.fds, &parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_fmq_descriptor(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const GBinderFmq* queue) /* since 1.1.14 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data) && G_LIKELY(queue)) {
 | 
			
		||||
        gbinder_writer_data_append_fmq_descriptor(data, queue);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_remote_object(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
@@ -994,6 +1131,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,
 | 
			
		||||
 
 | 
			
		||||
@@ -163,6 +163,12 @@ gbinder_writer_data_append_remote_object(
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fd(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    int fd)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_WRITER_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
							
							
						
						
									
										140
									
								
								test/ashmem-test/Makefile
									
									
									
									
									
										Normal 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)
 | 
			
		||||
							
								
								
									
										260
									
								
								test/ashmem-test/ashmem-test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								test/ashmem-test/ashmem-test.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										178
									
								
								test/binder-call/Makefile
									
									
									
									
									
										Normal 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 $@
 | 
			
		||||
							
								
								
									
										814
									
								
								test/binder-call/binder-call.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										814
									
								
								test/binder-call/binder-call.c
									
									
									
									
									
										Normal 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:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										97
									
								
								test/binder-call/binder-call.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								test/binder-call/binder-call.h
									
									
									
									
									
										Normal 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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										98
									
								
								test/binder-call/cmdline.l
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								test/binder-call/cmdline.l
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										399
									
								
								test/binder-call/cmdline.y
									
									
									
									
									
										Normal 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]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -9,6 +9,7 @@ all:
 | 
			
		||||
	@$(MAKE) -C unit_config $*
 | 
			
		||||
	@$(MAKE) -C unit_driver $*
 | 
			
		||||
	@$(MAKE) -C unit_eventloop $*
 | 
			
		||||
	@$(MAKE) -C unit_fmq $*
 | 
			
		||||
	@$(MAKE) -C unit_ipc $*
 | 
			
		||||
	@$(MAKE) -C unit_local_object $*
 | 
			
		||||
	@$(MAKE) -C unit_local_reply $*
 | 
			
		||||
@@ -23,6 +24,7 @@ all:
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl2 $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl3 $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_hidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicename $*
 | 
			
		||||
	@$(MAKE) -C unit_servicepoll $*
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -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))
 | 
			
		||||
 | 
			
		||||
@@ -54,6 +55,7 @@ static GHashTable* test_fd_map = NULL;
 | 
			
		||||
static GHashTable* test_node_map = NULL;
 | 
			
		||||
static GPrivate test_looper = G_PRIVATE_INIT(NULL);
 | 
			
		||||
static GPrivate test_tx_state = G_PRIVATE_INIT(NULL);
 | 
			
		||||
static guint32 last_auto_handle = 0;
 | 
			
		||||
 | 
			
		||||
G_LOCK_DEFINE_STATIC(test_binder);
 | 
			
		||||
static GMainLoop* test_binder_exit_loop = NULL;
 | 
			
		||||
@@ -124,6 +126,7 @@ typedef struct test_binder_node TestBinderNode;
 | 
			
		||||
struct test_binder_node {
 | 
			
		||||
    int fd;
 | 
			
		||||
    char* path;
 | 
			
		||||
    const char* name;
 | 
			
		||||
    TestBinder* binder;
 | 
			
		||||
    TestBinderNode* other;
 | 
			
		||||
    TEST_LOOPER looper_enabled;
 | 
			
		||||
@@ -145,7 +148,6 @@ typedef struct test_binder {
 | 
			
		||||
    GHashTable* object_map; /* GBinderLocalObject* => handle */
 | 
			
		||||
    GHashTable* handle_map; /* handle => GBinderLocalObject* */
 | 
			
		||||
    TestBinderSubmitThread* submit_thread;
 | 
			
		||||
    guint32 last_auto_handle;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    gboolean passthrough;
 | 
			
		||||
    TestBinderNode node[2];
 | 
			
		||||
@@ -436,13 +438,16 @@ test_binder_register_object_locked(
 | 
			
		||||
    g_assert(!g_hash_table_contains(binder->object_map, obj));
 | 
			
		||||
    g_assert(!g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)));
 | 
			
		||||
    if (h == AUTO_HANDLE) {
 | 
			
		||||
        h = ++(binder->last_auto_handle);
 | 
			
		||||
        h = ++last_auto_handle;
 | 
			
		||||
        while (g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)) ||
 | 
			
		||||
            g_hash_table_contains(binder->object_map, GINT_TO_POINTER(h))) {
 | 
			
		||||
            h = ++(binder->last_auto_handle);
 | 
			
		||||
            h = ++last_auto_handle;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (last_auto_handle < h) {
 | 
			
		||||
        /* Avoid re-using handles, to make debugging easier */
 | 
			
		||||
        last_auto_handle = h;
 | 
			
		||||
    }
 | 
			
		||||
    GDEBUG("Object %p <=> handle %u", obj, h);
 | 
			
		||||
    GDEBUG("Object %p <=> handle %u %s", obj, h, binder->node[0].name);
 | 
			
		||||
    g_hash_table_insert(binder->handle_map, GINT_TO_POINTER(h), obj);
 | 
			
		||||
    g_hash_table_insert(binder->object_map, obj, GINT_TO_POINTER(h));
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(obj), test_binder_local_object_gone, binder);
 | 
			
		||||
@@ -462,10 +467,10 @@ test_io_passthough_handle_to_object(
 | 
			
		||||
        gpointer obj = g_hash_table_lookup(binder->handle_map, key);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Handle %u => object %p %s", (guint) handle, obj,
 | 
			
		||||
            binder->node[0].path);
 | 
			
		||||
            binder->node[0].name);
 | 
			
		||||
        return GPOINTER_TO_SIZE(obj);
 | 
			
		||||
    }
 | 
			
		||||
    GDEBUG("Unexpected handle %u %s", (guint) handle, binder->node[0].path);
 | 
			
		||||
    GDEBUG("Unexpected handle %u %s", (guint) handle, binder->node[0].name);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -483,13 +488,13 @@ test_io_passthough_object_to_handle(
 | 
			
		||||
        guint64 handle = GPOINTER_TO_SIZE(value);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Object %p => handle %u %s", key, (guint) handle,
 | 
			
		||||
            binder->node[0].path);
 | 
			
		||||
            binder->node[0].name);
 | 
			
		||||
        return handle;
 | 
			
		||||
    } else if (key) {
 | 
			
		||||
        GDEBUG("Auto-registering object %p %s", key, binder->node[0].path);
 | 
			
		||||
        GDEBUG("Auto-registering object %p %s", key, binder->node[0].name);
 | 
			
		||||
        return test_binder_register_object_locked(binder, key, AUTO_HANDLE);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Unexpected object %p %s", key, binder->node[0].path);
 | 
			
		||||
        GDEBUG("Unexpected object %p %s", key, binder->node[0].name);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -564,7 +569,7 @@ test_binder_node_read(
 | 
			
		||||
        const guint32 cmd = node->next_cmd[0];
 | 
			
		||||
        const guint total = 4 + _IOC_SIZE(cmd);
 | 
			
		||||
 | 
			
		||||
        /* Alread have one ready */
 | 
			
		||||
        /* Already have one ready */
 | 
			
		||||
        if (!(test_binder_cmd_read_flags(cmd) & flags)) {
 | 
			
		||||
            GDEBUG("Cmd 0x%08x not for %d", cmd, gettid());
 | 
			
		||||
            *bytes_read = 0;
 | 
			
		||||
@@ -666,7 +671,7 @@ test_tx_state_acquire(
 | 
			
		||||
    while (g_atomic_pointer_get(&node->tx_state) != my_tx_state &&
 | 
			
		||||
        !g_atomic_pointer_compare_and_exchange(&node->tx_state, NULL,
 | 
			
		||||
         my_tx_state)) {
 | 
			
		||||
        GDEBUG("Thread %d is waiting to become a transacton thread",
 | 
			
		||||
        GDEBUG("Thread %d is waiting to become a transaction thread",
 | 
			
		||||
            my_tx_state->tid);
 | 
			
		||||
        test_io_short_wait();
 | 
			
		||||
    }
 | 
			
		||||
@@ -695,7 +700,7 @@ test_tx_state_release(
 | 
			
		||||
        my_tx_state->depth--;
 | 
			
		||||
        my_tx_state->stack = g_renew(TEST_TX_STATE, my_tx_state->stack,
 | 
			
		||||
            my_tx_state->depth);
 | 
			
		||||
        GDEBUG("Thread %d is still a transacton thread", my_tx_state->tid);
 | 
			
		||||
        GDEBUG("Thread %d is still a transaction thread", my_tx_state->tid);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -842,6 +847,8 @@ test_io_passthough_write_64(
 | 
			
		||||
                        buffer->buffer = GPOINTER_TO_SIZE(copy);
 | 
			
		||||
                        g_hash_table_replace(fd->destroy_map, copy, NULL);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    GDEBUG("Unexpected object type 0x%08x", *obj_ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
 | 
			
		||||
@@ -1593,6 +1600,7 @@ test_binder_fd_new(
 | 
			
		||||
    binder_fd->node = node;
 | 
			
		||||
    binder_fd->fd = dup(node->fd);
 | 
			
		||||
    binder_fd->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
    GDEBUG("fd %d => %d", binder_fd->fd, node->fd);
 | 
			
		||||
    return binder_fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1601,11 +1609,12 @@ gbinder_system_open(
 | 
			
		||||
    const char* path,
 | 
			
		||||
    int flags)
 | 
			
		||||
{
 | 
			
		||||
    static const char path_prefix[] = "/dev/";
 | 
			
		||||
    static const char binder_suffix[] = "binder";
 | 
			
		||||
    static const char binder_private_suffix[] = "binder-private";
 | 
			
		||||
    static const char private_suffix[] = "-private";
 | 
			
		||||
 | 
			
		||||
    if (path && g_str_has_prefix(path, "/dev/") &&
 | 
			
		||||
    if (path && g_str_has_prefix(path, path_prefix) &&
 | 
			
		||||
        (g_str_has_suffix(path, binder_suffix) ||
 | 
			
		||||
         g_str_has_suffix(path, binder_private_suffix))) {
 | 
			
		||||
        TestBinderFd* fd;
 | 
			
		||||
@@ -1630,7 +1639,7 @@ gbinder_system_open(
 | 
			
		||||
                node = binder->node + PRIVATE;
 | 
			
		||||
                node->path = g_strdup(path);
 | 
			
		||||
                binder->node[PUBLIC].path = g_strndup(path,
 | 
			
		||||
                    strlen(path) - strlen(private_suffix) - 1);
 | 
			
		||||
                    strlen(path) - strlen(private_suffix));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!test_node_map) {
 | 
			
		||||
@@ -1643,6 +1652,7 @@ gbinder_system_open(
 | 
			
		||||
                this_node->binder = binder;
 | 
			
		||||
                this_node->fd = fds[i];
 | 
			
		||||
                this_node->other = binder->node + ((i + 1) % 2);
 | 
			
		||||
                this_node->name = this_node->path + strlen(path_prefix);
 | 
			
		||||
                g_hash_table_replace(test_node_map, this_node->path, this_node);
 | 
			
		||||
            }
 | 
			
		||||
            binder->object_map = g_hash_table_new
 | 
			
		||||
@@ -1650,8 +1660,8 @@ gbinder_system_open(
 | 
			
		||||
            binder->handle_map = g_hash_table_new
 | 
			
		||||
                (g_direct_hash, g_direct_equal);
 | 
			
		||||
            GDEBUG("Created %s (%d) <=> %s (%d) binder",
 | 
			
		||||
                binder->node[0].path, binder->node[0].fd,
 | 
			
		||||
                binder->node[1].path, binder->node[1].fd);
 | 
			
		||||
                binder->node[0].name, binder->node[0].fd,
 | 
			
		||||
                binder->node[1].name, binder->node[1].fd);
 | 
			
		||||
        }
 | 
			
		||||
        fd = test_binder_fd_new(node);
 | 
			
		||||
        if (!test_fd_map) {
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,10 @@ test_run_in_context(
 | 
			
		||||
/* Helper macros */
 | 
			
		||||
 | 
			
		||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
 | 
			
		||||
#  define TEST_INT8_BYTES_4(v) \
 | 
			
		||||
    (guint8)(v), 0, 0, 0
 | 
			
		||||
#  define TEST_INT16_BYTES_4(v) \
 | 
			
		||||
    (guint8)(v), (guint8)((v) >> 8), 0, 0
 | 
			
		||||
#  define TEST_INT16_BYTES(v) \
 | 
			
		||||
    (guint8)(v), (guint8)((v) >> 8)
 | 
			
		||||
#  define TEST_INT32_BYTES(v) \
 | 
			
		||||
@@ -91,6 +95,10 @@ test_run_in_context(
 | 
			
		||||
    (guint8)(((guint64)(v)) >> 32), (guint8)(((guint64)(v)) >> 40), \
 | 
			
		||||
    (guint8)(((guint64)(v)) >> 48), (guint8)(((guint64)(v)) >> 56)
 | 
			
		||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
 | 
			
		||||
#  define TEST_INT8_BYTES_4(v) \
 | 
			
		||||
    0, 0, 0, (guint8)(v)
 | 
			
		||||
#  define TEST_INT16_BYTES_4(v) \
 | 
			
		||||
    0, 0, (guint8)((v) >> 8), (guint8)(v)
 | 
			
		||||
#  define TEST_INT16_BYTES(v) \
 | 
			
		||||
    (guint8)((v) >> 8), (guint8)(v)
 | 
			
		||||
#  define TEST_INT32_BYTES(v) \
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ unit_client \
 | 
			
		||||
unit_config \
 | 
			
		||||
unit_driver \
 | 
			
		||||
unit_eventloop \
 | 
			
		||||
unit_fmq \
 | 
			
		||||
unit_ipc \
 | 
			
		||||
unit_local_object \
 | 
			
		||||
unit_local_reply \
 | 
			
		||||
@@ -25,6 +26,7 @@ unit_remote_request \
 | 
			
		||||
unit_servicemanager \
 | 
			
		||||
unit_servicemanager_aidl \
 | 
			
		||||
unit_servicemanager_aidl2 \
 | 
			
		||||
unit_servicemanager_aidl3 \
 | 
			
		||||
unit_servicemanager_hidl \
 | 
			
		||||
unit_servicename \
 | 
			
		||||
unit_servicepoll \
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
@@ -79,6 +80,7 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_client_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_client_interface(NULL));
 | 
			
		||||
    g_assert(!gbinder_client_interface2(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_client_rpc_header(NULL, 0));
 | 
			
		||||
    gbinder_client_unref(NULL);
 | 
			
		||||
    g_assert(!gbinder_client_new_request(NULL));
 | 
			
		||||
    g_assert(!gbinder_client_new_request2(NULL, 0));
 | 
			
		||||
@@ -131,6 +133,11 @@ test_interfaces(
 | 
			
		||||
    };
 | 
			
		||||
    GBinderClient* client = gbinder_client_new2(obj, ifaces,
 | 
			
		||||
        G_N_ELEMENTS(ifaces));
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBytes* rpc_header;
 | 
			
		||||
    gsize len;
 | 
			
		||||
    const void* hdr;
 | 
			
		||||
 | 
			
		||||
    g_assert(client);
 | 
			
		||||
    g_assert_cmpstr(gbinder_client_interface(client), == ,"11");
 | 
			
		||||
@@ -138,7 +145,25 @@ test_interfaces(
 | 
			
		||||
    g_assert_cmpstr(gbinder_client_interface2(client, 22), == ,"22");
 | 
			
		||||
    g_assert_cmpstr(gbinder_client_interface2(client, 33), == ,"33");
 | 
			
		||||
    g_assert(!gbinder_client_interface2(client, 34));
 | 
			
		||||
    g_assert(!gbinder_client_rpc_header(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));
 | 
			
		||||
 | 
			
		||||
    /* Check the RPC header */
 | 
			
		||||
    rpc_header = gbinder_client_rpc_header(client, 33);
 | 
			
		||||
    req = gbinder_client_new_request2(client, 33);
 | 
			
		||||
    g_assert(rpc_header);
 | 
			
		||||
    g_assert(req);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    hdr = gbinder_writer_get_data(&writer, &len);
 | 
			
		||||
    g_assert(hdr);
 | 
			
		||||
    g_assert_cmpuint(len, == ,g_bytes_get_size(rpc_header));
 | 
			
		||||
    g_assert(!memcmp(hdr, g_bytes_get_data(rpc_header, NULL), len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
 | 
			
		||||
    /* Client with no interface info */
 | 
			
		||||
@@ -432,6 +457,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -230,6 +230,7 @@ test_dirs(
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
    char* file1 = g_build_filename(subdir, "a.conf", NULL);
 | 
			
		||||
    char* file2 = g_build_filename(subdir, "b.conf", NULL);
 | 
			
		||||
    char* file3 = g_build_filename(subdir, "c.conf", NULL);
 | 
			
		||||
    char* random_file = g_build_filename(subdir, "foo", NULL);
 | 
			
		||||
    static const char garbage[] = "foo";
 | 
			
		||||
    static const char config[] =
 | 
			
		||||
@@ -246,12 +247,18 @@ test_dirs(
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n";
 | 
			
		||||
    static const char config3[] =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder3 = aidl3\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder3 = aidl3\n";
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpint(mkdir(subdir, 0700), == ,0);
 | 
			
		||||
    g_assert_cmpint(mkdir(notafile, 0700), == ,0);
 | 
			
		||||
    g_assert(g_file_set_contents(file, config, -1, NULL));
 | 
			
		||||
    g_assert(g_file_set_contents(file1, config1, -1, NULL));
 | 
			
		||||
    g_assert(g_file_set_contents(file2, config2, -1, NULL));
 | 
			
		||||
    g_assert(g_file_set_contents(file3, config3, -1, NULL));
 | 
			
		||||
    g_assert(g_file_set_contents(random_file, garbage, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
@@ -263,9 +270,11 @@ test_dirs(
 | 
			
		||||
    k = gbinder_config_get();
 | 
			
		||||
    g_assert(k);
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/hbinder",b), == ,"hidl");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/hwbinder",b), == ,"hidl");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3");
 | 
			
		||||
 | 
			
		||||
    /* Remove the default file and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
@@ -274,8 +283,10 @@ test_dirs(
 | 
			
		||||
    g_assert(k);
 | 
			
		||||
    g_assert(!test_value(k,"Protocol","/dev/hbinder",b));
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/hwbinder",b), == ,"hidl");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3");
 | 
			
		||||
 | 
			
		||||
    /* Damage one of the files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
@@ -285,7 +296,9 @@ test_dirs(
 | 
			
		||||
    g_assert(!test_value(k,"Protocol","/dev/hbinder",b));
 | 
			
		||||
    g_assert(!test_value(k,"Protocol","/dev/hwbinder",b));
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3");
 | 
			
		||||
 | 
			
		||||
    /* Disallow access to one of the files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
@@ -295,12 +308,15 @@ test_dirs(
 | 
			
		||||
    g_assert(!test_value(k,"Protocol","/dev/hbinder",b));
 | 
			
		||||
    g_assert(!test_value(k,"Protocol","/dev/hwbinder",b));
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3");
 | 
			
		||||
 | 
			
		||||
    /* Delete the remaining files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    g_assert_cmpint(remove(file1), == ,0);
 | 
			
		||||
    g_assert_cmpint(remove(file2), == ,0);
 | 
			
		||||
    g_assert_cmpint(remove(file3), == ,0);
 | 
			
		||||
    g_assert(!gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    /* Undo all the damage */
 | 
			
		||||
@@ -312,6 +328,7 @@ test_dirs(
 | 
			
		||||
    g_free(file);
 | 
			
		||||
    g_free(file1);
 | 
			
		||||
    g_free(file2);
 | 
			
		||||
    g_free(file3);
 | 
			
		||||
    g_free(random_file);
 | 
			
		||||
 | 
			
		||||
    remove(notafile);
 | 
			
		||||
@@ -464,6 +481,20 @@ static const TestPresetsData test_presets_data [] = {
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "/dev/vndbinder = aidl2\n"
 | 
			
		||||
    },{
 | 
			
		||||
        "30",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 30",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 30\n"
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder = aidl3\n"
 | 
			
		||||
        "/dev/vndbinder = aidl3\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl3\n"
 | 
			
		||||
        "/dev/vndbinder = aidl3\n"
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_fmq/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_fmq/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_fmq
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										597
									
								
								unit/unit_fmq/unit_fmq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										597
									
								
								unit/unit_fmq/unit_fmq.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,597 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_fmq_p.h"
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/syscall.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
typedef struct test_fmq_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    gsize item_size;
 | 
			
		||||
    gsize max_num_items;
 | 
			
		||||
    gint type;
 | 
			
		||||
    gint flags;
 | 
			
		||||
    gint fd;
 | 
			
		||||
    gsize buffer_size;
 | 
			
		||||
} TestFmqData;
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static const TestFmqData test_fmq_tests_null[] = {
 | 
			
		||||
    { "wrong_size", 0, 8,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 },
 | 
			
		||||
    { "wrong_count", sizeof(guint32), 0,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 },
 | 
			
		||||
    { "wrong_buffer_size", sizeof(guint32), 8,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, 1, 0 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestFmqData* test = test_data;
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items,
 | 
			
		||||
        test->type, test->flags, test->fd, test->buffer_size);
 | 
			
		||||
 | 
			
		||||
    g_assert(!fmq);
 | 
			
		||||
    g_assert(!gbinder_fmq_ref(fmq));
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == 0);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write(fmq) == 0);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 0);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_fmq_begin_read(fmq, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_begin_read((GBinderFmq*)0x1, 0));
 | 
			
		||||
    g_assert(!gbinder_fmq_begin_write(fmq, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_begin_write((GBinderFmq*)0x1, 0));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_fmq_read(fmq, (void*)0x1, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_read((GBinderFmq*)0x1, NULL, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_read((GBinderFmq*)0x1, (void*)0x1, 0));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_fmq_write(fmq, (const void*)0x1, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_write((GBinderFmq*)0x1, NULL, 1));
 | 
			
		||||
    g_assert(!gbinder_fmq_write((GBinderFmq*)0x1, (const void*)0x1, 0));
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_fmq_wait_timeout(fmq, 0, (guint32*)0x1, 0) == -EINVAL);
 | 
			
		||||
    g_assert(gbinder_fmq_wait_timeout((GBinderFmq*)0x1, 0, NULL, 0) == -EINVAL);
 | 
			
		||||
    g_assert(gbinder_fmq_wait(fmq, 0, (guint32*)0x1) == -EINVAL);
 | 
			
		||||
    g_assert(gbinder_fmq_wait((GBinderFmq*)0x1, 0, NULL) == -EINVAL);
 | 
			
		||||
    g_assert(gbinder_fmq_wake(fmq, 0) == -EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * read/write guint8
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static const TestFmqData test_fmq_tests_read_write_guint8[] = {
 | 
			
		||||
    { "event_flag", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 },
 | 
			
		||||
    { "no_event_flag", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        0, -1, 0 },
 | 
			
		||||
    { "no_reset", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG |
 | 
			
		||||
        GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 },
 | 
			
		||||
    { "unsync", sizeof(guint8), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 },
 | 
			
		||||
    { "unsync_no_reset", sizeof(guint8), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG |
 | 
			
		||||
        GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_read_write_guint8(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestFmqData* test = test_data;
 | 
			
		||||
    guint i;
 | 
			
		||||
    guint8 in_data[test->max_num_items];
 | 
			
		||||
    guint8 out_data[test->max_num_items];
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items,
 | 
			
		||||
        test->type, test->flags, test->fd, test->buffer_size);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Intiailize input data with random numbers */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        in_data[i] = g_random_int() % G_MAXUINT8;
 | 
			
		||||
    }
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Write data one value at a time */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, &in_data[i], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == i + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Try to write one item to full buffer
 | 
			
		||||
     * only sync write fails if buffer is full */
 | 
			
		||||
    if (test->type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) {
 | 
			
		||||
        g_assert(!gbinder_fmq_write(fmq, &in_data[0], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Read data one value at a time */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(out_data[0] == in_data[i]);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) ==
 | 
			
		||||
            test->max_num_items - i - 1);
 | 
			
		||||
    }
 | 
			
		||||
    /* Try to read when there is no data */
 | 
			
		||||
    g_assert(!gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Fill whole buffer with data */
 | 
			
		||||
    g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items);
 | 
			
		||||
    /* Read whole buffer */
 | 
			
		||||
    g_assert(gbinder_fmq_read(fmq, &out_data, test->max_num_items));
 | 
			
		||||
    g_assert(!memcmp(in_data, out_data, test->max_num_items * test->item_size));
 | 
			
		||||
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Try to write too many items */
 | 
			
		||||
    g_assert(!gbinder_fmq_write(fmq, in_data, test->max_num_items + 1));
 | 
			
		||||
 | 
			
		||||
    /* Overwrite unsync queue */
 | 
			
		||||
    if (test->type == GBINDER_FMQ_TYPE_UNSYNC_WRITE) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items));
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, in_data, 1));
 | 
			
		||||
        /* Writing too much data to unsync queue causes read pointer to reset
 | 
			
		||||
         * on next read */
 | 
			
		||||
        g_assert(!gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * read/write gint64
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static const TestFmqData test_fmq_tests_read_write_gint64[] = {
 | 
			
		||||
    { "event_flag", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 },
 | 
			
		||||
    { "no_event_flag", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        0, -1, 0 },
 | 
			
		||||
    { "no_reset", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG |
 | 
			
		||||
        GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 },
 | 
			
		||||
    { "unsync", sizeof(gint64), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 },
 | 
			
		||||
    { "unsync_no_reset", sizeof(guint64), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG |
 | 
			
		||||
        GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_read_write_gint64(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestFmqData* test = test_data;
 | 
			
		||||
    guint i;
 | 
			
		||||
    gint64 in_data[test->max_num_items];
 | 
			
		||||
    gint64 out_data[test->max_num_items];
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items,
 | 
			
		||||
        test->type, test->flags, test->fd, test->buffer_size);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Intiailize input data with random numbers */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        in_data[i] = g_random_int();
 | 
			
		||||
    }
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Write data one value at a time */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, &in_data[i], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == i + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Try to write one item to full buffer
 | 
			
		||||
     * only sync write fails if buffer is full */
 | 
			
		||||
    if (test->type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) {
 | 
			
		||||
        g_assert(!gbinder_fmq_write(fmq, &in_data[0], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Read data one value at a time */
 | 
			
		||||
    for (i = 0; i < test->max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(out_data[0] == in_data[i]);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) ==
 | 
			
		||||
            test->max_num_items - i - 1);
 | 
			
		||||
    }
 | 
			
		||||
    /* Try to read when there is no data */
 | 
			
		||||
    g_assert(!gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Fill whole buffer with data */
 | 
			
		||||
    g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items);
 | 
			
		||||
    /* Read whole buffer */
 | 
			
		||||
    g_assert(gbinder_fmq_read(fmq, &out_data, test->max_num_items));
 | 
			
		||||
    g_assert(!memcmp(in_data, out_data, test->max_num_items * test->item_size));
 | 
			
		||||
 | 
			
		||||
    memset(out_data, 0, test->max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Try to write too many items */
 | 
			
		||||
    g_assert(!gbinder_fmq_write(fmq, in_data, test->max_num_items + 1));
 | 
			
		||||
 | 
			
		||||
    /* Overwrite unsync queue */
 | 
			
		||||
    if (test->type == GBINDER_FMQ_TYPE_UNSYNC_WRITE) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items));
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, in_data, 1));
 | 
			
		||||
        /* Writing too much data to unsync queue causes read pointer to reset
 | 
			
		||||
         * on next read */
 | 
			
		||||
        g_assert(!gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * read/write counters
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_read_write_counters(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    gint max_num_items = 8;
 | 
			
		||||
    gint write_count = 6;
 | 
			
		||||
    gint64 in_data[max_num_items];
 | 
			
		||||
    gint64 out_data[max_num_items];
 | 
			
		||||
    guint i;
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), max_num_items,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Intiailize input data with random numbers */
 | 
			
		||||
    for (i = 0; i < max_num_items; ++i) {
 | 
			
		||||
        in_data[i] = g_random_int();
 | 
			
		||||
    }
 | 
			
		||||
    memset(out_data, 0, max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Write data one value at a time */
 | 
			
		||||
    for (i = 0; i < write_count; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, &in_data[i], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == i + 1);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_write(fmq) ==
 | 
			
		||||
            max_num_items - i - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Read data one value at a time */
 | 
			
		||||
    for (i = 0; i < 2; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(out_data[0] == in_data[i]);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) ==
 | 
			
		||||
            write_count - i - 1);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_write(fmq) ==
 | 
			
		||||
            max_num_items - write_count + i + 1);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_write_contiguous(fmq) ==
 | 
			
		||||
            max_num_items - write_count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_fmq_read(fmq, &out_data, 2));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == 2);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write(fmq) == 6);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 2);
 | 
			
		||||
    g_assert(gbinder_fmq_write(fmq, in_data, 4));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == 6);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 4);
 | 
			
		||||
    g_assert(gbinder_fmq_read(fmq, &out_data, 6));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == 0);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 0);
 | 
			
		||||
    g_assert(gbinder_fmq_write(fmq, in_data, 6));
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == 6);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 6);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write(fmq) == 2);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 2);
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * read/write external fd
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_read_write_external_fd(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    gint max_num_items = 8;
 | 
			
		||||
    gint64 in_data[max_num_items];
 | 
			
		||||
    gint64 out_data[max_num_items];
 | 
			
		||||
    gsize item_size = sizeof(gint64);
 | 
			
		||||
    guint i;
 | 
			
		||||
    GBinderFmq* fmq;
 | 
			
		||||
 | 
			
		||||
    /* Allocate shared memory */
 | 
			
		||||
 | 
			
		||||
    int shmem_fd = syscall(__NR_memfd_create, "MessageQueue", MFD_CLOEXEC);
 | 
			
		||||
 | 
			
		||||
    g_assert(shmem_fd >= 0);
 | 
			
		||||
    g_assert(ftruncate(shmem_fd, max_num_items * item_size) >= 0);
 | 
			
		||||
 | 
			
		||||
    fmq = gbinder_fmq_new(item_size, max_num_items,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG,
 | 
			
		||||
        shmem_fd, max_num_items * item_size);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Intiailize input data with random numbers */
 | 
			
		||||
    for (i = 0; i < max_num_items; ++i) {
 | 
			
		||||
        in_data[i] = g_random_int();
 | 
			
		||||
    }
 | 
			
		||||
    memset(out_data, 0, max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* Write data one value at a time */
 | 
			
		||||
    for (i = 0; i < max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_write(fmq, &in_data[i], 1));
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == i + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Read data one value at a time */
 | 
			
		||||
    for (i = 0; i < max_num_items; ++i) {
 | 
			
		||||
        g_assert(gbinder_fmq_read(fmq, &out_data, 1));
 | 
			
		||||
        g_assert(out_data[0] == in_data[i]);
 | 
			
		||||
        g_assert(gbinder_fmq_available_to_read(fmq) == max_num_items - i - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * ref/unref
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_ref(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), 2,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
    g_assert(gbinder_fmq_ref(fmq) == fmq);
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * wait/wake
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_wait_wake(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    gint ms = 100;
 | 
			
		||||
    guint32 state = 0;
 | 
			
		||||
    int result = 0;
 | 
			
		||||
 | 
			
		||||
    /* Queue with event flag */
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), 2,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Wait until timeout */
 | 
			
		||||
    g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms), ==,
 | 
			
		||||
        -ETIMEDOUT);
 | 
			
		||||
 | 
			
		||||
    /* Invalid bit mask */
 | 
			
		||||
    g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x0, &state, ms),==,-EINVAL);
 | 
			
		||||
 | 
			
		||||
    /* Bit already set */
 | 
			
		||||
    result = gbinder_fmq_wake(fmq, 0x4);
 | 
			
		||||
    g_assert(result == 0 || result == -ENOSYS);
 | 
			
		||||
    /* Only run wake/wait tests if FUTEX_WAKE_BITSET is supported */
 | 
			
		||||
    if (result == 0) {
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_wait(fmq, 0x4, &state),==,0);
 | 
			
		||||
        g_assert_cmpuint(state,==,0x4);
 | 
			
		||||
 | 
			
		||||
        /* Bit already set, wait with more generic bit mask */
 | 
			
		||||
        state = 0;
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,0);
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_wait(fmq, 0xf, &state),==,0);
 | 
			
		||||
        g_assert_cmpuint(state,==,0x4);
 | 
			
		||||
 | 
			
		||||
        /* Bit already set, wait with different bit mask */
 | 
			
		||||
        state = 0;
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,0);
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms), ==,
 | 
			
		||||
            -ETIMEDOUT);
 | 
			
		||||
        g_assert_cmpint(gbinder_fmq_try_wait(fmq, 0x2, &state), == ,
 | 
			
		||||
            -ETIMEDOUT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Queue without event flag */
 | 
			
		||||
    fmq = gbinder_fmq_new(sizeof(gint64), 2, GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        0, -1, 0);
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
    g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms),==,-ENOSYS);
 | 
			
		||||
    g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,-ENOSYS);
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * zero copy
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_zero_copy(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    gint max_num_items = 8;
 | 
			
		||||
    gint write_count = 2;
 | 
			
		||||
    gint64 in_data[max_num_items];
 | 
			
		||||
    gint64 out_data[max_num_items];
 | 
			
		||||
    gsize item_size = sizeof(gint64);
 | 
			
		||||
    guint i;
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(item_size, max_num_items,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG,
 | 
			
		||||
        -1, 0);
 | 
			
		||||
    const void *read_ptr;
 | 
			
		||||
    void *write_ptr;
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
 | 
			
		||||
    /* Intiailize input data with random numbers */
 | 
			
		||||
    for (i = 0; i < max_num_items; ++i) {
 | 
			
		||||
        in_data[i] = g_random_int();
 | 
			
		||||
    }
 | 
			
		||||
    memset(out_data, 0, max_num_items);
 | 
			
		||||
 | 
			
		||||
    /* external write */
 | 
			
		||||
    g_assert_nonnull((write_ptr = gbinder_fmq_begin_write(fmq, write_count)));
 | 
			
		||||
    memcpy(write_ptr, in_data, write_count * item_size);
 | 
			
		||||
    gbinder_fmq_end_write(fmq, write_count);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_read(fmq) == write_count);
 | 
			
		||||
    g_assert(gbinder_fmq_available_to_write(fmq) ==
 | 
			
		||||
        (max_num_items - write_count));
 | 
			
		||||
 | 
			
		||||
    /* external read */
 | 
			
		||||
    g_assert_nonnull((read_ptr = gbinder_fmq_begin_read(fmq, write_count)));
 | 
			
		||||
    memcpy(out_data, read_ptr, write_count * item_size);
 | 
			
		||||
    gbinder_fmq_end_read(fmq, write_count);
 | 
			
		||||
 | 
			
		||||
    g_assert(!memcmp(in_data, out_data, write_count * item_size));
 | 
			
		||||
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/fmq/"
 | 
			
		||||
#define TEST_(t) TEST_PREFIX t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
    guint i;
 | 
			
		||||
    int test_fd;
 | 
			
		||||
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    gbinder_log.level = gutil_log_default.level;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_null); i++) {
 | 
			
		||||
        const TestFmqData* test = test_fmq_tests_null + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX, test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_null);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Some test environments don't know how handle this syscall */
 | 
			
		||||
    test_fd = syscall(__NR_memfd_create, "test", MFD_CLOEXEC);
 | 
			
		||||
    if (test_fd < 0 && errno == ENOSYS) {
 | 
			
		||||
        GINFO("Skipping tests that rely on memfd_create");
 | 
			
		||||
    } else {
 | 
			
		||||
        close(test_fd);
 | 
			
		||||
        for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_read_write_guint8); i++) {
 | 
			
		||||
            const TestFmqData* test = test_fmq_tests_read_write_guint8 + i;
 | 
			
		||||
            char* path = g_strconcat(TEST_("guint8/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
            g_test_add_data_func(path, test, test_read_write_guint8);
 | 
			
		||||
            g_free(path);
 | 
			
		||||
        }
 | 
			
		||||
        for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_read_write_gint64); i++) {
 | 
			
		||||
            const TestFmqData* test = test_fmq_tests_read_write_gint64 + i;
 | 
			
		||||
            char* path = g_strconcat(TEST_("gint64/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
            g_test_add_data_func(path, test, test_read_write_gint64);
 | 
			
		||||
            g_free(path);
 | 
			
		||||
        }
 | 
			
		||||
        g_test_add_func(TEST_("read_write_counters"), test_read_write_counters);
 | 
			
		||||
        g_test_add_func(TEST_("read_write_external_fd"),
 | 
			
		||||
            test_read_write_external_fd);
 | 
			
		||||
        g_test_add_func(TEST_("ref"), test_ref);
 | 
			
		||||
        g_test_add_func(TEST_("wait_wake"), test_wait_wake);
 | 
			
		||||
        g_test_add_func(TEST_("zero_copy"), test_zero_copy);
 | 
			
		||||
    }
 | 
			
		||||
#else /* GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2022 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -120,6 +120,15 @@ test_basic_find_none(
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_basic_find(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    return obj == user_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
@@ -127,6 +136,7 @@ test_basic(
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
 | 
			
		||||
    g_assert(ipc);
 | 
			
		||||
    g_assert(ipc2);
 | 
			
		||||
@@ -136,6 +146,12 @@ test_basic(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_find_local_object(NULL, test_basic_find_none, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL));
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL));
 | 
			
		||||
    g_assert(gbinder_ipc_find_local_object(ipc, test_basic_find, obj) == obj);
 | 
			
		||||
    gbinder_local_object_unref(obj); /* Above call added a reference */
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
 | 
			
		||||
    /* Second gbinder_ipc_new returns the same (default) object */
 | 
			
		||||
    g_assert(gbinder_ipc_new(NULL) == ipc);
 | 
			
		||||
@@ -756,7 +772,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 +829,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 +1344,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,7 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_local_reply_append_hidl_string_vec(NULL, NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_local_reply_append_local_object(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_append_remote_object(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_append_fd(NULL, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -170,6 +171,31 @@ test_bool(
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * fd
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const gint32 fd = 1;
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(&gbinder_io_32);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_local_reply_append_fd(reply, fd));
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int32
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -504,10 +530,14 @@ 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);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bool", test_bool);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "fd", test_fd);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int32", test_int32);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int64", test_int64);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "float", test_float);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ static const char TMP_DIR_TEMPLATE[] = "gbinder-test-protocol-XXXXXX";
 | 
			
		||||
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
 | 
			
		||||
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
 | 
			
		||||
#define UNSET_WORK_SOURCE (-1)
 | 
			
		||||
#define BINDER_SYS_HEADER GBINDER_FOURCC('S', 'Y', 'S', 'T')
 | 
			
		||||
 | 
			
		||||
typedef struct test_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
@@ -80,6 +81,15 @@ static const guint8 test_header_aidl2 [] = {
 | 
			
		||||
    TEST_INT16_BYTES('o'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 test_header_aidl3 [] = {
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_RPC_FLAGS),
 | 
			
		||||
    TEST_INT32_BYTES(UNSET_WORK_SOURCE),
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_SYS_HEADER),
 | 
			
		||||
    TEST_INT32_BYTES(3),
 | 
			
		||||
    TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'),
 | 
			
		||||
    TEST_INT16_BYTES('o'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 test_header_hidl [] = {
 | 
			
		||||
    'f', 'o', 'o', 0x00
 | 
			
		||||
};
 | 
			
		||||
@@ -97,6 +107,14 @@ static const TestHeaderData test_header_tests[] = {
 | 
			
		||||
      test_header_aidl2, 5 }, /* Short packet */
 | 
			
		||||
    { "aidl2/short/3", "adl2", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl2, 9 }, /* Short packet */
 | 
			
		||||
    { "aidl3/ok", "aidl3", GBINDER_DEFAULT_BINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_aidl3) },
 | 
			
		||||
    { "aidl3/short/1", "aidl3", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl3, 1 }, /* Short packet */
 | 
			
		||||
    { "aidl3/short/2", "aidl3", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl3, 5 }, /* Short packet */
 | 
			
		||||
    { "aidl3/short/3", "adl3", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl3, 9 }, /* Short packet */
 | 
			
		||||
    { "hidl/ok", "hidl", GBINDER_DEFAULT_HWBINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_hidl) },
 | 
			
		||||
    { "hidl/short", "hidl", GBINDER_DEFAULT_HWBINDER, NULL,
 | 
			
		||||
@@ -348,6 +366,7 @@ test_no_header2(
 | 
			
		||||
static const TestData test_no_header_data[] = {
 | 
			
		||||
    { "aidl", "aidl", GBINDER_DEFAULT_BINDER },
 | 
			
		||||
    { "aidl2", "aidl2", GBINDER_DEFAULT_BINDER },
 | 
			
		||||
    { "aidl3", "aidl3", GBINDER_DEFAULT_BINDER },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 
 | 
			
		||||
@@ -438,7 +438,7 @@ test_param(
 | 
			
		||||
 | 
			
		||||
typedef struct test_obj_data {
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    GBinderLocalObject* tmp_proxy;
 | 
			
		||||
    GBinderLocalObject* obj2;
 | 
			
		||||
    gboolean obj_call_handled;
 | 
			
		||||
    gboolean obj_call_finished;
 | 
			
		||||
    gboolean obj2_call_handled;
 | 
			
		||||
@@ -539,9 +539,9 @@ test_obj_cb(
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    /* Make sure temporary proxy won't get destroyed too early */
 | 
			
		||||
    test->tmp_proxy = test_binder_object(gbinder_driver_fd(obj->ipc->driver),
 | 
			
		||||
    test->obj2 = test_binder_object(gbinder_driver_fd(obj->ipc->driver),
 | 
			
		||||
        obj2->handle);
 | 
			
		||||
    g_assert(test->tmp_proxy);
 | 
			
		||||
    g_assert(test->obj2);
 | 
			
		||||
 | 
			
		||||
    /* Call remote object */
 | 
			
		||||
    client2 = gbinder_client_new(obj2, TEST_IFACE2);
 | 
			
		||||
@@ -599,39 +599,54 @@ test_obj_run(
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderRemoteObject* remote_proxy;
 | 
			
		||||
    GBinderClient* proxy_client;
 | 
			
		||||
    GBinderIpc* ipc_remote_obj;
 | 
			
		||||
    GBinderIpc* ipc_obj;
 | 
			
		||||
    GBinderIpc* ipc_proxy;
 | 
			
		||||
    GBinderIpc* ipc_remote_proxy;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    int fd_obj, fd_proxy;
 | 
			
		||||
    int fd_remote_obj, fd_obj, fd_proxy, fd_remote_proxy;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    ipc_proxy = gbinder_ipc_new(DEV);
 | 
			
		||||
    ipc_obj = gbinder_ipc_new(DEV_PRIV);
 | 
			
		||||
    fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
 | 
			
		||||
    ipc_remote_obj = gbinder_ipc_new(DEV_PRIV);
 | 
			
		||||
    ipc_obj = gbinder_ipc_new(DEV);
 | 
			
		||||
    ipc_proxy = gbinder_ipc_new(DEV2);
 | 
			
		||||
    ipc_remote_proxy = gbinder_ipc_new(DEV2_PRIV);
 | 
			
		||||
 | 
			
		||||
    fd_remote_obj = gbinder_driver_fd(ipc_remote_obj->driver);
 | 
			
		||||
    fd_obj = gbinder_driver_fd(ipc_obj->driver);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_obj_cb, &test);
 | 
			
		||||
    obj2 = gbinder_local_object_new(ipc_obj, TEST_IFACES2, test_obj2_cb, &test);
 | 
			
		||||
    remote_obj = gbinder_remote_object_new(ipc_proxy,
 | 
			
		||||
    fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
 | 
			
		||||
    fd_remote_proxy = gbinder_driver_fd(ipc_remote_proxy->driver);
 | 
			
		||||
 | 
			
		||||
    obj = gbinder_local_object_new(ipc_remote_obj, TEST_IFACES, test_obj_cb, &test);
 | 
			
		||||
    GDEBUG("obj %p", obj);
 | 
			
		||||
    remote_obj = gbinder_remote_object_new(ipc_obj,
 | 
			
		||||
        test_binder_register_object(fd_obj, obj, AUTO_HANDLE),
 | 
			
		||||
        REMOTE_OBJECT_CREATE_ALIVE);
 | 
			
		||||
 | 
			
		||||
    /* remote_proxy(DEV_PRIV) => proxy (DEV) => obj (DEV) => DEV_PRIV */
 | 
			
		||||
    g_assert(!gbinder_proxy_object_new(NULL, remote_obj));
 | 
			
		||||
    /* remote_proxy(DEV2_PRIV) => proxy (DEV) => obj(DEV2_PRIV) */
 | 
			
		||||
    g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
 | 
			
		||||
    remote_proxy = gbinder_remote_object_new(ipc_obj,
 | 
			
		||||
    GDEBUG("proxy %p", proxy);
 | 
			
		||||
    remote_proxy = gbinder_remote_object_new(ipc_remote_proxy,
 | 
			
		||||
        test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE),
 | 
			
		||||
        REMOTE_OBJECT_CREATE_ALIVE);
 | 
			
		||||
    proxy_client = gbinder_client_new(remote_proxy, TEST_IFACE);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_passthrough(fd_remote_obj, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_obj, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_proxy, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_remote_proxy, TRUE);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_looper_enabled(fd_remote_obj, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_remote_proxy, TEST_LOOPER_ENABLE);
 | 
			
		||||
 | 
			
		||||
    /* Pass object reference via proxy */
 | 
			
		||||
    obj2 = gbinder_local_object_new(ipc_remote_proxy, TEST_IFACES2, test_obj2_cb, &test);
 | 
			
		||||
    GDEBUG("obj2 %p", obj2);
 | 
			
		||||
    req = gbinder_client_new_request(proxy_client);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM1);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj2);
 | 
			
		||||
@@ -646,19 +661,24 @@ test_obj_run(
 | 
			
		||||
    g_assert(test.obj_call_finished);
 | 
			
		||||
    g_assert(test.obj2_call_handled);
 | 
			
		||||
    g_assert(test.obj2_call_finished);
 | 
			
		||||
    g_assert(test.tmp_proxy);
 | 
			
		||||
    gbinder_local_object_unref(test.tmp_proxy);
 | 
			
		||||
    g_assert(test.obj2);
 | 
			
		||||
    gbinder_local_object_unref(test.obj2);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd_remote_obj);
 | 
			
		||||
    test_binder_unregister_objects(fd_obj);
 | 
			
		||||
    test_binder_unregister_objects(fd_proxy);
 | 
			
		||||
    test_binder_unregister_objects(fd_remote_proxy);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_drop(obj);
 | 
			
		||||
    gbinder_local_object_drop(obj2);
 | 
			
		||||
    gbinder_local_object_drop(&proxy->parent);
 | 
			
		||||
    gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
    gbinder_remote_object_unref(remote_proxy);
 | 
			
		||||
    gbinder_client_unref(proxy_client);
 | 
			
		||||
    gbinder_ipc_unref(ipc_remote_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_proxy);
 | 
			
		||||
    gbinder_ipc_unref(ipc_remote_proxy);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
@@ -681,6 +701,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -65,6 +65,27 @@ typedef struct binder_buffer_object_64 {
 | 
			
		||||
#define BUFFER_OBJECT_SIZE_64 (GBINDER_MAX_BUFFER_OBJECT_SIZE)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(BinderObject64) == BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    /* NULL const pointers are tolerated */
 | 
			
		||||
    g_assert(gbinder_reader_at_end(NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_bytes_read(NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_bytes_remaining(NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_get_data(NULL, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_copy(&reader, NULL);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * empty
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -76,14 +97,19 @@ test_empty(
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gsize count = 1, elemsize = 1;
 | 
			
		||||
    gsize len;
 | 
			
		||||
    gsize size = 1;
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, NULL, 0, 0);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_get_data(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_get_data(&reader, &size));
 | 
			
		||||
    g_assert(!size);
 | 
			
		||||
    g_assert(!gbinder_reader_bytes_read(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_bytes_remaining(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_bool(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_int8(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_int16(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_int32(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_uint32(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_int64(&reader, NULL));
 | 
			
		||||
@@ -107,7 +133,7 @@ test_empty(
 | 
			
		||||
    g_assert(!gbinder_reader_read_string8(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_string16(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_skip_string16(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte_array(&reader, &len));
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte_array(&reader, &size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -152,8 +178,8 @@ void
 | 
			
		||||
test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint8 in_true[4] = { 0x01, 0xff, 0xff, 0xff };
 | 
			
		||||
    const guint8 in_false[4] = { 0x00, 0xff, 0xff, 0xff };
 | 
			
		||||
    const guint8 in_true[] = { TEST_INT8_BYTES_4(TRUE) };
 | 
			
		||||
    const guint8 in_false[] = { TEST_INT8_BYTES_4(FALSE) };
 | 
			
		||||
    gboolean out = FALSE;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
@@ -192,6 +218,108 @@ test_bool(
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int8
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_int8(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint8 in = 42;
 | 
			
		||||
    const guint8 in4[] = { TEST_INT8_BYTES_4(42) };
 | 
			
		||||
    guint8 out1 = 0;
 | 
			
		||||
    gint8 out2 = 0;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
 | 
			
		||||
    g_assert(driver);
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
 | 
			
		||||
    /* Not enough data */
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)),
 | 
			
		||||
        sizeof(in), NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in));
 | 
			
		||||
    g_assert(!gbinder_reader_read_uint8(&reader, &out1));
 | 
			
		||||
    g_assert(!gbinder_reader_at_end(&reader));
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
 | 
			
		||||
    /* Enough data */
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, g_memdup(in4, sizeof(in4)),
 | 
			
		||||
        sizeof(in4), NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_uint8(&reader, &out1));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpuint(in, == ,out1);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_int8(&reader, &out2));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(in, == ,out2);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_int8(&reader, NULL));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int16
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_int16(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint16 in = 42;
 | 
			
		||||
    const guint8 in4[] = { TEST_INT16_BYTES_4(42) };
 | 
			
		||||
    guint16 out1 = 0;
 | 
			
		||||
    gint16 out2 = 0;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
 | 
			
		||||
    g_assert(driver);
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
 | 
			
		||||
    /* Not enough data */
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)),
 | 
			
		||||
        sizeof(in), NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in));
 | 
			
		||||
    g_assert(!gbinder_reader_read_uint16(&reader, &out1));
 | 
			
		||||
    g_assert(!gbinder_reader_at_end(&reader));
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
 | 
			
		||||
    /* Enough data */
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, g_memdup(in4, sizeof(in4)),
 | 
			
		||||
        sizeof(in4), NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_uint16(&reader, &out1));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpuint(in, == ,out1);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_int16(&reader, &out2));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(in, == ,out2);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(in4));
 | 
			
		||||
    g_assert(gbinder_reader_read_int16(&reader, NULL));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int32
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -1864,7 +1992,6 @@ test_hidl_string_vec2(
 | 
			
		||||
        TEST_INT64_BYTES(2),
 | 
			
		||||
        TEST_INT64_BYTES(sizeof(GBinderHidlString) +
 | 
			
		||||
            GBINDER_HIDL_STRING_BUFFER_OFFSET)
 | 
			
		||||
                        
 | 
			
		||||
    };
 | 
			
		||||
    static const char* const result[] = { str1, str2, NULL };
 | 
			
		||||
 | 
			
		||||
@@ -1990,7 +2117,6 @@ test_hidl_string_vec5(
 | 
			
		||||
        TEST_INT64_BYTES(sizeof(str2)),
 | 
			
		||||
        TEST_INT64_BYTES(2),
 | 
			
		||||
        TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET)
 | 
			
		||||
                        
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), NULL);
 | 
			
		||||
@@ -2098,9 +2224,9 @@ test_copy(
 | 
			
		||||
    gint32 in_len2 = sizeof(in_data2) - 1;
 | 
			
		||||
    const void* out_data = NULL;
 | 
			
		||||
    gsize out_len = 0;
 | 
			
		||||
    void* tmp;
 | 
			
		||||
    guint8* ptr;
 | 
			
		||||
    gsize tmp_len = 2 * sizeof(guint32) + in_len1 + in_len2;
 | 
			
		||||
    void* buf;
 | 
			
		||||
    gsize buf_len = 2 * sizeof(guint32) + in_len1 + in_len2;
 | 
			
		||||
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
@@ -2109,7 +2235,7 @@ test_copy(
 | 
			
		||||
 | 
			
		||||
    /* test for data */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    ptr = tmp = g_malloc0(tmp_len);
 | 
			
		||||
    ptr = buf = g_malloc0(buf_len);
 | 
			
		||||
    memcpy(ptr, &in_len1, sizeof(in_len1));
 | 
			
		||||
    ptr += sizeof(in_len1);
 | 
			
		||||
    memcpy(ptr, in_data1, in_len1);
 | 
			
		||||
@@ -2119,28 +2245,40 @@ test_copy(
 | 
			
		||||
    memcpy(ptr, in_data2, in_len2);
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, tmp_len, NULL);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, tmp_len);
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, buf, buf_len, NULL);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, buf_len);
 | 
			
		||||
 | 
			
		||||
    /* Read the first array */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert((gsize)in_len1 == out_len);
 | 
			
		||||
    g_assert_cmpuint(in_len1, == ,out_len);
 | 
			
		||||
    g_assert(memcmp(in_data1, out_data, in_len1) == 0);
 | 
			
		||||
 | 
			
		||||
    /* Fetch raw data */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_get_data(&reader, &out_len)));
 | 
			
		||||
    g_assert(out_data == gbinder_reader_get_data(&reader, NULL));
 | 
			
		||||
    g_assert_cmpuint(out_len, == ,buf_len);
 | 
			
		||||
    g_assert(memcmp(out_data, buf, buf_len) == 0);
 | 
			
		||||
 | 
			
		||||
    /* Copy the reader */
 | 
			
		||||
    gbinder_reader_copy(&reader2, &reader);
 | 
			
		||||
 | 
			
		||||
    /* Read both and compare the output */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert((gsize)in_len2 == out_len);
 | 
			
		||||
    g_assert_cmpuint(in_len1, == ,out_len);
 | 
			
		||||
    g_assert(memcmp(in_data2, out_data, in_len2) == 0);
 | 
			
		||||
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader2, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader2));
 | 
			
		||||
    g_assert((gsize)in_len2 == out_len);
 | 
			
		||||
    g_assert_cmpuint(in_len2, == ,out_len);
 | 
			
		||||
    g_assert(memcmp(in_data2, out_data, in_len2) == 0);
 | 
			
		||||
 | 
			
		||||
    /* Fetch raw data from the copied reader */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_get_data(&reader2, &out_len)));
 | 
			
		||||
    g_assert(out_data == gbinder_reader_get_data(&reader2, NULL));
 | 
			
		||||
    g_assert_cmpuint(out_len, == ,buf_len);
 | 
			
		||||
    g_assert(memcmp(out_data, buf, buf_len) == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
@@ -2156,10 +2294,17 @@ 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_("null"), test_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);
 | 
			
		||||
    g_test_add_func(TEST_("int8"), test_int8);
 | 
			
		||||
    g_test_add_func(TEST_("int16"), test_int16);
 | 
			
		||||
    g_test_add_func(TEST_("int32"), test_int32);
 | 
			
		||||
    g_test_add_func(TEST_("int64"), test_int64);
 | 
			
		||||
    g_test_add_func(TEST_("float"), test_float);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -386,8 +386,17 @@ gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    return TEST_TYPE_DEFSERVICEMANAGER;
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl3_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -403,6 +412,7 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_servicemanager_new_with_type(0, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_new_local_object(NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_device(NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_is_present(NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_wait(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_list(NULL, NULL, NULL));
 | 
			
		||||
@@ -487,6 +497,7 @@ test_basic(
 | 
			
		||||
    obj = gbinder_servicemanager_new_local_object(sm, "foo.bar",
 | 
			
		||||
        test_transact_func, NULL);
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert_cmpstr(gbinder_servicemanager_device(sm), == ,dev);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_servicemanager_ref(sm) == sm);
 | 
			
		||||
@@ -1190,6 +1201,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -50,14 +50,24 @@ static TestOpt test_opt;
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl3_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -595,6 +605,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,8 @@ static const char TMP_DIR_TEMPLATE[] =
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -435,6 +436,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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager_aidl3/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager_aidl3/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_aidl3
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										473
									
								
								unit/unit_servicemanager_aidl3/unit_servicemanager_aidl3.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								unit/unit_servicemanager_aidl3/unit_servicemanager_aidl3.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,473 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_object.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] =
 | 
			
		||||
    "gbinder-test-servicemanager_aidl3-XXXXXX";
 | 
			
		||||
 | 
			
		||||
enum gbinder_stability_level {
 | 
			
		||||
    UNDECLARED = 0,
 | 
			
		||||
    VENDOR = 0b000011,
 | 
			
		||||
    SYSTEM = 0b001100,
 | 
			
		||||
    VINTF = 0b111111
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test service manager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define SVCMGR_HANDLE (0)
 | 
			
		||||
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
 | 
			
		||||
enum servicemanager_aidl_tx {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass ServiceManagerAidl3Class;
 | 
			
		||||
typedef struct service_manager_aidl3 {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
} ServiceManagerAidl3;
 | 
			
		||||
 | 
			
		||||
#define SERVICE_MANAGER_AIDL3_TYPE (service_manager_aidl3_get_type())
 | 
			
		||||
#define SERVICE_MANAGER_AIDL3(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        SERVICE_MANAGER_AIDL3_TYPE, ServiceManagerAidl3))
 | 
			
		||||
G_DEFINE_TYPE(ServiceManagerAidl3, service_manager_aidl3, \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
servicemanager_aidl3_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl3* self = user_data;
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    guint32 allow_isolated, dumpsys_priority;
 | 
			
		||||
    gint32 stability;
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    GDEBUG("%s %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GET_SERVICE_TRANSACTION:
 | 
			
		||||
    case CHECK_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        if (str) {
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            remote_obj = g_hash_table_lookup(self->objects, str);
 | 
			
		||||
            if (remote_obj) {
 | 
			
		||||
                GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
                GDEBUG("Found name '%s' => %p", str, remote_obj);
 | 
			
		||||
                gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
                gbinder_writer_append_int32(&writer, UNDECLARED);
 | 
			
		||||
                gbinder_writer_append_remote_object(&writer, remote_obj);
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Name '%s' not found", str);
 | 
			
		||||
                gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
            }
 | 
			
		||||
            g_free(str);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case ADD_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        remote_obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
        gbinder_reader_read_int32(&reader, &stability);
 | 
			
		||||
        if (str && remote_obj && stability == 0b001100 &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &allow_isolated) &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
 | 
			
		||||
            GDEBUG("Adding '%s'", str);
 | 
			
		||||
            g_hash_table_replace(self->objects, str, remote_obj);
 | 
			
		||||
            remote_obj = NULL;
 | 
			
		||||
            str = NULL;
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            *status = GBINDER_STATUS_OK;
 | 
			
		||||
        }
 | 
			
		||||
        g_free(str);
 | 
			
		||||
        gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIST_SERVICES_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        if (gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
 | 
			
		||||
            if (g_hash_table_size(self->objects) == 1) {
 | 
			
		||||
                GList* keys = g_hash_table_get_keys(self->objects);
 | 
			
		||||
                GList* l = g_list_nth(keys, 0);
 | 
			
		||||
                gint32 srv_size = 1;
 | 
			
		||||
                GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
                reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
                gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
                gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK);
 | 
			
		||||
                gbinder_writer_append_int32(&writer, srv_size);
 | 
			
		||||
                gbinder_writer_append_string16(&writer, l->data);
 | 
			
		||||
                g_list_free(keys);
 | 
			
		||||
                *status = GBINDER_STATUS_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Incorrect number of services %u",
 | 
			
		||||
                    g_hash_table_size(self->objects));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
ServiceManagerAidl3*
 | 
			
		||||
servicemanager_aidl3_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl3* self = g_object_new(SERVICE_MANAGER_AIDL3_TYPE, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    self->handle_on_looper_thread = handle_on_looper_thread;
 | 
			
		||||
    gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
 | 
			
		||||
        servicemanager_aidl3_handler, self);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_register_object(fd, obj, SVCMGR_HANDLE);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
service_manager_aidl3_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl3* self = SERVICE_MANAGER_AIDL3(object);
 | 
			
		||||
 | 
			
		||||
    if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
 | 
			
		||||
        return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl3_parent_class)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
service_manager_aidl3_handle_looper_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl3_parent_class)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl3_parent_class)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl3_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl3* self = SERVICE_MANAGER_AIDL3(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    G_OBJECT_CLASS(service_manager_aidl3_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl3_init(
 | 
			
		||||
    ServiceManagerAidl3* self)
 | 
			
		||||
{
 | 
			
		||||
    self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 | 
			
		||||
        (GDestroyNotify) gbinder_remote_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl3_class_init(
 | 
			
		||||
    ServiceManagerAidl3Class* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = service_manager_aidl3_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        service_manager_aidl3_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        service_manager_aidl3_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test context
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_context {
 | 
			
		||||
    const char* default_config_dir;
 | 
			
		||||
    const char* default_config_file;
 | 
			
		||||
    char* config_dir;
 | 
			
		||||
    char* config_subdir;
 | 
			
		||||
    char* config_file;
 | 
			
		||||
    GBinderLocalObject* object;
 | 
			
		||||
    ServiceManagerAidl3* service;
 | 
			
		||||
    GBinderServiceManager* client;
 | 
			
		||||
    int fd;
 | 
			
		||||
} TestContext;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_context_init(
 | 
			
		||||
    TestContext* test)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    /*
 | 
			
		||||
     * Also set defaults so that both /dev/binder and /dev/binder-private
 | 
			
		||||
     * use the same protocol.
 | 
			
		||||
     */
 | 
			
		||||
    const char* config =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "Default = aidl3\n"
 | 
			
		||||
        "/dev/binder = aidl3\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "Default = aidl3\n"
 | 
			
		||||
        "/dev/binder = aidl3\n";
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
 | 
			
		||||
    memset(test, 0, sizeof(*test));
 | 
			
		||||
    test->default_config_dir = gbinder_config_dir;
 | 
			
		||||
    test->default_config_file = gbinder_config_file;
 | 
			
		||||
    test->config_dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    test->config_subdir = g_build_filename(test->config_dir, "d", NULL);
 | 
			
		||||
    test->config_file = g_build_filename(test->config_dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(test->config_file, config, -1, NULL));
 | 
			
		||||
    GDEBUG("Config file %s", test->config_file);
 | 
			
		||||
    gbinder_config_dir = test->config_subdir; /* Doesn't exist */
 | 
			
		||||
    gbinder_config_file = test->config_file;
 | 
			
		||||
 | 
			
		||||
    ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    test->fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(test->fd, test->object, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(test->fd, TRUE);
 | 
			
		||||
 | 
			
		||||
    test->service = servicemanager_aidl3_new(other_dev, TRUE);
 | 
			
		||||
    test->client = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_context_deinit(
 | 
			
		||||
    TestContext* test)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_unregister_objects(test->fd);
 | 
			
		||||
    gbinder_local_object_unref(test->object);
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(test->service));
 | 
			
		||||
    gbinder_servicemanager_unref(test->client);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, NULL);
 | 
			
		||||
    remove(test->config_file);
 | 
			
		||||
    remove(test->config_dir);
 | 
			
		||||
    g_free(test->config_file);
 | 
			
		||||
    g_free(test->config_subdir);
 | 
			
		||||
    g_free(test->config_dir);
 | 
			
		||||
    gbinder_config_dir = test->default_config_dir;
 | 
			
		||||
    gbinder_config_file = test->default_config_file;
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_run()
 | 
			
		||||
{
 | 
			
		||||
    TestContext test;
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    int status = -1;
 | 
			
		||||
 | 
			
		||||
    test_context_init(&test);
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(!gbinder_servicemanager_get_service_sync(test.client,
 | 
			
		||||
        name, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    /* Register object */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, test.object);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
 | 
			
		||||
        name, test.object), == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1);
 | 
			
		||||
    g_assert(g_hash_table_contains(test.service->objects, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(test.client, name,
 | 
			
		||||
        &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    test_context_deinit(&test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_get_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list_run()
 | 
			
		||||
{
 | 
			
		||||
    TestContext test;
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    char** list;
 | 
			
		||||
 | 
			
		||||
    test_context_init(&test);
 | 
			
		||||
 | 
			
		||||
    /* Request the list */
 | 
			
		||||
    list = gbinder_servicemanager_list_sync(test.client);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(list);
 | 
			
		||||
    g_assert(!list[0]);
 | 
			
		||||
    g_strfreev(list);
 | 
			
		||||
 | 
			
		||||
    /* Register object */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, test.object);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
 | 
			
		||||
        name, test.object), == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    list = gbinder_servicemanager_list_sync(test.client);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(list[0], == ,name);
 | 
			
		||||
    g_strfreev(list);
 | 
			
		||||
 | 
			
		||||
    test_context_deinit(&test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_list_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_aidl3/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
 | 
			
		||||
    g_type_init();
 | 
			
		||||
    G_GNUC_END_IGNORE_DEPRECATIONS;
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -61,14 +61,24 @@ typedef struct test_config {
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl object */
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl3_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -464,6 +474,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -234,6 +234,12 @@ gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl3_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
@@ -520,6 +526,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -200,6 +200,12 @@ gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl3_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
@@ -477,6 +483,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -32,14 +32,17 @@
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_fmq_p.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
#include "gbinder_writer_p.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_intarray.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
@@ -58,8 +61,13 @@ test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    gsize size = 1;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(NULL, &writer);
 | 
			
		||||
    gbinder_writer_append_int8(NULL, 0);
 | 
			
		||||
    gbinder_writer_append_int8(&writer, 0);
 | 
			
		||||
    gbinder_writer_append_int16(NULL, 0);
 | 
			
		||||
    gbinder_writer_append_int16(&writer, 0);
 | 
			
		||||
    gbinder_writer_append_int32(NULL, 0);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, 0);
 | 
			
		||||
    gbinder_writer_append_int64(NULL, 0);
 | 
			
		||||
@@ -84,6 +92,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);
 | 
			
		||||
@@ -101,12 +110,23 @@ test_null(
 | 
			
		||||
    gbinder_writer_add_cleanup(NULL, g_free, 0);
 | 
			
		||||
    gbinder_writer_overwrite_int32(NULL, 0, 0);
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
    gbinder_writer_append_fmq_descriptor(NULL, NULL);
 | 
			
		||||
    gbinder_writer_append_fmq_descriptor(&writer, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_writer_bytes_written(NULL));
 | 
			
		||||
    g_assert(!gbinder_writer_get_data(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_writer_get_data(NULL, &size));
 | 
			
		||||
    g_assert_cmpuint(size, ==, 0);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(NULL));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(NULL));
 | 
			
		||||
    g_assert(!gbinder_writer_malloc(NULL, 0));
 | 
			
		||||
    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 +148,78 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int8
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_int8(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char encoded[] = {
 | 
			
		||||
        TEST_INT8_BYTES_4(0x80)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_int8(&writer, 0x80);
 | 
			
		||||
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert_cmpuint(data->bytes->len, == ,sizeof(encoded));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, encoded, data->bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * int16
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_int16(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char encoded[] = {
 | 
			
		||||
        TEST_INT16_BYTES_4(0x80ff)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_int16(&writer, 0x80ff);
 | 
			
		||||
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert_cmpuint(data->bytes->len, == ,sizeof(encoded));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, encoded, data->bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -173,11 +251,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -263,9 +341,9 @@ test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char encoded[] = {
 | 
			
		||||
        0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00
 | 
			
		||||
        TEST_INT8_BYTES_4(0),
 | 
			
		||||
        TEST_INT8_BYTES_4(1),
 | 
			
		||||
        TEST_INT8_BYTES_4(1)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
@@ -551,12 +629,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 +648,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 +659,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 +671,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(
 | 
			
		||||
@@ -936,6 +1036,49 @@ test_byte_array(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * fmq descriptor
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fmq_descriptor(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    const gint32 len = 3 * BUFFER_OBJECT_SIZE_64 /* Buffer objects */
 | 
			
		||||
        + sizeof(gint64) /* gint64 */
 | 
			
		||||
        + 4 * sizeof(gint64); /* binder_fd_array_object */
 | 
			
		||||
 | 
			
		||||
    GBinderFmq* fmq = gbinder_fmq_new(sizeof(guint32), 5,
 | 
			
		||||
        GBINDER_FMQ_TYPE_SYNC_READ_WRITE,
 | 
			
		||||
        GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(fmq);
 | 
			
		||||
    req = gbinder_local_request_new(&gbinder_io_64, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_fmq_descriptor(&writer, fmq);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert_cmpuint(offsets->count, == ,4);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
    g_assert(offsets->data[2] == 2 * BUFFER_OBJECT_SIZE_64 + sizeof(gint64));
 | 
			
		||||
    g_assert(offsets->data[3] == 3 * BUFFER_OBJECT_SIZE_64 + sizeof(gint64));
 | 
			
		||||
    g_assert_cmpuint(data->bytes->len, == ,len);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_fmq_unref(fmq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * bytes_written
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -948,14 +1091,22 @@ test_bytes_written(
 | 
			
		||||
    const guint32 value = 1234567;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    const void* data;
 | 
			
		||||
    gsize size = 0;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    g_assert(gbinder_writer_bytes_written(&writer) == 0);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, value);
 | 
			
		||||
    g_assert(gbinder_writer_bytes_written(&writer) == sizeof(value));
 | 
			
		||||
    
 | 
			
		||||
    g_assert_cmpuint(gbinder_writer_bytes_written(&writer), == ,sizeof(value));
 | 
			
		||||
    data = gbinder_writer_get_data(&writer, NULL);
 | 
			
		||||
    g_assert(data);
 | 
			
		||||
    g_assert(data == gbinder_writer_get_data(&writer, &size));
 | 
			
		||||
    g_assert_cmpuint(size, == ,sizeof(value));
 | 
			
		||||
    g_assert(!memcmp(data, &value, size));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -970,6 +1121,8 @@ int main(int argc, char* argv[])
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("cleanup"), test_cleanup);
 | 
			
		||||
    g_test_add_func(TEST_("int8"), test_int8);
 | 
			
		||||
    g_test_add_func(TEST_("int16"), test_int16);
 | 
			
		||||
    g_test_add_func(TEST_("int32"), test_int32);
 | 
			
		||||
    g_test_add_func(TEST_("int64"), test_int64);
 | 
			
		||||
    g_test_add_func(TEST_("float"), test_float);
 | 
			
		||||
@@ -1006,9 +1159,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++) {
 | 
			
		||||
@@ -1028,6 +1184,20 @@ int main(int argc, char* argv[])
 | 
			
		||||
    g_test_add_func(TEST_("remote_object"), test_remote_object);
 | 
			
		||||
    g_test_add_func(TEST_("byte_array"), test_byte_array);
 | 
			
		||||
    g_test_add_func(TEST_("bytes_written"), test_bytes_written);
 | 
			
		||||
 | 
			
		||||
#if GBINDER_FMQ_SUPPORTED
 | 
			
		||||
    {
 | 
			
		||||
        int test_fd = syscall(__NR_memfd_create, "test", MFD_CLOEXEC);
 | 
			
		||||
 | 
			
		||||
        if (test_fd < 0 && errno == ENOSYS) {
 | 
			
		||||
            GINFO("Skipping tests that rely on memfd_create");
 | 
			
		||||
        } else {
 | 
			
		||||
            close(test_fd);
 | 
			
		||||
            g_test_add_func(TEST_("fmq_descriptor"), test_fmq_descriptor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif /* GBINDER_FMQ_SUPPORTED */
 | 
			
		||||
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user